440 lines
12 KiB
C
440 lines
12 KiB
C
|
#ifndef RSTRING_H
|
||
|
#define RSTRING_H
|
||
|
#include "rmalloc.h"
|
||
|
#include "rtypes.h"
|
||
|
#include "rmath.h"
|
||
|
#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
|