4932 lines
135 KiB
C
Raw Normal View History

2025-01-14 18:05:52 +00:00
// RETOOR - Sep 9 2024
#define RREX3_DEBUG 1
#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, "#define abc ", "#define *(\\w.*)\n$"));
exit(0);
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"));
assert(rrex3(rrex, "#define abc", "#define (.*)"));
assert(!strcmp(rrex->matches[0], "abc"));
assert(rrex3(rrex, "#define abc", "#define (.*)$"));
assert(!strcmp(rrex->matches[0], "abc"));
assert(rrex3(rrex, "#define abc 1", "#define (.*) (.*)$"));
assert(!strcmp(rrex->matches[0], "abc"));
assert(!strcmp(rrex->matches[1], "1"));
assert(rrex3(rrex, "#define abc 1 ", "#define (.*) (.*) *$"));
assert(!strcmp(rrex->matches[0], "abc"));
printf("<<%s>>\n", rrex->matches[1]);
assert(!strcmp(rrex->matches[1], "1"));
assert(rrex3(rrex, "#define abc \"test with spaces\" ",
"#define (.*) *\"(.*)\" *$"));
assert(!strcmp(rrex->matches[0], "abc"));
printf("<<%s>>\n", rrex->matches[1]);
assert(!strcmp(rrex->matches[1], "test with spaces"));
rrex3_free(rrex);
}
#endif
// RETOOR - Sep 3 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
#ifndef RPRINT_H
#define RPRINT_H
#ifndef RLIB_TIME
#define RLIB_TIME
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC 1
#endif
typedef unsigned long long msecs_t;
typedef uint64_t nsecs_t;
nsecs_t nsecs() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (int64_t)ts.tv_sec * 1000000000LL + (int64_t)ts.tv_nsec;
}
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;
if (nanosleep(&req, &rem) == -1) {
if (errno == EINTR) {
printf("Sleep was interrupted. Remaining time: %ld.%09ld seconds\n",
rem.tv_sec, rem.tv_nsec);
} else {
perror("nanosleep");
}
} else {
// printf("Slept for %ld.%09ld seconds\n", req.tv_sec, req.tv_nsec);
}
}
void ssleep(double s) {
long nanoseconds = (long)(1000000000 * s);
long seconds = 0;
struct timespec req = {seconds, nanoseconds};
struct timespec rem;
if (nanosleep(&req, &rem) == -1) {
if (errno == EINTR) {
printf("Sleep was interrupted. Remaining time: %ld.%09ld seconds\n",
rem.tv_sec, rem.tv_nsec);
} else {
perror("nanosleep");
}
} else {
// printf("Slept for %ld.%09ld seconds\n", req.tv_sec, req.tv_nsec);
}
}
void msleep(long miliseonds) {
long nanoseconds = miliseonds * 1000000;
nsleep(nanoseconds);
}
char *format_time(int64_t nanoseconds) {
static 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;
snprintf(output, output_size, "%.2fs", s);
}
return 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(char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "", format, args);
va_end(args);
}
void rprintf(FILE *f, char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "", format, args);
va_end(args);
}
void rprint(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, char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\\l", format, args);
va_end(args);
}
void rprintl(char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\\l", format, args);
va_end(args);
}
// Black
void rprintkf(FILE *f, char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\e[30m", format, args);
va_end(args);
}
void rprintk(char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\e[30m", format, args);
va_end(args);
}
// Red
void rprintrf(FILE *f, char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\e[31m", format, args);
va_end(args);
}
void rprintr(char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\e[31m", format, args);
va_end(args);
}
// Green
void rprintgf(FILE *f, char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\e[32m", format, args);
va_end(args);
}
void rprintg(char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\e[32m", format, args);
va_end(args);
}
// Yellow
void rprintyf(FILE *f, char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\e[33m", format, args);
va_end(args);
}
void rprinty(char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\e[33m", format, args);
va_end(args);
}
// Blue
void rprintbf(FILE *f, char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\e[34m", format, args);
va_end(args);
}
void rprintb(char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\e[34m", format, args);
va_end(args);
}
// Magenta
void rprintmf(FILE *f, char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\e[35m", format, args);
va_end(args);
}
void rprintm(char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\e[35m", format, args);
va_end(args);
}
// Cyan
void rprintcf(FILE *f, char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\e[36m", format, args);
va_end(args);
}
void rprintc(char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\e[36m", format, args);
va_end(args);
}
// White
void rprintwf(FILE *f, char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(f, "\e[37m", format, args);
va_end(args);
}
void rprintw(char *format, ...) {
va_list args;
va_start(args, format);
rprintpf(stdout, "\e[37m", format, args);
va_end(args);
}
#endif
#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
#ifndef RMALLOC_H
#define RMALLOC_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned long long rmalloc_count = 0;
unsigned long long rmalloc_alloc_count = 0;
unsigned long long int rmalloc_free_count = 0;
void *rmalloc(size_t size) {
rmalloc_count++;
rmalloc_alloc_count++;
return malloc(size);
}
void *rrealloc(void *obj, size_t size) {
if (obj == NULL) {
rmalloc_count++;
rmalloc_alloc_count++;
}
return realloc(obj, size);
}
void *rfree(void *obj) {
rmalloc_count--;
rmalloc_free_count++;
free(obj);
return NULL;
}
#define malloc rmalloc
#define realloc rrealloc
#define free rfree
char *rmalloc_stats() {
static char res[100] = {0};
sprintf(res, "Memory usage: %lld allocated, %lld freed, %lld in use.",
rmalloc_alloc_count, rmalloc_free_count, rmalloc_count);
return res;
}
char *rstrdup(char *str) {
char *res = (char *)strdup(str);
rmalloc_alloc_count++;
rmalloc_count++;
return res;
}
#endif
#ifndef RTEST_H
#define RTEST_H
#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\n", rmalloc_stats());
if (rmalloc_count != 0) {
printf("MEMORY ERROR\n");
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, ".");
return true;
}
rprintrf(stderr, "\nERROR on line %d: %s", 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);
}
bool rtest_test_assert(char *expr, int res, int line) {
if (rtest_test_true(expr, res, line)) {
return true;
}
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
#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->expr == 0 && !*rrex3->str) {
printf("ERROR, EMPTY CHECK");
exit(1);
}
if (rrex3->valid == false) {
rrex3->expr++;
return;
}
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 == *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_plus(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_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;
}
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) {
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) {
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) {
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
if (!rrex3->valid) {
rrex3->expr++;
return;
}
rrex3_set_previous(rrex3);
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;
}
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;
}
}
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;
}
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, "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 characters test.
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,"char pony() {
// }","\\b\\w+(\\s+\\*+)?\\s+\\w+\\s*\\([^)]*\\)\s*\\{[^{}]*\\}"));
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 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>
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; // 100 milliseconds timeout
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;
}
__attribute__((unused)) size_t bytes_read =
fread(data, size, sizeof(char), fd);
fclose(fd);
return size;
}
#endif
#ifndef RSTRING_H
#define RSTRING_H
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned long _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, "%ld", _r_generate_key_current);
return key;
}
char *rformat_number(long lnumber) {
static char formatted[1024];
char number[1024];
sprintf(number, "%ld", 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++;
}
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 = '\\';
}
}
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;
}
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] == ' ') {
continue;
}
count = i;
size_t j;
for (j = 0; j < len - count; j++) {
output[j] = input[j + count];
}
output[j] = '\0';
break;
}
return count;
}
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);
free(lines);
return line_count;
}
#endif
#ifndef RLIB_TERMINAL_H
#define RLIB_TERMINAL_H
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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
#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;
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 *session;
unsigned long iterations;
void (*tick)(struct rterm_t *);
char *status_text;
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->show_cursor = true;
}
void rterm_getwinsize(winsize_t *w) {
// Get the terminal size
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, w) == -1) {
perror("ioctl");
exit(EXIT_FAILURE);
}
}
// 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] = 0;
raw.c_cc[VTIME] = 1; // 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[44m"); // 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) {
winsize_t ws = rt->size;
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() {
static rshell_keypress_t press;
press.c = 0;
press.ctrl = false;
press.shift = false;
press.escape = false;
press.pressed = rfd_wait(0, 100);
if (press.pressed) {
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 {
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->iterations || (x != rt->cursor.x || y != rt->cursor.y)) {
if (y == rt->size.ws_row) {
y--;
}
if (y < 0) {
y = 0;
}
rt->cursor.x = x;
rt->cursor.y = 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();
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
// Escape
if (key.escape) {
switch (key.c) {
case 65: // Move up
if (y > -1)
y--;
break;
case 66: // Move down
if (y < rt->size.ws_row)
y++;
break;
case 68: // Move left
if (x > 0)
x--;
if (key.ctrl)
x -= 4;
break;
case 67: // Move right
if (x < rt->size.ws_col) {
x++;
}
if (key.ctrl) {
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 RBENCH_H
#define RBENCH_H
#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>
#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
// END OF RLIB
#endif
#include <regex.h>
void benchmark(int times, char *str, char *expr) {
regmatch_t matches[10];
printf("Matching \"%s\" with \"%s\".\n", str, expr);
regex_t regex;
if (regcomp(&regex, expr, REG_EXTENDED)) {
printf("Creg: error in regular expression.\n");
exit(1);
}
printf("creg: ");
RBENCH(times, {
if (regexec(&regex, str, 0, matches, 0)) {
printf("Creg: error executing regular expression.\n");
}
})
regfree(&regex);
;
rrex3_t *rrex = rrex3_compile(NULL, expr);
printf("rrex3 (%s): ", rrex->compiled);
RBENCH(times, {
if (rrex3(rrex, str, NULL)) {
} else {
printf("Rrex3: error\n");
exit(0);
}
});
rrex3_free(rrex);
printf("\n");
}
int main() {
rrex3_test();
int times = 1;
benchmark(times, "\"stdio.h\"\"string.h\"\"sys/time.h\"",
"\".*\"\".*\"\".*\"");
benchmark(times, "abcdefghijklmnopqrstuvwxyz",
"abcdefghijklmnopqrstuvwxyz$");
benchmark(times, "aaaaaaaaaaaaaaaaaaaaaaaaaa",
"aaaaaaaaaaaaaaaaaaaaaaaaaa$");
benchmark(times, "abcdefghijklmnopqrstuvwxyz",
"..........................$");
// [abcm] failed
benchmark(times, "abcdefghijklmnopqrstuvwxyz", ".*z");
benchmark(times, "abcde", ".*e");
benchmark(times, "abcdef", ".*f");
benchmark(times, "abcdefghijklmnopqrstuvwxyz",
"[a]b*c+d\\w[f-g][g][h-i][i][^a][abcdefgk][l][m][n][o][p][a-z][r]"
"[s][t][u][v][w].*z$");
benchmark(times, "zzz",
"[abcdefghijklmnopqrstuvwxyz][abcdefghijklmnopqrstuvwxyz]["
"abcdefghijklmnopqrstuvwxyz]$");
benchmark(times, "7245 Sr", "[0-9][0-9][0-9][0-9] ?\\w\\w$");
benchmark(times,
"abcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmn"
"opqrstuvwxyzesting",
"[z-z][e-e]");
benchmark(times,
"abcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmn"
"opqrstuvwxyzesting",
"zesting");
benchmark(times, "\"stdio.h\"\"string.h\"\"sys/time.h\"",
"\"(.*)\"\"(.*)\"\"(.*)\"");
benchmark(times, " \"stdio.h\"\"string.h\"\"sys/time.h\"",
"\".+\"\".+\"\".+\"");
benchmark(times, " \"stdio.h\"\"string.h\"\"sys/time.h\"",
"\"(.+)\"\"(.+)\"\"(.+)\"");
}