diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/args.c | 111 | ||||
-rw-r--r-- | lib/args.h | 18 | ||||
-rw-r--r-- | lib/buffer.c | 60 | ||||
-rw-r--r-- | lib/buffer.h | 16 | ||||
-rw-r--r-- | lib/convert.c | 126 | ||||
-rw-r--r-- | lib/convert.h | 20 | ||||
-rw-r--r-- | lib/def.h | 31 | ||||
-rw-r--r-- | lib/error.c | 35 | ||||
-rw-r--r-- | lib/error.h | 13 | ||||
-rw-r--r-- | lib/file.c | 63 | ||||
-rw-r--r-- | lib/file.h | 14 | ||||
-rw-r--r-- | lib/lslib.h | 16 | ||||
-rw-r--r-- | lib/regex.c | 528 | ||||
-rw-r--r-- | lib/regex.h | 54 | ||||
-rw-r--r-- | lib/stack.c | 43 | ||||
-rw-r--r-- | lib/stack.h | 22 |
16 files changed, 1170 insertions, 0 deletions
diff --git a/lib/args.c b/lib/args.c new file mode 100644 index 0000000..f907746 --- /dev/null +++ b/lib/args.c @@ -0,0 +1,111 @@ +#include "args.h" +#include "error.h" +#include "convert.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef MAJOR +#define MAJOR 0 +#endif + +#ifndef MINOR +#define MINOR 0 +#endif + +#ifndef PATCH +#define PATCH 0 +#endif + +void check_arg (char* arg) { + if (arg == NULL) { + error("expected another argument after option"); + } +} + +void global_help(void (*help)(void)) { + printf("LazySphere v%d.%d.%d multi-call binary.\n\n", MAJOR, MINOR, PATCH); + help(); + exit(EXIT_SUCCESS); +} + +void parse_help(int argc, char** argv, void (*help)(void)) { + int i; + + if (argc < 1) return; + + for (i = 0; i < argc; i++) { + if (!prefix("-", argv[i]) || streql("-", argv[i])) break; + if (help != NULL && streql("--help", argv[i])) global_help(help); + } +} + +int parse_args(int argc, char** argv, void (*help)(void), int (*short_arg)(char, char*), int (*long_arg)(char*, char*)) { + int start, i, current; + char* next_arg; + + if (argc < 1) return 0; + + start = 0; + for (i = 0; i < argc; i++) { + + if (!prefix("-", argv[i]) || streql("-", argv[i])) break; + if (help != NULL && streql("--help", argv[i])) global_help(help); + + if (i + 1 == argc) { + next_arg = NULL; + } else { + next_arg = argv[i+1]; + } + + current = i; + + if (prefix("--", argv[i])) { + int r; + + if (long_arg == NULL) { + goto exit; + } + + r = long_arg(argv[current], next_arg); + + if (r == ARG_USED) { + i++; + start++; + } else if (r == ARG_IGNORE) { + goto exit; + } else if (r == ARG_INVALID) { + error("invalid argument %s", argv[current]); + + } + } else { + size_t j; + int r; + + if (short_arg == NULL) { + goto exit; + } + + for (j = 1; j < strlen(argv[current]); j++) { + + r = short_arg(argv[current][j], next_arg); + + if (r == ARG_USED) { + i++; + start++; + } else if (r == ARG_IGNORE) { + goto exit; + } else if (r == ARG_INVALID) { + error("invalid argument -%c", argv[current][j]); + } + } + } + + start++; + } + +exit: + + return start; +} diff --git a/lib/args.h b/lib/args.h new file mode 100644 index 0000000..0b89fca --- /dev/null +++ b/lib/args.h @@ -0,0 +1,18 @@ +#ifndef ARGS_H +#define ARGS_H + +#define UNUSED(x) (void)(x) +#define ARG_UNUSED 0 +#define ARG_USED 1 +#define ARG_IGNORE 2 +#define ARG_INVALID 3 + +void check_arg (char* arg); + +void global_help(void (*help)(void)); + +void parse_help (int argc, char** argv, void (*help)(void)); + +int parse_args (int argc, char** argv, void (*help)(void), int (*short_arg)(char, char*), int (*long_arg)(char*, char*)); + +#endif diff --git a/lib/buffer.c b/lib/buffer.c new file mode 100644 index 0000000..2bee94b --- /dev/null +++ b/lib/buffer.c @@ -0,0 +1,60 @@ +#include "buffer.h" +#include "convert.h" + +#include <limits.h> +#include <string.h> + +static int push_path_buffer_b(char* buf, int* index, const char* string) { + int save, string_len; + + save = *index; + if (*index > 1 || (*index == 1 && buf[0] != '/')) { + buf[(*index)++] = '/'; + } + + string_len = strlen(string); + memcpy(buf + *index, string, string_len + 1); + + *index += string_len; + + return save; +} + +static void pop_path_buffer_b(char* buf, int* index, int i) { + *index = i; + buf[*index] = '\0'; +} + +static char path_buffer[PATH_MAX + 1]; +static int path_buffer_index = 0; + +char* get_path_buffer(void) { + return path_buffer; +} + +int push_path_buffer(const char* string) { + return push_path_buffer_b(path_buffer, &path_buffer_index, string); +} + +void pop_path_buffer(int i) { + pop_path_buffer_b(path_buffer, &path_buffer_index, i); +} + +static char path_buffer_2[PATH_MAX + 1]; +static int path_buffer_index_2 = 0; + +char* get_path_buffer_2(void) { + return path_buffer_2; +} + +int push_path_buffer_2(const char* string) { + return push_path_buffer_b(path_buffer_2, &path_buffer_index_2, string); +} + +void pop_path_buffer_2(int i) { + pop_path_buffer_b(path_buffer_2, &path_buffer_index_2, i); +} + +bool is_dot_dir(const char* path) { + return streql(path, ".") || streql(path, ".."); +} diff --git a/lib/buffer.h b/lib/buffer.h new file mode 100644 index 0000000..fd47ee7 --- /dev/null +++ b/lib/buffer.h @@ -0,0 +1,16 @@ +#ifndef BUFFER_H +#define BUFFER_H + +#include "def.h" + +char* get_path_buffer(void); +int push_path_buffer(const char* string); +void pop_path_buffer(int i); + +char* get_path_buffer_2(void); +int push_path_buffer_2(const char* string); +void pop_path_buffer_2(int i); + +bool is_dot_dir(const char* path); + +#endif diff --git a/lib/convert.c b/lib/convert.c new file mode 100644 index 0000000..071268e --- /dev/null +++ b/lib/convert.c @@ -0,0 +1,126 @@ +#include "convert.h" +#include "error.h" + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +long int get_number(const char* text) { + char* end; + long int n = strtol(text, &end, 10); + if (text == end) { + error("%s is not a valid number", text); + } + return n; +} + +long int get_blkm(const char* text) { + char* end; + long int n = strtol(text, &end, 10); + if (text == end) { + error("%s is not a valid bkm", text); + } + if (*end == '\0') return n; + switch (*end) { + case 'K': + case 'k': + return n * 1024; + case 'B': + case 'b': + return n * 512; + case 'M': + case 'm': + return n * 1024 * 1204; + default: + error("invalid bkm type %c", *end); + } + + /* shouldnt get here anyways */ + return 0; +} + +mode_t get_mode(const char* next) { + char* end = NULL; + mode_t mode = (mode_t)strtol(next, &end, 8); + if (!end) return 0; + while(isspace(*end)) end++; + if (*end != '\0' || (unsigned) mode < 010000) { + error("invalid file mode: `%s`", next); + } + return mode; +} + +bool streql(const char* a, const char* b) { + int n = 0; + + if (*a != *b) return false; + + while (true) { + if (*(a+n) != *(b+n)) return false; + if (*(a+n) == '\0') return true; + ++n; + } +} + +bool prefix(const char* pre, const char* str) { + return strncmp(pre, str, strlen(pre)) == 0; +} + +static char fs_types[5] = {'K','M','G','T','P'}; +void print_file_size(size_t bytes, char buf[5]) { + int index, n; + float next; + + index = 0; + next = bytes; + + while (true) { + if (next < 1000) { + break; + } + + if (index == 5) { + printf("999P"); + return; + } + + next /= 1024; + index++; + } + + n = snprintf(buf, 4, "%u", (int)(next+.5)); + + if (index > 0) { + buf[n] = (fs_types[index - 1]); + n++; + } + + buf[n] = '\0'; +} + +static char* months[12] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +void print_date_time(time_t mills, char buf[13]) { + struct tm* info; + int n; + + info = localtime(&mills); + n = snprintf(buf, 5, "%s ", months[info->tm_mon]); + + if (info->tm_mday < 10) { + buf[n] = ' '; + n++; + } + + snprintf(buf + n, 13 - n, "%d %02d:%02d ", info->tm_mday, info->tm_hour, info->tm_sec); +} + +void print_file_path(char* path) { + if (streql("-", path)) { + printf("(standard input)"); + } else { + printf("%s", path); + } +} diff --git a/lib/convert.h b/lib/convert.h new file mode 100644 index 0000000..f07614b --- /dev/null +++ b/lib/convert.h @@ -0,0 +1,20 @@ +#ifndef CONVERT_H +#define CONVERT_H + +#include "def.h" + +#include <sys/stat.h> +#include <stddef.h> + +long int get_number(const char* text); +long int get_blkm(const char* text); +mode_t get_mode(const char* text); + +bool streql(const char* a, const char* b); +bool prefix(const char* pre, const char* str); + +void print_file_size(size_t bytes, char buf[5]); +void print_date_time(time_t mills, char buf[13]); +void print_file_path(char* path); + +#endif diff --git a/lib/def.h b/lib/def.h new file mode 100644 index 0000000..33a32a0 --- /dev/null +++ b/lib/def.h @@ -0,0 +1,31 @@ +#ifndef COLOR_H +#define COLOR_H + +#include <stdint.h> + +#define ANSCII "\x1b[" +#define NEXT ";" + +#define RESET "0" +#define BOLD "1" + +#define NORMAL "3" +#define BACKGROUND "4" +#define HIGHLIGHT "9" + +#define BLACK "0" +#define RED "1" +#define GREEN "2" +#define YELLOW "3" +#define BLUE "4" +#define MAGENTA "5" +#define TURQUOISE "6" +#define WHITE "7" + +#define COLOR "m" + +typedef uint8_t bool; +#define true 1 +#define false 0 + +#endif diff --git a/lib/error.c b/lib/error.c new file mode 100644 index 0000000..67828c9 --- /dev/null +++ b/lib/error.c @@ -0,0 +1,35 @@ +#include "error.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +extern char* cmd; + +void error_s(const char *format, ...) { + va_list list; + va_start(list, format); + + fprintf(stderr, "%s: ", cmd); + vfprintf(stderr, format, list); + fprintf(stderr, "\n"); +} + +void error(const char *format, ...) { + va_list list; + va_start(list, format); + + fprintf(stderr, "%s: ", cmd); + vfprintf(stderr, format, list); + fprintf(stderr, "\n"); + exit(EXIT_FAILURE); +} + +void output(const char *format, ...) { + va_list list; + va_start(list, format); + + fprintf(stdout, "%s: ", cmd); + vfprintf(stdout, format, list); + fprintf(stdout, "\n"); +} diff --git a/lib/error.h b/lib/error.h new file mode 100644 index 0000000..bae532e --- /dev/null +++ b/lib/error.h @@ -0,0 +1,13 @@ +#ifndef ERROR_H +#define ERROR_H + +__attribute__ ((__format__(printf, 1, 2))) +void error_s(const char* format, ...); + +__attribute__ ((__format__(printf, 1, 2))) +void error(const char* format, ...); + +__attribute__ ((__format__(printf, 1, 2))) +void output(const char* format, ...); + +#endif diff --git a/lib/file.c b/lib/file.c new file mode 100644 index 0000000..f89b489 --- /dev/null +++ b/lib/file.c @@ -0,0 +1,63 @@ +#include "file.h" +#include "error.h" +#include "convert.h" + +#include <paths.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <fcntl.h> + +FILE* get_file_s(const char* path, const char* type) { + struct stat s; + FILE* file; + + if (streql("-", path) && type[0] == 'r') { + clearerr(stdin); + fflush(stdin); + return stdin; + } + + if (lstat(path, &s) < 0) { + if (type[0] != 'r') goto read; + error_s("failed to read %s: %s", path, strerror(errno)); + return NULL; + } + + if (S_ISDIR(s.st_mode)) { + error_s("%s is a directory", path); + return NULL; + } + +read: + + file = fopen(path, type); + + if (file == NULL) { + error_s("failed to %s file %s: %s", type[0] == 'r' ? "read" : "write", path, strerror(errno)); + } + + return file; +} + +FILE* get_file(const char* path, const char* type) { + FILE* file = get_file_s(path, type); + if (file == NULL) exit(EXIT_FAILURE); + return file; +} + +int get_tty (void) { + int fd = open(_PATH_TTY, O_RDONLY); + if (fd < 0) error("failed to get tty: %s", strerror(errno)); + return fd; +} + +FILE* get_tty_stream(char* type) { + int fd = get_tty(); + FILE* file = fdopen(fd, type); + if (file == NULL) { + error("failed to open tty stream: %s", strerror(errno)); + } + return file; +} diff --git a/lib/file.h b/lib/file.h new file mode 100644 index 0000000..0bb5939 --- /dev/null +++ b/lib/file.h @@ -0,0 +1,14 @@ +#ifndef FILE_H +#define FILE_H + +#include <stdio.h> + +FILE* get_file_s(const char* path, const char* type); + +FILE* get_file(const char* path, const char* type); + +int get_tty(void); + +FILE* get_tty_stream(char* type); + +#endif diff --git a/lib/lslib.h b/lib/lslib.h new file mode 100644 index 0000000..dfa1202 --- /dev/null +++ b/lib/lslib.h @@ -0,0 +1,16 @@ +#ifndef SHARED_H +#define SHARED_H + +#include <stdint.h> +#include <stdio.h> + +#include "args.h" +#include "buffer.h" +#include "def.h" +#include "convert.h" +#include "error.h" +#include "file.h" +#include "regex.h" +#include "stack.h" + +#endif diff --git a/lib/regex.c b/lib/regex.c new file mode 100644 index 0000000..0e0dc52 --- /dev/null +++ b/lib/regex.c @@ -0,0 +1,528 @@ +/* + * + * Mini regex-module inspired by Rob Pike's regex code described in: + * + * http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html + * + * + * + * Supports: + * --------- + * '.' Dot, matches any character + * '^' Start anchor, matches beginning of string + * '$' End anchor, matches end of string + * '*' Asterisk, match zero or more (greedy) + * '+' Plus, match one or more (greedy) + * '?' Question, match zero or one (non-greedy) + * '[abc]' Character class, match if one of {'a', 'b', 'c'} + * '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} -- NOTE: feature is currently broken! + * '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z } + * '\s' Whitespace, \t \f \r \n \v and spaces + * '\S' Non-whitespace + * '\w' Alphanumeric, [a-zA-Z0-9_] + * '\W' Non-alphanumeric + * '\d' Digits, [0-9] + * '\D' Non-digits + * + * + */ + + + +#include "regex.h" +#include <stdio.h> +#include <ctype.h> + +/* Definitions: */ + +#define MAX_REGEXP_OBJECTS 30 /* Max number of regex symbols in expression. */ +#define MAX_CHAR_CLASS_LEN 40 /* Max length of character-class buffer in. */ + + +enum { UNUSED, DOT, BEGIN, END, QUESTIONMARK, STAR, PLUS, CHAR, CHAR_CLASS, INV_CHAR_CLASS, DIGIT, NOT_DIGIT, ALPHA, NOT_ALPHA, WHITESPACE, NOT_WHITESPACE}; + +typedef struct regex_t +{ + unsigned char type; /* CHAR, STAR, etc. */ + union + { + unsigned char ch; /* the character itself */ + unsigned char* ccl; /* OR a pointer to characters in class */ + } u; +} regex_t; + + + +/* Private function declarations: */ +static int matchpattern(regex_t* pattern, const char* text, int* matchlength); +static int matchcharclass(char c, const char* str); +static int matchstar(regex_t p, regex_t* pattern, const char* text, int* matchlength); +static int matchplus(regex_t p, regex_t* pattern, const char* text, int* matchlength); +static int matchone(regex_t p, char c); +static int matchdigit(char c); +static int matchalpha(char c); +static int matchwhitespace(char c); +static int matchmetachar(char c, const char* str); +static int matchrange(char c, const char* str); +static int matchdot(char c); +static int ismetachar(char c); + + + +/* Public functions: */ +int re_match(const char* pattern, const char* text, int* matchlength) +{ + return re_matchp(re_compile(pattern), text, matchlength); +} + +int re_matchp(re_t pattern, const char* text, int* matchlength) +{ + *matchlength = 0; + if (pattern != 0) + { + if (pattern[0].type == BEGIN) + { + return ((matchpattern(&pattern[1], text, matchlength)) ? 0 : -1); + } + else + { + int idx = -1; + + do + { + idx += 1; + + if (matchpattern(pattern, text, matchlength)) + { + if (text[0] == '\0') + return -1; + + return idx; + } + } + while (*text++ != '\0'); + } + } + return -1; +} + +re_t re_compile(const char* pattern) +{ + /* The sizes of the two static arrays below substantiates the static RAM usage of this module. + MAX_REGEXP_OBJECTS is the max number of symbols in the expression. + MAX_CHAR_CLASS_LEN determines the size of buffer for chars in all char-classes in the expression. */ + static regex_t re_compiled[MAX_REGEXP_OBJECTS]; + static unsigned char ccl_buf[MAX_CHAR_CLASS_LEN]; + int ccl_bufidx = 1; + + char c; /* current char in pattern */ + int i = 0; /* index into pattern */ + int j = 0; /* index into re_compiled */ + + while (pattern[i] != '\0' && (j+1 < MAX_REGEXP_OBJECTS)) + { + c = pattern[i]; + + switch (c) + { + /* Meta-characters: */ + case '^': { re_compiled[j].type = BEGIN; } break; + case '$': { re_compiled[j].type = END; } break; + case '.': { re_compiled[j].type = DOT; } break; + case '*': { re_compiled[j].type = STAR; } break; + case '+': { re_compiled[j].type = PLUS; } break; + case '?': { re_compiled[j].type = QUESTIONMARK; } break; +/* case '|': { re_compiled[j].type = BRANCH; } break; <-- not working properly */ + + /* Escaped character-classes (\s \w ...): */ + case '\\': + { + if (pattern[i+1] != '\0') + { + /* Skip the escape-char '\\' */ + i += 1; + /* ... and check the next */ + switch (pattern[i]) + { + /* Meta-character: */ + case 'd': { re_compiled[j].type = DIGIT; } break; + case 'D': { re_compiled[j].type = NOT_DIGIT; } break; + case 'w': { re_compiled[j].type = ALPHA; } break; + case 'W': { re_compiled[j].type = NOT_ALPHA; } break; + case 's': { re_compiled[j].type = WHITESPACE; } break; + case 'S': { re_compiled[j].type = NOT_WHITESPACE; } break; + + /* Escaped character, e.g. '.' or '$' */ + default: + { + re_compiled[j].type = CHAR; + re_compiled[j].u.ch = pattern[i]; + } break; + } + } + /* '\\' as last char in pattern -> invalid regular expression. */ +/* + else + { + re_compiled[j].type = CHAR; + re_compiled[j].ch = pattern[i]; + } +*/ + } break; + + /* Character class: */ + case '[': + { + /* Remember where the char-buffer starts. */ + int buf_begin = ccl_bufidx; + + /* Look-ahead to determine if negated */ + if (pattern[i+1] == '^') + { + re_compiled[j].type = INV_CHAR_CLASS; + i += 1; /* Increment i to avoid including '^' in the char-buffer */ + if (pattern[i+1] == 0) /* incomplete pattern, missing non-zero char after '^' */ + { + return 0; + } + } + else + { + re_compiled[j].type = CHAR_CLASS; + } + + /* Copy characters inside [..] to buffer */ + while ( (pattern[++i] != ']') + && (pattern[i] != '\0')) /* Missing ] */ + { + if (pattern[i] == '\\') + { + if (ccl_bufidx >= MAX_CHAR_CLASS_LEN - 1) + { + /* fputs("exceeded internal buffer!\n", stderr); */ + return 0; + } + if (pattern[i+1] == 0) /* incomplete pattern, missing non-zero char after '\\' */ + { + return 0; + } + ccl_buf[ccl_bufidx++] = pattern[i++]; + } + else if (ccl_bufidx >= MAX_CHAR_CLASS_LEN) + { + /* fputs("exceeded internal buffer!\n", stderr); */ + return 0; + } + ccl_buf[ccl_bufidx++] = pattern[i]; + } + if (ccl_bufidx >= MAX_CHAR_CLASS_LEN) + { + /* Catches cases such as [00000000000000000000000000000000000000][ */ + /* fputs("exceeded internal buffer!\n", stderr); */ + return 0; + } + /* Null-terminate string end */ + ccl_buf[ccl_bufidx++] = 0; + re_compiled[j].u.ccl = &ccl_buf[buf_begin]; + } break; + + /* Other characters: */ + default: + { + re_compiled[j].type = CHAR; + re_compiled[j].u.ch = c; + } break; + } + /* no buffer-out-of-bounds access on invalid patterns - see https://github.com/kokke/tiny-regex-c/commit/1a279e04014b70b0695fba559a7c05d55e6ee90b */ + if (pattern[i] == 0) + { + return 0; + } + + i += 1; + j += 1; + } + /* 'UNUSED' is a sentinel used to indicate end-of-pattern */ + re_compiled[j].type = UNUSED; + + return (re_t) re_compiled; +} + +void re_print(regex_t* pattern) +{ + const char* types[] = { "UNUSED", "DOT", "BEGIN", "END", "QUESTIONMARK", "STAR", "PLUS", "CHAR", "CHAR_CLASS", "INV_CHAR_CLASS", "DIGIT", "NOT_DIGIT", "ALPHA", "NOT_ALPHA", "WHITESPACE", "NOT_WHITESPACE", "BRANCH" }; + + int i; + int j; + char c; + for (i = 0; i < MAX_REGEXP_OBJECTS; ++i) + { + if (pattern[i].type == UNUSED) + { + break; + } + + printf("type: %s", types[pattern[i].type]); + if (pattern[i].type == CHAR_CLASS || pattern[i].type == INV_CHAR_CLASS) + { + printf(" ["); + for (j = 0; j < MAX_CHAR_CLASS_LEN; ++j) + { + c = pattern[i].u.ccl[j]; + if ((c == '\0') || (c == ']')) + { + break; + } + printf("%c", c); + } + printf("]"); + } + else if (pattern[i].type == CHAR) + { + printf(" '%c'", pattern[i].u.ch); + } + printf("\n"); + } +} + + + +/* Private functions: */ +static int matchdigit(char c) +{ + return isdigit(c); +} +static int matchalpha(char c) +{ + return isalpha(c); +} +static int matchwhitespace(char c) +{ + return isspace(c); +} +static int matchalphanum(char c) +{ + return ((c == '_') || matchalpha(c) || matchdigit(c)); +} +static int matchrange(char c, const char* str) +{ + return ( (c != '-') + && (str[0] != '\0') + && (str[0] != '-') + && (str[1] == '-') + && (str[2] != '\0') + && ( (c >= str[0]) + && (c <= str[2]))); +} +static int matchdot(char c) +{ +#if defined(RE_DOT_MATCHES_NEWLINE) && (RE_DOT_MATCHES_NEWLINE == 1) + (void)c; + return 1; +#else + return c != '\n' && c != '\r'; +#endif +} +static int ismetachar(char c) +{ + return ((c == 's') || (c == 'S') || (c == 'w') || (c == 'W') || (c == 'd') || (c == 'D')); +} + +static int matchmetachar(char c, const char* str) +{ + switch (str[0]) + { + case 'd': return matchdigit(c); + case 'D': return !matchdigit(c); + case 'w': return matchalphanum(c); + case 'W': return !matchalphanum(c); + case 's': return matchwhitespace(c); + case 'S': return !matchwhitespace(c); + default: return (c == str[0]); + } +} + +static int matchcharclass(char c, const char* str) +{ + do + { + if (matchrange(c, str)) + { + return 1; + } + else if (str[0] == '\\') + { + /* Escape-char: increment str-ptr and match on next char */ + str += 1; + if (matchmetachar(c, str)) + { + return 1; + } + else if ((c == str[0]) && !ismetachar(c)) + { + return 1; + } + } + else if (c == str[0]) + { + if (c == '-') + { + return ((str[-1] == '\0') || (str[1] == '\0')); + } + else + { + return 1; + } + } + } + while (*str++ != '\0'); + + return 0; +} + +static int matchone(regex_t p, char c) +{ + switch (p.type) + { + case DOT: return matchdot(c); + case CHAR_CLASS: return matchcharclass(c, (const char*)p.u.ccl); + case INV_CHAR_CLASS: return !matchcharclass(c, (const char*)p.u.ccl); + case DIGIT: return matchdigit(c); + case NOT_DIGIT: return !matchdigit(c); + case ALPHA: return matchalphanum(c); + case NOT_ALPHA: return !matchalphanum(c); + case WHITESPACE: return matchwhitespace(c); + case NOT_WHITESPACE: return !matchwhitespace(c); + default: return (p.u.ch == c); + } +} + +static int matchstar(regex_t p, regex_t* pattern, const char* text, int* matchlength) +{ + int prelen = *matchlength; + const char* prepoint = text; + while ((text[0] != '\0') && matchone(p, *text)) + { + text++; + (*matchlength)++; + } + while (text >= prepoint) + { + if (matchpattern(pattern, text--, matchlength)) + return 1; + (*matchlength)--; + } + + *matchlength = prelen; + return 0; +} + +static int matchplus(regex_t p, regex_t* pattern, const char* text, int* matchlength) +{ + const char* prepoint = text; + while ((text[0] != '\0') && matchone(p, *text)) + { + text++; + (*matchlength)++; + } + while (text > prepoint) + { + if (matchpattern(pattern, text--, matchlength)) + return 1; + (*matchlength)--; + } + + return 0; +} + +static int matchquestion(regex_t p, regex_t* pattern, const char* text, int* matchlength) +{ + if (p.type == UNUSED) + return 1; + if (matchpattern(pattern, text, matchlength)) + return 1; + if (*text && matchone(p, *text++)) + { + if (matchpattern(pattern, text, matchlength)) + { + (*matchlength)++; + return 1; + } + } + return 0; +} + + +#if 0 + +/* Recursive matching */ +static int matchpattern(regex_t* pattern, const char* text, int *matchlength) +{ + int pre = *matchlength; + if ((pattern[0].type == UNUSED) || (pattern[1].type == QUESTIONMARK)) + { + return matchquestion(pattern[1], &pattern[2], text, matchlength); + } + else if (pattern[1].type == STAR) + { + return matchstar(pattern[0], &pattern[2], text, matchlength); + } + else if (pattern[1].type == PLUS) + { + return matchplus(pattern[0], &pattern[2], text, matchlength); + } + else if ((pattern[0].type == END) && pattern[1].type == UNUSED) + { + return text[0] == '\0'; + } + else if ((text[0] != '\0') && matchone(pattern[0], text[0])) + { + (*matchlength)++; + return matchpattern(&pattern[1], text+1); + } + else + { + *matchlength = pre; + return 0; + } +} + +#else + +/* Iterative matching */ +static int matchpattern(regex_t* pattern, const char* text, int* matchlength) +{ + int pre = *matchlength; + do + { + if ((pattern[0].type == UNUSED) || (pattern[1].type == QUESTIONMARK)) + { + return matchquestion(pattern[0], &pattern[2], text, matchlength); + } + else if (pattern[1].type == STAR) + { + return matchstar(pattern[0], &pattern[2], text, matchlength); + } + else if (pattern[1].type == PLUS) + { + return matchplus(pattern[0], &pattern[2], text, matchlength); + } + else if ((pattern[0].type == END) && pattern[1].type == UNUSED) + { + return (text[0] == '\0'); + } +/* Branching is not working properly + else if (pattern[1].type == BRANCH) + { + return (matchpattern(pattern, text) || matchpattern(&pattern[2], text)); + } +*/ + (*matchlength)++; + } + while ((text[0] != '\0') && matchone(*pattern++, *text++)); + + *matchlength = pre; + return 0; +} + +#endif diff --git a/lib/regex.h b/lib/regex.h new file mode 100644 index 0000000..d4c61b6 --- /dev/null +++ b/lib/regex.h @@ -0,0 +1,54 @@ +/* + * + * Mini regex-module inspired by Rob Pike's regex code described in: + * + * http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html + * + * + * + * Supports: + * --------- + * '.' Dot, matches any character + * '^' Start anchor, matches beginning of string + * '$' End anchor, matches end of string + * '*' Asterisk, match zero or more (greedy) + * '+' Plus, match one or more (greedy) + * '?' Question, match zero or one (non-greedy) + * '[abc]' Character class, match if one of {'a', 'b', 'c'} + * '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} -- NOTE: feature is currently broken! + * '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z } + * '\s' Whitespace, \t \f \r \n \v and spaces + * '\S' Non-whitespace + * '\w' Alphanumeric, [a-zA-Z0-9_] + * '\W' Non-alphanumeric + * '\d' Digits, [0-9] + * '\D' Non-digits + * + * + */ + +#ifndef _TINY_REGEX_C +#define _TINY_REGEX_C + + +#ifndef RE_DOT_MATCHES_NEWLINE +/* Define to 0 if you DON'T want '.' to match '\r' + '\n' */ +#define RE_DOT_MATCHES_NEWLINE 1 +#endif + +/* Typedef'd pointer to get abstract datatype. */ +typedef struct regex_t* re_t; + +/* Compile regex string pattern to a regex_t-array. */ +re_t re_compile(const char* pattern); + + +/* Find matches of the compiled pattern inside text. */ +int re_matchp(re_t pattern, const char* text, int* matchlength); + + +/* Find matches of the txt pattern inside text (will compile automatically first). */ +int re_match(const char* pattern, const char* text, int* matchlength); + + +#endif /* ifndef _TINY_REGEX_C */ diff --git a/lib/stack.c b/lib/stack.c new file mode 100644 index 0000000..acffc1a --- /dev/null +++ b/lib/stack.c @@ -0,0 +1,43 @@ +#include "stack.h" + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void stack_init(struct Stack* stack, size_t size) { + stack->size = 0; + stack->capacity = size; + stack->data = malloc(size); +} + +void stack_push(struct Stack* stack, void* data, size_t len) { + size_t new_size = stack->size + len; + if (new_size >= stack->capacity) { + stack->capacity = new_size * 2; + stack->data = realloc(stack->data, stack->capacity); + } + memcpy((uint8_t*)stack->data + stack->size, data, len); + stack->size += len; +} + +void* stack_pop(struct Stack* stack, size_t len) { + if (stack->size < len) return NULL; + stack->size -= len; + return (uint8_t*)stack->data + stack->size; +} + +void stack_free(struct Stack *stack) { + free(stack->data); +} + +void stack_push_int(struct Stack *stack, int value) { + stack_push(stack, &value, sizeof(int)); +} + +bool stack_pop_int(struct Stack *stack, int* value) { + void* d = stack_pop(stack, sizeof(int)); + if (d == NULL) return false; + *value = *(int*)(d); + return true; +} diff --git a/lib/stack.h b/lib/stack.h new file mode 100644 index 0000000..38fb185 --- /dev/null +++ b/lib/stack.h @@ -0,0 +1,22 @@ +#ifndef STACK_H +#define STACK_H + +#include "def.h" + +#include <stddef.h> + +struct Stack { + size_t size; + size_t capacity; + void* data; +}; + +void stack_init(struct Stack* stack, size_t size); +void stack_push(struct Stack* stack, void* data, size_t len); +void* stack_pop(struct Stack* stack, size_t len); +void stack_free(struct Stack* stack); + +void stack_push_int(struct Stack* stack, int value); +bool stack_pop_int(struct Stack* stack, int* value); + +#endif |