// RETOOR - Sep 9 2024 #define RREX3_DEBUG 1 #ifndef RREX3_H #define RREX3_H #include #include #include #include #include #include #include #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("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 ", "#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 ", "#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 #include #include #include #include #include #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 #include #include #include #include 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 #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 #include #include 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 #include #include #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 #include #include #include #include #include #include #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("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 ", "#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 ", "#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 #include 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 #include #include #include #include #include #include #include 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 #include #include #include #include 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 #include #include #include 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 #include #include #include #include #include #include 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 #include #include 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 #include #include #include #include #include #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 #include #include #include #include #include #include #include #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 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\"", "\"(.+)\"\"(.+)\"\"(.+)\""); }