diff options
author | Ratakor <ratakor@disroot.org> | 2023-07-03 02:14:16 +0200 |
---|---|---|
committer | Ratakor <ratakor@disroot.org> | 2023-07-03 02:14:16 +0200 |
commit | 71293eaa1598c54a410537e7185a0a705f7298ac (patch) | |
tree | d964b9c0dac91d049562e3e37836e57948feb02e | |
parent | cf0fba532ab4b1d78f49005f324eb6d5a7149240 (diff) |
Update Makefiles + add librev0.0.12
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | Makefile | 51 | ||||
-rw-r--r-- | README.md | 1 | ||||
-rw-r--r-- | libre/LICENSE | 13 | ||||
-rw-r--r-- | libre/Makefile | 24 | ||||
-rw-r--r-- | libre/cthread/cthread.c | 343 | ||||
-rw-r--r-- | libre/cthread/cthread.h | 248 | ||||
-rw-r--r-- | libre/dalloc/dalloc.c | 402 | ||||
-rw-r--r-- | libre/dalloc/dalloc.h | 65 | ||||
-rw-r--r-- | libre/libre.h | 8 | ||||
-rw-r--r-- | libre/util/util.c (renamed from src/util.c) | 23 | ||||
-rw-r--r-- | libre/util/util.h (renamed from src/util.h) | 25 | ||||
-rw-r--r-- | src/Makefile | 56 | ||||
-rw-r--r-- | src/main.c | 4 | ||||
-rw-r--r-- | src/nolan.h | 2 | ||||
-rw-r--r-- | src/ocr.c | 1 |
16 files changed, 1218 insertions, 52 deletions
@@ -1,6 +1,6 @@ +*.o +*.csv tests/ -build/ images/ -*.csv nolan config.h @@ -1,46 +1,35 @@ # Copywrong © 2023 Ratakor. See LICENSE file for license details. -NAME = nolan -PREFIX ?= /usr/local -CC = cc +NAME = nolan +PREFIX ?= /usr/local +CC = cc -BUILD_DIR ?= build -SRC_DIR ?= src +LIBRE_DIR = libre +SRC_DIR = src -SRCS := ${wildcard ${SRC_DIR}/*.c} -OBJS := ${SRCS:%=${BUILD_DIR}/%.o} +CFLAGS ?= -O3 +LDFLAGS ?= -s -DISCORDLIBS = -ldiscord -lcurl -lpthread -TESSLIBS = -ltesseract -lleptonica -GDLIBS = -lgd -lpng -lz -ljpeg -lfreetype -lm +all: build -WARN_FLAGS = -Werror -Wall -Wextra -Wmissing-prototypes -Wpadded\ - -Waggregate-return -Wunused-macros -Wshadow -Wcast-align -CFLAGS += -std=c99 -pedantic -D_DEFAULT_SOURCE ${WARN_FLAGS} -LDFLAGS += -s ${DISCORDLIBS} ${TESSLIBS} ${GDLIBS} - -all: options ${NAME} - -options: +echo: @echo ${NAME} build options: + @echo "CC = ${CC}" + @echo "PREFIX = ${PREFIX}" @echo "CFLAGS = ${CFLAGS}" @echo "LDFLAGS = ${LDFLAGS}" - @echo "CC = ${CC}" - -${BUILD_DIR}/%.c.o: %.c - @mkdir -p ${dir $@} - ${CC} -c ${CFLAGS} $< -o $@ - -${OBJS}: config.h -config.h: - cp config.def.h $@ +build: + @CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}" ${MAKE} -C ${LIBRE_DIR} + @CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}" ${MAKE} -C ${SRC_DIR} -${NAME}: ${OBJS} - ${CC} -o $@ ${OBJS} ${LDFLAGS} +debug: + @${MAKE} -C ${LIBRE_DIR} $@ + @${MAKE} -C ${SRC_DIR} $@ clean: - rm -rf ${NAME} ${BUILD_DIR} + @${MAKE} -C ${LIBRE_DIR} $@ + @${MAKE} -C $(SRC_DIR) $@ install: all mkdir -p ${DESTDIR}${PREFIX}/bin @@ -50,4 +39,4 @@ install: all uninstall: rm -f ${DESTDIR}${PREFIX}/bin/${NAME} -.PHONY: all options clean install uninstall +.PHONY: all echo build debug clean install uninstall @@ -5,6 +5,7 @@ A discord bot for Orna. - [concord](https://github.com/Cogmasters/concord) - [tesseract](https://github.com/tesseract-ocr/tesseract) - [gd](https://github.com/libgd/libgd) +- [libre](https://github.com/ratakor/libre) (included) ## TODO diff --git a/libre/LICENSE b/libre/LICENSE new file mode 100644 index 0000000..f1c9616 --- /dev/null +++ b/libre/LICENSE @@ -0,0 +1,13 @@ +Copyright © 2023, Ratakor <ratakor@disroot.org> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/libre/Makefile b/libre/Makefile new file mode 100644 index 0000000..3fb7c06 --- /dev/null +++ b/libre/Makefile @@ -0,0 +1,24 @@ +OBJS = cthread.o \ + dalloc.o \ + util.o + +DFLAGS = -O0 -g -DDALLOC +CFLAGS += -std=c99 -pthread -D_DEFAULT_SOURCE + +all: ${OBJS} + +echo: + @echo "CC = ${CC}" + @echo "CFLAGS = ${CFLAGS}" + @echo "OBJS = ${OBJS}" + +${OBJS}: + ${CC} ${CFLAGS} -c ${@:.o=}/${@:.o=.c} -o $@ + +debug: + @CFLAGS="${DFLAGS}" ${MAKE} + +clean: + rm -f ${OBJS} + +.PHONY: all echo debug clean diff --git a/libre/cthread/cthread.c b/libre/cthread/cthread.c new file mode 100644 index 0000000..094f455 --- /dev/null +++ b/libre/cthread/cthread.c @@ -0,0 +1,343 @@ +/* + * Copyright © 2023, Ratakor <ratakor@disroot.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "cthread.h" + +#ifdef _WIN32 +#include <stdint.h> +#define WIN32_FAILURE 0 +typedef DWORD WINAPI w_func(LPVOID); +#endif /* _WIN32 */ + +int +cthread_create(cthread_t *thread, cthread_attr_t *attr, void *(*func)(void *), + void *arg) +{ +#ifdef _WIN32 + if (attr == NULL) + *thread = CreateThread(NULL, 0, (w_func *)func, arg, 0, NULL); + else + *thread = CreateThread(NULL, attr->stacksize, (w_func *)func, + arg, attr->w_dwCreationFlags, NULL); + + return *thread == NULL; +#else + pthread_attr_t a; + int ret; + + if ((ret = pthread_attr_init(&a)) != 0) + return ret; + + if (attr != NULL) { + if (attr->stacksize) + pthread_attr_setstacksize(&a, attr->stacksize); + if (attr->p_detachstate) + pthread_attr_setdetachstate(&a, attr->p_detachstate); + if (attr->p_guardsize) + pthread_attr_setguardsize(&a, attr->p_guardsize); +#ifndef __ANDROID__ + if (attr->p_inheritsched) + pthread_attr_setinheritsched(&a, attr->p_inheritsched); +#endif /* __ANDROID__ */ + if (attr->p_schedpolicy) + pthread_attr_setschedpolicy(&a, attr->p_schedpolicy); + if (attr->p_scope) + pthread_attr_setscope(&a, attr->p_scope); + if (attr->p_stack) + pthread_attr_setstack(&a, attr->p_stackaddr, + attr->p_stack); + } + + ret = pthread_create(thread, &a, func, arg); + pthread_attr_destroy(&a); + + return ret; +#endif /* _WIN32 */ +} + +int +cthread_join(cthread_t thread, void **retval) +{ +#ifdef _WIN32 + if (WaitForSingleObject(thread, INFINITE) == WAIT_FAILED) + return 0; + + return GetExitCodeThread(thread, (LPDWORD)retval) == WIN32_FAILURE; +#else + return pthread_join(thread, retval); +#endif /* _WIN32 */ +} + +int +cthread_detach(cthread_t thread) +{ +#ifdef _WIN32 + return CloseHandle(thread) == WIN32_FAILURE; +#else + return pthread_detach(thread); +#endif /* _WIN32 */ +} + +int +cthread_equal(cthread_t t1, cthread_t t2) +{ +#ifdef _WIN32 + return t1 == t2; +#else + return pthread_equal(t1, t2); +#endif /* _WIN32 */ +} + +cthread_t +cthread_self(void) +{ +#ifdef _WIN32 + return GetCurrentThread(); +#else + return pthread_self(); +#endif /* _WIN32 */ +} + +void +cthread_exit(void *retval) +{ +#ifdef _WIN32 + ExitThread((DWORD)(uintptr_t)retval); +#else + pthread_exit(retval); +#endif /* _WIN32 */ +} + +int +cthread_mutex_init(cthread_mutex_t *mutex, cthread_mutexattr_t *attr) +{ +#ifdef _WIN32 + if (attr == NULL) + *mutex = CreateMutex(NULL, FALSE, NULL); + else + *mutex = CreateMutex(NULL, attr->w_bInitialOwner, + attr->w_lpName); + + return *mutex == NULL; +#else + pthread_mutexattr_t a; + int ret; + + if ((ret = pthread_mutexattr_init(&a)) != 0) + return ret; + + if (attr != NULL) { + if (attr->p_pshared) + pthread_mutexattr_setpshared(&a, attr->p_pshared); + if (attr->p_type) + pthread_mutexattr_settype(&a, attr->p_type); +#if (defined __linux__ || defined __FreeBSD__) && !defined __ANDROID__ + if (attr->p_robust) + pthread_mutexattr_setrobust(&a, attr->p_robust); +#endif /* (__linux__ || __FreeBSD__) && !__ANDROID__ */ +#ifndef __ANDROID__ + if (attr->p_protocol) + pthread_mutexattr_setprotocol(&a, attr->p_protocol); + if (attr->p_prioceiling) + pthread_mutexattr_setprioceiling(&a, + attr->p_prioceiling); +#endif /* __ANDROID__ */ + } + + ret = pthread_mutex_init(mutex, &a); + pthread_mutexattr_destroy(&a); + + return ret; +#endif /* _WIN32 */ +} + +int +cthread_mutex_lock(cthread_mutex_t *mutex) +{ +#ifdef _WIN32 + cthread_mutex_t m; + + if (*mutex == CTHREAD_MUTEX_INITIALIZER) { + m = CreateMutex(NULL, FALSE, NULL); + if (InterlockedCompareExchangePointer(mutex, m, NULL) != NULL) + CloseHandle(m); + } + + return WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED; +#else + return pthread_mutex_lock(mutex); +#endif /* _WIN32 */ +} + +int +cthread_mutex_trylock(cthread_mutex_t *mutex) +{ +#ifdef _WIN32 + return WaitForSingleObject(*mutex, 0) == WAIT_FAILED; +#else + return pthread_mutex_trylock(mutex); +#endif /* _WIN32 */ +} + +int +cthread_mutex_unlock(cthread_mutex_t *mutex) +{ +#ifdef _WIN32 + return ReleaseMutex(*mutex) == WIN32_FAILURE; +#else + return pthread_mutex_unlock(mutex); +#endif /* _WIN32 */ +} + +int +cthread_mutex_destroy(cthread_mutex_t *mutex) +{ +#ifdef _WIN32 + return CloseHandle(*mutex) == WIN32_FAILURE; +#else + return pthread_mutex_destroy(mutex); +#endif /* _WIN32 */ +} + +int +cthread_cond_init(cthread_cond_t *cond, cthread_condattr_t *attr) +{ +#ifdef _WIN32 + if (attr == NULL) + *cond = CreateEvent(NULL, FALSE, FALSE, NULL); + else + *cond = CreateEvent(NULL, attr->w_bManualReset, + attr->w_bInitialState, attr->w_lpName); + + return *cond == NULL; +#else + pthread_condattr_t a; + int ret; + + if ((ret = pthread_condattr_init(&a)) != 0) + return ret; + + if (attr != NULL) { + if (attr->p_pshared) + pthread_condattr_setpshared(&a, attr->p_pshared); + if (attr->p_clock) + pthread_condattr_setclock(&a, attr->p_clock); + } + + ret = pthread_cond_init(cond, &a); + pthread_condattr_destroy(&a); + + return ret; +#endif /* _WIN32 */ +} + +int +cthread_cond_signal(cthread_cond_t *cond) +{ +#ifdef _WIN32 + return SetEvent(*cond) == WIN32_FAILURE; +#else + return pthread_cond_signal(cond); +#endif /* _WIN32 */ +} + +int +cthread_cond_broadcast(cthread_cond_t *cond) +{ +#ifdef _WIN32 + return SetEvent(*cond) == WIN32_FAILURE; +#else + return pthread_cond_broadcast(cond); +#endif /* _WIN32 */ +} + +int +cthread_cond_wait(cthread_cond_t *cond, cthread_mutex_t *mutex) +{ +#ifdef _WIN32 + if (cthread_mutex_unlock(mutex) == 1) + return 1; + + if (WaitForSingleObject(*cond, INFINITE) == WAIT_FAILED) + return 1; + + return cthread_mutex_lock(mutex); +#else + return pthread_cond_wait(cond, mutex); +#endif /* _WIN32 */ +} + +int +cthread_cond_destroy(cthread_cond_t *cond) +{ +#ifdef _WIN32 + return CloseHandle(*cond) == WIN32_FAILURE; +#else + return pthread_cond_destroy(cond); +#endif /* _WIN32 */ +} + +int +cthread_rwlock_init(cthread_rwlock_t *rwlock) +{ +#ifdef _WIN32 + *rwlock = CreateMutex(NULL, FALSE, NULL); + + return *rwlock == NULL; +#else + return pthread_rwlock_init(rwlock, NULL); +#endif /* _WIN32 */ +} + +int +cthread_rwlock_rdlock(cthread_rwlock_t *rwlock) +{ +#ifdef _WIN32 + return WaitForSingleObject(*rwlock, INFINITE) == WAIT_FAILED; +#else + return pthread_rwlock_rdlock(rwlock); +#endif /* _WIN32 */ +} + +int +cthread_rwlock_wrlock(cthread_rwlock_t *rwlock) +{ +#ifdef _WIN32 + return WaitForSingleObject(*rwlock, INFINITE) == WAIT_FAILED; +#else + return pthread_rwlock_wrlock(rwlock); +#endif /* _WIN32 */ +} + +int +cthread_rwlock_unlock(cthread_rwlock_t *rwlock) +{ +#ifdef _WIN32 + return ReleaseMutex(*rwlock) == WIN32_FAILURE; +#else + return pthread_rwlock_unlock(rwlock); +#endif /* _WIN32 */ +} + +int +cthread_rwlock_destroy(cthread_rwlock_t *rwlock) +{ +#ifdef _WIN32 + return CloseHandle(*rwlock) == WIN32_FAILURE; +#else + return pthread_rwlock_destroy(rwlock); +#endif /* _WIN32 */ +} diff --git a/libre/cthread/cthread.h b/libre/cthread/cthread.h new file mode 100644 index 0000000..27cb1fb --- /dev/null +++ b/libre/cthread/cthread.h @@ -0,0 +1,248 @@ +/* + * Copyright © 2023, Ratakor <ratakor@disroot.org> + * + * This library is free software. You can redistribute it and/or modify it + * under the terms of the ISC license. See cthread.c for details. + */ + +#ifndef CTHREAD_H +#define CTHREAD_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef _WIN32 +#include <windows.h> + +#define CTHREAD_MUTEX_INITIALIZER NULL + +typedef HANDLE cthread_t; +typedef HANDLE cthread_mutex_t; +typedef HANDLE cthread_cond_t; +typedef HANDLE cthread_rwlock_t; +#else +#include <pthread.h> + +#define CTHREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER + +typedef pthread_t cthread_t; +typedef pthread_mutex_t cthread_mutex_t; +typedef pthread_cond_t cthread_cond_t; +typedef pthread_rwlock_t cthread_rwlock_t; +#endif /* _WIN32 */ + +typedef struct { + size_t stacksize; + + int w_dwCreationFlags; + + int p_detachstate; + size_t p_guardsize; + int p_inheritsched; + int p_schedpolicy; + int p_scope; + size_t p_stack; + void *p_stackaddr; +} cthread_attr_t; + +typedef struct { + char *w_lpName; + int w_bInitialOwner; + + int p_pshared; + int p_type; + int p_robust; + int p_protocol; + int p_prioceiling; +} cthread_mutexattr_t; + +typedef struct { + char *w_lpName; + int w_bManualReset; + int w_bInitialState; + + int p_pshared; + int p_clock; +} cthread_condattr_t; + +/** + * Create a new thread. + * + * @param thread Pointer to the thread. + * @param attr Pointer to the thread attributes. NULL for default attributes. + * @param func Pointer to the function that will be executed in the new thread. + * @param arg Pointer to the data that will be passed to the thread function. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_create(cthread_t *thread, cthread_attr_t *attr, + void *(*func)(void *), void *arg); + +/** + * Join with a terminated thread. + * + * @param thread The thread to be joined. + * @param retval Pointer to store the exit code of the joined thread. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_join(cthread_t thread, void **retval); + +/** + * Detach a thread. + * + * @param thread Pointer to the thread. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_detach(cthread_t thread); + +/** + * Compares two threads for equality. + * + * @param t1 First thread to compare. + * @param t2 Second thread to compare. + * @return Non-zero if the threads are equal, zero otherwise. + */ +int cthread_equal(cthread_t t1, cthread_t t2); + +/** + * Obtain ID of the current thread. + * + * @return Thread ID of the current thread. + */ +cthread_t cthread_self(void); + +/** + * Terminate the current thread. + * + * @param retval Pointer to store the thread exit code. + */ +void cthread_exit(void *retval); + +/** + * Initialize a mutex. + * + * @param mutex Pointer to the mutex. + * @param attr Pointer to the mutex attributes. NULL for default attributes. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_mutex_init(cthread_mutex_t *mutex, cthread_mutexattr_t *attr); + +/** + * Lock a mutex. + * + * @param mutex Pointer to the mutex. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_mutex_lock(cthread_mutex_t *mutex); + +/** + * Try to lock a mutex without blocking. + * + * @param mutex Pointer to the mutex. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_mutex_trylock(cthread_mutex_t *mutex); + +/** + * Unlock a mutex. + * + * @param mutex Pointer to the mutex. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_mutex_unlock(cthread_mutex_t *mutex); + +/** + * Destroy a mutex. + * + * @param mutex Pointer to the mutex. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_mutex_destroy(cthread_mutex_t *mutex); + +/** + * Initialize a condition. + * + * @param cond Pointer to the condition. + * @param attr Pointer to the condition attributes. NULL for default. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_cond_init(cthread_cond_t *cond, cthread_condattr_t *attr); + +/** + * Signal a condition. + * + * @param cond Pointer to the condition. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_cond_signal(cthread_cond_t *cond); + +/** + * Broadcast a condition. + * + * @param cond Pointer to the condition. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_cond_broadcast(cthread_cond_t *cond); + +/** + * Wait on a condition. + * + * @param cond Pointer to the condition. + * @param mutex Pointer to the associated mutex. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_cond_wait(cthread_cond_t *cond, cthread_mutex_t *mutex); + +/** + * Destroy a condition. + * + * @param cond Pointer to the condition. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_cond_destroy(cthread_cond_t *cond); + +/** + * Initialize a read-write lock. + * + * @param rwlock Pointer to the read-write lock. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_rwlock_init(cthread_rwlock_t *rwlock); + +/** + * Lock a read-write lock for reading. + * + * @param rwlock Pointer to the read-write lock. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_rwlock_rdlock(cthread_rwlock_t *rwlock); + +/** + * Lock a read-write lock for writing. + * + * @param rwlock Pointer to the read-write lock. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_rwlock_wrlock(cthread_rwlock_t *rwlock); + +/** + * Unlock a read-write lock. + * + * @param rwlock Pointer to the read-write lock. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_rwlock_unlock(cthread_rwlock_t *rwlock); + +/** + * Destroy a read-write lock. + * + * @param rwlock Pointer to the read-write lock. + * @return 0 on success, non-zero error code on failure. + */ +int cthread_rwlock_destroy(cthread_rwlock_t *rwlock); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CTHREAD_H */ diff --git a/libre/dalloc/dalloc.c b/libre/dalloc/dalloc.c new file mode 100644 index 0000000..46fb5c9 --- /dev/null +++ b/libre/dalloc/dalloc.c @@ -0,0 +1,402 @@ +/* + * Copyright © 2023, Ratakor <ratakor@disroot.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef DALLOC +#define NO_DALLOC "dalloc: Define `DALLOC` to enable dalloc" +#else +void dalloc_check_all(void) __attribute__((destructor)); +#undef DALLOC +#endif /* DALLOC */ +#include "../libre.h" + +#define OVER_ALLOC 64 +#define MAGIC_NUMBER 0x99 +#define EXIT_STATUS 9 +#define MAX_POINTERS 1024 +#define COMMENT_MAX 128 +#ifndef PATH_MAX +#define PATH_MAX 256 +#endif /* PATH_MAX */ + +struct Pointer { + void *p; + size_t siz; + char comment[COMMENT_MAX]; + int ignored; + char file[PATH_MAX]; + int line; +}; + +static int overflow(unsigned char *p, size_t siz); +static size_t find_pointer_index(void *p, char *file, int line); + +static cthread_mutex_t dalloc_mutex = CTHREAD_MUTEX_INITIALIZER; +static struct Pointer pointers[MAX_POINTERS]; +static size_t npointers; + +static int +overflow(unsigned char *p, size_t siz) +{ + size_t i = 0; + + while (p[siz + i] == MAGIC_NUMBER && ++i < OVER_ALLOC); + + return i < OVER_ALLOC; +} + +static size_t +find_pointer_index(void *p, char *file, int line) +{ + size_t i = npointers; + + while (i-- > 0 && p != pointers[i].p); + + if (i == (size_t) -1) { + fprintf(stderr, "%s:%d: dalloc: Unknown pointer %p\n", + file, line, p); + cthread_mutex_unlock(&dalloc_mutex); + exit(EXIT_STATUS); + } + + return i; +} + +size_t +dalloc_check_overflow(void) +{ + size_t i, sum = 0; + +#ifdef NO_DALLOC + fprintf(stderr, "%s\n", NO_DALLOC); + return 0; +#endif /* NO_DALLOC */ + + cthread_mutex_lock(&dalloc_mutex); + fprintf(stderr, "Memory overflow:"); + for (i = 0; i < npointers; i++) { + if (!overflow(pointers[i].p, pointers[i].siz)) + continue; + + sum++; + fprintf(stderr, "\n%s:%d: %p, total: %zu bytes", + pointers[i].file, pointers[i].line, + pointers[i].p, pointers[i].siz); + if (pointers[i].comment[0]) + fprintf(stderr, " /* %s */", pointers[i].comment); + } + cthread_mutex_unlock(&dalloc_mutex); + + if (sum == 0) + fprintf(stderr, " 0 overflow :)\n"); + else + fprintf(stderr, "\nTotal overflow: %zu\n", sum); + + return sum; +} + +void +dalloc_check_free(void) +{ + size_t i, sum = 0; + +#ifdef NO_DALLOC + fprintf(stderr, "%s\n", NO_DALLOC); + return; +#endif /* NO_DALLOC */ + + cthread_mutex_lock(&dalloc_mutex); + fprintf(stderr, "Memory allocated and not freed:"); + for (i = 0; i < npointers; i++) { + if (pointers[i].ignored) + continue; + + sum += pointers[i].siz; + fprintf(stderr, "\n%s:%d: %p, %zu bytes", + pointers[i].file, pointers[i].line, + pointers[i].p, pointers[i].siz); + if (pointers[i].comment[0]) + fprintf(stderr, " /* %s */", pointers[i].comment); + } + cthread_mutex_unlock(&dalloc_mutex); + + if (sum == 0) + fprintf(stderr, " 0 byte :)\n"); + else + fprintf(stderr, "\nTotal: %zu bytes, %zu pointers\n", sum, i); +} + +void +dalloc_check_all(void) +{ +#ifdef NO_DALLOC + fprintf(stderr, "%s\n", NO_DALLOC); + return; +#endif /* NO_DALLOC */ + + dalloc_check_overflow(); + dalloc_check_free(); +} + +void +dalloc_sighandler(int sig) +{ +#ifdef _WIN32 + fprintf(stderr, "dalloc: signal %d\n", sig); +#else + fprintf(stderr, "dalloc: %s\n", strsignal(sig)); +#endif /* _WIN32 */ + +#ifdef NO_DALLOC + fprintf(stderr, "%s\n", NO_DALLOC); +#endif /* NO_DALLOC */ + + exit(EXIT_STATUS); +} + +void +___dalloc_ignore(void *p, char *file, int line) +{ + size_t i; + +#ifdef NO_DALLOC + return; +#endif /* NO_DALLOC */ + + cthread_mutex_lock(&dalloc_mutex); + i = find_pointer_index(p, file, line); + pointers[i].ignored = 1; + cthread_mutex_unlock(&dalloc_mutex); +} + +void +___dalloc_comment(void *p, char *comment, char *file, int line) +{ + size_t i, j; + +#ifdef NO_DALLOC + return; +#endif /* NO_DALLOC */ + + cthread_mutex_lock(&dalloc_mutex); + i = find_pointer_index(p, file, line); + for (j = 0; j < COMMENT_MAX && comment[j] != '\0'; j++) + pointers[i].comment[j] = comment[j]; + pointers[i].comment[j] = '\0'; + cthread_mutex_unlock(&dalloc_mutex); +} + +void +___free(void *p, char *file, int line) +{ + size_t i; + + if (p == NULL) + return; + + cthread_mutex_lock(&dalloc_mutex); + i = find_pointer_index(p, file, line); + if (overflow(pointers[i].p, pointers[i].siz)) { + fprintf(stderr, "%s:%d: dalloc: " + "Memory overflow on %p, total: %zu bytes\n", + file, line, pointers[i].p, pointers[i].siz); + fprintf(stderr, "The pointer "); + if (pointers[i].comment[0]) + fprintf(stderr, "'%s' ", pointers[i].comment); + fprintf(stderr, "was allocated in '%s' on line %d.\n", + pointers[i].file, pointers[i].line); + cthread_mutex_unlock(&dalloc_mutex); + exit(EXIT_STATUS); + } + + for (i++; i < npointers; i++) + pointers[i - 1] = pointers[i]; + npointers--; + free(p); + cthread_mutex_unlock(&dalloc_mutex); +} + + +void * +___malloc(size_t siz, char *file, int line) +{ + void *p = NULL; + size_t i; + + if (siz == 0) + return NULL; + + if (npointers == MAX_POINTERS) { + fprintf(stderr, "dalloc: Too much pointers (max:%d)\n", + MAX_POINTERS); + exit(EXIT_STATUS); + } + + if (siz + OVER_ALLOC < OVER_ALLOC) + errno = ENOMEM; + else + p = malloc(siz + OVER_ALLOC); + + if (p == NULL) { + fprintf(stderr, "%s:%d: dalloc: %s\n", + file, line, strerror(errno)); + exit(EXIT_STATUS); + } + + memset((unsigned char *)p + siz, MAGIC_NUMBER, OVER_ALLOC); + cthread_mutex_lock(&dalloc_mutex); + pointers[npointers].p = p; + pointers[npointers].siz = siz; + for (i = 0; i < PATH_MAX - 1 && file[i] != '\0'; i++) + pointers[npointers].file[i] = file[i]; + pointers[npointers].file[i] = '\0'; + pointers[npointers].line = line; + npointers++; + cthread_mutex_unlock(&dalloc_mutex); + + return p; +} + +void * +___calloc(size_t nmemb, size_t siz, char *file, int line) +{ + void *p; + + if (nmemb == 0 || siz == 0) + return NULL; + + if (nmemb > -1 / siz) { + fprintf(stderr, "%s:%d: dalloc: calloc: %s\n", + file, line, strerror(ENOMEM)); + exit(EXIT_STATUS); + } + + siz *= nmemb; + p = ___malloc(siz, file, line); + memset(p, 0, siz); + + return p; +} + +void * +___realloc(void *p, size_t siz, char *file, int line) +{ + size_t i, j; + + if (p == NULL) + return ___malloc(siz, file, line); + + if (siz == 0) { + ___free(p, file, line); + return NULL; + } + + cthread_mutex_lock(&dalloc_mutex); + i = find_pointer_index(p, file, line); + + if (overflow(pointers[i].p, pointers[i].siz)) { + fprintf(stderr, "%s:%d: dalloc: " + "Memory overflow on %p, total: %zu bytes\n", + file, line, pointers[i].p, pointers[i].siz); + fprintf(stderr, "The pointer "); + if (pointers[i].comment[0]) + fprintf(stderr, "'%s' ", pointers[i].comment); + fprintf(stderr, "was allocated in '%s' on line %d.\n", + pointers[i].file, pointers[i].line); + cthread_mutex_unlock(&dalloc_mutex); + exit(EXIT_STATUS); + } + + if (siz + OVER_ALLOC < OVER_ALLOC) { + p = NULL; + errno = ENOMEM; + } else { + p = realloc(p, siz + OVER_ALLOC); + } + + if (p == NULL) { + fprintf(stderr, "%s:%d: dalloc: %s\n", + file, line, strerror(errno)); + cthread_mutex_unlock(&dalloc_mutex); + exit(EXIT_STATUS); + } + + memset((unsigned char *)p + siz, MAGIC_NUMBER, OVER_ALLOC); + pointers[i].p = p; + pointers[i].siz = siz; + for (j = 0; j < PATH_MAX - 1 && file[j] != '\0'; j++) + pointers[i].file[j] = file[j]; + pointers[i].file[j] = '\0'; + pointers[i].line = line; + cthread_mutex_unlock(&dalloc_mutex); + + return p; +} + +void * +___reallocarray(void *p, size_t nmemb, size_t siz, char *file, int line) +{ + if (siz != 0 && nmemb > -1 / siz) { + fprintf(stderr, "%s:%d: dalloc: reallocarray: %s\n", + file, line, strerror(ENOMEM)); + exit(EXIT_STATUS); + } + + return ___realloc(p, nmemb * siz, file, line); +} + +char * +___strdup(const char *s, char *file, int line) +{ + char *p; + size_t siz; + + siz = strlen(s) + 1; + p = ___malloc(siz, file, line); + memcpy(p, s, siz); + + return p; +} + +char * +___strndup(const char *s, size_t n, char *file, int line) +{ + const char *end; + char *p; + size_t siz; + + end = memchr(s, '\0', n); + siz = (end ? (size_t)(end - s) : n) + 1; + p = ___malloc(siz, file, line); + memcpy(p, s, siz - 1); + p[siz - 1] = '\0'; + + return p; +} + +void +exitsegv(int dummy) +{ + int *x = NULL; + + dalloc_check_all(); + *x = dummy; +} diff --git a/libre/dalloc/dalloc.h b/libre/dalloc/dalloc.h new file mode 100644 index 0000000..c0fa081 --- /dev/null +++ b/libre/dalloc/dalloc.h @@ -0,0 +1,65 @@ +/* + * Copyright © 2023, Ratakor <ratakor@disroot.org> + * + * This library is free software. You can redistribute it and/or modify it + * under the terms of the ISC license. See dalloc.c for details. + */ + +#ifndef DALLOC_H +#define DALLOC_H + +#include <stdlib.h> +#include <string.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef DALLOC +#define free(p) (___free(p, __FILE__, __LINE__)) +#define malloc(siz) (___malloc(siz, __FILE__, __LINE__)) +#define calloc(nmemb, siz) (___calloc(nmemb, siz, __FILE__, __LINE__)) +#define realloc(p, siz) (___realloc(p, siz, __FILE__, __LINE__)) +#define reallocarray(p, n, s) (___reallocarray(p, n, s, __FILE__, __LINE__)) +#define strdup(s) (___strdup(s, __FILE__, __LINE__)) +#define strndup(s, n) (___strndup(s, n, __FILE__, __LINE__)) + +#ifdef _UTIL_H +#define emalloc(siz) (___malloc(siz, __FILE__, __LINE__)) +#define ecalloc(nmemb, siz) (___calloc(nmemb, siz, __FILE__, __LINE__)) +#define erealloc(p, siz) (___realloc(p, siz, __FILE__, __LINE__)) +#define ereallocarray(p, n, s) (___reallocarray(p, n, s, __FILE__, __LINE__)) +#define estrdup(s) (___strdup(s, __FILE__, __LINE__)) +#define estrndup(s, n) (___strndup(s, n, __FILE__, __LINE__)) +#endif /* _UTIL_H */ +#endif /* DALLOC */ + +#ifdef EXITSEGV +#define exit(dummy) (exitsegv(dummy)) +#endif /* EXITSEGV */ + +#define dalloc_ignore(p) (___dalloc_ignore(p, __FILE__, __LINE__)) +#define dalloc_comment(p, com) (___dalloc_comment(p, com, __FILE__, __LINE__)) + +size_t dalloc_check_overflow(void); +void dalloc_check_free(void); +void dalloc_check_all(void); +void dalloc_sighandler(int sig); + +void ___dalloc_ignore(void *p, char *file, int line); +void ___dalloc_comment(void *p, char *comment, char *file, int line); +void ___free(void *p, char *file, int line); +void *___malloc(size_t siz, char *file, int line); +void *___calloc(size_t nmemb, size_t siz, char *file, int line); +void *___realloc(void *p, size_t siz, char *file, int line); +void *___reallocarray(void *p, size_t nmemb, size_t siz, char *file, int line); +char *___strdup(const char *s, char *file, int line); +char *___strndup(const char *s, size_t n, char *file, int line); + +void exitsegv(int dummy); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* DALLOC_H */ diff --git a/libre/libre.h b/libre/libre.h new file mode 100644 index 0000000..a58fe78 --- /dev/null +++ b/libre/libre.h @@ -0,0 +1,8 @@ +#ifndef LIBRE_H +#define LIBRE_H + +#include "util/util.h" +#include "cthread/cthread.h" +#include "dalloc/dalloc.h" + +#endif /* LIBRE_H */ diff --git a/src/util.c b/libre/util/util.c index eb4998c..d9db063 100644 --- a/src/util.c +++ b/libre/util/util.c @@ -1,4 +1,4 @@ -/* Copywrong © 2023 Ratakor. See LICENSE file for license details. */ +/* Copyright © 2023 Ratakor. See LICENSE file for license details. */ #include <sys/stat.h> @@ -12,11 +12,11 @@ #include "util.h" size_t -strlcpy(char *dst, const char *src, size_t siz) +strlcpy(char *restrict dst, const char *restrict src, size_t siz) { const char *osrc = src; - if (siz == 0) + if (UNLIKELY(siz == 0)) return strlen(src); while (--siz != 0 && (*dst++ = *src++) != '\0'); @@ -51,7 +51,7 @@ estrlcpy(char *dst, const char *src, size_t siz) } size_t -strlcat(char *dst, const char *src, size_t siz) +strlcat(char *restrict dst, const char *restrict src, size_t siz) { size_t dsiz; @@ -116,6 +116,15 @@ erealloc(void *p, size_t siz) } void * +ereallocarray(void *p, size_t nmemb, size_t siz) +{ + if ((p = reallocarray(p, nmemb, siz)) == NULL) + err(1, "reallocarray"); + + return p; +} + +void * estrdup(const char *s) { char *p; @@ -151,7 +160,7 @@ efopen(const char *filename, const char *mode) char * nstrchr(const char *s, int c, size_t n) { - if (n == 0) + if (UNLIKELY(n == 0)) return NULL; do { @@ -160,6 +169,7 @@ nstrchr(const char *s, int c, size_t n) if (*s == c) n--; } while (*s++); + return NULL; } @@ -186,7 +196,7 @@ ufmt(char *dst, size_t dsiz, uint64_t n) size_t ifmt(char *dst, size_t dsiz, int64_t n) { - if (dsiz == 0) + if (UNLIKELY(dsiz == 0)) return ufmt(dst, dsiz, n); if (n < 0) { @@ -204,6 +214,7 @@ int file_exists(const char *filename) { struct stat buf; + return (stat(filename, &buf) == 0); } diff --git a/src/util.h b/libre/util/util.h index 060cce1..d0a75de 100644 --- a/src/util.h +++ b/libre/util/util.h @@ -1,29 +1,32 @@ -/* Copywrong © 2023 Ratakor. See LICENSE file for license details. */ +/* Copyright © 2023 Ratakor. See LICENSE file for license details. */ -#ifndef UTIL_H -#define UTIL_H +#ifndef _UTIL_H +#define _UTIL_H #include <sys/types.h> #include <stdint.h> #include <stdio.h> -#define LENGTH(X) (sizeof(X) / sizeof(X[0])) -#define STRLEN(X) (sizeof(X) - 1) -#define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) -#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) -#define UNUSED(X) ((void)(X)) +#define LENGTH(X) (sizeof(X) / sizeof(X[0])) +#define STRLEN(X) (sizeof(X) - 1) +#define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) +#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#define UNUSED(X) ((void)(X)) +#define LIKELY(X) __builtin_expect(!!(X), 1) +#define UNLIKELY(X) __builtin_expect(!!(X), 0) -size_t strlcpy(char *dst, const char *src, size_t siz); +size_t strlcpy(char *restrict dst, const char *restrict src, size_t siz); size_t wstrlcpy(char *dst, const char *src, size_t siz); size_t estrlcpy(char *dst, const char *src, size_t siz); -size_t strlcat(char *dst, const char *src, size_t siz); +size_t strlcat(char *restrict dst, const char *restrict src, size_t siz); size_t wstrlcat(char *dst, const char *src, size_t siz); size_t estrlcat(char *dst, const char *src, size_t siz); void *emalloc(size_t siz); void *ecalloc(size_t nmemb, size_t siz); void *erealloc(void *p, size_t siz); +void *ereallocarray(void *p, size_t nmemb, size_t siz); void *estrdup(const char *s); void *estrndup(const char *s, size_t n); FILE *efopen(const char *filename, const char *mode); @@ -34,4 +37,4 @@ size_t ifmt(char *dst, size_t dsiz, int64_t n); int file_exists(const char *filename); time_t ltime(void); -#endif /* UTIL_H */ +#endif /* _UTIL_H */ diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..a5e7771 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,56 @@ +# Copywrong © 2023 Ratakor. See LICENSE file for license details. + +BIN = ../nolan +CONFIG_H = ../config.h +LIBRE_DIR = ../libre + +LIBRE_OBJS = ${LIBRE_DIR}/cthread.o \ + ${LIBRE_DIR}/dalloc.o \ + ${LIBRE_DIR}/util.o + +OBJS = cmd_correct.o \ + cmd_help.o \ + cmd_info.o \ + cmd_lbraid.o \ + cmd_leaderboard.o \ + cmd_source.o \ + cmd_uraid.o \ + init.o \ + main.o \ + ocr.o \ + raids.o \ + roles.o \ + run.o \ + stats.o + +DISCORDLIBS = -ldiscord -lcurl -lpthread +TESSLIBS = -ltesseract -lleptonica +GDLIBS = -lgd -lpng -lz -ljpeg -lfreetype -lm + +WFLAGS = -Werror -Wall -Wextra -Wpedantic -Waggregate-return -Wshadow\ + -Wmissing-prototypes -Wunused-macros -Wdouble-promotion +DFLAGS = -O0 -g -DDALLOC +CFLAGS += -std=c99 -D_DEFAULT_SOURCE ${WFLAGS} +LDFLAGS += ${DISCORDLIBS} ${TESSLIBS} ${GDLIBS} + +all: ${BIN} + +echo: + @echo "CC = ${CC}" + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "OBJS = ${OBJS}" + +${BIN}: ${CONFIG_H} ${OBJS} + ${CC} ${OBJS} ${LIBRE_OBJS} ${LDFLAGS} -o $@ + +${CONFIG_H}: + @cp ../config.def.h $@ + +debug: + @CFLAGS="${DFLAGS}" ${MAKE} + +clean: + rm -f ${OBJS} ${BIN} + +.PHONY: all echo debug clean @@ -3,6 +3,8 @@ #include <concord/discord.h> #include <concord/discord-internal.h> +#include <signal.h> + #include "nolan.h" Player players[MAX_PLAYERS]; @@ -56,6 +58,8 @@ main(void) char *lb[] = { "lb", "leaderboard" }; struct discord *client; + signal(SIGINT, &dalloc_sighandler); + create_folders(); create_stats_file(); init_players(); diff --git a/src/nolan.h b/src/nolan.h index a5416e2..036f352 100644 --- a/src/nolan.h +++ b/src/nolan.h @@ -7,7 +7,7 @@ #include <concord/log.h> #include "../config.h" -#include "util.h" +#include "../libre/libre.h" #define MAX_SLAYERS 50 #define LINE_SIZE 300 + 1 @@ -25,7 +25,6 @@ write_data(void *ptr, size_t size, size_t nmemb, void *stream) return strlcpy(stream, ptr, MAX_MESSAGE_LEN); } - char * curl(char *url) { |