|
|
// RETOOR - Dec 5 2024
|
|
|
#ifndef SORM_H
|
|
|
#define SORM_H
|
|
|
#ifndef SORM_STR_H
|
|
|
#define SORM_STR_H
|
|
|
// RETOOR - Nov 28 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) {
|
|
|
for (unsigned int i = 0; i < sock->upstream_count; i++) {
|
|
|
nsock_close(sock->upstreams[i]);
|
|
|
sock->upstreams[i] = 0;
|
|
|
}
|
|
|
if (sock->upstream_count) {
|
|
|
free(sock->upstreams);
|
|
|
}
|
|
|
sock->upstream_count = 0;
|
|
|
sock->connected = false;
|
|
|
}
|
|
|
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 |