4932 lines
135 KiB
C
4932 lines
135 KiB
C
|
// 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(®ex, expr, REG_EXTENDED)) {
|
||
|
printf("Creg: error in regular expression.\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
printf("creg: ");
|
||
|
RBENCH(times, {
|
||
|
if (regexec(®ex, str, 0, matches, 0)) {
|
||
|
printf("Creg: error executing regular expression.\n");
|
||
|
}
|
||
|
})
|
||
|
regfree(®ex);
|
||
|
;
|
||
|
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\"",
|
||
|
"\"(.+)\"\"(.+)\"\"(.+)\"");
|
||
|
}
|