// RETOOR - Dec 5 2024 // MIT License // =========== // Copyright (c) 2024 Retoor // 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 substantial 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 RLIB_H #define RLIB_H // BEGIN OF RLIB /* * Line below will be filtered by rmerge <script language="Javva script" type="woeiii" src="Pony.html" after-tag="after tag" /> */ #ifndef RTYPES_H #define RTYPES_H #ifdef _POSIX_C_SOURCE #define _POSIX_C_SOURCE_TEMP _POSIX_C_SOURCE #undef _POSIX_C_SOURCE #endif #ifndef _POSIX_C_SOURCE #undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200112L #endif #include <stdbool.h> #include <stdint.h> // uint #include <string.h> #include <sys/types.h> // ulong #ifndef ulonglong #define ulonglong unsigned long long #endif #ifndef uint typedef unsigned int uint; #endif #ifndef byte typedef unsigned char byte; #endif #ifdef _POSIX_C_SOURCE_TEMP #undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE _POSIX_C_SOURCE_TEMP #undef _POSIX_C_SOURCE_TEMP #else #undef _POSIX_C_SOURCE #endif #endif #ifndef NSOCK_H #define NSOCK_H #ifndef RMALLOC_H #define RMALLOC_H #ifndef RMALLOC_OVERRIDE #define RMALLOC_OVERRIDE 1 #endif #ifdef _POSIX_C_SOURCE #define _POSIX_C_SOURCE_TEMP _POSIX_C_SOURCE #undef _POSIX_C_SOURCE #endif #ifndef _POSIX_C_SOURCE #undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200112L #endif #ifndef ulonglong #define ulonglong unsigned long long #endif #include <locale.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #ifndef RTEMP_H #define RTEMP_H #include <pthread.h> #ifndef RTEMPC_SLOT_COUNT #define RTEMPC_SLOT_COUNT 20 #endif #ifndef RTEMPC_SLOT_SIZE #define RTEMPC_SLOT_SIZE 1024 * 64 * 128 #endif bool _rtempc_initialized = 0; pthread_mutex_t _rtempc_thread_lock; bool rtempc_use_mutex = true; byte _current_rtempc_slot = 1; char _rtempc_buffer[RTEMPC_SLOT_COUNT][RTEMPC_SLOT_SIZE]; char *rtempc(char *data) { if (rtempc_use_mutex) { if (!_rtempc_initialized) { _rtempc_initialized = true; pthread_mutex_init(&_rtempc_thread_lock, NULL); } pthread_mutex_lock(&_rtempc_thread_lock); } uint current_rtempc_slot = _current_rtempc_slot; _rtempc_buffer[current_rtempc_slot][0] = 0; strcpy(_rtempc_buffer[current_rtempc_slot], data); _current_rtempc_slot++; if (_current_rtempc_slot == RTEMPC_SLOT_COUNT) { _current_rtempc_slot = 0; } if (rtempc_use_mutex) pthread_mutex_unlock(&_rtempc_thread_lock); return _rtempc_buffer[current_rtempc_slot]; } #define sstring(_pname, _psize) \ static char _##_pname[_psize]; \ _##_pname[0] = 0; \ char *_pname = _##_pname; #define string(_pname, _psize) \ char _##_pname[_psize]; \ _##_pname[0] = 0; \ char *_pname = _##_pname; #define sreset(_pname, _psize) _pname = _##_pname; #define sbuf(val) rtempc(val) #endif #ifdef _POSIX_C_SOURCE_TEMP #undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE _POSIX_C_SOURCE_TEMP #undef _POSIX_C_SOURCE_TEMP #else #undef _POSIX_C_SOURCE #endif ulonglong rmalloc_count = 0; ulonglong rmalloc_alloc_count = 0; ulonglong rmalloc_free_count = 0; ulonglong rmalloc_total_bytes_allocated = 0; void *_rmalloc_prev_realloc_obj = NULL; size_t _rmalloc_prev_realloc_obj_size = 0; void *rmalloc(size_t size) { void *result; while (!(result = malloc(size))) { fprintf(stderr, "Warning: malloc failed, trying again.\n"); } rmalloc_count++; rmalloc_alloc_count++; rmalloc_total_bytes_allocated += size; return result; } void *rcalloc(size_t count, size_t size) { void *result; while (!(result = calloc(count, size))) { fprintf(stderr, "Warning: calloc failed, trying again.\n"); } rmalloc_alloc_count++; rmalloc_count++; rmalloc_total_bytes_allocated += count * size; return result; } void *rrealloc(void *obj, size_t size) { if (!obj) { rmalloc_count++; } rmalloc_alloc_count++; if (obj == _rmalloc_prev_realloc_obj) { rmalloc_total_bytes_allocated += size - _rmalloc_prev_realloc_obj_size; _rmalloc_prev_realloc_obj_size = size - _rmalloc_prev_realloc_obj_size; } else { _rmalloc_prev_realloc_obj_size = size; } void *result; while (!(result = realloc(obj, size))) { fprintf(stderr, "Warning: realloc failed, trying again.\n"); } _rmalloc_prev_realloc_obj = result; return result; } char *rstrdup(const char *s) { if (!s) return NULL; char *result; size_t size = strlen(s) + 1; result = rmalloc(size); memcpy(result, s, size); rmalloc_total_bytes_allocated += size; return result; } void *rfree(void *obj) { rmalloc_count--; rmalloc_free_count++; free(obj); return NULL; } #if RMALLOC_OVERRIDE #define malloc rmalloc #define calloc rcalloc #define realloc rrealloc #define free rfree #define strdup rstrdup #endif char *rmalloc_lld_format(ulonglong num) { char res[100]; res[0] = 0; sprintf(res, "%'lld", num); char *resp = res; while (*resp) { if (*resp == ',') *resp = '.'; resp++; } return sbuf(res); } char *rmalloc_bytes_format(int factor, ulonglong num) { char *sizes[] = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}; if (num > 1024) { return rmalloc_bytes_format(factor + 1, num / 1024); } char res[100]; sprintf(res, "%s %s", rmalloc_lld_format(num), sizes[factor]); return sbuf(res); } char *rmalloc_stats() { static char res[200]; res[0] = 0; // int original_locale = localeconv(); setlocale(LC_NUMERIC, "en_US.UTF-8"); sprintf(res, "Memory usage: %s, %s (re)allocated, %s unqiue free'd, %s in use.", rmalloc_bytes_format(0, rmalloc_total_bytes_allocated), rmalloc_lld_format(rmalloc_alloc_count), rmalloc_lld_format(rmalloc_free_count), rmalloc_lld_format(rmalloc_count)); // setlocale(LC_NUMERIC, original_locale); setlocale(LC_NUMERIC, ""); return res; } #endif #include <arpa/inet.h> #include <errno.h> #include <fcntl.h> #include <netdb.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/select.h> #include <sys/socket.h> #include <unistd.h> #ifndef RLIB_RIO #define RLIB_RIO #include <dirent.h> #include <stdbool.h> #include <stdio.h> #include <string.h> #include <sys/dir.h> #include <sys/select.h> #include <sys/stat.h> #include <unistd.h> #ifndef RSTRING_LIST_H #define RSTRING_LIST_H #include <stdbool.h> #include <stdlib.h> #include <string.h> typedef struct rstring_list_t { unsigned int size; unsigned int count; char **strings; } rstring_list_t; rstring_list_t *rstring_list_new() { rstring_list_t *rsl = (rstring_list_t *)malloc(sizeof(rstring_list_t)); memset(rsl, 0, sizeof(rstring_list_t)); return rsl; } void rstring_list_free(rstring_list_t *rsl) { for (unsigned int i = 0; i < rsl->size; i++) { free(rsl->strings[i]); } if (rsl->strings) free(rsl->strings); free(rsl); rsl = NULL; } void rstring_list_add(rstring_list_t *rsl, char *str) { if (rsl->count == rsl->size) { rsl->size++; rsl->strings = (char **)realloc(rsl->strings, sizeof(char *) * rsl->size); } rsl->strings[rsl->count] = strdup(str); rsl->count++; } bool rstring_list_contains(rstring_list_t *rsl, char *str) { for (unsigned int i = 0; i < rsl->count; i++) { if (!strcmp(rsl->strings[i], str)) return true; } return false; } #endif bool rfile_exists(char *path) { struct stat s; return !stat(path, &s); } void rjoin_path(char *p1, char *p2, char *output) { output[0] = 0; strcpy(output, p1); if (output[strlen(output) - 1] != '/') { char slash[] = "/"; strcat(output, slash); } if (p2[0] == '/') { p2++; } strcat(output, p2); } int risprivatedir(const char *path) { struct stat statbuf; if (stat(path, &statbuf) != 0) { perror("stat"); return -1; } if (!S_ISDIR(statbuf.st_mode)) { return -2; } if ((statbuf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == S_IRWXU) { return 1; // Private (owner has all permissions, others have none) } return 0; } bool risdir(const char *path) { return !risprivatedir(path); } void rforfile(char *path, void callback(char *)) { if (!rfile_exists(path)) return; DIR *dir = opendir(path); struct dirent *d; while ((d = readdir(dir)) != NULL) { if (!d) break; if ((d->d_name[0] == '.' && strlen(d->d_name) == 1) || d->d_name[1] == '.') { continue; } char full_path[4096]; rjoin_path(path, d->d_name, full_path); if (risdir(full_path)) { callback(full_path); rforfile(full_path, callback); } else { callback(full_path); } } closedir(dir); } bool rfd_wait(int fd, int ms) { fd_set read_fds; struct timeval timeout; FD_ZERO(&read_fds); FD_SET(fd, &read_fds); timeout.tv_sec = 0; timeout.tv_usec = 1000 * ms; int ret = select(fd + 1, &read_fds, NULL, NULL, &timeout); return ret > 0 && FD_ISSET(fd, &read_fds); } bool rfd_wait_forever(int fd) { while ((!rfd_wait(fd, 10))) { } return true; } size_t rfile_size(char *path) { struct stat s; stat(path, &s); return s.st_size; } size_t rfile_readb(char *path, void *data, size_t size) { FILE *fd = fopen(path, "r"); if (!fd) { return 0; } size_t bytes_read = fread(data, sizeof(char), size, fd); fclose(fd); ((char *)data)[bytes_read] = 0; return bytes_read; } #endif int *nsock_socks = NULL; int *nsock_readable = NULL; void **nsock_data = NULL; int nsock_server_fd = 0; int nsock_max_socket_fd = 0; typedef enum nsock_type_t { NSOCK_NONE = 0, NSOCK_SERVER, NSOCK_CLIENT, NSOCK_UPSTREAM } nsock_type_t; typedef struct nsock_it { int fd; int *upstreams; bool connected; bool downstream; unsigned int upstream_count; nsock_type_t type; } nsock_t; nsock_t **nsocks = NULL; int nsocks_count = 0; void (*nsock_on_connect)(int fd) = NULL; void (*nsock_on_data)(int fd) = NULL; void (*nsock_on_close)(int fd) = NULL; void nsock_on_before_data(int fd); nsock_t *nsock_get(int fd) { if (nsock_socks[fd] == 0) { return NULL; } if (fd >= nsocks_count || nsocks[fd] == NULL) { if (fd >= nsocks_count) { nsocks_count = fd + 1; nsocks = (nsock_t **)realloc(nsocks, sizeof(nsock_t *) * sizeof(nsock_t) * (nsocks_count)); nsocks[fd] = (nsock_t *)calloc(1, sizeof(nsock_t)); } nsocks[fd]->upstreams = NULL; nsocks[fd]->fd = fd; nsocks[fd]->connected = false; nsocks[fd]->downstream = false; nsocks[fd]->upstream_count = 0; nsocks[fd]->type = NSOCK_CLIENT; return nsocks[fd]; } return nsocks[fd]; } void nsock_close(int fd) { if (nsock_on_close) nsock_on_close(fd); nsock_t *sock = nsock_get(fd); if (sock && sock->connected) { sock->connected = false; for (unsigned int i = 0; i < sock->upstream_count; i++) { nsock_t *upstream = nsock_get(sock->upstreams[i]); if (upstream->connected) nsock_close(sock->upstreams[i]); sock->upstreams[i] = 0; } if (sock->upstream_count) { free(sock->upstreams); } sock->upstream_count = 0; } nsock_socks[fd] = 0; close(fd); } nsock_t *nsock_create(int fd, nsock_type_t type) { if (fd <= 0) return NULL; nsock_socks[fd] = fd; nsock_t *sock = nsock_get(fd); sock->connected = true; sock->downstream = false; sock->type = type; return sock; } int *nsock_init(int socket_count) { if (nsock_socks) { return nsock_socks; } nsock_socks = (int *)calloc(1, sizeof(int) * sizeof(int *) * socket_count + 1); if (nsock_data) { free(nsock_data); nsock_data = NULL; } nsock_data = (void **)malloc(sizeof(void **) * socket_count + 1); nsock_socks[socket_count] = -1; return nsock_socks; } void nsock_free() { if (nsock_socks) free(nsock_socks); if (nsock_readable) free(nsock_readable); nsock_server_fd = 0; nsock_max_socket_fd = 0; if (nsock_data) { exit(1); } } void nsock_add_upstream(int source, int target, bool downstream) { if (!nsock_socks[target]) return; if (!nsock_socks[source]) return; nsock_t *sock = nsock_get(source); nsock_t *sock_target = nsock_get(target); sock_target->type = NSOCK_UPSTREAM; sock->upstreams = (int *)realloc(sock->upstreams, sizeof(int) * (sock->upstream_count + 1)); sock->downstream = downstream; sock->upstreams[sock->upstream_count] = target; sock->upstream_count++; } void *nsock_get_data(int socket) { return nsock_data[socket]; } void nsock_set_data(int socket, void *data) { nsock_data[socket] = data; } int nsock_connect(const char *host, unsigned int port) { char port_str[10] = {0}; sprintf(port_str, "%d", port); int status; int socket_fd = 0; struct addrinfo hints; struct addrinfo *res; struct addrinfo *p; if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { return false; } memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; if ((status = getaddrinfo(host, port_str, &hints, &res)) != 0) { return 0; } for (p = res; p != NULL; p = p->ai_next) { if ((socket_fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { continue; } if (connect(socket_fd, p->ai_addr, p->ai_addrlen) == -1) { close(socket_fd); continue; } break; } if (p == NULL) { freeaddrinfo(res); return 0; } freeaddrinfo(res); if (socket_fd) { if (nsock_socks == NULL) { nsock_init(2048); } nsock_socks[socket_fd] = socket_fd; nsock_t *sock = nsock_create(socket_fd, NSOCK_CLIENT); sock->connected = true; } return socket_fd; } void nsock_listen(int port) { int server_fd; struct sockaddr_in address; if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("Socket failed"); exit(EXIT_FAILURE); } int opt = 1; if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { perror("setsockopt failed"); close(server_fd); exit(EXIT_FAILURE); } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(port); if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("Bind failed"); close(server_fd); exit(EXIT_FAILURE); } if (listen(server_fd, 8096) < 0) { perror("Listen failed"); close(server_fd); exit(EXIT_FAILURE); } nsock_server_fd = server_fd; } int *nsock_select(suseconds_t timeout) { struct timeval tv; tv.tv_sec = 0; tv.tv_usec = timeout; int server_fd = nsock_server_fd; fd_set rfds; FD_ZERO(&rfds); FD_SET(server_fd, &rfds); int *socks = nsock_socks; fd_set efds; FD_ZERO(&efds); nsock_max_socket_fd = server_fd; for (int i = 0; socks[i] != -1; i++) { if (i == server_fd) continue; ; if (!socks[i]) continue; if (socks[i] > nsock_max_socket_fd) { nsock_max_socket_fd = socks[i]; } FD_SET(socks[i], &rfds); FD_SET(socks[i], &efds); } int activity = select(nsock_max_socket_fd + 1, &rfds, NULL, &efds, timeout == 0 ? NULL : &tv); if ((activity < 0) && (errno != EINTR)) { perror("Select error\n"); return NULL; } else if (activity == 0) { return NULL; } if (FD_ISSET(server_fd, &rfds)) { struct sockaddr_in address; int addrlen = sizeof(address); address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; int new_socket = 0; if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) { perror("Accept failed"); } else { nsock_socks[new_socket] = new_socket; nsock_create(new_socket, NSOCK_CLIENT); if (nsock_on_connect) nsock_on_connect(new_socket); if (new_socket > nsock_max_socket_fd) nsock_max_socket_fd = new_socket; } } if (nsock_readable) { free(nsock_readable); } nsock_readable = (int *)calloc(1, sizeof(int *) + sizeof(int) * (nsock_max_socket_fd + 2)); nsock_readable[nsock_max_socket_fd + 1] = -1; nsock_readable[0] = 0; int readable_count = 0; for (int i = 0; i < nsock_max_socket_fd + 1; i++) { nsock_t *sock = nsock_get(i); if (!sock) continue; if (FD_ISSET(i, &efds)) { nsock_close(nsock_socks[i]); nsock_socks[i] = 0; nsock_readable[i] = 0; } else if (FD_ISSET(i, &rfds) && i != server_fd) { nsock_readable[i] = i; readable_count++; nsock_on_before_data(i); } else { nsock_readable[i] = 0; sock->connected = false; } } return nsock_readable; } unsigned char *nsock_read(int fd, int length) { if (!nsock_socks[fd]) return NULL; unsigned char *buffer = (unsigned char *)malloc(length + 1); int bytes_read = read(fd, buffer, length); if (bytes_read <= 0) { nsock_close(fd); return NULL; } buffer[bytes_read] = 0; return buffer; } unsigned char *nsock_read_all(int fd, int length) { if (!nsock_socks[fd]) return NULL; unsigned char *buffer = (unsigned char *)malloc(length + 1); int bytes_read = 0; while (bytes_read < length) { int bytes_chunk = read(fd, buffer + bytes_read, length - bytes_read); if (bytes_chunk <= 0) { nsock_close(fd); return NULL; } bytes_read += bytes_chunk; } buffer[bytes_read] = 0; return buffer; } int nsock_write_all(int fd, unsigned char *data, int length) { if (!nsock_socks[fd]) return 0; int bytes_written = 0; while (bytes_written < length) { int bytes_chunk = write(fd, data + bytes_written, length - bytes_written); if (bytes_chunk <= 0) { nsock_close(fd); return 0; } bytes_written += bytes_chunk; } return bytes_written; } int nsock_execute_upstream(int source, size_t buffer_size) { int result = 0; nsock_t *sock = nsock_get(source); unsigned char data[buffer_size]; memset(data, 0, buffer_size); int bytes_read = read(source, data, buffer_size); if (bytes_read <= 0) { nsock_close(source); return 0; } bool downstreamed = false; for (unsigned int i = 0; i < sock->upstream_count; i++) { if (!nsock_socks[sock->upstreams[i]]) continue; int bytes_sent = nsock_write_all(sock->upstreams[i], data, bytes_read); if (bytes_sent <= 0) { nsock_close(sock->upstreams[i]); continue; } if (sock->downstream && downstreamed == false) { downstreamed = true; unsigned char data[4096]; memset(data, 0, 4096); int bytes_read = read(sock->upstreams[i], data, 4096); if (bytes_read <= 0) { nsock_close(source); return 0; } int bytes_sent = nsock_write_all(sock->fd, data, bytes_read); if (bytes_sent <= 0) { nsock_close(sock->upstreams[i]); return 0; } } result++; } return result; } void nsock_on_before_data(int fd) { if (!nsock_socks[fd]) return; nsock_t *sock = nsock_get(fd); if (sock->upstream_count) { int upstreamed_to_count = nsock_execute_upstream(fd, 4096); if (!upstreamed_to_count) { nsock_close(fd); } return; } else if (sock->type == NSOCK_UPSTREAM) { while (rfd_wait(sock->fd, 0)) { unsigned char *data = nsock_read(fd, 4096); (void)data; } } if (nsock_on_data) nsock_on_data(fd); } void nsock(int port, void (*on_connect)(int fd), void (*on_data)(int fd), void (*on_close)(int fd)) { nsock_init(2048); nsock_listen(port); nsock_on_connect = on_connect; nsock_on_data = on_data; nsock_on_close = on_close; int serve_in_terminal = nsock_on_connect == NULL && nsock_on_data == NULL && nsock_on_close == NULL; while (1) { int *readable = nsock_select(0); if (!serve_in_terminal) continue; if (!readable) continue; for (int i = 0; readable[i] != -1; i++) { if (!readable[i]) continue; char buffer[1024] = {0}; int bytes_read = read(readable[i], buffer, 1); buffer[bytes_read] = 0; if (bytes_read <= 0) { nsock_close(readable[i]); continue; } if (write(readable[i], buffer, bytes_read) <= 0) { nsock_close(readable[i]); continue; } } } } #endif #ifndef UUID_H #define UUID_H #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <time.h> typedef struct { unsigned char bytes[16]; } UUID; void generate_random_bytes(unsigned char *bytes, size_t len) { for (size_t i = 0; i < len; i++) { bytes[i] = rand() % 256; } } UUID generate_uuid4(void) { UUID uuid; generate_random_bytes(uuid.bytes, 16); uuid.bytes[6] &= 0x0f; uuid.bytes[6] |= 0x40; uuid.bytes[8] &= 0x3f; uuid.bytes[8] |= 0x80; return uuid; } void uuid_to_string(UUID uuid, char *str) { sprintf(str, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", uuid.bytes[0], uuid.bytes[1], uuid.bytes[2], uuid.bytes[3], uuid.bytes[4], uuid.bytes[5], uuid.bytes[6], uuid.bytes[7], uuid.bytes[8], uuid.bytes[9], uuid.bytes[10], uuid.bytes[11], uuid.bytes[12], uuid.bytes[13], uuid.bytes[14], uuid.bytes[15]); } char *uuid4() { srand(time(NULL)); UUID uuid = generate_uuid4(); char str[37]; uuid_to_string(uuid, str); return sbuf(str); } #endif #ifndef RNET_H #define RNET_H #ifdef _POSIX_C_SOURCE #define _POSIX_C_SOURCE_TEMP _POSIX_C_SOURCE #undef _POSIX_C_SOURCE #endif #ifndef _POSIX_C_SOURCE #undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200112L #endif #include <arpa/inet.h> #include <errno.h> #include <fcntl.h> #include <netdb.h> #include <signal.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/select.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> #ifdef _POSIX_C_SOURCE_TEMP #undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE _POSIX_C_SOURCE_TEMP #undef _POSIX_C_SOURCE_TEMP #else #undef _POSIX_C_SOURCE #endif #define NET_SOCKET_MAX_CONNECTIONS 50000 typedef struct rnet_socket_t { int fd; char name[50]; void *data; size_t bytes_received; size_t bytes_sent; bool connected; void (*on_read)(struct rnet_socket_t *); void (*on_close)(struct rnet_socket_t *); void (*on_connect)(struct rnet_socket_t *); } rnet_socket_t; typedef struct rnet_select_result_t { int server_fd; rnet_socket_t **sockets; unsigned int socket_count; } rnet_select_result_t; typedef struct rnet_server_t { int socket_fd; rnet_socket_t **sockets; unsigned int socket_count; unsigned int port; unsigned int backlog; rnet_select_result_t *select_result; int max_fd; void (*on_connect)(rnet_socket_t *socket); void (*on_close)(rnet_socket_t *socket); void (*on_read)(rnet_socket_t *socket); } rnet_server_t; void rnet_select_result_free(rnet_select_result_t *result); int net_socket_accept(int server_fd); int net_socket_connect(const char *, unsigned int); int net_socket_init(); rnet_server_t *net_socket_serve(unsigned int port, unsigned int backlog); rnet_select_result_t *net_socket_select(rnet_server_t *server); rnet_socket_t *net_socket_wait(rnet_socket_t *socket_fd); bool net_set_non_blocking(int sock); bool net_socket_bind(int sock, unsigned int port); bool net_socket_listen(int sock, unsigned int backlog); char *net_socket_name(int sock); size_t net_socket_write(rnet_socket_t *, unsigned char *, size_t); rnet_socket_t *get_net_socket_by_fd(int); unsigned char *net_socket_read(rnet_socket_t *, unsigned int buff_size); void _net_socket_close(int sock); void net_socket_close(rnet_socket_t *sock); rnet_server_t *rnet_server_new(int socket_fd, unsigned int port, unsigned int backlog) { rnet_server_t *server = malloc(sizeof(rnet_server_t)); server->socket_fd = socket_fd; server->sockets = NULL; server->socket_count = 0; server->port = port; server->backlog = backlog; server->max_fd = -1; server->select_result = NULL; server->on_connect = NULL; server->on_close = NULL; server->on_read = NULL; return server; } rnet_server_t *rnet_server_add_socket(rnet_server_t *server, rnet_socket_t *sock) { server->sockets = realloc(server->sockets, sizeof(rnet_socket_t *) * (server->socket_count + 1)); server->sockets[server->socket_count] = sock; server->socket_count++; sock->on_read = server->on_read; sock->on_connect = server->on_connect; sock->on_close = server->on_close; sock->connected = true; return server; } rnet_socket_t sockets[NET_SOCKET_MAX_CONNECTIONS] = {0}; unsigned long sockets_connected = 0; int net_socket_max_fd = 0; unsigned long sockets_total = 0; unsigned long sockets_disconnected = 0; unsigned long sockets_concurrent_record = 0; unsigned long sockets_errors = 0; bool net_set_non_blocking(int sock) { int flags = fcntl(sock, F_GETFL, 0); if (flags < 0) { perror("fcntl"); return false; } if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) { perror("fcntl"); return false; } return true; } int net_socket_init() { int socket_fd = -1; memset(sockets, 0, sizeof(sockets)); int opt = 1; if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("Socket failed.\n"); return false; } if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { perror("Setsockopt failed.\n"); close(socket_fd); return false; } net_set_non_blocking(socket_fd); return socket_fd; } char *net_socket_name(int fd) { rnet_socket_t *rnet_socket = get_net_socket_by_fd(fd); if (rnet_socket) { return rnet_socket->name; ; } // If socket disconnected or is no client from server return NULL; } bool net_socket_bind(int socket_fd, unsigned int port) { struct sockaddr_in address; address.sin_family = AF_INET; // IPv4 address.sin_addr.s_addr = INADDR_ANY; // Bind to any available address address.sin_port = htons(port); // Convert port to network byte order if (bind(socket_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("Bind failed"); close(socket_fd); return false; } return true; } int net_socket_connect(const char *host, unsigned int port) { char port_str[10] = {0}; sprintf(port_str, "%d", port); int status; int socket_fd = -1; struct addrinfo hints; struct addrinfo *res; struct addrinfo *p; if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { return false; } memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; if ((status = getaddrinfo(host, port_str, &hints, &res)) != 0) { return -1; } for (p = res; p != NULL; p = p->ai_next) { if ((socket_fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { continue; } if (connect(socket_fd, p->ai_addr, p->ai_addrlen) == -1) { close(socket_fd); continue; } break; } if (p == NULL) { freeaddrinfo(res); return -1; } freeaddrinfo(res); return socket_fd; } bool net_socket_listen(int socket_fd, unsigned int backlog) { if (listen(socket_fd, backlog) < 0) { // '3' is the backlog size perror("Listen failed"); close(socket_fd); return false; } return true; } rnet_server_t *net_socket_serve(unsigned int port, unsigned int backlog) { signal(SIGPIPE, SIG_IGN); int socket_fd = net_socket_init(); net_socket_bind(socket_fd, port); net_socket_listen(socket_fd, backlog); return rnet_server_new(socket_fd, port, backlog); } int net_socket_accept(int net_socket_server_fd) { struct sockaddr_in address; int addrlen = sizeof(address); int new_socket = -1; if ((new_socket = accept(net_socket_server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) { close(new_socket); return -1; } else { return new_socket; } } /* static void net_socket_stats(WrenVM *vm) { wrenSetSlotNewList(vm, 0); wrenSetSlotString(vm, 1, "sockets_total"); wrenInsertInList(vm, 0, -1, 1); wrenSetSlotDouble(vm, 1, (double)sockets_total); wrenInsertInList(vm, 0, -1, 1); wrenSetSlotString(vm, 1, "sockets_concurrent_record"); wrenInsertInList(vm, 0, -1, 1); wrenSetSlotDouble(vm, 1, (double)sockets_concurrent_record); wrenInsertInList(vm, 0, -1, 1); wrenSetSlotString(vm, 1, "sockets_connected"); wrenInsertInList(vm, 0, -1, 1); wrenSetSlotDouble(vm, 1, (double)sockets_connected); wrenInsertInList(vm, 0, -1, 1); wrenSetSlotString(vm, 1, "sockets_disconnected"); wrenInsertInList(vm, 0, -1, 1); wrenSetSlotDouble(vm, 1, (double)sockets_disconnected); wrenInsertInList(vm, 0, -1, 1); }*/ size_t net_socket_write(rnet_socket_t *sock, unsigned char *message, size_t size) { ssize_t sent_total = 0; ssize_t sent = 0; ssize_t to_send = size; while ((sent = send(sock->fd, message, to_send, 0))) { if (sent == -1) { sockets_errors++; net_socket_close(sock); break; } if (sent == 0) { printf("EDGE CASE?\n"); exit(1); sockets_errors++; net_socket_close(sock); break; } sent_total += sent; if (sent_total == to_send) break; } return sent_total; } unsigned char *net_socket_read(rnet_socket_t *sock, unsigned int buff_size) { if (buff_size > 1024 * 1024 + 1) { perror("Buffer too big. Maximum is 1024*1024.\n"); exit(1); } static unsigned char buffer[1024 * 1024]; buffer[0] = 0; ssize_t received = recv(sock->fd, buffer, buff_size, 0); if (received <= 0) { buffer[0] = 0; net_socket_close(sock); if (received < 0) { sockets_errors++; return NULL; } } buffer[received + 1] = 0; sock->bytes_received = received; return buffer; } rnet_socket_t *net_socket_wait(rnet_socket_t *sock) { if (!sock) return NULL; if (sock->fd == -1) return NULL; fd_set read_fds; FD_ZERO(&read_fds); FD_SET(sock->fd, &read_fds); int max_socket_fd = sock->fd; int activity = select(max_socket_fd + 1, &read_fds, NULL, NULL, NULL); if ((activity < 0) && (errno != EINTR)) { // perror("Select error"); net_socket_close(sock); return NULL; } if (FD_ISSET(sock->fd, &read_fds)) { return sock; } return NULL; } void rnet_safe_str(char *str, size_t length) { if (!str || !length || !*str) return; for (unsigned int i = 0; i < length; i++) { if (str[i] < 32 || str[i] > 126) if (str[i] != 0) str[i] = '.'; } str[length] = 0; } rnet_select_result_t *rnet_new_socket_select_result(int socket_fd) { rnet_select_result_t *result = (rnet_select_result_t *)malloc(sizeof(rnet_select_result_t)); memset(result, 0, sizeof(rnet_select_result_t)); result->server_fd = socket_fd; result->socket_count = 0; result->sockets = NULL; return result; } void rnet_select_result_add(rnet_select_result_t *result, rnet_socket_t *sock) { result->sockets = realloc(result->sockets, sizeof(rnet_socket_t *) * (result->socket_count + 1)); result->sockets[result->socket_count] = sock; result->socket_count++; } void rnet_select_result_free(rnet_select_result_t *result) { free(result); } rnet_select_result_t *net_socket_select(rnet_server_t *server) { fd_set read_fds; FD_ZERO(&read_fds); FD_SET(server->socket_fd, &read_fds); server->max_fd = server->socket_fd; int socket_fd = -1; for (unsigned int i = 0; i < server->socket_count; i++) { socket_fd = server->sockets[i]->fd; if (!server->sockets[i]->connected) { continue; } if (socket_fd > 0) { FD_SET(socket_fd, &read_fds); if (socket_fd > server->max_fd) { server->max_fd = socket_fd; } } } int new_socket = -1; struct sockaddr_in address; int addrlen = sizeof(struct sockaddr_in); int activity = select(server->max_fd + 1, &read_fds, NULL, NULL, NULL); if ((activity < 0) && (errno != EINTR)) { perror("Select error\n"); return NULL; } if (FD_ISSET(server->socket_fd, &read_fds)) { if ((new_socket = accept(server->socket_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) { perror("Accept failed\n"); return NULL; } // net_set_non_blocking(new_socket); char name[50] = {0}; sprintf(name, "fd:%.4d:ip:%12s:port:%.6d", new_socket, inet_ntoa(address.sin_addr), ntohs(address.sin_port)); rnet_socket_t *sock_obj = NULL; for (unsigned int i = 0; i < server->socket_count; i++) { if (server->sockets && server->sockets[i]->fd == -1) { sock_obj = server->sockets[i]; } } if (!sock_obj) { sock_obj = (rnet_socket_t *)malloc(sizeof(rnet_socket_t)); rnet_server_add_socket(server, sock_obj); } sock_obj->fd = new_socket; strcpy(sock_obj->name, name); sockets_connected++; sockets_total++; sockets_concurrent_record = sockets_connected > sockets_concurrent_record ? sockets_connected : sockets_concurrent_record; if (new_socket > net_socket_max_fd) { net_socket_max_fd = new_socket; } sock_obj->connected = true; sock_obj->on_connect(sock_obj); } rnet_select_result_t *result = rnet_new_socket_select_result(server->socket_fd); unsigned int readable_count = 0; for (unsigned int i = 0; i < server->socket_count; i++) { if (server->sockets[i]->fd == -1) continue; if (FD_ISSET(server->sockets[i]->fd, &read_fds)) { rnet_select_result_add(result, server->sockets[i]); readable_count++; if (server->sockets[i]->on_read) { server->sockets[i]->on_read(server->sockets[i]); } } } if (server->select_result) { rnet_select_result_free(server->select_result); server->select_result = NULL; } if (readable_count == 0) rnet_select_result_free(result); return readable_count ? result : NULL; } rnet_socket_t *get_net_socket_by_fd(int sock) { for (int i = 0; i < net_socket_max_fd; i++) { if (sockets[i].fd == sock) { return &sockets[i]; } } return NULL; } void _net_socket_close(int sock) { if (sock > 0) { sockets_connected--; sockets_disconnected++; if (sock > 0) { if (close(sock) == -1) { perror("Error closing socket.\n"); } } } } void net_socket_close(rnet_socket_t *sock) { sock->connected = false; if (sock->on_close) sock->on_close(sock); _net_socket_close(sock->fd); sock->fd = -1; } #undef _POSIX_C_SOURCE #endif #include <stdio.h> #ifndef RLIB_RARGS_H #define RLIB_RARGS_H #include <stdbool.h> #include <stdlib.h> #include <string.h> bool rargs_isset(int argc, char *argv[], char *key) { for (int i = 0; i < argc; i++) { if (!strcmp(argv[i], key)) { return true; } } return false; } char *rargs_get_option_string(int argc, char *argv[], char *key, const char *def) { for (int i = 0; i < argc; i++) { if (!strcmp(argv[i], key)) { if (i < argc - 1) { return argv[i + 1]; } } } return (char *)def; } int rargs_get_option_int(int argc, char *argv[], char *key, int def) { for (int i = 0; i < argc; i++) { if (!strcmp(argv[i], key)) { if (i < argc - 1) { return atoi(argv[i + 1]); } } } return def; } bool rargs_get_option_bool(int argc, char *argv[], char *key, bool def) { for (int i = 0; i < argc; i++) { if (!strcmp(argv[i], key)) { if (i < argc - 1) { if (!strcmp(argv[i + 1], "false")) return false; if (!strcmp(argv[i + 1], "0")) return false; return true; } } } return def; } #endif #ifndef RCAT_H #define RCAT_H #include <stdio.h> #include <stdlib.h> void rcat(char *filename) { FILE *f = fopen(filename, "rb"); if (!f) { printf("rcat: couldn't open \"%s\" for read.\n", filename); return; } unsigned char c; while ((c = fgetc(f)) && !feof(f)) { printf("%c", c); } fclose(f); fflush(stdout); } int rcat_main(int argc, char *argv[]) { if (argc < 2) { printf("Usage: [filename]\n"); return 1; } rcat(argv[1]); return 0; } #endif #ifndef RLIZA_H #define RLIZA_H #ifndef RBUFFER_H #define RBUFFER_H #include <assert.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct rbuffer_t { unsigned char *data; unsigned char *_data; size_t size; size_t pos; bool eof; } rbuffer_t; rbuffer_t *rbuffer_new(unsigned char *data, size_t size); void rbuffer_free(rbuffer_t *rfb); void rbuffer_reset(rbuffer_t *rfb); void rbuffer_write(rbuffer_t *rfb, const unsigned char *data, size_t size); size_t rbuffer_push(rbuffer_t *rfb, unsigned char); unsigned char rbuffer_pop(rbuffer_t *rfb); unsigned char *rbuffer_expect(rbuffer_t *rfb, char *options, char *ignore); void rbuffer_set(rbuffer_t *rfb, const unsigned char *data, size_t size); void rbuffer_set(rbuffer_t *rfb, const unsigned char *data, size_t size) { if (rfb->_data) { free(rfb->_data); rfb->_data = NULL; rfb->data = NULL; rfb->eof = true; } if (size) { rfb->_data = (unsigned char *)malloc(size); memcpy(rfb->_data, data, size); rfb->data = rfb->_data; rfb->eof = false; } rfb->size = size; rfb->pos = 0; } rbuffer_t *rbuffer_new(unsigned char *data, size_t size) { rbuffer_t *rfb = (rbuffer_t *)malloc(sizeof(rbuffer_t)); if (size) { rfb->_data = (unsigned char *)malloc(size); memcpy(rfb->_data, data, size); rfb->eof = false; } else { rfb->_data = NULL; rfb->eof = true; } rfb->size = size; rfb->pos = 0; rfb->data = rfb->_data; return rfb; } void rbuffer_free(rbuffer_t *rfb) { if (rfb->_data) free(rfb->_data); free(rfb); } size_t rbuffer_push(rbuffer_t *rfb, unsigned char c) { if (rfb->pos < rfb->size) { rfb->_data[rfb->pos++] = c; return 1; } rfb->_data = realloc(rfb->_data, rfb->size ? rfb->size + 1 : rfb->size + 2); rfb->_data[rfb->pos++] = c; rfb->size++; return rfb->pos; } void rbuffer_write(rbuffer_t *rfb, const unsigned char *data, size_t size) { unsigned char *data_ptr = (unsigned char *)data; for (size_t i = 0; i < size; i++) { rbuffer_push(rfb, data_ptr[i]); } } unsigned char rbuffer_peek(rbuffer_t *rfb) { unsigned char result = EOF; if (rfb->pos != rfb->size) { result = rfb->_data[rfb->pos]; return result; } rfb->eof = true; return EOF; } unsigned char rbuffer_pop(rbuffer_t *rfb) { unsigned char result = EOF; if (rfb->pos <= rfb->size) { result = rfb->_data[rfb->pos]; rfb->pos++; rfb->data++; if (rfb->pos == rfb->size) { rfb->eof = true; } return result; } rfb->eof = true; return result; } void rbuffer_reset(rbuffer_t *rfb) { rfb->data = rfb->_data; rfb->pos = 0; } unsigned char ustrncmp(const unsigned char *s1, const unsigned char *s2, size_t n) { return strncmp((char *)s1, (char *)s2, n); while (n && *s1 == *s2) { n--; s1++; s2++; } return *s1 != *s2; } size_t ustrlen(const unsigned char *s) { return strlen((char *)s); } unsigned char *rbuffer_to_string(rbuffer_t *rfb) { unsigned char *result = rfb->_data; rfb->_data = NULL; rfb->data = NULL; rbuffer_free(rfb); return result; } unsigned char *rbuffer_match_option(rbuffer_t *rfb, char *options) { char *option = NULL; char options_cpy[1024] = {0}; strcpy(options_cpy, options); char *memory = options_cpy; while ((option = strtok_r(option == NULL ? memory : NULL, "|", &memory)) != NULL) { size_t option_length = strlen(option); if (option_length > rfb->size - rfb->pos) { continue; } if (!strcmp(option, "\\d") && *rfb->data >= '0' && *rfb->data <= '9') { return rfb->data; } if (rfb->size - rfb->pos >= 5 && !strcmp(option, "\\b") && ((!ustrncmp(rfb->data, (unsigned char *)"true", 4) || !ustrncmp(rfb->data, (unsigned char *)"false", 5)))) { return rfb->data; } if (!ustrncmp(rfb->data, (unsigned char *)option, option_length)) { return rfb->data; } } return NULL; } unsigned char *rbuffer_expect(rbuffer_t *rfb, char *options, char *ignore) { while (rfb->pos < rfb->size) { if (rbuffer_match_option(rfb, options) != NULL) { return rfb->data; } if (rbuffer_match_option(rfb, ignore)) { printf("SKIP:%s\n", rfb->data); rbuffer_pop(rfb); continue; } break; } return NULL; } unsigned char *rbuffer_consume(rbuffer_t *rfb, char *options, char *ignore) { unsigned char *result = NULL; if ((result = rbuffer_expect(rfb, options, ignore)) != NULL) { rbuffer_pop(rfb); } return result; } #endif #ifndef RSTRING_H #define RSTRING_H #ifndef RMATH_H #define RMATH_H #include <math.h> #ifndef ceil double ceil(double x) { if (x == (double)(long long)x) { return x; } else if (x > 0.0) { return (double)(long long)x + 1.0; } else { return (double)(long long)x; } } #endif #ifndef floor double floor(double x) { if (x >= 0.0) { return (double)(long long)x; } else { double result = (double)(long long)x; return (result == x) ? result : result - 1.0; } } #endif #ifndef modf double modf(double x, double *iptr) { double int_part = (x >= 0.0) ? floor(x) : ceil(x); *iptr = int_part; return x - int_part; } #endif #endif #include <ctype.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> char *rstrtimestamp() { time_t current_time; time(¤t_time); struct tm *local_time = localtime(¤t_time); static char time_string[100]; time_string[0] = 0; strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S", local_time); return time_string; } ulonglong _r_generate_key_current = 0; char *_rcat_int_int(int a, int b) { static char res[20]; res[0] = 0; sprintf(res, "%d%d", a, b); return res; } char *_rcat_int_double(int a, double b) { static char res[20]; res[0] = 0; sprintf(res, "%d%f", a, b); return res; } char *_rcat_charp_int(char *a, int b) { char res[20]; sprintf(res, "%c", b); return strcat(a, res); } char *_rcat_charp_double(char *a, double b) { char res[20]; sprintf(res, "%f", b); return strcat(a, res); } char *_rcat_charp_charp(char *a, char *b) { ; return strcat(a, b); } char *_rcat_charp_char(char *a, char b) { char extra[] = {b, 0}; return strcat(a, extra); } char *_rcat_charp_bool(char *a, bool *b) { if (b) { return strcat(a, "true"); } else { return strcat(a, "false"); } } #define rcat(x, y) \ _Generic((x), \ int: _Generic((y), int: _rcat_int_int, double: _rcat_int_double, char *: _rcat_charp_charp), \ char *: _Generic((y), \ int: _rcat_charp_int, \ double: _rcat_charp_double, \ char *: _rcat_charp_charp, \ char: _rcat_charp_char, \ bool: _rcat_charp_bool))((x), (y)) char *rgenerate_key() { _r_generate_key_current++; static char key[100]; key[0] = 0; sprintf(key, "%lld", _r_generate_key_current); return key; } char *rformat_number(long long lnumber) { static char formatted[1024]; char number[1024] = {0}; sprintf(number, "%lld", lnumber); int len = strlen(number); int commas_needed = (len - 1) / 3; int new_len = len + commas_needed; formatted[new_len] = '\0'; int i = len - 1; int j = new_len - 1; int count = 0; while (i >= 0) { if (count == 3) { formatted[j--] = '.'; count = 0; } formatted[j--] = number[i--]; count++; } if (lnumber < 0) formatted[j--] = '-'; return formatted; } bool rstrextractdouble(char *str, double *d1) { for (size_t i = 0; i < strlen(str); i++) { if (isdigit(str[i])) { str += i; sscanf(str, "%lf", d1); return true; } } return false; } void rstrstripslashes(const char *content, char *result) { size_t content_length = strlen((char *)content); unsigned int index = 0; for (unsigned int i = 0; i < content_length; i++) { char c = content[i]; if (c == '\\') { i++; c = content[i]; if (c == 'r') { c = '\r'; } else if (c == 't') { c = '\t'; } else if (c == 'b') { c = '\b'; } else if (c == 'n') { c = '\n'; } else if (c == 'f') { c = '\f'; } else if (c == '\\') { // No need tbh c = '\\'; i++; } } result[index] = c; index++; } result[index] = 0; } int rstrstartswith(const char *s1, const char *s2) { if (s1 == NULL) return s2 == NULL; if (s1 == s2 || s2 == NULL || *s2 == 0) return true; size_t len_s2 = strlen(s2); size_t len_s1 = strlen(s1); if (len_s2 > len_s1) return false; return !strncmp(s1, s2, len_s2); } bool rstrendswith(const char *s1, const char *s2) { if (s1 == NULL) return s2 == NULL; if (s1 == s2 || s2 == NULL || *s2 == 0) return true; size_t len_s2 = strlen(s2); size_t len_s1 = strlen(s1); if (len_s2 > len_s1) { return false; } s1 += len_s1 - len_s2; return !strncmp(s1, s2, len_s2); } void rstraddslashes(const char *content, char *result) { size_t content_length = strlen((char *)content); unsigned int index = 0; for (unsigned int i = 0; i < content_length; i++) { if (content[i] == '\r') { result[index] = '\\'; index++; result[index] = 'r'; index++; continue; } else if (content[i] == '\t') { result[index] = '\\'; index++; result[index] = 't'; index++; continue; } else if (content[i] == '\n') { result[index] = '\\'; index++; result[index] = 'n'; index++; continue; } else if (content[i] == '\\') { result[index] = '\\'; index++; result[index] = '\\'; index++; continue; } else if (content[i] == '\b') { result[index] = '\\'; index++; result[index] = 'b'; index++; continue; } else if (content[i] == '\f') { result[index] = '\\'; index++; result[index] = 'f'; index++; continue; } else if (content[i] == '"') { result[index] = '\\'; index++; result[index] = '"'; index++; continue; } result[index] = content[i]; index++; result[index] = 0; } } int rstrip_whitespace(char *input, char *output) { output[0] = 0; int count = 0; size_t len = strlen(input); for (size_t i = 0; i < len; i++) { if (input[i] == '\t' || input[i] == ' ' || input[i] == '\n') { continue; } count = i; size_t j; for (j = 0; j < len - count; j++) { output[j] = input[j + count]; } output[j] = '\0'; break; } return count; } /* * Converts "pony" to \"pony\". Addslashes does not * Converts "pony\npony" to "pony\n" * "pony" */ void rstrtocstring(const char *input, char *output) { int index = 0; char clean_input[strlen(input) * 2]; char *iptr = clean_input; rstraddslashes(input, clean_input); output[index] = '"'; index++; while (*iptr) { if (*iptr == '"') { output[index] = '\\'; output++; } else if (*iptr == '\\' && *(iptr + 1) == 'n') { output[index] = '\\'; output++; output[index] = 'n'; output++; output[index] = '"'; output++; output[index] = '\n'; output++; output[index] = '"'; output++; iptr++; iptr++; continue; } output[index] = *iptr; index++; iptr++; } if (output[index - 1] == '"' && output[index - 2] == '\n') { output[index - 1] = 0; } else if (output[index - 1] != '"') { output[index] = '"'; output[index + 1] = 0; } } size_t rstrtokline(char *input, char *output, size_t offset, bool strip_nl) { size_t len = strlen(input); output[0] = 0; size_t new_offset = 0; size_t j; size_t index = 0; for (j = offset; j < len + offset; j++) { if (input[j] == 0) { index++; break; } index = j - offset; output[index] = input[j]; if (output[index] == '\n') { index++; break; } } output[index] = 0; new_offset = index + offset; if (strip_nl) { if (output[index - 1] == '\n') { output[index - 1] = 0; } } return new_offset; } void rstrjoin(char **lines, size_t count, char *glue, char *output) { output[0] = 0; for (size_t i = 0; i < count; i++) { strcat(output, lines[i]); if (i != count - 1) strcat(output, glue); } } int rstrsplit(char *input, char **lines) { int index = 0; size_t offset = 0; char line[1024]; while ((offset = rstrtokline(input, line, offset, false)) && *line) { if (!*line) { break; } lines[index] = (char *)malloc(strlen(line) + 1); strcpy(lines[index], line); index++; } return index; } bool rstartswithnumber(char *str) { return isdigit(str[0]); } void rstrmove2(char *str, unsigned int start, size_t length, unsigned int new_pos) { size_t str_len = strlen(str); char new_str[str_len + 1]; memset(new_str, 0, str_len); if (start < new_pos) { strncat(new_str, str + length, str_len - length - start); new_str[new_pos] = 0; strncat(new_str, str + start, length); strcat(new_str, str + strlen(new_str)); memset(str, 0, str_len); strcpy(str, new_str); } else { strncat(new_str, str + start, length); strncat(new_str, str, start); strncat(new_str, str + start + length, str_len - start); memset(str, 0, str_len); strcpy(str, new_str); } new_str[str_len] = 0; } void rstrmove(char *str, unsigned int start, size_t length, unsigned int new_pos) { size_t str_len = strlen(str); if (start >= str_len || new_pos >= str_len || start + length > str_len) { return; } char temp[length + 1]; strncpy(temp, str + start, length); temp[length] = 0; if (start < new_pos) { memmove(str + start, str + start + length, new_pos - start); strncpy(str + new_pos - length + 1, temp, length); } else { memmove(str + new_pos + length, str + new_pos, start - new_pos); strncpy(str + new_pos, temp, length); } } int cmp_line(const void *left, const void *right) { char *l = *(char **)left; char *r = *(char **)right; char lstripped[strlen(l) + 1]; rstrip_whitespace(l, lstripped); char rstripped[strlen(r) + 1]; rstrip_whitespace(r, rstripped); double d1, d2; bool found_d1 = rstrextractdouble(lstripped, &d1); bool found_d2 = rstrextractdouble(rstripped, &d2); if (found_d1 && found_d2) { double frac_part1; double int_part1; frac_part1 = modf(d1, &int_part1); double frac_part2; double int_part2; frac_part2 = modf(d2, &int_part2); if (d1 == d2) { return strcmp(lstripped, rstripped); } else if (frac_part1 && frac_part2) { return d1 > d2; } else if (frac_part1 && !frac_part2) { return 1; } else if (frac_part2 && !frac_part1) { return -1; } else if (!frac_part1 && !frac_part2) { return d1 > d2; } } return 0; } int rstrsort(char *input, char *output) { char **lines = (char **)malloc(strlen(input) * 10); int line_count = rstrsplit(input, lines); qsort(lines, line_count, sizeof(char *), cmp_line); rstrjoin(lines, line_count, "", output); for (int i = 0; i < line_count; i++) { free(lines[i]); } free(lines); return line_count; } #endif #include <assert.h> #include <ctype.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> typedef enum rliza_type_t { RLIZA_STRING = 's', RLIZA_BOOLEAN = 'b', RLIZA_NUMBER = 'n', RLIZA_OBJECT = 'o', RLIZA_ARRAY = 'a', RLIZA_NULL = 0, RLIZA_KEY = 'k', RLIZA_INTEGER = 'i' } rliza_type_t; typedef struct rliza_t { rliza_type_t type; struct rliza_t *value; char *key; union { char *string; bool boolean; double number; struct rliza_t **map; long long integer; } content; unsigned int count; char *(*get_string)(struct rliza_t *, char *); long long (*get_integer)(struct rliza_t *, char *); double (*get_number)(struct rliza_t *, char *); bool (*get_boolean)(struct rliza_t *, char *); struct rliza_t *(*get_array)(struct rliza_t *, char *); struct rliza_t *(*get_object)(struct rliza_t *, char *); void (*set_string)(struct rliza_t *, char *, char *); void (*set_integer)(struct rliza_t *, char *, long long); void (*set_number)(struct rliza_t *, char *, double); void (*set_boolean)(struct rliza_t *, char *, bool); void (*set_array)(struct rliza_t *self, char *key, struct rliza_t *array); void (*set_object)(struct rliza_t *self, char *key, struct rliza_t *object); } rliza_t; void rliza_free(rliza_t *rliza) { if (rliza->key) { free(rliza->key); rliza->key = NULL; } if (rliza->value) { rliza_free(rliza->value); rliza->value = NULL; } // if (rliza->content.array) { // printf("JAAAA\n"); // } // if (rliza->content.object) { // rliza_free(rliza->content.object); // rliza->content.object = NULL; //} if (rliza->type == RLIZA_STRING) { if (rliza->content.string) { free(rliza->content.string); rliza->content.string = NULL; // else if (rliza->type == RLIZA_NUMBER) { // printf("STDring freed\n"); } } else if (rliza->type == RLIZA_OBJECT || rliza->type == RLIZA_ARRAY) { if (rliza->content.map) { for (unsigned int i = 0; i < rliza->count; i++) { rliza_free(rliza->content.map[i]); } free(rliza->content.map); } } // free(rliza->content.array); //} free(rliza); } rliza_t *rliza_new(rliza_type_t type); rliza_t *rliza_new_string(char *string); rliza_t *rliza_new_null(); rliza_t *rliza_new_boolean(bool value); rliza_t *rliza_new_number(double value); rliza_t *rliza_new_integer(long long value); rliza_t *rliza_new_key_value(char *key, rliza_t *value); rliza_t *rliza_new_key_string(char *key, char *string); rliza_t *rliza_new_key_bool(char *key, bool value); rliza_t *rliza_new_key_number(char *key, double value); void rliza_push(rliza_t *self, rliza_t *obj); void rliza_push_object(rliza_t *self, rliza_t *object); void rliza_set_object(rliza_t *self, char *key, rliza_t *object); void rliza_set_string(rliza_t *self, char *key, char *string); void rliza_set_boolean(rliza_t *self, char *key, bool value); void rliza_set_number(rliza_t *self, char *key, double value); void rliza_set_integer(rliza_t *self, char *key, long long value); char *rliza_get_string(rliza_t *self, char *key); long long rliza_get_integer(rliza_t *self, char *key); double rliza_get_number(rliza_t *self, char *key); bool rliza_get_boolean(rliza_t *self, char *key); rliza_t *rliza_get_array(rliza_t *self, char *key); rliza_t *rliza_get_object(rliza_t *self, char *key); void rliza_set_array(rliza_t *self, char *key, rliza_t *array); char *rliza_dumps(rliza_t *rliza); rliza_t *rliza_loads(char **content); rliza_t *_rliza_loads(char **content); char *rliza_get_string(rliza_t *self, char *key) { for (unsigned int i = 0; i < self->count; i++) { if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) { if (self->content.map[i]->type == RLIZA_STRING || self->content.map[i]->type == RLIZA_NULL) { return self->content.map[i]->content.string; } } } return NULL; } long long rliza_get_integer(rliza_t *self, char *key) { for (unsigned int i = 0; i < self->count; i++) { if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) { if (self->content.map[i]->type == RLIZA_INTEGER || self->content.map[i]->type == RLIZA_NULL) { return self->content.map[i]->content.integer; } } } return 0; } double rliza_get_number(rliza_t *self, char *key) { for (unsigned int i = 0; i < self->count; i++) { if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) { if (self->content.map[i]->type == RLIZA_NUMBER || self->content.map[i]->type == RLIZA_NULL) { return self->content.map[i]->content.number; } } } return 0; } bool rliza_get_boolean(rliza_t *self, char *key) { for (unsigned int i = 0; i < self->count; i++) { if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) { if (self->content.map[i]->type == RLIZA_BOOLEAN || self->content.map[i]->type == RLIZA_NULL) { return self->content.map[i]->content.boolean; } } } return false; } rliza_t *rliza_get_object(rliza_t *self, char *key) { for (unsigned int i = 0; i < self->count; i++) { if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) { return self->content.map[i]; } } return NULL; } rliza_t *rliza_get_array(rliza_t *self, char *key) { for (unsigned int i = 0; i < self->count; i++) { if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) { if (self->content.map[i]->type == RLIZA_ARRAY || self->content.map[i]->type == RLIZA_NULL) { return self->content.map[i]; } } } return NULL; } rliza_t *rliza_new_null() { rliza_t *rliza = rliza_new(RLIZA_NULL); return rliza; } rliza_t *rliza_new_string(char *string) { rliza_t *rliza = rliza_new(RLIZA_STRING); if (string == NULL) { rliza->type = RLIZA_NULL; rliza->content.string = NULL; return rliza; } else { rliza->content.string = strdup(string); } return rliza; } rliza_t *rliza_new_boolean(bool value) { rliza_t *rliza = rliza_new(RLIZA_BOOLEAN); rliza->content.boolean = value; return rliza; } rliza_t *rliza_new_number(double value) { rliza_t *rliza = rliza_new(RLIZA_NUMBER); rliza->content.number = value; return rliza; } rliza_t *rliza_new_integer(long long value) { rliza_t *rliza = rliza_new(RLIZA_INTEGER); rliza->content.integer = value; return rliza; } rliza_t *rliza_new_key_array(char *key) { rliza_t *rliza = rliza_new(RLIZA_ARRAY); rliza->key = strdup(key); return rliza; } rliza_t *rliza_new_key_value(char *key, rliza_t *value) { rliza_t *rliza = rliza_new(RLIZA_OBJECT); if (key) { rliza->key = strdup(key); } rliza->value = value; return rliza; } rliza_t *rliza_new_key_string(char *key, char *string) { rliza_t *rliza = rliza_new_key_value(key, rliza_new_string(string)); return rliza; } rliza_t *rliza_new_key_bool(char *key, bool value) { rliza_t *rliza = rliza_new_key_value(key, rliza_new_boolean(value)); return rliza; } rliza_t *rliza_new_key_number(char *key, double value) { rliza_t *rliza = rliza_new_key_value(key, rliza_new_number(value)); return rliza; } void rliza_set_null(rliza_t *self, char *key) { rliza_t *obj = rliza_get_object(self, key); if (!obj) { obj = rliza_new_null(); obj->key = strdup(key); rliza_push_object(self, obj); } if (obj->type == RLIZA_OBJECT) { rliza_free(obj->value); obj->value = NULL; } else if (obj->type == RLIZA_STRING) { if (obj->content.string) free(obj->content.string); obj->content.string = NULL; } else if (obj->type == RLIZA_ARRAY) { for (unsigned int i = 0; i < obj->count; i++) { rliza_free(obj->content.map[i]); } } else if (obj->type == RLIZA_NUMBER) { obj->content.number = 0; } else if (obj->type == RLIZA_INTEGER) { obj->content.integer = 0; } obj->type = RLIZA_NULL; } rliza_t *rliza_duplicate(rliza_t *rliza) { if (!rliza) return NULL; char *str = rliza_dumps(rliza); char *strp = str; rliza_t *obj = rliza_loads(&strp); free(str); return obj; } rliza_t *rliza_new_object(rliza_t *obj) { rliza_t *rliza = rliza_new(RLIZA_OBJECT); rliza->value = obj; return rliza; } void rliza_set_object(rliza_t *self, char *key, rliza_t *value) { rliza_t *obj = rliza_duplicate(value); obj->key = strdup(key); obj->type = RLIZA_OBJECT; rliza_push(self, obj); } void rliza_set_string(rliza_t *self, char *key, char *string) { rliza_t *obj = rliza_get_object(self, key); if (!obj) { obj = rliza_new_string(string); obj->key = strdup(key); obj->type = RLIZA_STRING; rliza_push_object(self, obj); } else { obj->content.string = strdup(string); } } void rliza_set_array(rliza_t *self, char *key, rliza_t *array) { rliza_t *obj = rliza_get_object(self, key); if (obj) rliza_free(obj); if (array->key) { free(array->key); array->key = strdup(key); } rliza_push_object(self, array); } void rliza_set_number(rliza_t *self, char *key, double value) { rliza_t *obj = rliza_get_object(self, key); if (!obj) { obj = rliza_new_number(value); obj->key = strdup(key); obj->type = RLIZA_NUMBER; rliza_push_object(self, obj); } else { obj->content.number = value; } } void rliza_push_object(rliza_t *self, rliza_t *object) { self->content.map = realloc(self->content.map, (sizeof(rliza_t **)) * (self->count + 1)); self->content.map[self->count] = object; self->count++; } void rliza_set_integer(rliza_t *self, char *key, long long value) { rliza_t *obj = rliza_get_object(self, key); if (!obj) { obj = rliza_new_integer(value); obj->key = strdup(key); obj->type = RLIZA_INTEGER; rliza_push_object(self, obj); } else { obj->content.integer = value; } } void rliza_set_boolean(rliza_t *self, char *key, bool value) { rliza_t *obj = rliza_get_object(self, key); if (!obj) { obj = rliza_new_boolean(value); obj->key = strdup(key); obj->type = RLIZA_BOOLEAN; rliza_push_object(self, obj); } else { obj->content.boolean = value; } } rliza_t *rliza_new(rliza_type_t type) { rliza_t *rliza = (rliza_t *)calloc(1, sizeof(rliza_t)); rliza->type = type; rliza->get_boolean = rliza_get_boolean; rliza->get_integer = rliza_get_integer; rliza->get_number = rliza_get_number; rliza->get_string = rliza_get_string; rliza->get_array = rliza_get_array; rliza->get_object = rliza_get_object; rliza->set_string = rliza_set_string; rliza->set_number = rliza_set_number; rliza->set_boolean = rliza_set_boolean; rliza->set_integer = rliza_set_integer; rliza->set_array = rliza_set_array; rliza->set_object = rliza_set_object; return rliza; } void *rliza_coalesce(void *result, void *default_value) { if (result == NULL) return default_value; return result; } char *rliza_seek_string(char **content, char **options) { while (**content == ' ' || **content == '\n' || **content == '\t' || **content == '\r') { (*content)++; } if (**content == 0) { return NULL; } char *option = NULL; unsigned int option_index = 0; while (true) { option = options[option_index]; if (option == NULL) break; option_index++; if (option[0] == 'd') { if (**content >= '0' && **content <= '9') { return (char *)*content; } } else if (!strncmp(option, *content, strlen(option))) { return (char *)*content; } } return *content; } char *rliza_extract_quotes(char **content) { rbuffer_t *buffer = rbuffer_new(NULL, 0); assert(**content == '"'); char previous = 0; while (true) { (*content)++; if (!**content) { rbuffer_free(buffer); return NULL; } if (**content == '"' && previous != '\\') { break; } rbuffer_push(buffer, **content); previous = **content; } assert(**content == '"'); (*content)++; rbuffer_push(buffer, 0); char *result = (char *)rbuffer_to_string(buffer); return result; } rliza_t *_rliza_loads(char **content) { static char *seek_for1[] = {"[", "{", "\"", "d", "true", "false", "null", NULL}; char *token = (char *)rliza_seek_string(content, seek_for1); if (!token) return NULL; rliza_t *rliza = rliza_new(RLIZA_NULL); if (**content == '"') { char *extracted = rliza_extract_quotes(content); if (!extracted) { rliza_free(rliza); return NULL; } // char *extracted_with_slashes = (char *)malloc(strlen((char *)extracted) * 2 + 1); // rstraddslashes(extracted, extracted_with_slashes); rliza->type = RLIZA_STRING; rliza->content.string = extracted; // extracted_with_slashes; // extracted_without_slashes; // free(extracted); return rliza; } else if (**content == '{') { rliza->type = RLIZA_OBJECT; (*content)++; char *result = NULL; static char *seek_for2[] = {"\"", ",", "}", NULL}; while ((result = (char *)rliza_seek_string(content, seek_for2)) != NULL && *result) { if (!**content) { rliza_free(rliza); return NULL; } if (**content == ',') { (*content)++; if (!**content) { rliza_free(rliza); return NULL; } continue; } char *key = NULL; if (**content == '"') { key = rliza_extract_quotes((char **)content); if (!key || !*key) { rliza_free(rliza); return NULL; } char *escaped_key = (char *)malloc(strlen((char *)key) * 2 + 1); rstrstripslashes((char *)key, escaped_key); static char *seek_for3[] = {":", NULL}; char *devider = rliza_seek_string(content, seek_for3); if (!devider || !*devider) { free(escaped_key); free(key); rliza_free(rliza); return NULL; } (*content)++; if (!**content) { free(key); free(escaped_key); rliza_free(rliza); return NULL; } rliza_t *value = _rliza_loads(content); if (!value) { free(key); free(escaped_key); rliza_free(rliza); return NULL; } if (value->key) free(value->key); value->key = escaped_key; free(key); rliza_push_object(rliza, value); } else if (**content == '}') { break; } else { // Parse error rliza_free(rliza); return NULL; } }; if ((**content != '}')) { rliza_free(rliza); return NULL; } (*content)++; return rliza; } else if (**content == '[') { rliza->type = RLIZA_ARRAY; (*content)++; char *result; static char *seek_for4[] = {"[", "{", "\"", "d", ",", "]", "null", "true", "false", NULL}; while ((result = (char *)rliza_seek_string(content, seek_for4)) != NULL && *result) { if (**content == ',') { (*content)++; } else if (**content == ']') { break; } rliza_t *obj = _rliza_loads(content); if (!obj) { rliza_free(rliza); return NULL; } rliza_push(rliza, obj); if (!**content) { rliza_free(rliza); return NULL; } } if (**content != ']') { rliza_free(rliza); return NULL; } (*content)++; return rliza; } else if (**content >= '0' && **content <= '9') { char *ptr = *content; bool is_decimal = false; while (**content) { if (**content == '.') { is_decimal = true; } else if (!isdigit(**content)) { break; } (*content)++; } if (*(*content - 1) == '.') { rliza_free(rliza); return NULL; } if (!**content) { rliza_free(rliza); return NULL; } if (is_decimal) { rliza->type = RLIZA_NUMBER; rliza->content.number = strtod(ptr, NULL); } else { rliza->type = RLIZA_INTEGER; rliza->content.integer = strtoll(ptr, NULL, 10); } return rliza; } else if (!strncmp(*content, "true", 4)) { rliza->type = RLIZA_BOOLEAN; rliza->content.boolean = true; *content += 4; return rliza; } else if (!strncmp(*content, "false", 5)) { rliza->type = RLIZA_BOOLEAN; rliza->content.boolean = false; *content += 5; return rliza; } else if (!strncmp(*content, "null", 4)) { rliza->type = RLIZA_NULL; *content += 4; return rliza; } // Parsing error rliza_free(rliza); return NULL; } rliza_t *rliza_loads(char **content) { if (!content || !**content) { return NULL; } char *original_content = *content; rliza_t *result = _rliza_loads(content); if (!result) { *content = original_content; } return result; } char *rliza_dumps(rliza_t *rliza) { size_t size = 4096; char *content = (char *)calloc(size, sizeof(char)); content[0] = 0; if (rliza->type == RLIZA_INTEGER) { if (rliza->key) { sprintf(content, "\"%s\":%lld", rliza->key, rliza->content.integer); } else { sprintf(content, "%lld", rliza->content.integer); } } else if (rliza->type == RLIZA_STRING) { // char *escaped_string = (char *)calloc(strlen((char *)rliza->content.string) * 2 + 1024,sizeof(char)); char *escaped_string = rliza->content.string; // rstrstripslashes((char *)rliza->content.string, escaped_string); size_t min_size = strlen((char *)escaped_string) + (rliza->key ? strlen(rliza->key) : 0) + 1024; if (size < min_size) { size = min_size + 1; content = realloc(content, size); } if (rliza->key) { char *escaped_key = (char *)malloc(strlen((char *)rliza->key) * 2 + 20); rstrstripslashes((char *)rliza->key, escaped_key); if (strlen(content) > size) { size = size + strlen(escaped_string) + 20; content = realloc(content, size); } sprintf(content, "\"%s\":\"%s\"", escaped_key, escaped_string); free(escaped_key); } else { size = size + strlen(escaped_string) + 20; content = realloc(content, size); sprintf(content, "\"%s\"", escaped_string); } // free(escaped_string); } else if (rliza->type == RLIZA_NUMBER) { if (rliza->key) { sprintf(content, "\"%s\":%f", rliza->key, rliza->content.number); } else { sprintf(content, "%f", rliza->content.number); } int last_zero = 0; bool beyond_dot = false; for (size_t i = 0; i < strlen(content); i++) { if (content[i] == '.') { beyond_dot = true; } else if (beyond_dot == true) { if (content[i - 1] != '.') { if (content[i] == '0') { if (!last_zero) last_zero = i; } else { last_zero = 0; } } } } if (last_zero != 0) { content[last_zero] = 0; } } else if (rliza->type == RLIZA_BOOLEAN) { if (rliza->key) { sprintf(content, "\"%s\":%s", rliza->key, rliza->content.boolean ? "true" : "false"); } else { sprintf(content, "%s", rliza->content.boolean ? "true" : "false"); } } else if (rliza->type == RLIZA_OBJECT) { strcat(content, "{"); if (rliza->key) { strcat(content, "\""); strcat(content, rliza->key); strcat(content, "\":{"); } // bool add_braces = false; for (unsigned i = 0; i < rliza->count; i++) { char *content_chunk = rliza_dumps(rliza->content.map[i]); char *content_chunk_stripped = content_chunk; if (*content_chunk_stripped == '{') { content_chunk_stripped++; content_chunk_stripped[strlen(content_chunk_stripped) - 1] = 0; } if (strlen(content_chunk_stripped) + strlen(content) > size) { size += strlen(content_chunk_stripped) + 20; content = realloc(content, size); } strcat(content, content_chunk_stripped); free(content_chunk); strcat(content, ","); } if (content[strlen(content) - 1] == ',') { content[strlen(content) - 1] = '\0'; if (rliza->key) { strcat(content, "}"); } } strcat(content, "}"); } else if (rliza->type == RLIZA_ARRAY) { if (rliza->key) { char *escaped_key = (char *)malloc(strlen((char *)rliza->key) * 2 + 1); rstraddslashes((char *)rliza->key, escaped_key); if (strlen(escaped_key) > size) { size = strlen(escaped_key) + 10; content = realloc(content, size); } sprintf(content, "\"%s\":[", escaped_key); free(escaped_key); } else strcpy(content, "["); for (unsigned i = 0; i < rliza->count; i++) { char *content_chunk = rliza_dumps(rliza->content.map[i]); char *content_chunk_stripped = content_chunk; if (*content_chunk_stripped == '{') { // content_chunk_stripped++; // content_chunk_stripped[strlen(content_chunk_stripped) - 1] = 0; } if (strlen(content_chunk_stripped) + strlen(content) > size) { size += strlen(content_chunk_stripped) + 20; content = realloc(content, size); } strcat(content, content_chunk_stripped); free(content_chunk); strcat(content, ","); } if (content[strlen(content) - 1] != '[') content[strlen(content) - 1] = 0; strcat(content, "]"); } else if (rliza->type == RLIZA_NULL) { if (rliza->key) { char *escaped_key = (char *)malloc(strlen((char *)rliza->key) * 2 + 1); rstraddslashes((char *)rliza->key, escaped_key); sprintf(content, "\"%s\":null", escaped_key); free(escaped_key); } else strcpy(content, "null"); } return content; } void rliza_dumpss(rliza_t *rliza) { char *output = rliza_dumps(rliza); printf("%s\n", output); free(output); } void rliza_push(rliza_t *self, rliza_t *obj) { rliza_push_object(self, obj); } int rliza_validate(char *json_content) { if (!json_content || !*json_content) { return false; } char *json_contentp = json_content; rliza_t *to_object = _rliza_loads(&json_contentp); if (to_object) { rliza_free(to_object); return json_contentp - json_content; } return false; } #endif #ifndef RCOV_H #define RCOV_H #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #ifndef RBENCH_H #define RBENCH_H #ifndef RPRINT_H #define RPRINT_H #ifndef RLIB_TIME #define RLIB_TIME #ifndef _POSIX_C_SOURCE_199309L #define _POSIX_C_SOURCE_199309L #endif #include <sys/time.h> #include <time.h> #undef _POSIX_C_SOURCE_199309L #include <errno.h> #include <stdint.h> #include <stdio.h> #include <string.h> #ifndef CLOCK_MONOTONIC #define CLOCK_MONOTONIC 1 #endif typedef uint64_t nsecs_t; void nsleep(nsecs_t nanoseconds); void tick() { nsleep(1); } typedef unsigned long long msecs_t; nsecs_t nsecs() { unsigned int lo, hi; __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi)); return ((uint64_t)hi << 32) | lo; } msecs_t rnsecs_to_msecs(nsecs_t nsecs) { return nsecs / 1000 / 1000; } nsecs_t rmsecs_to_nsecs(msecs_t msecs) { return msecs * 1000 * 1000; } msecs_t usecs() { struct timeval tv; gettimeofday(&tv, NULL); return (long long)(tv.tv_sec) * 1000000 + (long long)(tv.tv_usec); } msecs_t msecs() { struct timeval tv; gettimeofday(&tv, NULL); return (long long)(tv.tv_sec) * 1000 + (tv.tv_usec / 1000); } char *msecs_strs(msecs_t ms) { static char str[22]; str[0] = 0; sprintf(str, "%f", ms * 0.001); for (int i = strlen(str); i > 0; i--) { if (str[i] > '0') break; str[i] = 0; } return str; } char *msecs_strms(msecs_t ms) { static char str[22]; str[0] = 0; sprintf(str, "%lld", ms); return str; } char *msecs_str(long long ms) { static char result[30]; result[0] = 0; if (ms > 999) { char *s = msecs_strs(ms); sprintf(result, "%ss", s); } else { char *s = msecs_strms(ms); sprintf(result, "%sMs", s); } return result; } void nsleep(nsecs_t nanoseconds) { long seconds = 0; int factor = 0; while (nanoseconds > 1000000000) { factor++; nanoseconds = nanoseconds / 10; } if (factor) { seconds = 1; factor--; while (factor) { seconds = seconds * 10; factor--; } } struct timespec req = {seconds, nanoseconds}; struct timespec rem; nanosleep(&req, &rem); } void ssleep(double s) { long nanoseconds = (long)(1000000000 * s); // long seconds = 0; // struct timespec req = {seconds, nanoseconds}; // struct timespec rem; nsleep(nanoseconds); } void msleep(long miliseonds) { long nanoseconds = miliseonds * 1000000; nsleep(nanoseconds); } char *format_time(int64_t nanoseconds) { char output[1024]; size_t output_size = sizeof(output); output[0] = 0; if (nanoseconds < 1000) { // Less than 1 microsecond snprintf(output, output_size, "%ldns", nanoseconds); } else if (nanoseconds < 1000000) { // Less than 1 millisecond double us = nanoseconds / 1000.0; snprintf(output, output_size, "%.2fµs", us); } else if (nanoseconds < 1000000000) { // Less than 1 second double ms = nanoseconds / 1000000.0; snprintf(output, output_size, "%.2fms", ms); } else { // 1 second or more double s = nanoseconds / 1000000000.0; if (s > 60 * 60) { s = s / 60 / 60; snprintf(output, output_size, "%.2fh", s); } else if (s > 60) { s = s / 60; snprintf(output, output_size, "%.2fm", s); } else { snprintf(output, output_size, "%.2fs", s); } } return sbuf(output); } #endif #include <stdarg.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> long rpline_number = 0; nsecs_t rprtime = 0; int8_t _env_rdisable_colors = -1; bool _rprint_enable_colors = true; bool rprint_is_color_enabled() { if (_env_rdisable_colors == -1) { _env_rdisable_colors = getenv("RDISABLE_COLORS") != NULL; } if (_env_rdisable_colors) { _rprint_enable_colors = false; } return _rprint_enable_colors; } void rprint_disable_colors() { _rprint_enable_colors = false; } void rprint_enable_colors() { _rprint_enable_colors = true; } void rprint_toggle_colors() { _rprint_enable_colors = !_rprint_enable_colors; } void rclear() { printf("\033[2J"); } void rprintpf(FILE *f, const char *prefix, const char *format, va_list args) { char *pprefix = (char *)prefix; char *pformat = (char *)format; bool reset_color = false; bool press_any_key = false; char new_format[4096]; bool enable_color = rprint_is_color_enabled(); memset(new_format, 0, 4096); int new_format_length = 0; char temp[1000]; memset(temp, 0, 1000); if (enable_color && pprefix[0]) { strcat(new_format, pprefix); new_format_length += strlen(pprefix); reset_color = true; } while (true) { if (pformat[0] == '\\' && pformat[1] == 'i') { strcat(new_format, "\e[3m"); new_format_length += strlen("\e[3m"); reset_color = true; pformat++; pformat++; } else if (pformat[0] == '\\' && pformat[1] == 'u') { strcat(new_format, "\e[4m"); new_format_length += strlen("\e[4m"); reset_color = true; pformat++; pformat++; } else if (pformat[0] == '\\' && pformat[1] == 'b') { strcat(new_format, "\e[1m"); new_format_length += strlen("\e[1m"); reset_color = true; pformat++; pformat++; } else if (pformat[0] == '\\' && pformat[1] == 'C') { press_any_key = true; rpline_number++; pformat++; pformat++; reset_color = false; } else if (pformat[0] == '\\' && pformat[1] == 'k') { press_any_key = true; rpline_number++; pformat++; pformat++; } else if (pformat[0] == '\\' && pformat[1] == 'c') { rpline_number++; strcat(new_format, "\e[2J\e[H"); new_format_length += strlen("\e[2J\e[H"); pformat++; pformat++; } else if (pformat[0] == '\\' && pformat[1] == 'L') { rpline_number++; temp[0] = 0; sprintf(temp, "%ld", rpline_number); strcat(new_format, temp); new_format_length += strlen(temp); pformat++; pformat++; } else if (pformat[0] == '\\' && pformat[1] == 'l') { rpline_number++; temp[0] = 0; sprintf(temp, "%.5ld", rpline_number); strcat(new_format, temp); new_format_length += strlen(temp); pformat++; pformat++; } else if (pformat[0] == '\\' && pformat[1] == 'T') { nsecs_t nsecs_now = nsecs(); nsecs_t end = rprtime ? nsecs_now - rprtime : 0; temp[0] = 0; sprintf(temp, "%s", format_time(end)); strcat(new_format, temp); new_format_length += strlen(temp); rprtime = nsecs_now; pformat++; pformat++; } else if (pformat[0] == '\\' && pformat[1] == 't') { rprtime = nsecs(); pformat++; pformat++; } else { new_format[new_format_length] = *pformat; new_format_length++; if (!*pformat) break; // printf("%c",*pformat); pformat++; } } if (reset_color) { strcat(new_format, "\e[0m"); new_format_length += strlen("\e[0m"); } new_format[new_format_length] = 0; vfprintf(f, new_format, args); fflush(stdout); if (press_any_key) { nsecs_t s = nsecs(); fgetc(stdin); rprtime += nsecs() - s; } } void rprintp(const char *format, ...) { va_list args; va_start(args, format); rprintpf(stdout, "", format, args); va_end(args); } void rprintf(FILE *f, const char *format, ...) { va_list args; va_start(args, format); rprintpf(f, "", format, args); va_end(args); } void rprint(const char *format, ...) { va_list args; va_start(args, format); rprintpf(stdout, "", format, args); va_end(args); } #define printf rprint // Print line void rprintlf(FILE *f, const char *format, ...) { va_list args; va_start(args, format); rprintpf(f, "\\l", format, args); va_end(args); } void rprintl(const char *format, ...) { va_list args; va_start(args, format); rprintpf(stdout, "\\l", format, args); va_end(args); } // Black void rprintkf(FILE *f, const char *format, ...) { va_list args; va_start(args, format); rprintpf(f, "\e[30m", format, args); va_end(args); } void rprintk(const char *format, ...) { va_list args; va_start(args, format); rprintpf(stdout, "\e[30m", format, args); va_end(args); } // Red void rprintrf(FILE *f, const char *format, ...) { va_list args; va_start(args, format); rprintpf(f, "\e[31m", format, args); va_end(args); } void rprintr(const char *format, ...) { va_list args; va_start(args, format); rprintpf(stdout, "\e[31m", format, args); va_end(args); } // Green void rprintgf(FILE *f, const char *format, ...) { va_list args; va_start(args, format); rprintpf(f, "\e[32m", format, args); va_end(args); } void rprintg(const char *format, ...) { va_list args; va_start(args, format); rprintpf(stdout, "\e[32m", format, args); va_end(args); } // Yellow void rprintyf(FILE *f, const char *format, ...) { va_list args; va_start(args, format); rprintpf(f, "\e[33m", format, args); va_end(args); } void rprinty(const char *format, ...) { va_list args; va_start(args, format); rprintpf(stdout, "\e[33m", format, args); va_end(args); } // Blue void rprintbf(FILE *f, const char *format, ...) { va_list args; va_start(args, format); rprintpf(f, "\e[34m", format, args); va_end(args); } void rprintb(const char *format, ...) { va_list args; va_start(args, format); rprintpf(stdout, "\e[34m", format, args); va_end(args); } // Magenta void rprintmf(FILE *f, const char *format, ...) { va_list args; va_start(args, format); rprintpf(f, "\e[35m", format, args); va_end(args); } void rprintm(const char *format, ...) { va_list args; va_start(args, format); rprintpf(stdout, "\e[35m", format, args); va_end(args); } // Cyan void rprintcf(FILE *f, const char *format, ...) { va_list args; va_start(args, format); rprintpf(f, "\e[36m", format, args); va_end(args); } void rprintc(const char *format, ...) { va_list args; va_start(args, format); rprintpf(stdout, "\e[36m", format, args); va_end(args); } // White void rprintwf(FILE *f, const char *format, ...) { va_list args; va_start(args, format); rprintpf(f, "\e[37m", format, args); va_end(args); } void rprintw(const char *format, ...) { va_list args; va_start(args, format); rprintpf(stdout, "\e[37m", format, args); va_end(args); } #endif #include <errno.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/time.h> #include <time.h> #ifndef RLIB_TERMINAL_H #define RLIB_TERMINAL_H #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #ifndef RTEST_H #define RTEST_H #ifndef REMO_H #define REMO_H #include <ctype.h> #include <stdbool.h> #include <stdio.h> #include <string.h> typedef struct { const char *str; const char *description; } remo_t; remo_t remo[] = { {"\U0001F600", "Grinning Face"}, // 😀 {"\U0001F601", "Beaming Face with Smiling Eyes"}, // 😁 {"\U0001F602", "Face with Tears of Joy"}, // 😂 {"\U0001F923", "Rolling on the Floor Laughing"}, // 🤣 {"\U0001F603", "Grinning Face with Big Eyes"}, // 😃 {"\U0001F604", "Grinning Face with Smiling Eyes"}, // 😄 {"\U0001F609", "Winking Face"}, // 😉 {"\U0001F60A", "Smiling Face with Smiling Eyes"}, // 😊 {"\U0001F60D", "Smiling Face with Heart-Eyes"}, // 😍 {"\U0001F618", "Face Blowing a Kiss"}, // 😘 {"\U0001F617", "Kissing Face"}, // 😗 {"\U0001F61A", "Kissing Face with Closed Eyes"}, // 😚 {"\U0001F642", "Slightly Smiling Face"}, // 🙂 {"\U0001F643", "Upside-Down Face"}, // 🙃 {"\U0001F970", "Smiling Face with Hearts"}, // 🥰 {"\U0001F60B", "Face Savoring Food"}, // 😋 {"\U0001F61B", "Face with Tongue"}, // 😛 {"\U0001F61C", "Winking Face with Tongue"}, // 😜 {"\U0001F92A", "Zany Face"}, // 🤪 {"\U0001F929", "Star-Struck"}, // 🤩 {"\U0001F631", "Face Screaming in Fear"}, // 😱 {"\U0001F62D", "Loudly Crying Face"}, // 😭 {"\U0001F624", "Face with Steam From Nose"}, // 😤 {"\U0001F620", "Angry Face"}, // 😠 {"\U0001F621", "Pouting Face"}, // 😡 {"\U0001F47B", "Ghost"}, // 👻 {"\U0001F480", "Skull"}, // 💀 {"\U0001F4A9", "Pile of Poo"}, // 💩 {"\U0001F47D", "Alien"}, // 👽 // Geometric Shapes {"\U000025A0", "Black Square"}, // ■ {"\U000025B2", "Upward Triangle"}, // ▲ {"\U000025CF", "Black Circle"}, // ● {"\U000025CB", "White Circle"}, // ○ {"\U00002B1B", "Large Black Square"}, // ⬛ {"\U00002B1C", "Large White Square"}, // ⬜ // Mathematical Symbols {"\U00002200", "For All"}, // ∀ {"\U00002203", "Exists"}, // ∃ {"\U00002205", "Empty Set"}, // ∅ {"\U00002207", "Nabla"}, // ∇ {"\U0000220F", "N-Ary Product"}, // ∏ {"\U00002212", "Minus Sign"}, // − {"\U0000221E", "Infinity"}, // ∞ // Arrows {"\U00002190", "Left Arrow"}, // ← {"\U00002191", "Up Arrow"}, // ↑ {"\U00002192", "Right Arrow"}, // → {"\U00002193", "Down Arrow"}, // ↓ {"\U00002195", "Up Down Arrow"}, // ↕ {"\U00002197", "Up Right Arrow"}, // ↗ {"\U00002198", "Down Right Arrow"}, // ↘ {"\U000027A1", "Black Right Arrow"}, // ➡️ // Dingbats {"\U00002714", "Check Mark"}, // ✔️ {"\U00002716", "Heavy Multiplication X"}, // ✖️ {"\U00002728", "Sparkles"}, // ✨ {"\U00002757", "Exclamation Mark"}, // ❗ {"\U0000274C", "Cross Mark"}, // ❌ {"\U00002795", "Heavy Plus Sign"}, // ➕ // Miscellaneous Symbols {"\U00002600", "Sun"}, // ☀️ {"\U00002614", "Umbrella with Rain Drops"}, // ☔ {"\U00002620", "Skull and Crossbones"}, // ☠️ {"\U000026A0", "Warning Sign"}, // ⚠️ {"\U000026BD", "Soccer Ball"}, // ⚽ {"\U000026C4", "Snowman"}, // ⛄ // Stars and Asterisks {"\U00002733", "Eight Pointed Black Star"}, // ✳️ {"\U00002734", "Eight Spoked Asterisk"}, // ✴️ {"\U00002B50", "White Star"}, // ⭐ {"\U0001F31F", "Glowing Star"}, // 🌟 {"\U00002728", "Sparkles"}, // ✨ // Animals and Nature {"\U0001F98A", "Fox"}, // 🦊 {"\U0001F415", "Dog"}, // 🐕 {"\U0001F431", "Cat Face"}, // 🐱 {"\U0001F435", "Monkey Face"}, // 🐵 {"\U0001F408", "Black Cat"}, // 🐈 {"\U0001F98C", "Deer"}, // 🦌 {"\U0001F344", "Mushroom"}, // 🍄 {"\U0001F333", "Tree"}, // 🌳 // Weather and Space Symbols {"\U0001F308", "Rainbow"}, // 🌈 {"\U0001F320", "Shooting Star"}, // 🌠 {"\U00002600", "Sun"}, // ☀️ {"\U00002601", "Cloud"}, // ☁️ {"\U000026A1", "High Voltage"}, // ⚡ {"\U0001F525", "Fire"}, // 🔥 {"\U000026C4", "Snowman"}, // ⛄ {"\U0001F30A", "Water Wave"}, // 🌊 // Transport and Map Symbols {"\U0001F68C", "Bus"}, // 🚌 {"\U0001F697", "Car"}, // 🚗 {"\U0001F6B2", "Bicycle"}, // 🚲 {"\U0001F6A2", "Ship"}, // 🚢 {"\U0001F681", "Helicopter"}, // 🚁 {"\U0001F680", "Rocket"}, // 🚀 {"\U0001F6EB", "Airplane"}, // 🛫 // Currency Symbols {"\U00000024", "Dollar Sign"}, // $ {"\U000000A3", "Pound Sign"}, // £ {"\U000000A5", "Yen Sign"}, // ¥ {"\U000020AC", "Euro Sign"}, // € {"\U0001F4B5", "Dollar Banknote"}, // 💵 {"\U0001F4B4", "Yen Banknote"}, // 💴 // Card Suits {"\U00002660", "Black Spade Suit"}, // ♠️ {"\U00002663", "Black Club Suit"}, // ♣️ {"\U00002665", "Black Heart Suit"}, // ♥️ {"\U00002666", "Black Diamond Suit"}, // ♦️ {"\U0001F0CF", "Joker Card"}, // 🃏 // Office Supplies and Objects {"\U0001F4DA", "Books"}, // 📚 {"\U0001F4D7", "Green Book"}, // 📗 {"\U0001F4C8", "Chart with Upwards Trend"}, // 📈 {"\U0001F4C9", "Chart with Downwards Trend"}, // 📉 {"\U0001F4B0", "Money Bag"}, // 💰 {"\U0001F4B8", "Money with Wings"}, // 💸 {"\U0001F4E6", "Package"}, // 📦 // Miscellaneous Symbols {"\U00002757", "Exclamation Mark"}, // ❗ {"\U00002714", "Check Mark"}, // ✔️ {"\U0000274C", "Cross Mark"}, // ❌ {"\U00002705", "Check Mark Button"}, // ✅ {"\U00002B50", "White Star"}, // ⭐ {"\U0001F31F", "Glowing Star"}, // 🌟 {"\U0001F4A1", "Light Bulb"}, // 💡 {"\U0001F4A3", "Bomb"}, // 💣 {"\U0001F4A9", "Pile of Poo"}, // 💩 // Musical Symbols {"\U0001F3B5", "Musical Note"}, // 🎵 {"\U0001F3B6", "Multiple Musical Notes"}, // 🎶 {"\U0001F3BC", "Musical Score"}, // 🎼 {"\U0001F399", "Studio Microphone"}, // 🎙️ {"\U0001F3A4", "Microphone"}, // 🎤 // Food and Drink {"\U0001F35F", "Cheese Wedge"}, // 🧀 {"\U0001F355", "Slice of Pizza"}, // 🍕 {"\U0001F32D", "Taco"}, // 🌮 {"\U0001F37D", "Beer Mug"}, // 🍻 {"\U0001F96B", "Cup with Straw"}, // 🥤 {"\U0001F32E", "Hot Pepper"}, // 🌶️ {"\U0001F95A", "Potato"}, // 🥔 // Zodiac Signs {"\U00002600", "Aries"}, // ♈ {"\U00002601", "Taurus"}, // ♉ {"\U00002602", "Gemini"}, // ♊ {"\U00002603", "Cancer"}, // ♋ {"\U00002604", "Leo"}, // ♌ {"\U00002605", "Virgo"}, // ♍ {"\U00002606", "Libra"}, // ♎ {"\U00002607", "Scorpio"}, // ♏ {"\U00002608", "Sagittarius"}, // ♐ {"\U00002609", "Capricorn"}, // ♑ {"\U0000260A", "Aquarius"}, // ♒ {"\U0000260B", "Pisces"}, // ♓ // Miscellaneous Shapes {"\U0001F4C8", "Chart Increasing"}, // 📈 {"\U0001F4C9", "Chart Decreasing"}, // 📉 {"\U0001F4CA", "Bar Chart"}, // 📊 {"\U0001F7E6", "Orange Circle"}, // 🟠 {"\U0001F7E7", "Yellow Circle"}, // 🟡 {"\U0001F7E8", "Green Circle"}, // 🟢 {"\U0001F7E9", "Blue Circle"}, // 🔵 {"\U0001F7EA", "Purple Circle"}, // 🟣 // Flags {"\U0001F1E6\U0001F1E9", "Flag of France"}, // 🇫🇷 {"\U0001F1E8\U0001F1E6", "Flag of Germany"}, // 🇩🇪 {"\U0001F1FA\U0001F1F8", "Flag of United States"}, // 🇺🇸 {"\U0001F1E7\U0001F1F7", "Flag of Canada"}, // 🇨🇦 {"\U0001F1EE\U0001F1F2", "Flag of Italy"}, // 🇮🇹 {"\U0001F1F8\U0001F1EC", "Flag of Australia"}, // 🇦🇺 {"\U0001F1F3\U0001F1F4", "Flag of Spain"}, // 🇪🇸 // Additional Miscellaneous Symbols {"\U0001F4A5", "Collision"}, // 💥 {"\U0001F4A6", "Sweat Droplets"}, // 💦 {"\U0001F4A8", "Dashing Away"}, // 💨 {"\U0001F50B", "Battery"}, // 🔋 {"\U0001F4BB", "Laptop Computer"}, // 💻 {"\U0001F4DE", "Telephone"}, // 📞 {"\U0001F4E7", "Incoming Envelope"}, // 📧 }; size_t remo_count = sizeof(remo) / sizeof(remo[0]); void rstrtolower(const char *input, char *output) { while (*input) { *output = tolower(*input); input++; output++; } *output = 0; } bool rstrinstr(const char *haystack, const char *needle) { char lower1[strlen(haystack) + 1]; char lower2[strlen(needle) + 1]; rstrtolower(haystack, lower1); rstrtolower(needle, lower2); return strstr(lower1, lower2) ? true : false; } void remo_print() { for (size_t i = 0; i < remo_count; i++) { printf("%s - %s\n", remo[i].str, remo[i].description); } } const char *remo_get(char *name) { for (size_t i = 0; i < remo_count; i++) { if (rstrinstr(remo[i].description, name)) { return remo[i].str; } } return NULL; } #endif #include <stdbool.h> #include <stdio.h> #include <unistd.h> #define debug(fmt, ...) printf("%s:%d: " fmt, __FILE__, __LINE__, __VA_ARGS__); char *rcurrent_banner; int rassert_count = 0; unsigned short rtest_is_first = 1; unsigned int rtest_fail_count = 0; int rtest_end(char *content) { // Returns application exit code. 0 == success printf("%s", content); printf("\n@assertions: %d\n", rassert_count); printf("@memory: %s%s\n", rmalloc_stats(), rmalloc_count == 0 ? remo_get("rainbow") : "fire"); if (rmalloc_count != 0) { printf("MEMORY ERROR %s\n", remo_get("cross mark")); return rtest_fail_count > 0; } return rtest_fail_count > 0; } void rtest_test_banner(char *content, char *file) { if (rtest_is_first == 1) { char delimiter[] = "."; char *d = delimiter; char f[2048]; strcpy(f, file); printf("%s tests", strtok(f, d)); rtest_is_first = 0; setvbuf(stdout, NULL, _IONBF, 0); } printf("\n - %s ", content); } bool rtest_test_true_silent(char *expr, int res, int line) { rassert_count++; if (res) { return true; } rprintrf(stderr, "\nERROR on line %d: %s", line, expr); rtest_fail_count++; return false; } bool rtest_test_true(char *expr, int res, int line) { rassert_count++; if (res) { fprintf(stdout, "%s", remo_get("Slightly Smiling Face")); return true; } rprintrf(stderr, "\nERROR %s on line %d: %s\n", remo_get("skull"), line, expr); rtest_fail_count++; return false; } bool rtest_test_false_silent(char *expr, int res, int line) { return rtest_test_true_silent(expr, !res, line); } bool rtest_test_false(char *expr, int res, int line) { return rtest_test_true(expr, !res, line); } void rtest_test_skip(char *expr, int line) { rprintgf(stderr, "\n @skip(%s) on line %d\n", expr, line); } void rtest_test_assert(char *expr, int res, int line) { if (rtest_test_true(expr, res, line)) { return; } rtest_end(""); exit(40); } #define rtest_banner(content) \ rcurrent_banner = content; \ rtest_test_banner(content, __FILE__); #define rtest_true(expr) rtest_test_true(#expr, expr, __LINE__); #define rtest_assert(expr) \ { \ int __valid = expr ? 1 : 0; \ rtest_test_true(#expr, __valid, __LINE__); \ }; \ ; #define rassert(expr) \ { \ int __valid = expr ? 1 : 0; \ rtest_test_true(#expr, __valid, __LINE__); \ }; \ ; #define rtest_asserts(expr) \ { \ int __valid = expr ? 1 : 0; \ rtest_test_true_silent(#expr, __valid, __LINE__); \ }; #define rasserts(expr) \ { \ int __valid = expr ? 1 : 0; \ rtest_test_true_silent(#expr, __valid, __LINE__); \ }; #define rtest_false(expr) \ rprintf(" [%s]\t%s\t\n", expr == 0 ? "OK" : "NOK", #expr); \ assert_count++; \ assert(#expr); #define rtest_skip(expr) rtest_test_skip(#expr, __LINE__); FILE *rtest_create_file(char *path, char *content) { FILE *fd = fopen(path, "wb"); char c; int index = 0; while ((c = content[index]) != 0) { fputc(c, fd); index++; } fclose(fd); fd = fopen(path, "rb"); return fd; } void rtest_delete_file(char *path) { unlink(path); } #endif char *rfcaptured = NULL; void rfcapture(FILE *f, char *buff, size_t size) { rfcaptured = buff; setvbuf(f, rfcaptured, _IOFBF, size); } void rfstopcapture(FILE *f) { setvbuf(f, 0, _IOFBF, 0); } bool _r_disable_stdout_toggle = false; FILE *_r_original_stdout = NULL; bool rr_enable_stdout() { if (_r_disable_stdout_toggle) return false; if (!_r_original_stdout) { stdout = fopen("/dev/null", "rb"); return false; } if (_r_original_stdout && _r_original_stdout != stdout) { fclose(stdout); } stdout = _r_original_stdout; return true; } bool rr_disable_stdout() { if (_r_disable_stdout_toggle) { return false; } if (_r_original_stdout == NULL) { _r_original_stdout = stdout; } if (stdout == _r_original_stdout) { stdout = fopen("/dev/null", "rb"); return true; } return false; } bool rr_toggle_stdout() { if (!_r_original_stdout) { rr_disable_stdout(); return true; } else if (stdout != _r_original_stdout) { rr_enable_stdout(); return true; } else { rr_disable_stdout(); return true; } } typedef struct rprogressbar_t { unsigned long current_value; unsigned long min_value; unsigned long max_value; unsigned int length; bool changed; double percentage; unsigned int width; unsigned long draws; FILE *fout; } rprogressbar_t; rprogressbar_t *rprogressbar_new(long min_value, long max_value, unsigned int width, FILE *fout) { rprogressbar_t *pbar = (rprogressbar_t *)malloc(sizeof(rprogressbar_t)); pbar->min_value = min_value; pbar->max_value = max_value; pbar->current_value = min_value; pbar->width = width; pbar->draws = 0; pbar->length = 0; pbar->changed = false; pbar->fout = fout ? fout : stdout; return pbar; } void rprogressbar_free(rprogressbar_t *pbar) { free(pbar); } void rprogressbar_draw(rprogressbar_t *pbar) { if (!pbar->changed) { return; } else { pbar->changed = false; } pbar->draws++; char draws_text[22]; draws_text[0] = 0; sprintf(draws_text, "%ld", pbar->draws); char *draws_textp = draws_text; // bool draws_text_len = strlen(draws_text); char bar_begin_char = ' '; char bar_progress_char = ' '; char bar_empty_char = ' '; char bar_end_char = ' '; char content[4096] = {0}; char bar_content[1024]; char buff[2048] = {0}; bar_content[0] = '\r'; bar_content[1] = bar_begin_char; unsigned int index = 2; for (unsigned long i = 0; i < pbar->length; i++) { if (*draws_textp) { bar_content[index] = *draws_textp; draws_textp++; } else { bar_content[index] = bar_progress_char; } index++; } char infix[] = "\033[0m"; for (unsigned long i = 0; i < strlen(infix); i++) { bar_content[index] = infix[i]; index++; } for (unsigned long i = 0; i < pbar->width - pbar->length; i++) { bar_content[index] = bar_empty_char; index++; } bar_content[index] = bar_end_char; bar_content[index + 1] = '\0'; sprintf(buff, "\033[43m%s\033[0m \033[33m%.2f%%\033[0m ", bar_content, pbar->percentage * 100); strcat(content, buff); if (pbar->width == pbar->length) { strcat(content, "\r"); for (unsigned long i = 0; i < pbar->width + 10; i++) { strcat(content, " "); } strcat(content, "\r"); } fprintf(pbar->fout, "%s", content); fflush(pbar->fout); } bool rprogressbar_update(rprogressbar_t *pbar, unsigned long value) { if (value == pbar->current_value) { return false; } pbar->current_value = value; pbar->percentage = (double)pbar->current_value / (double)(pbar->max_value - pbar->min_value); unsigned long new_length = (unsigned long)(pbar->percentage * pbar->width); pbar->changed = new_length != pbar->length; if (pbar->changed) { pbar->length = new_length; rprogressbar_draw(pbar); return true; } return false; } size_t rreadline(char *data, size_t len, bool strip_ln) { __attribute__((unused)) char *unused = fgets(data, len, stdin); size_t length = strlen(data); if (length && strip_ln) data[length - 1] = 0; return length; } void rlib_test_progressbar() { rtest_banner("Progress bar"); rprogressbar_t *pbar = rprogressbar_new(0, 1000, 10, stderr); rprogressbar_draw(pbar); // No draws executed, nothing to show rassert(pbar->draws == 0); rprogressbar_update(pbar, 500); rassert(pbar->percentage == 0.5); rprogressbar_update(pbar, 500); rprogressbar_update(pbar, 501); rprogressbar_update(pbar, 502); // Should only have drawn one time since value did change, but percentage // did not rassert(pbar->draws == 1); // Changed is false because update function calls draw rassert(pbar->changed == false); rprogressbar_update(pbar, 777); rassert(pbar->percentage == 0.777); rprogressbar_update(pbar, 1000); rassert(pbar->percentage == 1); } #endif #define RBENCH(times, action) \ { \ unsigned long utimes = (unsigned long)times; \ nsecs_t start = nsecs(); \ for (unsigned long i = 0; i < utimes; i++) { \ { \ action; \ } \ } \ nsecs_t end = nsecs(); \ printf("%s\n", format_time(end - start)); \ } #define RBENCHP(times, action) \ { \ printf("\n"); \ nsecs_t start = nsecs(); \ unsigned int prev_percentage = 0; \ unsigned long utimes = (unsigned long)times; \ for (unsigned long i = 0; i < utimes; i++) { \ unsigned int percentage = ((long double)i / (long double)times) * 100; \ int percentage_changed = percentage != prev_percentage; \ __attribute__((unused)) int first = i == 0; \ __attribute__((unused)) int last = i == utimes - 1; \ { action; }; \ if (percentage_changed) { \ printf("\r%d%%", percentage); \ fflush(stdout); \ \ prev_percentage = percentage; \ } \ } \ nsecs_t end = nsecs(); \ printf("\r%s\n", format_time(end - start)); \ } struct rbench_t; typedef struct rbench_function_t { #ifdef __cplusplus void (*call)(); #else void(*call); #endif char name[256]; char group[256]; void *arg; void *data; bool first; bool last; int argc; unsigned long times_executed; nsecs_t average_execution_time; nsecs_t total_execution_time; } rbench_function_t; typedef struct rbench_t { unsigned int function_count; rbench_function_t functions[100]; rbench_function_t *current; rprogressbar_t *progress_bar; bool show_progress; int winner; bool stdout; unsigned long times; bool silent; nsecs_t execution_time; #ifdef __cplusplus void (*add_function)(struct rbench_t *r, const char *name, const char *group, void (*)()); #else void (*add_function)(struct rbench_t *r, const char *name, const char *group, void *); #endif void (*rbench_reset)(struct rbench_t *r); struct rbench_t *(*execute)(struct rbench_t *r, long times); struct rbench_t *(*execute1)(struct rbench_t *r, long times, void *arg1); struct rbench_t *(*execute2)(struct rbench_t *r, long times, void *arg1, void *arg2); struct rbench_t *(*execute3)(struct rbench_t *r, long times, void *arg1, void *arg2, void *arg3); } rbench_t; FILE *_rbench_stdout = NULL; FILE *_rbench_stdnull = NULL; void rbench_toggle_stdout(rbench_t *r) { if (!r->stdout) { if (_rbench_stdout == NULL) { _rbench_stdout = stdout; } if (_rbench_stdnull == NULL) { _rbench_stdnull = fopen("/dev/null", "wb"); } if (stdout == _rbench_stdout) { stdout = _rbench_stdnull; } else { stdout = _rbench_stdout; } } } void rbench_restore_stdout(rbench_t *r) { if (r->stdout) return; if (_rbench_stdout) { stdout = _rbench_stdout; } if (_rbench_stdnull) { fclose(_rbench_stdnull); _rbench_stdnull = NULL; } } rbench_t *rbench_new(); rbench_t *_rbench = NULL; rbench_function_t *rbf; rbench_t *rbench() { if (_rbench == NULL) { _rbench = rbench_new(); } return _rbench; } typedef void *(*rbench_call)(); typedef void *(*rbench_call1)(void *); typedef void *(*rbench_call2)(void *, void *); typedef void *(*rbench_call3)(void *, void *, void *); #ifdef __cplusplus void rbench_add_function(rbench_t *rp, const char *name, const char *group, void (*call)()) { #else void rbench_add_function(rbench_t *rp, const char *name, const char *group, void *call) { #endif rbench_function_t *f = &rp->functions[rp->function_count]; rp->function_count++; f->average_execution_time = 0; f->total_execution_time = 0; f->times_executed = 0; f->call = call; strcpy(f->name, name); strcpy(f->group, group); } void rbench_reset_function(rbench_function_t *f) { f->average_execution_time = 0; f->times_executed = 0; f->total_execution_time = 0; } void rbench_reset(rbench_t *rp) { for (unsigned int i = 0; i < rp->function_count; i++) { rbench_reset_function(&rp->functions[i]); } } int rbench_get_winner_index(rbench_t *r) { int winner = 0; nsecs_t time = 0; for (unsigned int i = 0; i < r->function_count; i++) { if (time == 0 || r->functions[i].total_execution_time < time) { winner = i; time = r->functions[i].total_execution_time; } } return winner; } bool rbench_was_last_function(rbench_t *r) { for (unsigned int i = 0; i < r->function_count; i++) { if (i == r->function_count - 1 && r->current == &r->functions[i]) return true; } return false; } rbench_function_t *rbench_execute_prepare(rbench_t *r, int findex, long times, int argc) { rbench_toggle_stdout(r); if (findex == 0) { r->execution_time = 0; } rbench_function_t *rf = &r->functions[findex]; rf->argc = argc; rbf = rf; r->current = rf; if (r->show_progress) r->progress_bar = rprogressbar_new(0, times, 20, stderr); r->times = times; // printf(" %s:%s gets executed for %ld times with %d // arguments.\n",rf->group, rf->name, times,argc); rbench_reset_function(rf); return rf; } void rbench_execute_finish(rbench_t *r) { rbench_toggle_stdout(r); if (r->progress_bar) { free(r->progress_bar); r->progress_bar = NULL; } r->current->average_execution_time = r->current->total_execution_time / r->current->times_executed; ; // printf(" %s:%s finished executing in // %s\n",r->current->group,r->current->name, // format_time(r->current->total_execution_time)); // rbench_show_results_function(r->current); if (rbench_was_last_function(r)) { rbench_restore_stdout(r); unsigned int winner_index = rbench_get_winner_index(r); r->winner = winner_index + 1; if (!r->silent) rprintgf(stderr, "Benchmark results:\n"); nsecs_t total_time = 0; for (unsigned int i = 0; i < r->function_count; i++) { rbf = &r->functions[i]; total_time += rbf->total_execution_time; bool is_winner = winner_index == i; if (is_winner) { if (!r->silent) rprintyf(stderr, " > %s:%s:%s\n", format_time(rbf->total_execution_time), rbf->group, rbf->name); } else { if (!r->silent) rprintbf(stderr, " %s:%s:%s\n", format_time(rbf->total_execution_time), rbf->group, rbf->name); } } if (!r->silent) rprintgf(stderr, "Total execution time: %s\n", format_time(total_time)); } rbench_restore_stdout(r); rbf = NULL; r->current = NULL; } struct rbench_t *rbench_execute(rbench_t *r, long times) { for (unsigned int i = 0; i < r->function_count; i++) { rbench_function_t *f = rbench_execute_prepare(r, i, times, 0); rbench_call c = (rbench_call)f->call; nsecs_t start = nsecs(); f->first = true; c(); f->first = false; f->last = false; f->times_executed++; for (int j = 1; j < times; j++) { c(); f->times_executed++; f->last = f->times_executed == r->times - 1; if (r->progress_bar) { rprogressbar_update(r->progress_bar, f->times_executed); } } f->total_execution_time = nsecs() - start; r->execution_time += f->total_execution_time; rbench_execute_finish(r); } return r; } struct rbench_t *rbench_execute1(rbench_t *r, long times, void *arg1) { for (unsigned int i = 0; i < r->function_count; i++) { rbench_function_t *f = rbench_execute_prepare(r, i, times, 1); rbench_call1 c = (rbench_call1)f->call; nsecs_t start = nsecs(); f->first = true; c(arg1); f->first = false; f->last = false; f->times_executed++; for (int j = 1; j < times; j++) { c(arg1); f->times_executed++; f->last = f->times_executed == r->times - 1; if (r->progress_bar) { rprogressbar_update(r->progress_bar, f->times_executed); } } f->total_execution_time = nsecs() - start; r->execution_time += f->total_execution_time; rbench_execute_finish(r); } return r; } struct rbench_t *rbench_execute2(rbench_t *r, long times, void *arg1, void *arg2) { for (unsigned int i = 0; i < r->function_count; i++) { rbench_function_t *f = rbench_execute_prepare(r, i, times, 2); rbench_call2 c = (rbench_call2)f->call; nsecs_t start = nsecs(); f->first = true; c(arg1, arg2); f->first = false; f->last = false; f->times_executed++; for (int j = 1; j < times; j++) { c(arg1, arg2); f->times_executed++; f->last = f->times_executed == r->times - 1; if (r->progress_bar) { rprogressbar_update(r->progress_bar, f->times_executed); } } f->total_execution_time = nsecs() - start; r->execution_time += f->total_execution_time; rbench_execute_finish(r); } return r; } struct rbench_t *rbench_execute3(rbench_t *r, long times, void *arg1, void *arg2, void *arg3) { for (unsigned int i = 0; i < r->function_count; i++) { rbench_function_t *f = rbench_execute_prepare(r, i, times, 3); rbench_call3 c = (rbench_call3)f->call; nsecs_t start = nsecs(); f->first = true; c(arg1, arg2, arg3); f->first = false; f->last = false; f->times_executed++; for (int j = 1; j < times; j++) { c(arg1, arg2, arg3); f->times_executed++; f->last = f->times_executed == r->times - 1; if (r->progress_bar) { rprogressbar_update(r->progress_bar, f->times_executed); } } f->total_execution_time = nsecs() - start; rbench_execute_finish(r); } return r; } rbench_t *rbench_new() { rbench_t *r = (rbench_t *)malloc(sizeof(rbench_t)); memset(r, 0, sizeof(rbench_t)); r->add_function = rbench_add_function; r->rbench_reset = rbench_reset; r->execute1 = rbench_execute1; r->execute2 = rbench_execute2; r->execute3 = rbench_execute3; r->execute = rbench_execute; r->stdout = true; r->silent = false; r->winner = 0; r->show_progress = true; return r; } void rbench_free(rbench_t *r) { free(r); } #endif bool check_lcov() { char buffer[1024 * 64]; FILE *fp; fp = popen("lcov --help", "r"); if (fp == NULL) { return false; } if (fgets(buffer, sizeof(buffer), fp) == NULL) { return false; } pclose(fp); return strstr(buffer, "lcov: not found") ? false : true; } int rcov_main(int argc, char *argv[]) { if (argc < 2) { printf("Usage: [source.c]\n"); return 1; } char argstr[4096] = {0}; for (int i = 2; i < argc; i++) { strcat(argstr, argv[i]); strcat(argstr, " "); } if (!check_lcov()) { printf("lcov is not installed. Please execute `sudo apt install lcov`.\n"); return 1; } char *source_file = argv[1]; char *commands[] = {"rm -f *.gcda 2>/dev/null", "rm -f *.gcno 2>/dev/null", "rm -f %s.coverage.info 2>/dev/null", "gcc -pg -fprofile-arcs -ftest-coverage -g -o %s_coverage.o %s", "./%s_coverage.o", "lcov --capture --directory . --output-file %s.coverage.info", "genhtml %s.coverage.info --output-directory /tmp/%s.coverage", "rm -f *.gcda 2>/dev/null", "rm -f *.gcno 2>/dev/null", "rm -f %s.coverage.info 2>/dev/null", //"cat gmon.out", "gprof %s_coverage.o gmon.out > output.rcov_analysis", "rm -f gmon.out", "cat output.rcov_analysis", "rm output.rcov_analysis", "rm -f %s_coverage.o", "google-chrome /tmp/%s.coverage/index.html"}; uint command_count = sizeof(commands) / sizeof(commands[0]); RBENCH(1,{ for (uint i = 0; i < command_count; i++) { char *formatted_command = sbuf(""); sprintf(formatted_command, commands[i], source_file, source_file); // printf("%s\n", formatted_command); if (formatted_command[0] == '.' && formatted_command[1] == '/') { strcat(formatted_command, " "); strcat(formatted_command, argstr); } if (system(formatted_command)) { printf("`%s` returned non-zero code.\n", formatted_command); } }); } return 0; } #endif #ifndef RHTTP_H #define RHTTP_H #include <arpa/inet.h> #include <pthread.h> #include <signal.h> #include <stdarg.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> #define BUFF_SIZE 8096 #define RHTTP_MAX_CONNECTIONS 100 int rhttp_opt_error = 1; int rhttp_opt_warn = 1; int rhttp_opt_info = 1; int rhttp_opt_port = 8080; int rhttp_opt_debug = 0; int rhttp_opt_request_logging = 0; int rhttp_sock = 0; int rhttp_opt_buffered = 0; int rhttp_c = 0; int rhttp_c_mutex_initialized = 0; pthread_mutex_t rhttp_c_mutex; char rhttp_opt_host[1024] = "0.0.0.0"; unsigned int rhttp_connections_handled = 0; typedef struct rhttp_header_t { char *name; char *value; struct rhttp_header_t *next; } rhttp_header_t; typedef struct rhttp_request_t { int c; int closed; bool keep_alive; nsecs_t start; char *raw; char *line; char *body; char *method; char *path; char *version; void *context; unsigned int bytes_received; rhttp_header_t *headers; } rhttp_request_t; char *rhttp_current_timestamp() { time_t current_time; time(¤t_time); struct tm *local_time = localtime(¤t_time); static char time_string[100]; time_string[0] = 0; strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S", local_time); return time_string; } void rhttp_logs(const char *prefix, const char *level, const char *format, va_list args) { char buf[strlen(format) + BUFSIZ + 1]; buf[0] = 0; sprintf(buf, "%s%s %s %s\e[0m", prefix, rhttp_current_timestamp(), level, format); vfprintf(stdout, buf, args); } void rhttp_log_info(const char *format, ...) { if (!rhttp_opt_info) return; va_list args; va_start(args, format); rhttp_logs("\e[32m", "INFO ", format, args); va_end(args); } void rhttp_log_debug(const char *format, ...) { if (!rhttp_opt_debug) return; va_list args; va_start(args, format); if (rhttp_opt_debug) rhttp_logs("\e[33m", "DEBUG", format, args); va_end(args); } void rhttp_log_warn(const char *format, ...) { if (!rhttp_opt_warn) return; va_list args; va_start(args, format); rhttp_logs("\e[34m", "WARN ", format, args); va_end(args); } void rhttp_log_error(const char *format, ...) { if (!rhttp_opt_error) return; va_list args; va_start(args, format); rhttp_logs("\e[35m", "ERROR", format, args); va_end(args); } void http_request_init(rhttp_request_t *r) { r->raw = NULL; r->line = NULL; r->body = NULL; r->method = NULL; r->path = NULL; r->version = NULL; r->start = 0; r->headers = NULL; r->bytes_received = 0; r->closed = 0; } void rhttp_free_header(rhttp_header_t *h) { if (!h) return; rhttp_header_t *next = h->next; free(h->name); free(h->value); free(h); if (next) rhttp_free_header(next); } void rhttp_rhttp_free_headers(rhttp_request_t *r) { if (!r->headers) return; rhttp_free_header(r->headers); r->headers = NULL; } rhttp_header_t *rhttp_parse_headers(rhttp_request_t *s) { int first = 1; char *body = strdup(s->body); char *body_original = body; while (body && *body) { char *line = __strtok_r(first ? body : NULL, "\r\n", &body); if (!line) break; rhttp_header_t *h = (rhttp_header_t *)malloc(sizeof(rhttp_header_t)); h->name = NULL; h->value = NULL; h->next = NULL; char *name = __strtok_r(line, ": ", &line); first = 0; if (!name) { rhttp_free_header(h); break; } h->name = strdup(name); char *value = __strtok_r(NULL, "\r\n", &line); if (!value) { rhttp_free_header(h); break; } h->value = value ? strdup(value + 1) : strdup(""); h->next = s->headers; s->headers = h; } free(body_original); return s->headers; } void rhttp_free_request(rhttp_request_t *r) { if (r->raw) { free(r->raw); free(r->body); free(r->method); free(r->path); free(r->version); rhttp_rhttp_free_headers(r); } free(r); } long rhttp_header_get_long(rhttp_request_t *r, const char *name) { rhttp_header_t *h = r->headers; while (h) { if (!strcmp(h->name, name)) return strtol(h->value, NULL, 10); h = h->next; } return -1; } char *rhttp_header_get_string(rhttp_request_t *r, const char *name) { rhttp_header_t *h = r->headers; while (h) { if (!strcmp(h->name, name)) return h->value && *h->value ? h->value : NULL; h = h->next; } return NULL; } void rhttp_print_header(rhttp_header_t *h) { rhttp_log_debug("Header: <%s> \"%s\"\n", h->name, h->value); } void rhttp_print_headers(rhttp_header_t *h) { while (h) { rhttp_print_header(h); h = h->next; } } void rhttp_print_request_line(rhttp_request_t *r) { rhttp_log_info("%s %s %s\n", r->method, r->path, r->version); } void rhttp_print_request(rhttp_request_t *r) { rhttp_print_request_line(r); if (rhttp_opt_debug) rhttp_print_headers(r->headers); } void rhttp_close(rhttp_request_t *r) { if (!r) return; if (!r->closed) close(r->c); rhttp_free_request(r); } rhttp_request_t *rhttp_parse_request(int s) { rhttp_request_t *request = (rhttp_request_t *)malloc(sizeof(rhttp_request_t)); http_request_init(request); char buf[BUFF_SIZE] = {0}; request->c = s; int breceived = 0; while (!rstrendswith(buf, "\r\n\r\n")) { int chunk_size = read(s, buf + breceived, 1); if (chunk_size <= 0) { close(request->c); request->closed = 1; return request; } breceived += chunk_size; } if (breceived <= 0) { close(request->c); request->closed = 1; return request; } buf[breceived] = '\0'; char *original_buf = buf; char *b = original_buf; request->raw = strdup(b); b = original_buf; char *line = strtok(b, "\r\n"); b = original_buf; char *body = b + strlen(line) + 2; request->body = strdup(body); b = original_buf; char *method = strtok(b, " "); char *path = strtok(NULL, " "); char *version = strtok(NULL, " "); request->bytes_received = breceived; request->line = line; request->start = nsecs(); request->method = strdup(method); request->path = strdup(path); request->version = strdup(version); request->headers = NULL; request->keep_alive = false; if (rhttp_parse_headers(request)) { char *keep_alive_string = rhttp_header_get_string(request, "Connection"); if (keep_alive_string && !strcmp(keep_alive_string, "keep-alive")) { request->keep_alive = 1; } } return request; } void rhttp_close_server() { close(rhttp_sock); close(rhttp_c); printf("Connections handled: %d\n", rhttp_connections_handled); printf("Gracefully closed\n"); exit(0); } size_t rhttp_send_drain(int s, void *tsend, size_t to_send_len) { if (to_send_len == 0 && *(unsigned char *)tsend) { to_send_len = strlen(tsend); } unsigned char *to_send = (unsigned char *)malloc(to_send_len); unsigned char *to_send_original = to_send; memcpy(to_send, tsend, to_send_len); // to_send[to_send_len] = '\0'; long bytes_sent = 0; long bytes_sent_total = 0; while (1) { bytes_sent = send(s, to_send + bytes_sent_total, to_send_len - bytes_sent_total, 0); if (bytes_sent <= 0) { bytes_sent_total = 0; break; } bytes_sent_total += bytes_sent; if (bytes_sent_total == (long)to_send_len) { break; } else if (!bytes_sent) { bytes_sent_total = 0; // error break; } else { rhttp_log_info("Extra send of %d/%d bytes.\n", bytes_sent_total, to_send_len); } } free(to_send_original); return bytes_sent_total; } typedef int (*rhttp_request_handler_t)(rhttp_request_t *r); void rhttp_serve(const char *host, int port, int backlog, int request_logging, int request_debug, rhttp_request_handler_t handler, void *context) { signal(SIGPIPE, SIG_IGN); rhttp_sock = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(host ? host : "0.0.0.0"); rhttp_opt_debug = request_debug; rhttp_opt_request_logging = request_logging; int opt = 1; setsockopt(rhttp_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); if (bind(rhttp_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { printf("Binding error\n"); exit(1); } listen(rhttp_sock, backlog); while (1) { struct sockaddr_in client_addr; int addrlen = sizeof(client_addr); rhttp_c = accept(rhttp_sock, (struct sockaddr *)&client_addr, (socklen_t *)&addrlen); rhttp_connections_handled++; while (true) { rhttp_request_t *r = rhttp_parse_request(rhttp_c); r->context = context; if (!r->closed) { if (!handler(r) && !r->closed) { rhttp_close(r); } } if (!r->keep_alive && !r->closed) { rhttp_close(r); } else if (r->keep_alive && !r->closed) { } if (r->closed) { break; } rhttp_free_request(r); } } } unsigned int rhttp_calculate_number_char_count(unsigned int number) { unsigned int width = 1; unsigned int tcounter = number; while (tcounter / 10 >= 1) { tcounter = tcounter / 10; width++; } return width; } int rhttp_file_response(rhttp_request_t *r, char *path) { if (!*path) return 0; FILE *f = fopen(path, "rb"); if (f == NULL) return 0; size_t file_size = rfile_size(path); char response[1024] = {0}; char content_type_header[100] = {0}; char *ext = strstr(path, "."); char *text_extensions = ".h,.c,.html"; if (strstr(text_extensions, ext)) { sprintf(content_type_header, "Content-Type: %s\r\n", "text/html"); } sprintf(response, "HTTP/1.1 200 OK\r\n%sContent-Length:%ld\r\n\r\n", content_type_header, file_size); if (!rhttp_send_drain(r->c, response, 0)) { rhttp_log_error("Error sending file: %s\n", path); } size_t bytes = 0; size_t bytes_sent = 0; unsigned char file_buff[1024]; while ((bytes = fread(file_buff, sizeof(char), sizeof(file_buff), f))) { if (!rhttp_send_drain(r->c, file_buff, bytes)) { rhttp_log_error("Error sending file during chunking: %s\n", path); } bytes_sent += bytes; } if (bytes_sent != file_size) { rhttp_send_drain(r->c, file_buff, file_size - bytes_sent); } close(r->c); fclose(f); return 1; }; int rhttp_file_request_handler(rhttp_request_t *r) { char *path = r->path; while (*path == '/' || *path == '.') path++; if (strstr(path, "..")) { return 0; } return rhttp_file_response(r, path); }; unsigned int counter = 100000000; int rhttp_counter_request_handler(rhttp_request_t *r) { if (!strncmp(r->path, "/counter", strlen("/counter"))) { counter++; unsigned int width = rhttp_calculate_number_char_count(counter); char to_send2[1024] = {0}; sprintf(to_send2, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nConnection: " "close\r\n\r\n%d", width, counter); rhttp_send_drain(r->c, to_send2, 0); close(r->c); return 1; } return 0; } int rhttp_root_request_handler(rhttp_request_t *r) { if (!strcmp(r->path, "/")) { char to_send[1024] = {0}; sprintf(to_send, "HTTP/1.1 200 OK\r\nContent-Length: 3\r\nConnection: " "close\r\n\r\nOk!"); rhttp_send_drain(r->c, to_send, 0); close(r->c); return 1; } return 0; } int rhttp_error_404_handler(rhttp_request_t *r) { char to_send[1024] = {0}; sprintf(to_send, "HTTP/1.1 404 Document not found\r\nContent-Length: " "0\r\nConnection: close\r\n\r\n"); rhttp_send_drain(r->c, to_send, 0); close(r->c); return 1; } int rhttp_default_request_handler(rhttp_request_t *r) { if (rhttp_opt_debug || rhttp_opt_request_logging) rhttp_print_request(r); if (rhttp_counter_request_handler(r)) { // Counter handler rhttp_log_info("Counter handler found for: %s\n", r->path); } else if (rhttp_root_request_handler(r)) { // Root handler rhttp_log_info("Root handler found for: %s\n", r->path); } else if (rhttp_file_request_handler(r)) { rhttp_log_info("File %s sent\n", r->path); } else if (rhttp_error_404_handler(r)) { rhttp_log_warn("Error 404 for: %s\n", r->path); // Error handler } else { rhttp_log_warn("No handler found for: %s\n", r->path); close(rhttp_c); } return 0; } int rhttp_main(int argc, char *argv[]) { setvbuf(stdout, NULL, _IOLBF, BUFSIZ); int opt; while ((opt = getopt(argc, argv, "p:drh:bewi")) != -1) { switch (opt) { case 'i': rhttp_opt_info = 1; rhttp_opt_warn = 1; rhttp_opt_error = 1; break; case 'e': rhttp_opt_error = 1; rhttp_opt_warn = 0; rhttp_opt_info = 0; break; case 'w': rhttp_opt_warn = 1; rhttp_opt_error = 1; rhttp_opt_info = 0; break; case 'p': rhttp_opt_port = atoi(optarg); break; case 'b': rhttp_opt_buffered = 1; printf("Logging is buffered. Output may be incomplete.\n"); break; case 'h': strcpy(rhttp_opt_host, optarg); break; case 'd': printf("Debug enabled\n"); rhttp_opt_debug = 1; rhttp_opt_warn = 1; rhttp_opt_info = 1; rhttp_opt_error = 1; break; case 'r': printf("Request logging enabled\n"); rhttp_opt_request_logging = 1; break; default: printf("Usage: %s [-p port] [-h host] [-b]\n", argv[0]); return 1; } } printf("Starting server on: %s:%d\n", rhttp_opt_host, rhttp_opt_port); if (rhttp_opt_buffered) setvbuf(stdout, NULL, _IOFBF, BUFSIZ); rhttp_serve(rhttp_opt_host, rhttp_opt_port, 1024, rhttp_opt_request_logging, rhttp_opt_debug, rhttp_default_request_handler, NULL); return 0; } /* CLIENT CODE */ typedef struct rhttp_client_request_t { char *host; int port; char *path; bool is_done; char *request; char *response; pthread_t thread; int bytes_received; } rhttp_client_request_t; rhttp_client_request_t *rhttp_create_request(const char *host, int port, const char *path) { rhttp_client_request_t *r = (rhttp_client_request_t *)malloc(sizeof(rhttp_client_request_t)); char request_line[4096] = {0}; sprintf(request_line, "GET %s HTTP/1.1\r\n" "Host: localhost:8000\r\n" "Connection: close\r\n" "Accept: */*\r\n" "User-Agent: mhttpc\r\n" "Accept-Language: en-US,en;q=0.5\r\n" "Accept-Encoding: gzip, deflate\r\n" "\r\n", path); r->request = strdup(request_line); r->host = strdup(host); r->port = port; r->path = strdup(path); r->is_done = false; r->response = NULL; r->bytes_received = 0; return r; } int rhttp_execute_request(rhttp_client_request_t *r) { int s = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(r->port); addr.sin_addr.s_addr = inet_addr(r->host); if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { return 0; } send(s, r->request, strlen(r->request), 0); char buf[1024 * 1024] = {0}; int ret = recv(s, buf, 1024 * 1024, 0); if (ret > 0) { r->response = strdup(buf); } close(s); return ret; } void rhttp_reset_request(rhttp_client_request_t *r) { free(r->response); r->is_done = false; r->response = NULL; r->bytes_received = 0; } void rhttp_free_client_request(rhttp_client_request_t *r) { if (r->request) free(r->request); if (r->response) free(r->response); if (r->host) free(r->host); if (r->path) free(r->path); free(r); } void rhttp_client_bench(int workers, int times, const char *host, int port, const char *path) { rhttp_client_request_t *requests[workers]; while (times > 0) { for (int i = 0; i < workers && times; i++) { requests[i] = rhttp_create_request(host, port, path); rhttp_execute_request(requests[i]); times--; } } } char *rhttp_client_get(const char *host, int port, const char *path) { if (!rhttp_c_mutex_initialized) { rhttp_c_mutex_initialized = 1; pthread_mutex_init(&rhttp_c_mutex, NULL); } char http_response[1024 * 1024]; http_response[0] = 0; rhttp_client_request_t *r = rhttp_create_request(host, port, path); unsigned int reconnects = 0; unsigned int reconnects_max = 100000; while (!rhttp_execute_request(r)) { reconnects++; tick(); if (reconnects == reconnects_max) { fprintf(stderr, "Maxium reconnects exceeded for %s:%d\n", host, port); rhttp_free_client_request(r); return NULL; } } r->is_done = true; char *body = r->response ? strstr(r->response, "\r\n\r\n") : NULL; pthread_mutex_lock(&rhttp_c_mutex); if (body) { strcpy(http_response, body + 4); } else { strcpy(http_response, r->response); } rhttp_free_client_request(r); char *result = sbuf(http_response); pthread_mutex_unlock(&rhttp_c_mutex); return result; } /*END CLIENT CODE */ #endif #ifndef RJSON_H #define RJSON_H typedef struct rjson_t { char *content; size_t length; size_t size; } rjson_t; rjson_t *rjson() { rjson_t *json = rmalloc(sizeof(rjson_t)); json->size = 1024; json->length = 0; json->content = (char *)rmalloc(json->size); json->content[0] = 0; return json; } void rjson_write(rjson_t *rjs, char *content) { size_t len = strlen(content); while (rjs->size < rjs->length + len + 1) { rjs->content = realloc(rjs->content, rjs->size + 1024); rjs->size += 1024; } strcat(rjs->content, content); rjs->length += len; } void rjson_object_start(rjson_t *rjs) { if (rstrendswith(rjs->content, "}")) rjson_write(rjs, ","); rjson_write(rjs, "{"); } void rjson_object_close(rjson_t *rjs) { if (rstrendswith(rjs->content, ",")) { rjs->content[rjs->length - 1] = 0; rjs->length--; } rjson_write(rjs, "}"); } void rjson_array_start(rjson_t *rjs) { if (rjs->length && (rstrendswith(rjs->content, "}") || rstrendswith(rjs->content, "]"))) rjson_write(rjs, ","); rjson_write(rjs, "["); } void rjson_array_close(rjson_t *rjs) { if (rstrendswith(rjs->content, ",")) { rjs->content[rjs->length - 1] = 0; rjs->length--; } rjson_write(rjs, "]"); } void rjson_kv_string(rjson_t *rjs, char *key, char *value) { if (rjs->length && !rstrendswith(rjs->content, "{") && !rstrendswith(rjs->content, "[")) { rjson_write(rjs, ","); } rjson_write(rjs, "\""); rjson_write(rjs, key); rjson_write(rjs, "\":\""); char *value_str = (char *)rmalloc(strlen(value) + 4096); rstraddslashes(value, value_str); rjson_write(rjs, value_str); free(value_str); rjson_write(rjs, "\""); } void rjson_kv_int(rjson_t *rjs, char *key, ulonglong value) { if (rjs->length && !rstrendswith(rjs->content, "{") && !rstrendswith(rjs->content, "[")) { rjson_write(rjs, ","); } rjson_write(rjs, "\""); rjson_write(rjs, key); rjson_write(rjs, "\":"); char value_str[100] = {0}; sprintf(value_str, "%lld", value); rjson_write(rjs, value_str); } void rjson_kv_number(rjson_t *rjs, char *key, ulonglong value) { if (rjs->length && !rstrendswith(rjs->content, "{") && !rstrendswith(rjs->content, "[")) { rjson_write(rjs, ","); } rjson_write(rjs, "\""); rjson_write(rjs, key); rjson_write(rjs, "\":"); rjson_write(rjs, "\""); rjson_write(rjs, sbuf(rformat_number(value))); rjson_write(rjs, "\""); } void rjson_kv_bool(rjson_t *rjs, char *key, int value) { if (rjs->length && !rstrendswith(rjs->content, "{") && !rstrendswith(rjs->content, "[")) { rjson_write(rjs, ","); } rjson_write(rjs, "\""); rjson_write(rjs, key); rjson_write(rjs, "\":"); rjson_write(rjs, value > 0 ? "true" : "false"); } void rjson_kv_duration(rjson_t *rjs, char *key, nsecs_t value) { if (rjs->length && !rstrendswith(rjs->content, "{") && !rstrendswith(rjs->content, "[")) { rjson_write(rjs, ","); } rjson_write(rjs, "\""); rjson_write(rjs, key); rjson_write(rjs, "\":"); rjson_write(rjs, "\""); rjson_write(rjs, sbuf(format_time(value))); rjson_write(rjs, "\""); } void rjson_free(rjson_t *rsj) { free(rsj->content); free(rsj); } void rjson_key(rjson_t *rsj, char *key) { rjson_write(rsj, "\""); rjson_write(rsj, key); rjson_write(rsj, "\":"); } #endif #ifndef RAUTOCOMPLETE_H #define RAUTOCOMPLETE_H #define R4_DEBUG #ifndef RREX4_H #define RREX4_H #include <assert.h> #include <ctype.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define R4_DEBUG_a #ifdef R4_DEBUG static int _r4_debug = 1; #else static int _r4_debug = 0; #endif static char *_format_function_name(const char *name) { static char result[100]; result[0] = 0; char *new_name = (char *)name; new_name += 11; if (new_name[0] == '_') new_name += 1; if (strlen(new_name) == 0) { return " -"; } strcpy(result, new_name); return result; } #define DEBUG_VALIDATE_FUNCTION \ if (_r4_debug || r4->debug) \ printf("DEBUG: %s %s <%s> \"%s\"\n", _format_function_name(__func__), r4->valid ? "valid" : "INVALID", r4->expr, r4->str); struct r4_t; void r4_enable_debug() { _r4_debug = true; } void r4_disable_debug() { _r4_debug = false; } typedef bool (*r4_function)(struct r4_t *); typedef struct r4_t { bool debug; bool valid; bool in_block; bool is_greedy; bool in_range; unsigned int backtracking; unsigned int loop_count; unsigned int in_group; unsigned int match_count; unsigned int validation_count; unsigned int start; unsigned int end; unsigned int length; bool (*functions[254])(struct r4_t *); bool (*slash_functions[254])(struct r4_t *); char *_str; char *_expr; char *match; char *str; char *expr; char *str_previous; char *expr_previous; char **matches; } r4_t; static bool v4_initiated = false; typedef bool (*v4_function_map)(r4_t *); v4_function_map v4_function_map_global[256]; v4_function_map v4_function_map_slash[256]; v4_function_map v4_function_map_block[256]; void r4_free_matches(r4_t *r) { if (!r) return; if (r->match) { free(r->match); r->match = NULL; } if (!r->match_count) { return; } for (unsigned i = 0; i < r->match_count; i++) { free(r->matches[i]); } free(r->matches); r->match_count = 0; r->matches = NULL; } void r4_free(r4_t *r) { if (!r) return; r4_free_matches(r); free(r); } static bool r4_backtrack(r4_t *r4); static bool r4_validate(r4_t *r4); static void r4_match_add(r4_t *r4, char *extracted); static bool r4_validate_literal(r4_t *r4) { DEBUG_VALIDATE_FUNCTION if (!r4->valid) return false; if (*r4->str != *r4->expr) { r4->valid = false; } else { r4->str++; } r4->expr++; if (r4->in_block || r4->in_range || !r4->is_greedy) { return r4->valid; } return r4_validate(r4); } static bool r4_validate_question_mark(r4_t *r4) { DEBUG_VALIDATE_FUNCTION r4->valid = true; r4->expr++; return r4_validate(r4); } static bool r4_validate_plus(r4_t *r4) { DEBUG_VALIDATE_FUNCTION r4->expr++; if (r4->valid == false) { return r4_validate(r4); } char *expr_left = r4->expr_previous; char *expr_right = r4->expr; char *str = r4->str; char *return_expr = NULL; if (*expr_right == ')') { return_expr = expr_right; expr_right++; } r4->is_greedy = false; r4->expr = expr_left; while (r4->valid) { if (*expr_right) { r4->expr = expr_right; r4->is_greedy = true; if (r4_backtrack(r4)) { if (return_expr) { r4->str = str; r4->expr = return_expr; } return r4_validate(r4); } else { r4->is_greedy = false; } } r4->valid = true; r4->expr = expr_left; r4->str = str; r4_validate(r4); str = r4->str; } r4->is_greedy = true; r4->valid = true; r4->expr = return_expr ? return_expr : expr_right; return r4_validate(r4); } static bool r4_validate_dollar(r4_t *r4) { DEBUG_VALIDATE_FUNCTION r4->expr++; r4->valid = *r4->str == 0; return r4_validate(r4); } static bool r4_validate_roof(r4_t *r4) { DEBUG_VALIDATE_FUNCTION if (r4->str != r4->_str) { return false; } r4->expr++; return r4_validate(r4); } static bool r4_validate_dot(r4_t *r4) { DEBUG_VALIDATE_FUNCTION if (*r4->str == 0) { return false; } r4->expr++; r4->valid = *r4->str != '\n'; r4->str++; if (r4->in_block || r4->in_range || !r4->is_greedy) { return r4->valid; } return r4_validate(r4); } static bool r4_validate_asterisk(r4_t *r4) { DEBUG_VALIDATE_FUNCTION r4->expr++; if (r4->valid == false) { r4->valid = true; return r4->valid; // return r4_validate(r4); } char *expr_left = r4->expr_previous; char *expr_right = r4->expr; char *str = r4->str; char *return_expr = NULL; if (*expr_right == ')') { return_expr = expr_right; expr_right++; } r4->is_greedy = false; r4->expr = expr_left; while (r4->valid) { if (*expr_right) { r4->expr = expr_right; r4->is_greedy = true; if (r4_backtrack(r4)) { if (return_expr) { r4->str = str; r4->expr = return_expr; } return r4_validate(r4); } else { r4->is_greedy = false; } } r4->valid = true; r4->expr = expr_left; r4->str = str; r4_validate(r4); str = r4->str; } r4->is_greedy = true; r4->valid = true; r4->expr = return_expr ? return_expr : expr_right; return r4_validate(r4); } static bool r4_validate_pipe(r4_t *r4) { DEBUG_VALIDATE_FUNCTION r4->expr++; if (r4->valid == true) { return true; } else { r4->valid = true; } return r4_validate(r4); } static bool r4_validate_digit(r4_t *r4) { DEBUG_VALIDATE_FUNCTION if (!isdigit(*r4->str)) { r4->valid = false; } else { r4->str++; } r4->expr++; if (r4->in_block || r4->in_range || !r4->is_greedy) { return r4->valid; } return r4_validate(r4); } static bool r4_validate_not_digit(r4_t *r4) { DEBUG_VALIDATE_FUNCTION if (isdigit(*r4->str)) { r4->valid = false; } else { r4->str++; } r4->expr++; if (r4->in_block || r4->in_range || !r4->is_greedy) { return r4->valid; } return r4_validate(r4); } static bool r4_validate_word(r4_t *r4) { DEBUG_VALIDATE_FUNCTION if (!isalpha(*r4->str)) { r4->valid = false; } else { r4->str++; } r4->expr++; if (r4->in_block || r4->in_range || !r4->is_greedy) { return r4->valid; } return r4_validate(r4); } static bool r4_validate_not_word(r4_t *r4) { DEBUG_VALIDATE_FUNCTION if (isalpha(*r4->str)) { r4->valid = false; } else { r4->str++; } r4->expr++; if (r4->in_block || r4->in_range || !r4->is_greedy) { return r4->valid; } return r4_validate(r4); } static bool r4_isrange(char *s) { if (!isalnum(*s)) { return false; } if (*(s + 1) != '-') { return false; } return isalnum(*(s + 2)); } static bool r4_validate_block_open(r4_t *r4) { DEBUG_VALIDATE_FUNCTION if (r4->valid == false) { return false; } char *expr_self = r4->expr; r4->expr++; bool reversed = *r4->expr == '^'; if (reversed) { r4->expr++; } bool valid_once = false; r4->in_block = true; while (*r4->expr != ']') { r4->valid = true; if (r4_isrange(r4->expr)) { char s = *r4->expr; char e = *(r4->expr + 2); r4->expr += 2; if (s > e) { char tempc = s; s = e; e = tempc; } if (*r4->str >= s && *r4->str <= e) { if (!reversed) { r4->str++; } valid_once = true; break; } else { r4->expr++; } } else if (r4_validate(r4)) { valid_once = true; if (reversed) r4->str--; break; } } char *expr_end = strchr(r4->expr, ']'); r4->expr = expr_end ? expr_end : r4->expr; r4->in_block = false; r4->valid = expr_end && (!reversed ? valid_once : !valid_once); r4->expr++; r4->expr_previous = expr_self; if (r4->in_range || !r4->is_greedy) { return r4->valid; } return r4_validate(r4); } static bool r4_validate_whitespace(r4_t *r4) { DEBUG_VALIDATE_FUNCTION r4->valid = strchr("\r\t \n", *r4->str) != NULL; r4->expr++; if (r4->valid) { r4->str++; } if (r4->in_range || r4->in_block || !r4->is_greedy) { return r4->valid; } return r4_validate(r4); } static bool r4_validate_not_whitespace(r4_t *r4) { DEBUG_VALIDATE_FUNCTION r4->valid = strchr("\r\t \n", *r4->str) == NULL; r4->expr++; if (r4->valid) { r4->str++; } if (r4->in_range || r4->in_block || !r4->is_greedy) { return r4->valid; } return r4_validate(r4); } static bool r4_validate_range(r4_t *r4) { DEBUG_VALIDATE_FUNCTION; if (r4->valid == false) { r4->expr++; return false; } char *previous = r4->expr_previous; r4->in_range = true; r4->expr++; unsigned int start = 0; while (isdigit(*r4->expr)) { start = 10 * start; start += *r4->expr - '0'; r4->expr++; } if (start != 0) start--; unsigned int end = 0; bool variable_end_range = false; if (*r4->expr == ',') { r4->expr++; if (!isdigit(*r4->expr)) { variable_end_range = true; } } while (isdigit(*r4->expr)) { end = end * 10; end += *r4->expr - '0'; r4->expr++; } r4->expr++; bool valid = true; char *expr_right = r4->expr; for (unsigned int i = 0; i < start; i++) { r4->expr = previous; valid = r4_validate(r4); if (!*r4->str) break; if (!valid) { break; } } r4->expr = expr_right; r4->in_range = false; if (!r4->valid) return false; return r4_validate(r4); for (unsigned int i = start; i < end; i++) { r4->expr = previous; valid = r4_validate(r4); if (!valid) { break; } } while (variable_end_range) { r4->in_range = false; valid = r4_validate(r4); r4->in_range = true; if (valid) { break; } r4->in_range = true; valid = r4_validate(r4); r4->in_range = false; if (!valid) { break; } } r4->valid = valid; return r4_validate(r4); } static bool r4_validate_group_close(r4_t *r4) { DEBUG_VALIDATE_FUNCTION return r4->valid; } static bool r4_validate_group_open(r4_t *r4) { DEBUG_VALIDATE_FUNCTION char *expr_previous = r4->expr_previous; r4->expr++; bool save_match = r4->in_group == 0; r4->in_group++; char *str_extract_start = r4->str; bool valid = r4_validate(r4); if (!valid || *r4->expr != ')') { // this is a valid case if not everything between () matches r4->in_group--; if (save_match == false) { r4->valid = true; } // Not direct return? Not sure return r4_validate(r4); } // if(save_match){ // r4->match_count++; // } if (save_match) { char *str_extract_end = r4->str; unsigned int extracted_length = str_extract_end - str_extract_start; // strlen(str_extract_start) - strlen(str_extract_end); char *str_extracted = (char *)calloc(sizeof(char), extracted_length + 1); strncpy(str_extracted, str_extract_start, extracted_length); r4_match_add(r4, str_extracted); } assert(*r4->expr == ')'); r4->expr++; r4->in_group--; r4->expr_previous = expr_previous; return r4_validate(r4); } static bool r4_validate_slash(r4_t *r4) { DEBUG_VALIDATE_FUNCTION // The handling code for handling slashes is implemented in r4_validate char *expr_previous = r4->expr_previous; r4->expr++; r4_function f = v4_function_map_slash[(int)*r4->expr]; r4->expr_previous = expr_previous; return f(r4); } static void r4_match_add(r4_t *r4, char *extracted) { r4->matches = (char **)realloc(r4->matches, (r4->match_count + 1) * sizeof(char *)); r4->matches[r4->match_count] = extracted; r4->match_count++; } static bool r4_validate_word_boundary_start(r4_t *r4) { DEBUG_VALIDATE_FUNCTION r4->expr++; if (!r4->valid) { return r4->valid; } r4->valid = isalpha(*r4->str) && (r4->str == r4->_str || !isalpha(*(r4->str - 1))); if (r4->in_range || r4->in_block || !r4->is_greedy) { return r4->valid; } return r4_validate(r4); } static bool r4_validate_word_boundary_end(r4_t *r4) { DEBUG_VALIDATE_FUNCTION r4->expr++; if (!r4->valid) { return r4->valid; } r4->valid = isalpha(*r4->str) && (*(r4->str + 1) == 0 || !isalpha(*(r4->str + 1))); if (r4->in_range || r4->in_block || !r4->is_greedy) { return r4->valid; } return r4_validate(r4); } static void v4_init_function_maps() { if (v4_initiated) return; v4_initiated = true; for (__uint8_t i = 0; i < 255; i++) { v4_function_map_global[i] = r4_validate_literal; v4_function_map_slash[i] = r4_validate_literal; v4_function_map_block[i] = r4_validate_literal; } v4_function_map_global['*'] = r4_validate_asterisk; v4_function_map_global['?'] = r4_validate_question_mark; v4_function_map_global['+'] = r4_validate_plus; v4_function_map_global['$'] = r4_validate_dollar; v4_function_map_global['^'] = r4_validate_roof; v4_function_map_global['.'] = r4_validate_dot; v4_function_map_global['|'] = r4_validate_pipe; v4_function_map_global['\\'] = r4_validate_slash; v4_function_map_global['['] = r4_validate_block_open; v4_function_map_global['{'] = r4_validate_range; v4_function_map_global['('] = r4_validate_group_open; v4_function_map_global[')'] = r4_validate_group_close; v4_function_map_slash['b'] = r4_validate_word_boundary_start; v4_function_map_slash['B'] = r4_validate_word_boundary_end; v4_function_map_slash['d'] = r4_validate_digit; v4_function_map_slash['w'] = r4_validate_word; v4_function_map_slash['D'] = r4_validate_not_digit; v4_function_map_slash['W'] = r4_validate_not_word; v4_function_map_slash['s'] = r4_validate_whitespace; v4_function_map_slash['S'] = r4_validate_not_whitespace; v4_function_map_block['\\'] = r4_validate_slash; v4_function_map_block['{'] = r4_validate_range; } void r4_init(r4_t *r4) { v4_init_function_maps(); if (r4 == NULL) return; r4->debug = _r4_debug; r4->valid = true; r4->validation_count = 0; r4->match_count = 0; r4->start = 0; r4->end = 0; r4->length = 0; r4->matches = NULL; } static bool r4_looks_behind(char c) { return strchr("?*+{", c) != NULL; } r4_t *r4_new() { r4_t *r4 = (r4_t *)malloc(sizeof(r4_t)); r4_init(r4); return r4; } static bool r4_pipe_next(r4_t *r4) { char *expr = r4->expr; while (*expr) { if (*expr == '|') { r4->expr = expr + 1; r4->valid = true; return true; } expr++; } return false; } static bool r4_backtrack(r4_t *r4) { if (_r4_debug) printf("\033[36mDEBUG: backtrack start (%d)\n", r4->backtracking); r4->backtracking++; char *str = r4->str; char *expr = r4->expr; bool result = r4_validate(r4); r4->backtracking--; if (result == false) { r4->expr = expr; r4->str = str; } if (_r4_debug) printf("DEBUG: backtrack end (%d) result: %d %s\n", r4->backtracking, result, r4->backtracking == 0 ? "\033[0m" : ""); return result; } static bool r4_validate(r4_t *r4) { DEBUG_VALIDATE_FUNCTION r4->validation_count++; char c_val = *r4->expr; if (c_val == 0) { return r4->valid; } if (!r4_looks_behind(c_val)) { r4->expr_previous = r4->expr; } else if (r4->expr == r4->_expr) { // Regex may not start with a look behind ufnction return false; } if (!r4->valid && !r4_looks_behind(*r4->expr)) { if (!r4_pipe_next(r4)) { return false; } } r4_function f; if (r4->in_block) { f = v4_function_map_block[(int)c_val]; } else { f = v4_function_map_global[(int)c_val]; } r4->valid = f(r4); return r4->valid; } char *r4_get_match(r4_t *r) { char *match = (char *)malloc(r->length + 1); strncpy(match, r->_str + r->start, r->length); match[r->length] = 0; return match; } static bool r4_search(r4_t *r) { bool valid = true; char *str_next = r->str; while (*r->str) { if (!(valid = r4_validate(r))) { // Move next until we find a match if (!r->backtracking) { r->start++; } str_next++; r->str = str_next; r->expr = r->_expr; r->valid = true; } else { /// HIGH DOUBT if (!r->backtracking) { // r->start = 0; } break; } } r->valid = valid; if (r->valid) { r->end = strlen(r->_str) - strlen(r->str); r->length = r->end - r->start; r->match = r4_get_match(r); } return r->valid; } r4_t *r4(const char *str, const char *expr) { r4_t *r = r4_new(); r->_str = (char *)str; r->_expr = (char *)expr; r->match = NULL; r->str = r->_str; r->expr = r->_expr; r->str_previous = r->_str; r->expr_previous = r->expr; r->in_block = false; r->is_greedy = true; r->in_group = 0; r->loop_count = 0; r->backtracking = 0; r->in_range = false; r4_search(r); return r; } r4_t *r4_next(r4_t *r, char *expr) { if (expr) { r->_expr = expr; } r->backtracking = 0; r->expr = r->_expr; r->is_greedy = true; r->in_block = false; r->in_range = false; r->in_group = false; r4_free_matches(r); r4_search(r); return r; } bool r4_match(char *str, char *expr) { r4_t *r = r4(str, expr); bool result = r->valid; r4_free(r); return result; } #endif #define rautocomplete_new rstring_list_new #define rautocomplete_free rstring_list_free #define rautocomplete_add rstring_list_add #define rautocomplete_find rstring_list_find #define rautocomplete_t rstring_list_t #define rautocomplete_contains rstring_list_contains char *r4_escape(char *content) { size_t size = strlen(content) * 2 + 1; char *escaped = (char *)calloc(size, sizeof(char)); char *espr = escaped; char *to_escape = "?*+()[]{}^$\\"; *espr = '('; espr++; while (*content) { if (strchr(to_escape, *content)) { *espr = '\\'; espr++; } *espr = *content; espr++; content++; } *espr = '.'; espr++; *espr = '+'; espr++; *espr = ')'; espr++; *espr = 0; return escaped; } char *rautocomplete_find(rstring_list_t *list, char *expr) { if (!list->count) return NULL; if (!expr || !strlen(expr)) return NULL; char *escaped = r4_escape(expr); for (unsigned int i = list->count - 1; i == 0; i--) { char *match; r4_t *r = r4(list->strings[i], escaped); if (r->valid && r->match_count == 1) { match = strdup(r->matches[0]); } r4_free(r); if (match) { free(escaped); return match; } } free(escaped); return NULL; } #endif #ifndef RKEYTABLE_H #define RKEYTABLE_H /* DERIVED FROM HASH TABLE K&R */ #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct rnklist { struct rnklist *next; struct rnklist *last; char *name; char *defn; } rnklist; static rnklist *rkeytab = NULL; rnklist *rlkget(char *s) { rnklist *np; for (np = rkeytab; np != NULL; np = np->next) if (strcmp(s, np->name) == 0) return np; // Found return NULL; // Not found } char *rkget(char *s) { rnklist *np = rlkget(s); return np ? np->defn : NULL; } rnklist *rkset(char *name, char *defn) { rnklist *np; if ((np = (rlkget(name))) == NULL) { // Not found np = (rnklist *)malloc(sizeof(rnklist)); np->name = strdup(name); np->next = NULL; np->last = NULL; if (defn) { np->defn = strdup(defn); } else { np->defn = NULL; } if (rkeytab == NULL) { rkeytab = np; rkeytab->last = np; } else { if (rkeytab->last) rkeytab->last->next = np; rkeytab->last = np; } } else { if (np->defn) free((void *)np->defn); if (defn) { np->defn = strdup(defn); } else { np->defn = NULL; } } return np; } #endif #ifndef RHASHTABLE_H #define RHASHTABLE_H /* ORIGINAL SOURCE IS FROM K&R */ #include <stdio.h> #include <stdlib.h> #include <string.h> #define HASHSIZE 101 // Structure for the table entries typedef struct rnlist { struct rnlist *next; char *name; char *defn; } rnlist; // Hash table array static rnlist *rhashtab[HASHSIZE]; // Hash function unsigned rhash(char *s) { unsigned hashval; for (hashval = 0; *s != '\0'; s++) hashval = *s + 31 * hashval; return hashval % HASHSIZE; } rnlist *rlget(char *s) { rnlist *np; for (np = rhashtab[rhash(s)]; np != NULL; np = np->next) if (strcmp(s, np->name) == 0) return np; // Found return NULL; // Not found } // Lookup function char *rget(char *s) { rnlist *np = rlget(s); return np ? np->defn : NULL; } // Install function (adds a name and definition to the table) struct rnlist *rset(char *name, char *defn) { struct rnlist *np = NULL; unsigned hashval; if ((rlget(name)) == NULL) { // Not found np = (struct rnlist *)malloc(sizeof(*np)); if (np == NULL || (np->name = strdup(name)) == NULL) return NULL; hashval = rhash(name); np->next = rhashtab[hashval]; rhashtab[hashval] = np; } else { if (np->defn) free((void *)np->defn); np->defn = NULL; } if ((np->defn = strdup(defn)) == NULL) return NULL; return np; } #endif #ifndef RREX3_H #define RREX3_H #include <assert.h> #include <ctype.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #ifndef RREX3_DEBUG #define RREX3_DEBUG 0 #endif struct rrex3_t; typedef void (*rrex3_function)(struct rrex3_t *); typedef struct rrex3_t { void (*functions[254])(struct rrex3_t *); void (*slash_functions[254])(struct rrex3_t *); bool valid; int match_count; int match_capacity; char **matches; bool exit; char *__expr; char *__str; char *_expr; char *_str; char *expr; char *str; char *compiled; bool inside_brackets; bool inside_parentheses; bool pattern_error; bool match_from_start; char bytecode; rrex3_function function; struct { void (*function)(struct rrex3_t *); char *expr; char *str; char bytecode; } previous; struct { void (*function)(struct rrex3_t *); char *expr; char *str; char bytecode; } failed; } rrex3_t; static bool isdigitrange(char *s) { if (!isdigit(*s)) { return false; } if (*(s + 1) != '-') { return false; } return isdigit(*(s + 2)); } static bool isalpharange(char *s) { if (!isalpha(*s)) { return false; } if (*(s + 1) != '-') { return false; } return isalpha(*(s + 2)); } void rrex3_free_matches(rrex3_t *rrex3) { if (!rrex3->matches) return; for (int i = 0; i < rrex3->match_count; i++) { free(rrex3->matches[i]); } free(rrex3->matches); rrex3->matches = NULL; rrex3->match_count = 0; rrex3->match_capacity = 0; } void rrex3_free(rrex3_t *rrex3) { if (!rrex3) return; if (rrex3->compiled) { free(rrex3->compiled); rrex3->compiled = NULL; } rrex3_free_matches(rrex3); free(rrex3); rrex3 = NULL; } static bool rrex3_move(rrex3_t *, bool); static void rrex3_set_previous(rrex3_t *); inline static void rrex3_cmp_asterisk(rrex3_t *); void rrex3_cmp_literal_range(rrex3_t *rrex3) { #if RREX3_DEBUG == 1 printf("Range check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif rrex3_set_previous(rrex3); char start = *rrex3->expr; rrex3->expr++; rrex3->expr++; char end = *rrex3->expr; if (*rrex3->str >= start && *rrex3->str <= end) { rrex3->str++; rrex3->valid = true; } else { rrex3->valid = false; } rrex3->expr++; } bool rrex3_is_function(char chr) { if (chr == ']' || chr == ')' || chr == '\\' || chr == '?' || chr == '+' || chr == '*') return true; return false; } inline static void rrex3_cmp_literal(rrex3_t *rrex3) { rrex3_set_previous(rrex3); if (rrex3->inside_brackets) { if (isalpharange(rrex3->expr) || isdigitrange(rrex3->expr)) { rrex3_cmp_literal_range(rrex3); return; } } #if RREX3_DEBUG == 1 printf("Literal check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif if (*rrex3->expr == 0 && !*rrex3->str) { printf("ERROR, EMPTY CHECK\n"); // exit(1); } if (rrex3->valid == false) { rrex3->expr++; return; } if (*rrex3->expr == *rrex3->str) { rrex3->expr++; rrex3->str++; rrex3->valid = true; // if(*rrex3->expr &&rrex3->functions[(int)*rrex3->expr] == // rrex3_cmp_literal && !rrex3->inside_brackets && //! rrex3_is_function(*rrex3->expr)){ rrex3_cmp_literal(rrex3); // if(rrex3->valid == false){ // rrex3->expr--; // rrex3->valid = true; // } // } return; } rrex3->expr++; rrex3->valid = false; } inline static void rrex3_cmp_dot(rrex3_t *rrex3) { #if RREX3_DEBUG == 1 printf("Dot check (any char): %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif rrex3_set_previous(rrex3); rrex3->expr++; if (!rrex3->valid) { return; } if (*rrex3->str && *rrex3->str != '\n') { rrex3->str++; if (*rrex3->expr && *rrex3->expr == '.') { rrex3_cmp_dot(rrex3); return; } /*else if(*rrex3->expr && (*rrex3->expr == '*' || *rrex3->expr == '+')){ char * next = strchr(rrex3->str,*(rrex3->expr + 1)); char * space = strchr(rrex3->str,'\n'); if(next && (!space || space > next)){ rrex3->str = next; } }*/ } else { rrex3->valid = false; } } inline static void rrex3_cmp_question_mark(rrex3_t *rrex3) { #if RREX3_DEBUG == 1 printf("Question mark check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif rrex3_set_previous(rrex3); if (rrex3->valid == false) rrex3->valid = true; rrex3->expr++; } inline static void rrex3_cmp_whitespace(rrex3_t *rrex3) { #if RREX3_DEBUG == 1 printf("Whitespace check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif rrex3_set_previous(rrex3); char c = *rrex3->expr; rrex3->valid = c == ' ' || c == '\n' || c == '\t'; if (rrex3->valid) { rrex3->str++; } rrex3->expr++; } inline static void rrex3_cmp_whitespace_upper(rrex3_t *rrex3) { #if RREX3_DEBUG == 1 printf("Non whitespace check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif rrex3_set_previous(rrex3); char c = *rrex3->expr; rrex3->valid = !(c == ' ' || c == '\n' || c == '\t'); if (rrex3->valid) { rrex3->str++; } rrex3->expr++; } inline static void rrex3_cmp_plus2(rrex3_t *rrex3) { #if RREX3_DEBUG == 1 printf("Plus check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif rrex3_set_previous(rrex3); if (rrex3->valid) { rrex3->str--; } else { return; } char *original_expr = rrex3->expr; char *next = original_expr + 1; char *loop_expr = rrex3->previous.expr - 1; if (*loop_expr == '+') { rrex3->valid = false; rrex3->pattern_error = true; rrex3->expr++; return; } bool success_next = false; bool success_next_once = false; bool success_current = false; char *next_next = NULL; char *next_str = rrex3->str; while (*rrex3->str) { // Check if next matches char *original_str = rrex3->str; rrex3->expr = next; rrex3->valid = true; if (rrex3_move(rrex3, false)) { success_next = true; next_next = rrex3->expr; next_str = rrex3->str; success_next_once = true; } else { success_next = false; } if (success_next_once && !success_next) { break; } // Check if current matches rrex3->str = original_str; rrex3->expr = loop_expr; rrex3->valid = true; if (!*rrex3->str || !rrex3_move(rrex3, false)) { success_current = false; } else { success_current = true; if (!success_next) { next_next = rrex3->expr + 1; // +1 is the * itself next_str = rrex3->str; } } if (success_next && !success_current) { break; } } if (!next_next) rrex3->expr = next; else { rrex3->expr = next_next; } rrex3->str = next_str; rrex3->valid = true; } inline static void rrex3_cmp_plus(rrex3_t *rrex3) { #if RREX3_DEBUG == 1 rprintg("Asterisk start check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif if (!rrex3->valid) { rrex3->expr++; return; } char *left = rrex3->previous.expr; // printf("%s\n",rrex3->str); char *right = rrex3->expr + 1; if (*right == ')') { right++; } int right_valid = 0; bool right_valid_once = false; char *expr = right; char *right_str = rrex3->str; ; char *right_expr = NULL; char *str = rrex3->str; bool first_time = true; bool left_valid = true; char *str_prev = NULL; bool valid_from_start = true; ; while (*rrex3->str) { if (!left_valid && !right_valid) { break; } if (right_valid && !left_valid) { str = right_str; break; } rrex3->expr = right; rrex3->str = str; #if RREX3_DEBUG == 1 printf("r"); #endif if (*rrex3->str && rrex3_move(rrex3, false)) { right_valid++; right_str = rrex3->str; expr = rrex3->expr; if (!right_valid_once) { right_expr = rrex3->expr; right_valid_once = true; } } else { right_valid = 0; } if (first_time) { first_time = false; valid_from_start = right_valid; } if (right_valid && !valid_from_start && right_valid > 0) { expr = right_expr - 1; ; if (*(right - 1) == ')') { expr = right - 1; } break; } if ((!right_valid && right_valid_once)) { expr = right_expr; if (*(right - 1) == ')') { str = str_prev; expr = right - 1; } break; } str_prev = str; rrex3->valid = true; rrex3->str = str; rrex3->expr = left; #if RREX3_DEBUG == 1 printf("l"); #endif if (rrex3_move(rrex3, false)) { left_valid = true; str = rrex3->str; } else { left_valid = false; } } rrex3->expr = expr; rrex3->str = str; rrex3->valid = true; #if RREX3_DEBUG == 1 rprintg("Asterisk end check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif } inline static void rrex3_cmp_asterisk(rrex3_t *rrex3) { #if RREX3_DEBUG == 1 rprintg("Asterisk start check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif if (!rrex3->valid) { rrex3->valid = true; rrex3->expr++; return; } rrex3->str = rrex3->previous.str; char *left = rrex3->previous.expr; // printf("%s\n",rrex3->str); char *right = rrex3->expr + 1; if (*right == ')') { right++; } int right_valid = 0; bool right_valid_once = false; char *expr = right; char *right_str = rrex3->str; ; char *right_expr = NULL; char *str = rrex3->str; bool first_time = true; bool left_valid = true; char *str_prev = NULL; bool valid_from_start = true; ; while (*rrex3->str) { if (!left_valid && !right_valid) { break; } if (right_valid && !left_valid) { str = right_str; break; } rrex3->expr = right; rrex3->str = str; #if RREX3_DEBUG == 1 printf("r"); #endif if (*rrex3->str && rrex3_move(rrex3, false)) { right_valid++; right_str = rrex3->str; expr = rrex3->expr; if (!right_valid_once) { right_expr = rrex3->expr; right_valid_once = true; } } else { right_valid = 0; } if (first_time) { first_time = false; valid_from_start = right_valid; } if (right_valid && !valid_from_start && right_valid > 0) { expr = right_expr - 1; if (*(right - 1) == ')') { expr = right - 1; } break; } if ((!right_valid && right_valid_once)) { expr = right_expr; if (*(right - 1) == ')') { str = str_prev; expr = right - 1; } break; } str_prev = str; rrex3->valid = true; rrex3->str = str; rrex3->expr = left; #if RREX3_DEBUG == 1 printf("l"); #endif if (rrex3_move(rrex3, false)) { left_valid = true; str = rrex3->str; } else { left_valid = false; } } rrex3->expr = expr; rrex3->str = str; rrex3->valid = true; #if RREX3_DEBUG == 1 rprintg("Asterisk end check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif } inline static void rrex3_cmp_asterisk2(rrex3_t *rrex3) { #if RREX3_DEBUG == 1 rprintg("Asterisk start check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif if (!rrex3->valid) { rrex3->valid = true; rrex3->expr++; return; } if (*rrex3->previous.expr == '*') { // Support for ** rrex3->valid = false; // rrex3->pattern_error = true; rrex3->expr++; return; } rrex3->str = rrex3->previous.str; ; char *next = rrex3->expr + 1; char *next_original = NULL; if (*next == '*') { next++; } if (*next == ')' && *(next + 1)) { next_original = next; next++; } char *loop_expr = rrex3->previous.expr; bool success_next = false; bool success_next_once = false; bool success_current = false; char *right_next = NULL; char *right_str = rrex3->str; while (*rrex3->str && *rrex3->expr && *rrex3->expr != ')') { // Remember original_str because it's modified // by checking right and should be restored // for checking left so they're matching the // same value. char *original_str = rrex3->str; // Check if right matches. // if(*next != ')'){ rrex3->expr = next; rrex3->valid = true; if (rrex3_move(rrex3, false)) { // Match rright. success_next = true; if (!next_original) { if (!success_next_once) { right_next = rrex3->expr; } } else { right_next = next_original; break; } right_str = rrex3->str; success_next_once = true; } else { // No match Right. success_next = false; } //} if (success_next_once && !success_next) { // Matched previous time but now doesn't. break; } // Check if left matches. rrex3->str = original_str; rrex3->expr = loop_expr; rrex3->valid = true; if (!rrex3_move(rrex3, false)) { // No match left. success_current = false; } else { // Match left. success_current = true; // NOT SURE< WITHOUT DOET HETZELFDE: // original_str = rrex3->str; if (!success_next) { right_str = rrex3->str; if (*rrex3->expr != ')') { right_next = rrex3->expr + 1; // +1 is the * itself } else { // break; } } } if ((success_next && !success_current) || (!success_next && !success_current)) { break; } } rrex3->expr = right_next; rrex3->str = right_str; rrex3->valid = true; #if RREX3_DEBUG == 1 rprintg("Asterisk end check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif } inline static void rrex3_cmp_roof(rrex3_t *rrex3) { rrex3_set_previous(rrex3); #if RREX3_DEBUG == 1 printf("<Roof check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif rrex3->valid = rrex3->str == rrex3->_str; rrex3->match_from_start = true; rrex3->expr++; } inline static void rrex3_cmp_dollar(rrex3_t *rrex3) { rrex3_set_previous(rrex3); #if RREX3_DEBUG == 1 printf("Dollar check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif if (*rrex3->str || !rrex3->valid) { rrex3->valid = false; } rrex3->expr++; } inline static void rrex3_cmp_w(rrex3_t *rrex3) { rrex3_set_previous(rrex3); rrex3->expr++; #if RREX3_DEBUG == 1 printf("Word check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif if (isalpha(*rrex3->str)) { rrex3->str++; } else { rrex3->valid = false; } } inline static void rrex3_cmp_w_upper(rrex3_t *rrex3) { rrex3_set_previous(rrex3); rrex3->expr++; #if RREX3_DEBUG == 1 printf("!Word check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif if (!isalpha(*rrex3->str)) { rrex3->str++; } else { rrex3->valid = false; } } inline static void rrex3_cmp_d(rrex3_t *rrex3) { rrex3_set_previous(rrex3); rrex3->expr++; #if RREX3_DEBUG == 1 printf("Digit check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif if (isdigit(*rrex3->str)) { rrex3->str++; } else { rrex3->valid = false; } } inline static void rrex3_cmp_d_upper(rrex3_t *rrex3) { rrex3_set_previous(rrex3); rrex3->expr++; #if RREX3_DEBUG == 1 printf("!Digit check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif if (!isdigit(*rrex3->str)) { rrex3->str++; } else { rrex3->valid = false; } } inline static void rrex3_cmp_slash(rrex3_t *rrex3) { rrex3_set_previous(rrex3); rrex3->expr++; rrex3->bytecode = *rrex3->expr; rrex3->function = rrex3->slash_functions[(int)rrex3->bytecode]; rrex3->function(rrex3); } inline static int collect_digits(rrex3_t *rrex3) { char output[20]; unsigned int digit_count = 0; while (isdigit(*rrex3->expr)) { output[digit_count] = *rrex3->expr; rrex3->expr++; digit_count++; } output[digit_count] = 0; return atoi(output); } inline static void rrex3_cmp_range(rrex3_t *rrex3) { char *loop_code = rrex3->previous.expr; char *expr_original = rrex3->expr; rrex3->expr++; int range_start = collect_digits(rrex3) - 1; int range_end = 0; if (*rrex3->expr == ',') { rrex3->expr++; range_end = collect_digits(rrex3); } rrex3->expr++; int times_valid = 0; while (*rrex3->str) { rrex3->expr = loop_code; rrex3_move(rrex3, false); if (rrex3->valid == false) { break; } else { times_valid++; } if (range_end) { if (times_valid >= range_start && times_valid == range_end - 1) { rrex3->valid = true; } else { rrex3->valid = false; } break; } else if (range_start) { if (times_valid == range_start) { rrex3->valid = true; break; } } } rrex3->valid = times_valid >= range_start; if (rrex3->valid && range_end) { rrex3->valid = times_valid <= range_end; } rrex3->expr = strchr(expr_original, '}') + 1; } inline static void rrex3_cmp_word_start_or_end(rrex3_t *rrex3) { #if RREX3_DEBUG == 1 if (*rrex3->expr != 'B') { printf("Check word start or end: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); } #endif rrex3_set_previous(rrex3); bool valid = false; if (isalpha(*rrex3->str)) { if (rrex3->_str != rrex3->str) { if (!isalpha(*(rrex3->str - 1))) { valid = true; } } else { valid = true; } } else if (isalpha(isalpha(*rrex3->str) && !isalpha(*rrex3->str + 1))) { valid = true; } rrex3->expr++; rrex3->valid = valid; } inline static void rrex3_cmp_word_not_start_or_end(rrex3_t *rrex3) { #if RREX3_DEBUG == 1 printf("Check word NOT start or end: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif rrex3_set_previous(rrex3); rrex3_cmp_word_start_or_end(rrex3); rrex3->valid = !rrex3->valid; } inline static void rrex3_cmp_brackets(rrex3_t *rrex3) { #if RREX3_DEBUG == 1 rprintb("\\l Brackets start: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif rrex3_set_previous(rrex3); char *original_expr = rrex3->expr; rrex3->expr++; rrex3->inside_brackets = true; bool valid_once = false; bool reversed = false; if (*rrex3->expr == '^') { reversed = true; rrex3->expr++; } bool valid = false; while (*rrex3->expr != ']' && *rrex3->expr != 0) { rrex3->valid = true; valid = rrex3_move(rrex3, false); if (reversed) { valid = !valid; } if (valid) { valid_once = true; if (!reversed) { valid_once = true; break; } } else { if (reversed) { valid_once = false; break; } } } if (valid_once && reversed) { rrex3->str++; } while (*rrex3->expr != ']' && *rrex3->expr != 0) rrex3->expr++; if (*rrex3->expr != 0) rrex3->expr++; rrex3->valid = valid_once; rrex3->inside_brackets = false; char *previous_expr = rrex3->expr; rrex3->expr = original_expr; rrex3_set_previous(rrex3); rrex3->expr = previous_expr; #if RREX3_DEBUG == 1 rprintb("\\l Brackets end: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif } inline static void rrex3_cmp_pipe(rrex3_t *rrex3) { rrex3_set_previous(rrex3); #if RREX3_DEBUG == 1 printf("Pipe check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif if (rrex3->valid == true) { rrex3->exit = true; } else { rrex3->valid = true; } rrex3->expr++; } inline static void rrex3_cmp_parentheses(rrex3_t *rrex3) { #if RREX3_DEBUG == 1 rprinty("\\l Parentheses start check: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif rrex3_set_previous(rrex3); if (!rrex3->valid) { rrex3->expr++; return; } if (rrex3->match_count == rrex3->match_capacity) { rrex3->match_capacity++; rrex3->matches = (char **)realloc(rrex3->matches, rrex3->match_capacity * sizeof(char *)); } rrex3->matches[rrex3->match_count] = (char *)malloc(strlen(rrex3->str) + 1); strcpy(rrex3->matches[rrex3->match_count], rrex3->str); char *original_expr = rrex3->expr; char *original_str = rrex3->str; rrex3->expr++; rrex3->inside_parentheses = true; while (*rrex3->expr != ')' && !rrex3->exit) { rrex3_move(rrex3, false); } while (*rrex3->expr != ')') { rrex3->expr++; } rrex3->expr++; rrex3->inside_parentheses = false; char *previous_expr = rrex3->expr; rrex3->expr = original_expr; rrex3_set_previous(rrex3); rrex3->expr = previous_expr; if (rrex3->valid == false) { rrex3->str = original_str; free(rrex3->matches[rrex3->match_count]); } else { rrex3->matches[rrex3->match_count][strlen(rrex3->matches[rrex3->match_count]) - strlen(rrex3->str)] = 0; rrex3->match_count++; } #if RREX3_DEBUG == 1 rprinty("\\l Parentheses end: %c:%c:%d\n", *rrex3->expr, *rrex3->str, rrex3->valid); #endif } inline static void rrex3_reset(rrex3_t *rrex3) { rrex3_free_matches(rrex3); rrex3->valid = true; rrex3->pattern_error = false; rrex3->inside_brackets = false; rrex3->inside_parentheses = false; rrex3->exit = false; rrex3->previous.expr = NULL; rrex3->previous.str = NULL; rrex3->previous.bytecode = 0; rrex3->failed.expr = NULL; rrex3->failed.str = NULL; rrex3->failed.bytecode = 0; rrex3->match_from_start = false; } void rrex3_init(rrex3_t *rrex3) { for (__uint8_t i = 0; i < 254; i++) { rrex3->functions[i] = rrex3_cmp_literal; rrex3->slash_functions[i] = rrex3_cmp_literal; } rrex3->functions['?'] = rrex3_cmp_question_mark; rrex3->functions['^'] = rrex3_cmp_roof; rrex3->functions['$'] = rrex3_cmp_dollar; rrex3->functions['.'] = rrex3_cmp_dot; rrex3->functions['*'] = rrex3_cmp_asterisk; rrex3->functions['+'] = rrex3_cmp_plus; rrex3->functions['|'] = rrex3_cmp_pipe; rrex3->functions['\\'] = rrex3_cmp_slash; rrex3->functions['{'] = rrex3_cmp_range; rrex3->functions['['] = rrex3_cmp_brackets; rrex3->functions['('] = rrex3_cmp_parentheses; rrex3->slash_functions['w'] = rrex3_cmp_w; rrex3->slash_functions['W'] = rrex3_cmp_w_upper; rrex3->slash_functions['d'] = rrex3_cmp_d; rrex3->slash_functions['D'] = rrex3_cmp_d_upper; rrex3->slash_functions['s'] = rrex3_cmp_whitespace; rrex3->slash_functions['S'] = rrex3_cmp_whitespace_upper; rrex3->slash_functions['b'] = rrex3_cmp_word_start_or_end; rrex3->slash_functions['B'] = rrex3_cmp_word_not_start_or_end; rrex3->match_count = 0; rrex3->match_capacity = 0; rrex3->matches = NULL; rrex3->compiled = NULL; rrex3_reset(rrex3); } rrex3_t *rrex3_new() { rrex3_t *rrex3 = (rrex3_t *)malloc(sizeof(rrex3_t)); rrex3_init(rrex3); return rrex3; } rrex3_t *rrex3_compile(rrex3_t *rrex, char *expr) { rrex3_t *rrex3 = rrex ? rrex : rrex3_new(); char *compiled = (char *)malloc(strlen(expr) + 1); unsigned int count = 0; while (*expr) { if (*expr == '[' && *(expr + 2) == ']') { *compiled = *(expr + 1); expr++; expr++; } else if (*expr == '[' && *(expr + 1) == '0' && *(expr + 2) == '-' && *(expr + 3) == '9' && *(expr + 4) == ']') { *compiled = '\\'; compiled++; *compiled = 'd'; count++; expr++; expr++; expr++; expr++; } else { *compiled = *expr; } if (*compiled == '[') { // in_brackets = true; } else if (*compiled == ']') { // in_brackets = false; } expr++; compiled++; count++; } *compiled = 0; compiled -= count; rrex3->compiled = compiled; return rrex3; } inline static void rrex3_set_previous(rrex3_t *rrex3) { rrex3->previous.function = rrex3->function; rrex3->previous.expr = rrex3->expr; rrex3->previous.str = rrex3->str; rrex3->previous.bytecode = *rrex3->expr; } static bool rrex3_move(rrex3_t *rrex3, bool resume_on_fail) { char *original_expr = rrex3->expr; char *original_str = rrex3->str; rrex3->bytecode = *rrex3->expr; rrex3->function = rrex3->functions[(int)rrex3->bytecode]; rrex3->function(rrex3); if (!*rrex3->expr && !*rrex3->str) { rrex3->exit = true; return rrex3->valid; } else if (!*rrex3->expr) { // rrex3->valid = true; return rrex3->valid; } if (rrex3->pattern_error) { rrex3->valid = false; return rrex3->valid; } if (resume_on_fail && !rrex3->valid && *rrex3->expr) { // rrex3_set_previous(rrex3); rrex3->failed.bytecode = rrex3->bytecode; rrex3->failed.function = rrex3->function; rrex3->failed.expr = original_expr; rrex3->failed.str = original_str; rrex3->bytecode = *rrex3->expr; rrex3->function = rrex3->functions[(int)rrex3->bytecode]; rrex3->function(rrex3); if (!rrex3->valid && !rrex3->pattern_error) { if (*rrex3->str) { char *pipe_position = strstr(rrex3->expr, "|"); if (pipe_position != NULL) { rrex3->expr = pipe_position + 1; rrex3->str = rrex3->_str; rrex3->valid = true; return true; } } if (rrex3->match_from_start) { rrex3->valid = false; return rrex3->valid; } if (!*rrex3->str++) { rrex3->valid = false; return rrex3->valid; } rrex3->expr = rrex3->_expr; if (*rrex3->str) rrex3->valid = true; } } else { } return rrex3->valid; } rrex3_t *rrex3(rrex3_t *rrex3, char *str, char *expr) { #if RREX3_DEBUG == 1 printf("Regex check: %s:%s:%d\n", expr, str, 1); #endif bool self_initialized = false; if (rrex3 == NULL) { self_initialized = true; rrex3 = rrex3_new(); } else { rrex3_reset(rrex3); } rrex3->_str = str; rrex3->_expr = rrex3->compiled ? rrex3->compiled : expr; rrex3->str = rrex3->_str; rrex3->expr = rrex3->_expr; while (*rrex3->expr && !rrex3->exit) { if (!rrex3_move(rrex3, true)) return NULL; } rrex3->expr = rrex3->_expr; if (rrex3->valid) { return rrex3; } else { if (self_initialized) { rrex3_free(rrex3); } return NULL; } } void rrex3_test() { rrex3_t *rrex = rrex3_new(); assert(rrex3(rrex, "\"stdio.h\"\"string.h\"\"sys/time.h\"", "\"(.*)\"\"(.*)\"\"(.*)\"")); assert(rrex3(rrex, "aaaaaaa", "a*a$")); // assert(rrex3("ababa", "a*b*a*b*a$")); assert(rrex3(rrex, "#include\"test.h\"a", "#include.*\".*\"a$")); assert(rrex3(rrex, "#include \"test.h\"a", "#include.*\".*\"a$")); assert(rrex3(rrex, "aaaaaad", "a*d$")); assert(rrex3(rrex, "abcdef", "abd?cdef")); assert(!rrex3(rrex, "abcdef", "abd?def")); assert(rrex3(rrex, "abcdef", "def")); assert(!rrex3(rrex, "abcdef", "^def")); assert(rrex3(rrex, "abcdef", "def$")); assert(!rrex3(rrex, "abcdef", "^abc$")); assert(rrex3(rrex, "aB!.#1", "......")); assert(!rrex3(rrex, "aB!.#\n", " ......")); assert(!rrex3(rrex, "aaaaaad", "q+d$")); assert(rrex3(rrex, "aaaaaaa", "a+a$")); assert(rrex3(rrex, "aaaaaad", "q*d$")); assert(!rrex3(rrex, "aaaaaad", "^q*d$")); // Asterisk function assert(rrex3(rrex, "123321", "123*321")); assert(rrex3(rrex, "pony", "p*ony")); assert(rrex3(rrex, "pppony", "p*ony")); assert(rrex3(rrex, "ppony", "p*pony")); assert(rrex3(rrex, "pppony", "pp*pony")); assert(rrex3(rrex, "pppony", ".*pony")); assert(rrex3(rrex, "pony", ".*ony")); assert(rrex3(rrex, "pony", "po*ny")); // assert(rrex3(rrex,"ppppony", "p*pppony")); // Plus function assert(rrex3(rrex, "pony", "p+ony")); assert(!rrex3(rrex, "ony", "p+ony")); assert(rrex3(rrex, "ppony", "p+pony")); assert(rrex3(rrex, "pppony", "pp+pony")); assert(rrex3(rrex, "pppony", ".+pony")); assert(rrex3(rrex, "pony", ".+ony")); assert(rrex3(rrex, "pony", "po+ny")); // Slash functions assert(rrex3(rrex, "a", "\\w")); assert(!rrex3(rrex, "1", "\\w")); assert(rrex3(rrex, "1", "\\W")); assert(!rrex3(rrex, "a", "\\W")); assert(rrex3(rrex, "a", "\\S")); assert(!rrex3(rrex, " ", "\\s")); assert(!rrex3(rrex, "\t", "\\s")); assert(!rrex3(rrex, "\n", "\\s")); assert(rrex3(rrex, "1", "\\d")); assert(!rrex3(rrex, "a", "\\d")); assert(rrex3(rrex, "a", "\\D")); assert(!rrex3(rrex, "1", "\\D")); assert(rrex3(rrex, "abc", "\\b")); assert(rrex3(rrex, "abc", "\\babc")); assert(!rrex3(rrex, "abc", "a\\b")); assert(!rrex3(rrex, "abc", "ab\\b")); assert(!rrex3(rrex, "abc", "abc\\b")); assert(rrex3(rrex, "abc", "a\\Bbc")); assert(rrex3(rrex, "abc", "ab\\B")); assert(!rrex3(rrex, "1ab", "1\\Bab")); assert(rrex3(rrex, "abc", "a\\Bbc")); // Escaping of special chars assert(rrex3(rrex, "()+*.\\", "\\(\\)\\+\\*\\.\\\\")); // Pipe // assert(rrex3(rrex,"abc","abc|def")); assert(rrex3(rrex, "abc", "def|jkl|abc")); assert(rrex3(rrex, "abc", "abc|def")); assert(rrex3(rrex, "rhq", "def|rhq|rha")); assert(rrex3(rrex, "abc", "abc|def")); // Repeat assert(rrex3(rrex, "aaaaa", "a{4}")); assert(rrex3(rrex, "aaaa", "a{1,3}a")); // Range assert(rrex3(rrex, "abc", "[abc][abc][abc]$")); assert(rrex3(rrex, "def", "[^abc][^abc][^abc]$")); assert(rrex3(rrex, "defabc", "[^abc][^abc][^abc]abc")); assert(rrex3(rrex, "0-9", "0-9")); assert(rrex3(rrex, "55-9", "[^6-9]5-9$")); assert(rrex3(rrex, "a", "[a-z]$")); assert(rrex3(rrex, "A", "[A-Z]$")); assert(rrex3(rrex, "5", "[0-9]$")); assert(!rrex3(rrex, "a", "[^a-z]$")); assert(!rrex3(rrex, "A", "[^A-Z]$")); assert(!rrex3(rrex, "5", "[^0-9]$")); assert(rrex3(rrex, "123abc", "[0-9]*abc$")); assert(rrex3(rrex, "123123", "[0-9]*$")); // Parentheses assert(rrex3(rrex, "datadata", "(data)*")); assert(rrex3(rrex, "datadatapony", "(data)*pony$")); assert(!rrex3(rrex, "datadatapony", "(d*p*ata)*pond$")); assert(rrex3(rrex, "datadatadato", "(d*p*ata)*dato")); assert(rrex3(rrex, "datadatadato", "(d*p*ata)*dato$")); assert(!rrex3(rrex, "datadatadato", "(d*p*a*ta)*gato$")); // Matches assert(rrex3(rrex, "123", "(123)")); assert(!strcmp(rrex->matches[0], "123")); assert(rrex3(rrex, "123321a", "(123)([0-4][2]1)a$")); assert(!strcmp(rrex->matches[1], "321")); assert(rrex3(rrex, "123321a", "(123)([0-4][2]1)a$")); assert(!strcmp(rrex->matches[1], "321")); assert(rrex3(rrex, "aaaabc", "(.*)c")); assert(rrex3(rrex, "abcde", ".....$")); assert(rrex3(rrex, "abcdefghijklmnopqrstuvwxyz", "..........................$")); // printf("(%d)\n", rrex->valid); assert(rrex3(rrex, "#include <stdio.h>", "#include.*<(.*)>")); assert(!strcmp(rrex->matches[0], "stdio.h")); assert(rrex3(rrex, "#include \"stdlib.h\"", "#include.\"(.*)\"")); assert(!strcmp(rrex->matches[0], "stdlib.h")); assert(rrex3(rrex, "\"stdio.h\"\"string.h\"\"sys/time.h\"", "\"(.*)\"\"(.*)\"\"(.*)\"")); assert(!strcmp(rrex->matches[0], "stdio.h")); assert(!strcmp(rrex->matches[1], "string.h")); assert(!strcmp(rrex->matches[2], "sys/time.h")); assert(rrex3(rrex, " #include <stdio.h>", "#include.+<(.+)>")); assert(!strcmp(rrex->matches[0], "stdio.h")); assert(rrex3(rrex, " #include \"stdlib.h\"", "#include.+\"(.+)\"")); assert(!strcmp(rrex->matches[0], "stdlib.h")); assert(rrex3(rrex, " \"stdio.h\"\"string.h\"\"sys/time.h\"", "\"(.+)\"\"(.+)\"\"(.+)\"")); assert(!strcmp(rrex->matches[0], "stdio.h")); assert(!strcmp(rrex->matches[1], "string.h")); assert(!strcmp(rrex->matches[2], "sys/time.h")); assert(rrex3(rrex, "int abc ", "int (.*)[; ]?$")); assert(!strcmp(rrex->matches[0], "abc")); assert(rrex3(rrex, "int abc;", "int (.*)[; ]?$")); assert(!strcmp(rrex->matches[0], "abc")); assert(rrex3(rrex, "int abc", "int (.*)[; ]?$")); assert(!strcmp(rrex->matches[0], "abc")); rrex3_free(rrex); } #endif #ifndef RARENA_H #define RARENA_H #include <stdlib.h> #include <string.h> typedef struct arena_t { unsigned char *memory; unsigned int pointer; unsigned int size; } arena_t; arena_t *arena_construct() { arena_t *arena = (arena_t *)rmalloc(sizeof(arena_t)); arena->memory = NULL; arena->pointer = 0; arena->size = 0; return arena; } arena_t *arena_new(size_t size) { arena_t *arena = arena_construct(); arena->memory = (unsigned char *)rmalloc(size); arena->size = size; return arena; } void *arena_alloc(arena_t *arena, size_t size) { if (arena->pointer + size > arena->size) { return NULL; } void *p = arena->memory + arena->pointer; arena->pointer += size; return p; } void arena_free(arena_t *arena) { // Just constructed and unused arena memory is NULL so no free needed if (arena->memory) { rfree(arena->memory); } rfree(arena); } void arena_reset(arena_t *arena) { arena->pointer = 0; } #endif #ifndef RCASE_H #define RCASE_H #include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #define RCAMEL_CASE 1 #define RSNAKE_CASE 2 #define RINVALID_CASE 0 #define RCONST_TEST_T 4; int rdetermine_case(const char *str) { int length = strlen(str); char p = 0; while (*str) { if (p == '_' && islower(*str)) return RSNAKE_CASE; if (p != '_' && !isupper(p) && isupper(*str)) return RCAMEL_CASE; p = *str; str++; } return RINVALID_CASE; if (length == 0) { return RINVALID_CASE; } if (strchr(str, '_')) { if (str[0] == '_' || str[length - 1] == '_' || strstr(str, "__")) { return RINVALID_CASE; } for (int i = 0; i < length; i++) { if (!islower(str[i]) && str[i] != '_') { return RINVALID_CASE; } } return RSNAKE_CASE; } else { if (!islower(str[0])) { return RINVALID_CASE; } for (int i = 1; i < length; i++) { if (str[i] == '_') { return RINVALID_CASE; } if (isupper(str[i]) && isupper(str[i - 1])) { return RINVALID_CASE; } } return RCAMEL_CASE; } } char *rsnake_to_camel(const char *snake_case) { int length = strlen(snake_case); char *camel_case = (char *)malloc(length + 1); int j = 0; int toUpper = 0; for (int i = 0; i < length; i++) { if (i > 0 && snake_case[i] == '_' && snake_case[i + 1] == 'T') { toUpper = 1; if (snake_case[i + 1] == 'T' && (snake_case[i + 2] != '\n' || snake_case[i + 2] != '\0' || snake_case[i + 2] != ' ')) { toUpper = 0; } } if (snake_case[i] == '_' && snake_case[i + 1] != 't') { toUpper = 1; if (snake_case[i + 1] == 't' && (snake_case[i + 2] != '\n' || snake_case[i + 2] != '\0' || snake_case[i + 2] != ' ')) { toUpper = 0; } } else if (snake_case[i] == '_' && snake_case[i + 1] == 't' && !isspace(snake_case[i + 2])) { toUpper = 1; } else if (snake_case[i] == '_' && snake_case[i + 1] == 'T' && !isspace(snake_case[i + 2])) { toUpper = 1; camel_case[j++] = '_'; j++; } else { if (toUpper) { camel_case[j++] = toupper(snake_case[i]); toUpper = 0; } else { camel_case[j++] = snake_case[i]; } } } camel_case[j] = '\0'; return camel_case; } char *rcamel_to_snake(const char *camelCase) { int length = strlen(camelCase); char *snake_case = (char *)malloc(2 * length + 1); int j = 0; for (int i = 0; i < length; i++) { if (isupper(camelCase[i])) { if (i != 0) { snake_case[j++] = '_'; } snake_case[j++] = tolower(camelCase[i]); } else { snake_case[j++] = camelCase[i]; } } snake_case[j] = '\0'; return snake_case; } char *rflip_case(char *content) { if (rdetermine_case(content) == RSNAKE_CASE) { return rcamel_to_snake(content); } else if (rdetermine_case(content) == RCAMEL_CASE) { return rsnake_to_camel(content); } else { rprintr("Could not determine case\n"); return NULL; } } char *rflip_case_file(char *filepath) { size_t file_size = rfile_size(filepath); if (file_size == 0) { return NULL; } char *content = (char *)malloc(file_size); char *result = NULL; if (rfile_readb(filepath, content, file_size)) { result = rflip_case(content); if (result) { free(content); return result; } else { return content; } } return result; } int rcase_main(int argc, char *argv[]) { if (argc < 2) { printf("usage: rcase <file>\n"); return 1; } for (int i = 1; i < argc; i++) { char *result = rflip_case_file(argv[i]); if (result) { printf("%s\n", result); free(result); } } return 0; } #endif #ifndef RTERM_H #define RTERM_H #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <termios.h> #include <unistd.h> typedef struct winsize winsize_t; typedef struct rshell_keypress_t { bool pressed; bool ctrl; bool shift; bool escape; char c; int ms; int fd; } rshell_keypress_t; typedef struct rterm_t { bool show_cursor; bool show_footer; int ms_tick; rshell_keypress_t key; void (*before_cursor_move)(struct rterm_t *); void (*after_cursor_move)(struct rterm_t *); void (*after_key_press)(struct rterm_t *); void (*before_key_press)(struct rterm_t *); void (*before_draw)(struct rterm_t *); void (*after_draw)(struct rterm_t *); void *session; unsigned long iterations; void (*tick)(struct rterm_t *); char *status_text; char *_status_text_previous; winsize_t size; struct { int x; int y; int pos; int available; } cursor; } rterm_t; typedef void (*rterm_event)(rterm_t *); void rterm_init(rterm_t *rterm) { memset(rterm, 0, sizeof(rterm_t)); rterm->show_cursor = true; rterm->cursor.x = 0; rterm->cursor.y = 0; rterm->ms_tick = 100; rterm->_status_text_previous = NULL; } void rterm_getwinsize(winsize_t *w) { // Get the terminal size if (ioctl(STDOUT_FILENO, TIOCGWINSZ, w) == -1) { perror("ioctl"); exit(EXIT_FAILURE); } } void rrawfd(int fd) { struct termios orig_termios; tcgetattr(fd, &orig_termios); // Get current terminal attributes struct termios raw = orig_termios; raw.c_lflag &= ~(ICANON | ISIG | ECHO); // ECHO // Disable canonical mode and echoing raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 240; // Set timeout for read input tcsetattr(fd, TCSAFLUSH, &raw); } // Terminal setup functions void enableRawMode(struct termios *orig_termios) { struct termios raw = *orig_termios; raw.c_lflag &= ~(ICANON | ECHO); // Disable canonical mode and echoing raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 240; // Set timeout for read input tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw); } void disableRawMode(struct termios *orig_termios) { tcsetattr(STDIN_FILENO, TCSAFLUSH, orig_termios); // Restore original terminal settings } void rterm_clear_screen() { printf("\x1b[2J"); // Clear the entire screen printf("\x1b[H"); // Move cursor to the home position (0,0) } void setBackgroundColor() { printf("\x1b[34m"); // Set background color to blue } void rterm_move_cursor(int x, int y) { printf("\x1b[%d;%dH", y + 1, x + 1); // Move cursor to (x, y) } void cursor_set(rterm_t *rt, int x, int y) { rt->cursor.x = x; rt->cursor.y = y; rt->cursor.pos = y * rt->size.ws_col + x; rterm_move_cursor(rt->cursor.x, rt->cursor.y); } void cursor_restore(rterm_t *rt) { rterm_move_cursor(rt->cursor.x, rt->cursor.y); } void rterm_print_status_bar(rterm_t *rt, char c, unsigned long i) { if (rt->_status_text_previous && !strcmp(rt->_status_text_previous, rt->status_text)) { return; } if (rt->_status_text_previous) { free(rt->_status_text_previous); } rt->_status_text_previous = strdup(rt->status_text); winsize_t ws = rt->size; cursor_set(rt, rt->cursor.x, rt->cursor.y); rterm_move_cursor(0, ws.ws_row - 1); char output_str[1024]; output_str[0] = 0; // strcat(output_str, "\x1b[48;5;240m"); for (int i = 0; i < ws.ws_col; i++) { strcat(output_str, " "); } char content[500]; content[0] = 0; if (!rt->status_text) { sprintf(content, "\rp:%d:%d | k:%c:%d | i:%ld ", rt->cursor.x + 1, rt->cursor.y + 1, c == 0 ? '0' : c, c, i); } else { sprintf(content, "\r%s", rt->status_text); } strcat(output_str, content); // strcat(output_str, "\x1b[0m"); printf("%s", output_str); cursor_restore(rt); } void rterm_show_cursor() { printf("\x1b[?25h"); // Show the cursor } void rterm_hide_cursor() { printf("\x1b[?25l"); // Hide the cursor } rshell_keypress_t rshell_getkey(rterm_t *rt) { static rshell_keypress_t press; press.c = 0; press.ctrl = false; press.shift = false; press.escape = false; press.pressed = rfd_wait(0, rt->ms_tick); if (!press.pressed) { return press; } press.c = getchar(); char ch = press.c; if (ch == '\x1b') { // Get detail ch = getchar(); if (ch == '[') { // non char key: press.escape = true; ch = getchar(); // is a number. 1 if shift + arrow press.c = ch; if (ch >= '0' && ch <= '9') ch = getchar(); press.c = ch; if (ch == ';') { ch = getchar(); press.c = ch; if (ch == '5') { press.ctrl = true; press.c = getchar(); // De arrow } } } else if (ch == 27) { press.escape = true; press.c = ch; } else { press.c = ch; } } return press; } // Main function void rterm_loop(rterm_t *rt) { struct termios orig_termios; tcgetattr(STDIN_FILENO, &orig_termios); // Get current terminal attributes enableRawMode(&orig_termios); int x = 0, y = 0; // Initial cursor position char ch = 0; ; while (1) { rterm_getwinsize(&rt->size); rt->cursor.available = rt->size.ws_col * rt->size.ws_row; if (rt->tick) { rt->tick(rt); } rterm_hide_cursor(); setBackgroundColor(); rterm_clear_screen(); if (rt->before_draw) { rt->before_draw(rt); } rterm_print_status_bar(rt, ch, rt->iterations); if (rt->after_draw) { rt->after_draw(rt); } if (!rt->iterations || (x != rt->cursor.x || y != rt->cursor.y)) { if (rt->cursor.y == rt->size.ws_row) { rt->cursor.y--; } if (rt->cursor.y < 0) { rt->cursor.y = 0; } x = rt->cursor.x; y = rt->cursor.y; if (rt->before_cursor_move) rt->before_cursor_move(rt); cursor_set(rt, rt->cursor.x, rt->cursor.y); if (rt->after_cursor_move) rt->after_cursor_move(rt); // x = rt->cursor.x; // y = rt->cursor.y; } if (rt->show_cursor) rterm_show_cursor(); fflush(stdout); rt->key = rshell_getkey(rt); if (rt->key.pressed && rt->before_key_press) { rt->before_key_press(rt); } rshell_keypress_t key = rt->key; ch = key.c; if (ch == 'q') break; // Press 'q' to quit if (key.c == -1) { nsleep(1000 * 1000); } // Escape if (key.escape) { switch (key.c) { case 65: // Move up if (rt->cursor.y > -1) rt->cursor.y--; break; case 66: // Move down if (rt->cursor.y < rt->size.ws_row) rt->cursor.y++; break; case 68: // Move left if (rt->cursor.x > 0) rt->cursor.x--; if (key.ctrl) rt->cursor.x -= 4; break; case 67: // Move right if (rt->cursor.x < rt->size.ws_col) { rt->cursor.x++; } if (key.ctrl) { rt->cursor.x += 4; } break; } } if (rt->key.pressed && rt->after_key_press) { rt->after_key_press(rt); } rt->iterations++; // usleep (1000); } // Cleanup printf("\x1b[0m"); // Reset colors rterm_clear_screen(); disableRawMode(&orig_termios); } #endif #ifndef RTREE_H #define RTREE_H #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct rtree_t { struct rtree_t *next; struct rtree_t *children; char c; void *data; } rtree_t; rtree_t *rtree_new() { rtree_t *b = (rtree_t *)rmalloc(sizeof(rtree_t)); b->next = NULL; b->children = NULL; b->c = 0; b->data = NULL; return b; } rtree_t *rtree_set(rtree_t *b, char *c, void *data) { while (b) { if (b->c == 0) { b->c = *c; c++; if (*c == 0) { b->data = data; // printf("SET1 %c\n", b->c); return b; } } else if (b->c == *c) { c++; if (*c == 0) { b->data = data; return b; } if (b->children) { b = b->children; } else { b->children = rtree_new(); b = b->children; } } else if (b->next) { b = b->next; } else { b->next = rtree_new(); b = b->next; b->c = *c; c++; if (*c == 0) { b->data = data; return b; } else { b->children = rtree_new(); b = b->children; } } } return NULL; } rtree_t *rtree_find(rtree_t *b, char *c) { while (b) { if (b->c == *c) { c++; if (*c == 0) { return b; } b = b->children; continue; } b = b->next; } return NULL; } void rtree_free(rtree_t *b) { if (!b) return; rtree_free(b->children); rtree_free(b->next); rfree(b); } void *rtree_get(rtree_t *b, char *c) { rtree_t *t = rtree_find(b, c); if (t) { return t->data; } return NULL; } #endif #ifndef RLEXER_H #define RLEXER_H #include <ctype.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #define RTOKEN_VALUE_SIZE 1024 typedef enum rtoken_type_t { RT_UNKNOWN = 0, RT_SYMBOL, RT_NUMBER, RT_STRING, RT_PUNCT, RT_OPERATOR, RT_EOF = 10, RT_BRACE_OPEN, RT_CURLY_BRACE_OPEN, RT_BRACKET_OPEN, RT_BRACE_CLOSE, RT_CURLY_BRACE_CLOSE, RT_BRACKET_CLOSE } rtoken_type_t; typedef struct rtoken_t { rtoken_type_t type; char value[RTOKEN_VALUE_SIZE]; unsigned int line; unsigned int col; } rtoken_t; static char *_content; static unsigned int _content_ptr; static unsigned int _content_line; static unsigned int _content_col; static int isgroupingchar(char c) { return (c == '{' || c == '}' || c == '(' || c == ')' || c == '[' || c == ']' || c == '"' || c == '\''); } static int isoperator(char c) { return (c == '+' || c == '-' || c == '/' || c == '*' || c == '=' || c == '>' || c == '<' || c == '|' || c == '&'); } static rtoken_t rtoken_new() { rtoken_t token; memset(&token, 0, sizeof(token)); token.type = RT_UNKNOWN; return token; } rtoken_t rlex_number() { rtoken_t token = rtoken_new(); token.col = _content_col; token.line = _content_line; bool first_char = true; int dot_count = 0; char c; while (isdigit(c = _content[_content_ptr]) || (first_char && _content[_content_ptr] == '-') || (dot_count == 0 && _content[_content_ptr] == '.')) { if (c == '.') dot_count++; first_char = false; char chars[] = {c, 0}; strcat(token.value, chars); _content_ptr++; _content_col++; } token.type = RT_NUMBER; return token; } static rtoken_t rlex_symbol() { rtoken_t token = rtoken_new(); token.col = _content_col; token.line = _content_line; char c; while (isalpha(_content[_content_ptr]) || _content[_content_ptr] == '_') { c = _content[_content_ptr]; char chars[] = {c, 0}; strcat(token.value, chars); _content_ptr++; _content_col++; } token.type = RT_SYMBOL; return token; } static rtoken_t rlex_operator() { rtoken_t token = rtoken_new(); token.col = _content_col; token.line = _content_line; char c; bool is_first = true; while (isoperator(_content[_content_ptr])) { if (!is_first) { if (_content[_content_ptr - 1] == '=' && _content[_content_ptr] == '-') { break; } } c = _content[_content_ptr]; char chars[] = {c, 0}; strcat(token.value, chars); _content_ptr++; _content_col++; is_first = false; } token.type = RT_OPERATOR; return token; } static rtoken_t rlex_punct() { rtoken_t token = rtoken_new(); token.col = _content_col; token.line = _content_line; char c; bool is_first = true; while (ispunct(_content[_content_ptr])) { if (!is_first) { if (_content[_content_ptr] == '"') { break; } if (_content[_content_ptr] == '\'') { break; } if (isgroupingchar(_content[_content_ptr])) { break; } if (isoperator(_content[_content_ptr])) { break; } } c = _content[_content_ptr]; char chars[] = {c, 0}; strcat(token.value, chars); _content_ptr++; _content_col++; is_first = false; } token.type = RT_PUNCT; return token; } static rtoken_t rlex_string() { rtoken_t token = rtoken_new(); char c; token.col = _content_col; token.line = _content_line; char str_chr = _content[_content_ptr]; _content_ptr++; while (_content[_content_ptr] != str_chr) { c = _content[_content_ptr]; if (c == '\\') { _content_ptr++; c = _content[_content_ptr]; if (c == 'n') { c = '\n'; } else if (c == 'r') { c = '\r'; } else if (c == 't') { c = '\t'; } else if (c == str_chr) { c = str_chr; } _content_col++; } char chars[] = {c, 0}; strcat(token.value, chars); _content_ptr++; _content_col++; } _content_ptr++; token.type = RT_STRING; return token; } void rlex(char *content) { _content = content; _content_ptr = 0; _content_col = 1; _content_line = 1; } static void rlex_repeat_str(char *dest, char *src, unsigned int times) { for (size_t i = 0; i < times; i++) { strcat(dest, src); } } rtoken_t rtoken_create(rtoken_type_t type, char *value) { rtoken_t token = rtoken_new(); token.type = type; token.col = _content_col; token.line = _content_line; strcpy(token.value, value); return token; } rtoken_t rlex_next() { while (true) { _content_col++; if (_content[_content_ptr] == 0) { return rtoken_create(RT_EOF, "eof"); } else if (_content[_content_ptr] == '\n') { _content_line++; _content_col = 1; _content_ptr++; } else if (isspace(_content[_content_ptr])) { _content_ptr++; } else if (isdigit(_content[_content_ptr]) || (_content[_content_ptr] == '-' && isdigit(_content[_content_ptr + 1]))) { return rlex_number(); } else if (isalpha(_content[_content_ptr]) || _content[_content_ptr] == '_') { return rlex_symbol(); } else if (_content[_content_ptr] == '"' || _content[_content_ptr] == '\'') { return rlex_string(); } else if (isoperator(_content[_content_ptr])) { return rlex_operator(); } else if (ispunct(_content[_content_ptr])) { if (_content[_content_ptr] == '{') { _content_ptr++; return rtoken_create(RT_CURLY_BRACE_OPEN, "{"); } if (_content[_content_ptr] == '}') { _content_ptr++; return rtoken_create(RT_CURLY_BRACE_CLOSE, "}"); } if (_content[_content_ptr] == '(') { _content_ptr++; return rtoken_create(RT_BRACE_OPEN, "("); } if (_content[_content_ptr] == ')') { _content_ptr++; return rtoken_create(RT_BRACE_CLOSE, ")"); } if (_content[_content_ptr] == '[') { _content_ptr++; return rtoken_create(RT_BRACKET_OPEN, "["); } if (_content[_content_ptr] == ']') { _content_ptr++; return rtoken_create(RT_BRACKET_CLOSE, "]"); } return rlex_punct(); } } } char *rlex_format(char *content) { rlex(content); char *result = (char *)malloc(strlen(content) + 4096); result[0] = 0; unsigned int tab_index = 0; char *tab_chars = " "; unsigned int col = 0; rtoken_t token_previous; token_previous.value[0] = 0; token_previous.type = RT_UNKNOWN; while (true) { rtoken_t token = rlex_next(); if (token.type == RT_EOF) { break; } // col = strlen(token.value); if (col == 0) { rlex_repeat_str(result, tab_chars, tab_index); // col = strlen(token.value);// strlen(tab_chars) * tab_index; } if (token.type == RT_STRING) { strcat(result, "\""); char string_with_slashes[strlen(token.value) * 2 + 1]; rstraddslashes(token.value, string_with_slashes); strcat(result, string_with_slashes); strcat(result, "\""); // col+= strlen(token.value) + 2; // printf("\n"); // printf("<<<%s>>>\n",token.value); memcpy(&token_previous, &token, sizeof(token)); continue; } if (!(strcmp(token.value, "{"))) { if (col != 0) { strcat(result, "\n"); rlex_repeat_str(result, " ", tab_index); } strcat(result, token.value); tab_index++; strcat(result, "\n"); col = 0; memcpy(&token_previous, &token, sizeof(token)); continue; } else if (!(strcmp(token.value, "}"))) { unsigned int tab_indexed = 0; if (tab_index) tab_index--; strcat(result, "\n"); rlex_repeat_str(result, tab_chars, tab_index); tab_indexed++; strcat(result, token.value); strcat(result, "\n"); col = 0; memcpy(&token_previous, &token, sizeof(token)); continue; } if ((token_previous.type == RT_SYMBOL && token.type == RT_NUMBER) || (token_previous.type == RT_NUMBER && token.type == RT_SYMBOL) || (token_previous.type == RT_PUNCT && token.type == RT_SYMBOL) || (token_previous.type == RT_BRACE_CLOSE && token.type == RT_SYMBOL) || (token_previous.type == RT_SYMBOL && token.type == RT_SYMBOL)) { if (token_previous.value[0] != ',' && token_previous.value[0] != '.') { if (token.type != RT_OPERATOR && token.value[0] != '.') { strcat(result, "\n"); rlex_repeat_str(result, tab_chars, tab_index); } } } if (token.type == RT_OPERATOR) { strcat(result, " "); } if (token.type == RT_STRING) { strcat(result, "\""); } strcat(result, token.value); if (token.type == RT_STRING) { strcat(result, "\""); } if (token.type == RT_OPERATOR) { strcat(result, " "); } if (!strcmp(token.value, ",")) { strcat(result, " "); } col += strlen(token.value); memcpy(&token_previous, &token, sizeof(token)); } return result; } #endif #ifndef RLIB_MAIN #define RLIB_MAIN #ifndef RMERGE_H #define RMERGE_H // #include "../mrex/rmatch.h" #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> bool has_error = false; char *extract_script_src_include(char *line, char *include_path) { include_path[0] = 0; rrex3_t *rrex; rrex = rrex3(NULL, line, "<script.*src=\"(.*)\".*<.*script.*>"); if (rrex) { strcpy(include_path, rrex->matches[0]); rrex3_free(rrex); return include_path; } return NULL; } char *extract_c_local_include(char *line, char *include_path) { // /* char res; res= rmatch_extract(line, "#include.*"\".*\""); printf("%MATCH:%s\n", res); */ include_path[0] = 0; rrex3_t *rrex; rrex = rrex3(NULL, line, "[^\\\\*]^#include .*\"(.*)\""); if (rrex) { strcpy(include_path, rrex->matches[0]); rrex3_free(rrex); return include_path; } return NULL; } char *rmerge_readline(FILE *f) { static char data[4096]; data[0] = 0; int index = 0; char c; while ((c = fgetc(f)) != EOF) { if (c != '\0') { data[index] = c; index++; if (c == '\n') break; } } data[index] = 0; if (data[0] == 0) return NULL; return data; } void writestring(FILE *f, char *line) { char c; while ((c = *line) != '\0') { fputc(c, f); line++; } } char files_history[8096]; char files_duplicate[8096]; bool is_merging = false; void merge_file(char *source, FILE *d) { if (is_merging == false) { is_merging = true; files_history[0] = 0; files_duplicate[0] = 0; } if (strstr(files_history, source)) { if (strstr(files_duplicate, source)) { rprintmf(stderr, "\\l Already included: %s. Already on duplicate list.\n", source); } else { rprintcf(stderr, "\\l Already included: %s. Adding to duplicate list.\n", source); strcat(files_duplicate, source); strcat(files_duplicate, "\n"); } return; } else { rprintgf(stderr, "\\l Merging: %s.\n", source); strcat(files_history, source); strcat(files_history, "\n"); } FILE *fd = fopen(source, "rb"); if (!fd) { rprintrf(stderr, "\\l File does not exist: %s\n", source); has_error = true; return; } char *line; char include_path[4096]; while ((line = rmerge_readline(fd))) { include_path[0] = 0; if (!*line) break; // char *inc = extract_c_local_include(line, include_path); if (!inc) inc = extract_script_src_include(line, include_path); /* if (!strncmp(line, "#include ", 9)) { int index = 0; while (line[index] != '"' && line[index] != 0) { index++; } if (line[index] == '"') { int pindex = 0; index++; while (line[index] != '"') { include_path[pindex] = line[index]; pindex++; index++; } if (line[index] != '"') { include_path[0] = 0; } else { include_path[pindex] = '\0'; } } }*/ if (inc) { merge_file(inc, d); } else { writestring(d, line); } } fclose(fd); writestring(d, "\n"); } int rmerge_main(int argc, char *argv[]) { char *file_input = NULL; if (argc != 2) { printf("Usage: <input-file>\n"); } else { file_input = argv[1]; // file_output = argv[2]; } FILE *f = tmpfile(); printf("// RETOOR - %s\n", __DATE__); merge_file(file_input, f); rewind(f); char *data; int line_number = 0; while ((data = rmerge_readline(f))) { if (line_number) { printf("/*%.5d*/ ", line_number); line_number++; } printf("%s", data); } printf("\n"); if (has_error) { rprintrf(stderr, "\\l Warning: there are errors while merging this file.\n"); } else { rprintgf(stderr, "\\l Merge succesful without error(s).%s\n", remo_get("fire")); } return 0; } #endif void forward_argument(int *argcc, char *argv[]) { int argc = *argcc; for (int i = 0; i < argc; i++) { argv[i] = argv[i + 1]; } argc--; *argcc = argc; } int rlib_main(int argc, char *argv[]) { if (argc == 1) { printf("rlib\n\n"); printf("options:\n"); printf(" httpd - a http file server. Accepts port as argument.\n"); printf(" rmerge - a merge tool. Converts c source files to one file \n" " with local includes by giving main file as argument.\n"); printf(" rcov - coverage tool theat cleans up after himself. Based on " "lcov.\n"); printf(" rcase - tool to swap input file automatically between" " camel case and snake case.\n"); return 0; } forward_argument(&argc, argv); if (!strcmp(argv[0], "httpd")) { return rhttp_main(argc, argv); } if (!strcmp(argv[0], "rmerge")) { return rmerge_main(argc, argv); } if (!strcmp(argv[0], "rcov")) { return rcov_main(argc, argv); } if (!strcmp(argv[0], "rcase")) { return rcase_main(argc, argv); } return 0; } #endif // END OF RLIB #endif