From 967be9e750221ab2ab783f95df79bb26d290a45e Mon Sep 17 00:00:00 2001 From: Martial Simon Date: Mon, 15 Sep 2025 01:07:58 +0200 Subject: add: added projects --- myfind/ast_evaluation/Makefile | 9 ++ myfind/ast_evaluation/ast_evaluation.c | 29 ++++ myfind/ast_evaluation/expression.h | 28 ++++ myfind/ast_evaluation/main.c | 24 +++ myfind/ast_evaluation/memory.c | 36 +++++ myfind/ast_evaluation/memory.h | 10 ++ myfind/ast_evaluation/parser.c | 258 +++++++++++++++++++++++++++++++++ myfind/ast_evaluation/parser.h | 9 ++ myfind/display_perm/Makefile | 15 ++ myfind/display_perm/display_perm.c | 25 ++++ myfind/group_belong/Makefile | 17 +++ myfind/group_belong/group_belong.c | 25 ++++ myfind/is_newer/Makefile | 15 ++ myfind/is_newer/is_newer.c | 29 ++++ myfind/myexecvp/Makefile | 17 +++ myfind/myexecvp/myexecvp.c | 26 ++++ myfind/myfind/Makefile | 22 +++ myfind/myfind/src/ast_evaluation.c | 37 +++++ myfind/myfind/src/lexer.c | 205 ++++++++++++++++++++++++++ myfind/myfind/src/lexer.h | 44 ++++++ myfind/myfind/src/myfind.c | 63 ++++++++ myfind/myfind/src/myfind.h | 9 ++ myfind/myfind/src/print.c | 75 ++++++++++ myfind/myfind/src/shunting_yard.c | 177 ++++++++++++++++++++++ myfind/myfind/src/shunting_yard.h | 10 ++ myfind/myfind/src/stack/stack.c | 30 ++++ myfind/myfind/src/stack/stack.h | 15 ++ myfind/myfind/src/ugtests.c | 82 +++++++++++ myfind/myfind/src/ugtests.h | 10 ++ myfind/myfind/tests/fun/tests.sh | 196 +++++++++++++++++++++++++ myfind/myfind/tests/unit/eval.c | 32 ++++ myfind/myfind/tests/unit/lexing.c | 45 ++++++ myfind/myfind/tests/unit/shunting.c | 30 ++++ myfind/simple_ls/Makefile | 15 ++ myfind/simple_ls/simple_ls.c | 41 ++++++ myfind/simple_stat/Makefile | 15 ++ myfind/simple_stat/simple_stat.c | 31 ++++ myfind/user_belong/Makefile | 17 +++ myfind/user_belong/user_belong.c | 25 ++++ 39 files changed, 1798 insertions(+) create mode 100644 myfind/ast_evaluation/Makefile create mode 100644 myfind/ast_evaluation/ast_evaluation.c create mode 100644 myfind/ast_evaluation/expression.h create mode 100644 myfind/ast_evaluation/main.c create mode 100644 myfind/ast_evaluation/memory.c create mode 100644 myfind/ast_evaluation/memory.h create mode 100644 myfind/ast_evaluation/parser.c create mode 100644 myfind/ast_evaluation/parser.h create mode 100644 myfind/display_perm/Makefile create mode 100644 myfind/display_perm/display_perm.c create mode 100644 myfind/group_belong/Makefile create mode 100644 myfind/group_belong/group_belong.c create mode 100644 myfind/is_newer/Makefile create mode 100644 myfind/is_newer/is_newer.c create mode 100644 myfind/myexecvp/Makefile create mode 100644 myfind/myexecvp/myexecvp.c create mode 100644 myfind/myfind/Makefile create mode 100644 myfind/myfind/src/ast_evaluation.c create mode 100644 myfind/myfind/src/lexer.c create mode 100644 myfind/myfind/src/lexer.h create mode 100644 myfind/myfind/src/myfind.c create mode 100644 myfind/myfind/src/myfind.h create mode 100644 myfind/myfind/src/print.c create mode 100644 myfind/myfind/src/shunting_yard.c create mode 100644 myfind/myfind/src/shunting_yard.h create mode 100644 myfind/myfind/src/stack/stack.c create mode 100644 myfind/myfind/src/stack/stack.h create mode 100644 myfind/myfind/src/ugtests.c create mode 100644 myfind/myfind/src/ugtests.h create mode 100755 myfind/myfind/tests/fun/tests.sh create mode 100644 myfind/myfind/tests/unit/eval.c create mode 100644 myfind/myfind/tests/unit/lexing.c create mode 100644 myfind/myfind/tests/unit/shunting.c create mode 100644 myfind/simple_ls/Makefile create mode 100644 myfind/simple_ls/simple_ls.c create mode 100644 myfind/simple_stat/Makefile create mode 100644 myfind/simple_stat/simple_stat.c create mode 100644 myfind/user_belong/Makefile create mode 100644 myfind/user_belong/user_belong.c (limited to 'myfind') diff --git a/myfind/ast_evaluation/Makefile b/myfind/ast_evaluation/Makefile new file mode 100644 index 0000000..2aca197 --- /dev/null +++ b/myfind/ast_evaluation/Makefile @@ -0,0 +1,9 @@ +CC ? = gcc CPPFLAGS = -D_DEFAULT_SOURCE CFLAGS = -Wall - Werror - Wextra - std = + c99 - pedantic + - Wvla + + BIN = ast_evaluation OBJS = main.o $(BIN).o memory.o parser.o + + all + : $(BIN) $(BIN) + : $(OBJS) clean : $(RM) $(BIN) $(OBJS) diff --git a/myfind/ast_evaluation/ast_evaluation.c b/myfind/ast_evaluation/ast_evaluation.c new file mode 100644 index 0000000..7ad91af --- /dev/null +++ b/myfind/ast_evaluation/ast_evaluation.c @@ -0,0 +1,29 @@ +#include +#include + +#include "expression.h" + +int eval_expr(struct my_expr *expr) +{ + if (expr->type == EXPR_NUMBER) + return expr->data.value; + else if (expr->type == EXPR_NEGATION) + return -1 * eval_expr(expr->data.children.left); + else + { + int right = eval_expr(expr->data.children.right); + switch (expr->type) + { + case EXPR_ADDITION: + return eval_expr(expr->data.children.left) + right; + case EXPR_SUBTRACTION: + return eval_expr(expr->data.children.left) - right; + case EXPR_MULTIPLICATION: + return eval_expr(expr->data.children.left) * right; + default: + if (expr->data.children.right == NULL || (right == 0)) + err(1, "Division by zero not allowed!"); + return eval_expr(expr->data.children.left) / right; + } + } +} diff --git a/myfind/ast_evaluation/expression.h b/myfind/ast_evaluation/expression.h new file mode 100644 index 0000000..d81718d --- /dev/null +++ b/myfind/ast_evaluation/expression.h @@ -0,0 +1,28 @@ +#ifndef EXPRESSION_H +#define EXPRESSION_H + +enum my_expr_type +{ + EXPR_ADDITION = 0, + EXPR_SUBTRACTION, + EXPR_MULTIPLICATION, + EXPR_DIVISION, + EXPR_NEGATION, + EXPR_NUMBER +}; + +struct my_expr +{ + enum my_expr_type type; + union + { + struct + { + struct my_expr *left; + struct my_expr *right; + } children; + int value; + } data; +}; + +#endif /* ! EXPRESSION_H */ diff --git a/myfind/ast_evaluation/main.c b/myfind/ast_evaluation/main.c new file mode 100644 index 0000000..2794cd9 --- /dev/null +++ b/myfind/ast_evaluation/main.c @@ -0,0 +1,24 @@ +#include +#include +#include + +#include "expression.h" +#include "parser.h" + +int eval_expr(struct my_expr *expr); + +int main(int argc, char **argv) +{ + static struct my_expr *expr = NULL; + + if (argc < 2) + errx(1, "Usage: %s ([token] ...)", argv[0]); + + unsigned args_length = argc - 1; + expr = parse_expr(argv + 1, args_length); + + printf("%d\n", eval_expr(expr)); + + free_expr(expr); + return 0; +} diff --git a/myfind/ast_evaluation/memory.c b/myfind/ast_evaluation/memory.c new file mode 100644 index 0000000..fa32451 --- /dev/null +++ b/myfind/ast_evaluation/memory.c @@ -0,0 +1,36 @@ +#include "memory.h" + +#include +#include + +static inline void memory_exhausted(void) +{ + err(1, "Memory exhausted."); +} + +void *my_malloc(size_t size) +{ + void *ptr = malloc(size); + if (size && !ptr) + memory_exhausted(); + + return ptr; +} + +void *my_calloc(size_t nmemb, size_t size) +{ + void *ptr = calloc(nmemb, size); + if (size && nmemb && !ptr) + memory_exhausted(); + + return ptr; +} + +void *my_reallocarray(void *ptr, size_t nmemb, size_t size) +{ + ptr = reallocarray(ptr, nmemb, size); + if (size && nmemb && !ptr) + memory_exhausted(); + + return ptr; +} diff --git a/myfind/ast_evaluation/memory.h b/myfind/ast_evaluation/memory.h new file mode 100644 index 0000000..deab8f1 --- /dev/null +++ b/myfind/ast_evaluation/memory.h @@ -0,0 +1,10 @@ +#ifndef MEMORY_H +#define MEMORY_H + +#include + +void *my_malloc(size_t size); +void *my_calloc(size_t nmemb, size_t size); +void *my_reallocarray(void *ptr, size_t nmemb, size_t size); + +#endif /* ! MEMORY_H */ diff --git a/myfind/ast_evaluation/parser.c b/myfind/ast_evaluation/parser.c new file mode 100644 index 0000000..66ab59b --- /dev/null +++ b/myfind/ast_evaluation/parser.c @@ -0,0 +1,258 @@ +#include "parser.h" + +#include +#include +#include +#include +#include + +#include "memory.h" + +enum my_token_type +{ + TOKEN_PLUS = 0, + TOKEN_MINUS, + TOKEN_MULTIPLY, + TOKEN_DIVIDE, + TOKEN_LEFT_PARENTHESIS, + TOKEN_RIGHT_PARENTHESIS, + TOKEN_NUMBER +}; + +struct my_token +{ + enum my_token_type type; + int value; +}; + +struct my_tokens +{ + struct my_token *data; + unsigned length; +}; + +struct token_model +{ + const char *str; + enum my_token_type token_type; +}; + +static struct my_token parse_token(const char *str) +{ + static struct token_model exprs[] = { { "+", TOKEN_PLUS }, + { "-", TOKEN_MINUS }, + { "*", TOKEN_MULTIPLY }, + { "/", TOKEN_DIVIDE }, + { "(", TOKEN_LEFT_PARENTHESIS }, + { ")", TOKEN_RIGHT_PARENTHESIS } }; + + struct my_token token = { TOKEN_NUMBER, 0 }; + for (unsigned i = 0; i < sizeof(exprs) / sizeof(*exprs); ++i) + { + if (!strcmp(str, exprs[i].str)) + { + token.type = exprs[i].token_type; + return token; + } + } + + if (sscanf(str, "%d", &token.value) == 1) + return token; + + errx(1, "Token not recognized: %s", str); +} + +static const char *token_str(struct my_token *token) +{ + static const char *token_strs[] = { "+", "-", "*", "/", "(", ")" }; + static char number_str[11]; + + if (token->type != TOKEN_NUMBER) + return token_strs[token->type]; + + sprintf(number_str, "%d", token->value); + return number_str; +} + +static struct my_expr *new_expr(void) +{ + return my_malloc(sizeof(struct my_expr)); +} + +static struct my_expr *clone_expr(struct my_expr *expr) +{ + return memcpy(new_expr(), expr, sizeof(*expr)); +} + +/** + * Returns the index of the matching right parenthesis + */ +static unsigned find_parenthesis(struct my_token *tokens, unsigned index, + unsigned length) +{ + unsigned level = 1; + + for (unsigned i = index; i < length; ++i) + { + enum my_token_type token_type = tokens[i].type; + if (token_type == TOKEN_LEFT_PARENTHESIS) + ++level; + if (token_type == TOKEN_RIGHT_PARENTHESIS) + --level; + + if (!level) + return i; + } + + errx(1, "Expected token: ')'"); +} + +static struct my_expr *parse_operations(struct my_token *tokens, + unsigned *index, unsigned length); + +static struct my_expr *parse_operand(struct my_token *tokens, unsigned *index, + unsigned length) +{ + assert(*index < length); + struct my_token *token = tokens + (*index)++; + + // Handle negation (e.g. "- 4") + if (token->type == TOKEN_MINUS) + { + if (length == 1) + errx(1, "Expected expression after token: -"); + + struct my_expr expr = { .data.value = 0, .type = EXPR_NUMBER }; + expr.type = EXPR_NEGATION; + expr.data.children.left = parse_operand(tokens, index, length); + return clone_expr(&expr); + } + if (token->type == TOKEN_NUMBER) + { + struct my_expr expr = { EXPR_NUMBER, { .value = token->value } }; + return clone_expr(&expr); + } + else if (token->type == TOKEN_LEFT_PARENTHESIS) + { + unsigned right = find_parenthesis(tokens, *index, length); + struct my_expr *expr = parse_operations(tokens, index, right); + *index = right + 1; + return expr; + } + + errx(1, "Expected tokens: -, '(' or number, but got: %s", token_str(token)); +} + +static struct my_expr *build_op_mult(struct my_expr **expr_stack, + unsigned expr_stack_length, + enum my_expr_type *op_stack, + unsigned op_stack_length) +{ + if (!op_stack_length) + return *expr_stack; + + struct my_expr expr = { .data.value = 0, .type = EXPR_NUMBER }; + expr.type = op_stack[op_stack_length - 1]; + expr.data.children.left = build_op_mult(expr_stack, expr_stack_length - 1, + op_stack, op_stack_length - 1); + + expr.data.children.right = expr_stack[expr_stack_length - 1]; + return clone_expr(&expr); +} + +static struct my_expr *build_op_sum(struct my_expr **expr_stack, + unsigned expr_stack_length, + enum my_expr_type *op_stack, + unsigned op_stack_length) +{ + /* + * Here, i will overflow at the end of the loop. + * No worries: overflow is perfectly defined for unsigned types. + * In that case, 0 - 1 == ~0, which is not < op_stack_length. + */ + for (unsigned i = op_stack_length - 1; i < op_stack_length; --i) + if (op_stack[i] == EXPR_ADDITION || op_stack[i] == EXPR_SUBTRACTION) + { + struct my_expr expr = { .data.value = 0, .type = EXPR_NUMBER }; + expr.type = op_stack[i]; + expr.data.children.left = + build_op_sum(expr_stack, i + 1, op_stack, i); + + expr.data.children.right = + build_op_mult(expr_stack + i + 1, expr_stack_length - (i + 1), + op_stack + i + 1, op_stack_length - (i + 1)); + return clone_expr(&expr); + } + + return build_op_mult(expr_stack, expr_stack_length, op_stack, + op_stack_length); +} + +static struct my_expr *parse_operations(struct my_token *tokens, + unsigned *index, unsigned length) +{ + assert(*index < length); + + unsigned expr_stack_length = 0; + struct my_expr **expr_stack = my_malloc(sizeof(*expr_stack)); + expr_stack[expr_stack_length++] = parse_operand(tokens, index, length); + + unsigned op_stack_length = 0; + enum my_expr_type *op_stack = NULL; + + while (*index < length) + { + struct my_token *token = tokens + (*index)++; + + // If the token is not an operator + if (!(token->type < TOKEN_LEFT_PARENTHESIS)) + errx(1, "Expected +, -, * or /, got %s", token_str(token)); + if (*index >= length) + errx(1, "Expected expression after %s", token_str(token)); + + expr_stack = my_reallocarray(expr_stack, expr_stack_length + 1, + sizeof(*expr_stack)); + expr_stack[expr_stack_length++] = parse_operand(tokens, index, length); + + op_stack = + my_reallocarray(op_stack, op_stack_length + 1, sizeof(*op_stack)); + // Legitimate cast: ops mapping between both enums matches + int expr_type_int = token->type; + op_stack[op_stack_length++] = expr_type_int; + } + + struct my_expr *expr = + build_op_sum(expr_stack, expr_stack_length, op_stack, op_stack_length); + + free(expr_stack); + free(op_stack); + + return expr; +} + +void free_expr(struct my_expr *expr) +{ + if (expr->type != EXPR_NUMBER) + { + if (expr->data.children.right) + free_expr(expr->data.children.right); + if (expr->data.children.left) + free_expr(expr->data.children.left); + } + free(expr); +} + +struct my_expr *parse_expr(char **strs, unsigned length) +{ + struct my_token *tokens = my_calloc(length, sizeof(struct my_token)); + + for (unsigned i = 0; i < length; ++i) + tokens[i] = parse_token(strs[i]); + + unsigned index = 0; + struct my_expr *expr = parse_operations(tokens, &index, length); + + free(tokens); + + return expr; +} diff --git a/myfind/ast_evaluation/parser.h b/myfind/ast_evaluation/parser.h new file mode 100644 index 0000000..89a4136 --- /dev/null +++ b/myfind/ast_evaluation/parser.h @@ -0,0 +1,9 @@ +#ifndef PARSER_H +#define PARSER_H + +#include "expression.h" + +void free_expr(struct my_expr *expr); +struct my_expr *parse_expr(char **strs, unsigned length); + +#endif /* ! PARSER_H */ diff --git a/myfind/display_perm/Makefile b/myfind/display_perm/Makefile new file mode 100644 index 0000000..4caed42 --- /dev/null +++ b/myfind/display_perm/Makefile @@ -0,0 +1,15 @@ +CC = gcc +CFLAGS = -std=c99 -pedantic -Werror -Wall -Wextra -Wvla + +SRC = display_perm.c +OBJ = $(SRC:.c=.o) + +all: $(OBJ) + $(CC) -o display_perm $(OBJ) + +$(OBJ): $(SRC) + +.PHONY: clean + +clean : + $(RM) $(OBJ) display_perm diff --git a/myfind/display_perm/display_perm.c b/myfind/display_perm/display_perm.c new file mode 100644 index 0000000..3ad17e6 --- /dev/null +++ b/myfind/display_perm/display_perm.c @@ -0,0 +1,25 @@ +#include +#include + +int main(int argc, char **argv) +{ + if (argc != 2) + return 2; + + struct stat s; + stat(argv[1], &s); + + // User + printf("%d", + ((s.st_mode & S_IRUSR) != 0) * 4 + ((s.st_mode & S_IWUSR) != 0) * 2 + + ((s.st_mode & S_IXUSR) != 0)); + printf("%d", + ((s.st_mode & S_IRGRP) != 0) * 4 + ((s.st_mode & S_IWGRP) != 0) * 2 + + ((s.st_mode & S_IXGRP) != 0)); + printf("%d", + ((s.st_mode & S_IROTH) != 0) * 4 + ((s.st_mode & S_IWOTH) != 0) * 2 + + ((s.st_mode & S_IXOTH) != 0)); + printf("\n"); + + return 0; +} diff --git a/myfind/group_belong/Makefile b/myfind/group_belong/Makefile new file mode 100644 index 0000000..b5672f4 --- /dev/null +++ b/myfind/group_belong/Makefile @@ -0,0 +1,17 @@ +CC = gcc CFLAGS = -std = c99 - pedantic - Werror - Wall - Wextra + - Wvla + + SRC = group_belong.c OBJ = $(SRC + :.c =.o) + + all + : $(OBJ) $(CC) + - o group_belong $(OBJ) + + $(OBJ) + : $(SRC) + + .PHONY + : clean + + clean : $(RM) $(OBJ) group_belong diff --git a/myfind/group_belong/group_belong.c b/myfind/group_belong/group_belong.c new file mode 100644 index 0000000..20302d5 --- /dev/null +++ b/myfind/group_belong/group_belong.c @@ -0,0 +1,25 @@ +#include +#include +#include + +int main(int argc, char **argv) +{ + if (argc != 3) + return 2; + + struct group *g = getgrnam(argv[2]); + if (!g) + { + printf("group %s does not exist\n", argv[2]); + return 0; + } + struct stat s; + stat(argv[1], &s); + + printf("%s does ", argv[1]); + if (s.st_gid != g->gr_gid) + printf("not "); + printf("belong to group %s\n", argv[2]); + + return 0; +} diff --git a/myfind/is_newer/Makefile b/myfind/is_newer/Makefile new file mode 100644 index 0000000..3787a95 --- /dev/null +++ b/myfind/is_newer/Makefile @@ -0,0 +1,15 @@ +CC = gcc +CFLAGS = -std=c99 -pedantic -Werror -Wall -Wextra -Wvla -D_DEFAULT_SOURCE + +SRC = is_newer.c +OBJ = $(SRC:.c=.o) + +is_newer: $(OBJ) + $(CC) $(OBJ) -o is_newer + +$(OBJ): $(SRC) + +.PHONY: clean + +clean : + $(RM) $(OBJ) is_newer diff --git a/myfind/is_newer/is_newer.c b/myfind/is_newer/is_newer.c new file mode 100644 index 0000000..9f00ffc --- /dev/null +++ b/myfind/is_newer/is_newer.c @@ -0,0 +1,29 @@ +#include +#include + +#define _POSIX_C_SOURCE 200809L + +int main(int argc, char **argv) +{ + if (argc != 3) + return 2; + + struct stat f1; + if (stat(argv[1], &f1)) + return 2; + struct stat f2; + if (stat(argv[2], &f2)) + return 2; + + printf("%s is ", argv[1]); + if (f2.st_mtime == f1.st_mtime) + { + if (f2.st_mtim.tv_nsec >= f1.st_mtim.tv_nsec) + printf("not "); + } + else if (f2.st_mtime > f1.st_mtime) + printf("not "); + printf("newer than %s\n", argv[2]); + + return 0; +} diff --git a/myfind/myexecvp/Makefile b/myfind/myexecvp/Makefile new file mode 100644 index 0000000..f5ae43f --- /dev/null +++ b/myfind/myexecvp/Makefile @@ -0,0 +1,17 @@ +CC = gcc CFLAGS = -std = c99 - pedantic - Werror - Wall - Wextra + - Wvla + + SRC = myexecvp.c OBJ = $(SRC + :.c =.o) + + all + : $(OBJ) $(CC) + - o myexecvp $(OBJ) + + $(OBJ) + : $(SRC) + + .PHONY + : clean + + clean : $(RM) $(OBJ) myexecvp diff --git a/myfind/myexecvp/myexecvp.c b/myfind/myexecvp/myexecvp.c new file mode 100644 index 0000000..30cd519 --- /dev/null +++ b/myfind/myexecvp/myexecvp.c @@ -0,0 +1,26 @@ +#include +#include +#include + +int main(int argc, char **argv) +{ + if (argc < 2) + return 1; + + pid_t child = fork(); + if (child == -1) + { + return 1; + } + else if (child == 0) + { + execvp(argv[1], argv + 1); + return 1; + } + else + { + int retval; + waitpid(child, &retval, 0); + return (retval == 0) ? 0 : 1; + } +} diff --git a/myfind/myfind/Makefile b/myfind/myfind/Makefile new file mode 100644 index 0000000..05cebe9 --- /dev/null +++ b/myfind/myfind/Makefile @@ -0,0 +1,22 @@ +CC = gcc +CFLAGS = -std=c99 -pedantic -Werror -Wall -Wextra -Wvla -D_DEFAULT_SOURCE -Isrc + +SRC = src/myfind.c src/print.c src/lexer.c src/stack/stack.c src/shunting_yard.c src/ast_evaluation.c src/ugtests.c +TEST = src/lexer.c src/shunting_yard.c src/stack/stack.c src/ast_evaluation.c tests/unit/shunting.c tests/unit/lexing.c tests/unit/eval.c +OBJ = $(SRC:.c=.o) +TESTOBJ = $(TEST:.c=.o) + +all: myfind + +myfind: $(OBJ) + $(CC) -o myfind $(OBJ) + +$(OBJ): $(SRC) + +.PHONY: clean check + +clean: + $(RM) myfind $(OBJ) mytests $(TESTOBJ) + +check: + tests/fun/tests.sh diff --git a/myfind/myfind/src/ast_evaluation.c b/myfind/myfind/src/ast_evaluation.c new file mode 100644 index 0000000..0c77052 --- /dev/null +++ b/myfind/myfind/src/ast_evaluation.c @@ -0,0 +1,37 @@ +#include + +#include "lexer.h" +#include "myfind.h" + +int eval_expr(struct ast *expr, const char *fn) +{ + if (expr->type == AND) + { + int left = eval_expr(expr->data.children.left, fn); + if (left == -1) + return -1; + else if (left) + return eval_expr(expr->data.children.right, fn); + else + return 0; + } + else if (expr->type == OR) + { + int left = eval_expr(expr->data.children.left, fn); + if (left == -1) + return -1; + else if (left) + return 1; + else + return eval_expr(expr->data.children.right, fn); + } + + /* + * else if (expr->type == EXPR_NEGATION) + * return -1 * eval_expr(expr->data.children.left); + */ + else + { + return expr->data.test.fun(expr->data.test.param, fn); + } +} diff --git a/myfind/myfind/src/lexer.c b/myfind/myfind/src/lexer.c new file mode 100644 index 0000000..a02e075 --- /dev/null +++ b/myfind/myfind/src/lexer.c @@ -0,0 +1,205 @@ +#include "lexer.h" + +#include +#include +#include +#include +#include + +#include "ugtests.h" + +int print(__attribute__((unused)) const char *param, const char *filename) +{ + printf("%s\n", filename); + return 1; +} + +int name(const char *param, const char *filename) +{ + const char *f = filename + strlen(filename) - 1; + for (; f >= filename && *f != '/'; f--) + { + continue; + } + if (*f == '/') + f++; + return !fnmatch(param, f, FNM_PATHNAME); +} + +int type(const char *param, const char *filename) +{ + // assume that param is valid e.g. single char + struct stat s; + if (lstat(filename, &s)) + { + fprintf(stderr, "myfind: Error trying to stat path %s\n", filename); + return -1; + } + switch (*param) + { + case 'b': + return S_ISBLK(s.st_mode); + case 'c': + return S_ISCHR(s.st_mode); + case 'd': + return S_ISDIR(s.st_mode); + case 'f': + return S_ISREG(s.st_mode); + case 'l': + return S_ISLNK(s.st_mode); + case 'p': + return S_ISFIFO(s.st_mode); + case 's': + return S_ISSOCK(s.st_mode); + default: + fprintf(stderr, "myfind: Error trying to stat path %s\n", filename); + return -1; + } +} + +int is_newer(const char *param, const char *filename) +{ + struct stat f1; + if (lstat(filename, &f1)) + { + fprintf(stderr, "myfind: Error trying to stat path %s\n", filename); + return -1; + } + struct stat f2; + if (lstat(param, &f2)) + { + fprintf(stderr, "myfind: Error trying to stat path %s\n", param); + return -1; + } + + if (f2.st_mtime == f1.st_mtime) + { + if (f2.st_mtim.tv_nsec >= f1.st_mtim.tv_nsec) + return 0; + } + else if (f2.st_mtime > f1.st_mtime) + return 0; + + return 1; +} + +struct template +{ + char *flag; + enum type type; + int (*fun)(__attribute__((unused)) const char *, const char *); +}; + +static struct template templates[] = { + { .flag = "-print", .type = PRINT, .fun = print }, + { .flag = "-name", .type = NAME, .fun = name }, + { .flag = "-type", .type = TYPE, .fun = type }, + { .flag = "-newer", .type = NEWER, .fun = is_newer }, + { .flag = "-group", .type = GROUP, .fun = group_belong }, + { .flag = "-user", .type = USER, .fun = user_belong }, + { .flag = "-perm", .type = PERM, .fun = perm }, + { .flag = "-o", .type = OR, .fun = NULL }, + { .flag = "-a", .type = AND, .fun = NULL } +}; + +void init_token(struct ast **tok, struct template temp) +{ + *tok = malloc(sizeof(struct ast)); + (*tok)->type = temp.type; + if ((*tok)->type == OR || (*tok)->type == AND) + { + (*tok)->data.children.left = NULL; + (*tok)->data.children.right = NULL; + } + else + { + (*tok)->data.test.param = NULL; + (*tok)->data.test.fun = temp.fun; + } +} + +void cleanup(struct ast **tokens, int len) +{ + for (int k = 0; k < len; k++) + free(tokens[k]); + free(tokens); +} + +void *error(struct ast **tokens, int len, enum type flag) +{ + char *f; + switch (flag) + { + case NAME: + f = "-name"; + break; + case TYPE: + f = "-type"; + break; + case PERM: + f = "-perm"; + break; + case GROUP: + f = "-group"; + break; + case USER: + f = "-user"; + break; + default: + f = "-newer"; + break; + } + + fprintf(stderr, "myfind: lex: invalid/missing %s argument\n", f); + cleanup(tokens, len); + return NULL; +} + +struct ast **lex(char **argv, int *len, int *act) +{ + // if error : free tokens + struct ast **tokens = malloc((*len * 2 - 1) * sizeof(struct ast *)); + *act = 0; + + int n = 0; + for (int i = 0; i < *len; i++, n++) + { + int j; + for (j = 0; j < 9; j++) + { + if (strcmp(argv[i], templates[j].flag) == 0) + { + init_token(tokens + n, templates[j]); + if (tokens[n]->type == PRINT) + *act = 1; + // enventual offset & param set + // refactor + if (tokens[n]->type == NAME || tokens[n]->type == TYPE + || tokens[n]->type == NEWER || tokens[n]->type == GROUP + || tokens[n]->type == USER || tokens[n]->type == PERM) + { + struct stat s; + if ((i + 1 >= *len) + || (tokens[n]->type == TYPE && argv[i + 1][1] != '\0') + || (tokens[n]->type == NEWER && lstat(argv[i + 1], &s))) + { + return error(tokens, n + 1, tokens[n]->type); + } + tokens[n]->data.test.param = argv[i + 1]; + i++; + } + break; + } + } + if (j == 9) + { + fprintf(stderr, "myfind: lex: Invalid argument %s\n", argv[i]); + cleanup(tokens, n + 1); + return NULL; + } + } + + *len = n; + + return tokens; +} diff --git a/myfind/myfind/src/lexer.h b/myfind/myfind/src/lexer.h new file mode 100644 index 0000000..27c44c6 --- /dev/null +++ b/myfind/myfind/src/lexer.h @@ -0,0 +1,44 @@ +#ifndef LEXER_H +#define LEXER_H + +#include + +enum type +{ + OR, + AND, + NAME, + TYPE, + NEWER, + GROUP, + USER, + PERM, + PRINT +}; + +struct eval +{ + char *param; // static, don't free + int (*fun)(__attribute__((unused)) const char *, const char *); +}; + +// Only operators may have children as they constitute the nodes of the AST +struct ast +{ + enum type type; + union + { + struct + { + struct ast *left; + struct ast *right; + } children; + struct eval test; + } data; +}; + +int print(const char *param, const char *filename); + +struct ast **lex(char **argv, int *len, int *act); + +#endif /* ! LEXER_H */ diff --git a/myfind/myfind/src/myfind.c b/myfind/myfind/src/myfind.c new file mode 100644 index 0000000..e18913c --- /dev/null +++ b/myfind/myfind/src/myfind.c @@ -0,0 +1,63 @@ +#include "myfind.h" + +#include + +#include "lexer.h" +#include "shunting_yard.h" + +int main(int argc, char **argv) +{ + if (argc == 1) + { + return print_r(".", NULL) ? 1 : 0; + } + + int i; + for (i = 1; i < argc && argv[i][0] != '-'; i++) + { + continue; + } + // i = nb of starting points = start index of expressions + + int err; + if (i == argc) + { + // eval all and match filenames + for (int j = 1; j < i; j++) + { + err = print_r(argv[j], NULL); + } + } + else + { + int len = argc - i; + int act; + // lex argv + i + struct ast **tokens = lex(argv + i, &len, &act); + if (tokens == NULL) + return 1; + // build AST + struct ast *root = shunting_yard(tokens, len, act); + if (root == NULL) + { + free(tokens); + return 1; + } + // eval all and match filenames + if (i == 1) + err = print_r(".", root); + else + { + for (int j = 1; j < i; j++) + { + err = print_r(argv[j], root); + } + } + free(tokens); + ast_destroy(root); + } + + // CLEANUP + + return err ? 1 : 0; +} diff --git a/myfind/myfind/src/myfind.h b/myfind/myfind/src/myfind.h new file mode 100644 index 0000000..a09cc4c --- /dev/null +++ b/myfind/myfind/src/myfind.h @@ -0,0 +1,9 @@ +#ifndef MYFIND_H +#define MYFIND_H + +#include "lexer.h" + +int eval_expr(struct ast *expr, const char *fn); +int print_r(char *path, struct ast *expr); + +#endif /* ! MYFIND_H */ diff --git a/myfind/myfind/src/print.c b/myfind/myfind/src/print.c new file mode 100644 index 0000000..6a6a6eb --- /dev/null +++ b/myfind/myfind/src/print.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include + +#include "myfind.h" + +int recurse(struct dirent *dir, char *path, struct ast *ast) +{ + if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")) + return 0; + size_t pathlen = strlen(path); + char *copy = malloc(pathlen + strlen(dir->d_name) + 2); + strcpy(copy, path); + if (path[pathlen - 1] != '/') + strcat(copy, "/"); + strcat(copy, dir->d_name); + int res = print_r(copy, ast); + free(copy); + return res; +} + +int print_r(char *path, struct ast *ast) +{ + if (!ast) + printf("%s\n", path); + else + { + if (eval_expr(ast, path) == -1) + return 1; + } + + // Si path == file : return + struct stat s; + if (lstat(path, &s) != 0) + { + fprintf(stderr, + "myfind: error while trying to stat path (%s) in print_r", + path); + return 1; + } + if (!(S_ISDIR(s.st_mode))) + { + return 0; + } + // Sinon : + // Parcourir le dir + // APPEL RECURSIF : + // copier dir et cat dossier + // si retour erreur alors erreur + // free la copie + // closedir + DIR *d = opendir(path); + if (!d) + { + fprintf(stderr, "myfind: error while trying to open dir in print_r"); + return 1; + } + + struct dirent *dir; + int res = 0; + while ((dir = readdir(d))) + { + if (recurse(dir, path, ast)) + res = 1; + } + if (closedir(d)) + { + fprintf(stderr, "myfind: error while trying to close dir in print_r"); + return 1; + } + return res; +} diff --git a/myfind/myfind/src/shunting_yard.c b/myfind/myfind/src/shunting_yard.c new file mode 100644 index 0000000..62395af --- /dev/null +++ b/myfind/myfind/src/shunting_yard.c @@ -0,0 +1,177 @@ +#include "shunting_yard.h" + +#include +#include + +#include "lexer.h" +#include "myfind.h" +#include "stack/stack.h" + +void init_and(struct stack **operators) +{ + struct ast *and = malloc(sizeof(struct ast)); + and->type = AND; + and->data.children.left = NULL; + and->data.children.right = NULL; + *operators = stack_push(*operators, and); +} + +void init_print(struct ast **tok) +{ + *tok = malloc(sizeof(struct ast)); + (*tok)->type = PRINT; + (*tok)->data.test.param = NULL; + (*tok)->data.test.fun = print; +} + +void ast_destroy(struct ast *ast) +{ + if (ast == NULL) + return; + if (ast->type != OR && ast->type != AND) + { + free(ast); + return; + } + ast_destroy(ast->data.children.left); + ast_destroy(ast->data.children.right); + free(ast); +} + +int pop_operators(struct stack **operators, struct stack **operands) +{ + struct ast *top; + while (*operators && (top = stack_peek(*operators))->type == AND) + { + *operators = stack_pop(*operators); + if (*operands == NULL) + { + fprintf(stderr, "myfind: pop_operators: Missing operand"); + return 0; + } + top->data.children.right = stack_peek(*operands); + *operands = stack_pop(*operands); + if (*operands == NULL) + { + fprintf(stderr, "myfind: pop_operators: Missing operand"); + return 0; + } + top->data.children.left = stack_peek(*operands); + *operands = stack_pop(*operands); + *operands = stack_push(*operands, top); + } + return 1; +} + +int clear_operators(struct stack **operators, struct stack **operands) +{ + if (*operators == NULL) + return (*operands)->next == NULL ? 1 : 0; + + while (*operators) + { + struct ast *top = stack_peek(*operators); + *operators = stack_pop(*operators); + if (*operands == NULL) + { + fprintf(stderr, "myfind: clear_operators: Missing operand"); + return 0; + } + top->data.children.left = stack_peek(*operands); + *operands = stack_pop(*operands); + if (*operands == NULL) + { + fprintf(stderr, "myfind: clear_operators: Missing operand"); + return 0; + } + top->data.children.right = stack_peek(*operands); + *operands = stack_pop(*operands); + *operands = stack_push(*operands, top); + } + return (*operands)->next == NULL ? 1 : 0; +} + +void empty_stack(struct stack *s) +{ + while (s) + { + ast_destroy(stack_peek(s)); + s = stack_pop(s); + } +} + +void *empty_stacks(struct stack *s1, struct stack *s2) +{ + empty_stack(s1); + empty_stack(s2); + return NULL; +} + +struct ast *fix_root(struct ast *root) +{ + struct ast *print; + init_print(&print); + struct ast *and = malloc(sizeof(struct ast)); + and->type = AND; + and->data.children.left = root; + and->data.children.right = print; + return and; +} + +void free_tokens(struct ast **tokens, int len) +{ + fprintf(stderr, "myfind: invalid operator count\n"); + for (int i = 0; i < len; i++) + ast_destroy(tokens[i]); +} + +struct ast *shunting_yard(struct ast **tokens, int len, int act) +{ + struct stack *operators = NULL; + struct stack *operands = NULL; + int op = 0; + for (int i = 0; i < len; i++) + { + if (tokens[i]->type != OR && tokens[i]->type != AND) + { + if (op) + { + // refactor + init_and(&operators); + } + operands = stack_push(operands, tokens[i]); + op = 1; + } + else + { + if (!op) + { + free_tokens(tokens + i, len - i); + return empty_stacks(operators, operands); + } + if (tokens[i]->type == AND) + operators = stack_push(operators, tokens[i]); + else if (tokens[i]->type == OR) + { + if (!pop_operators(&operators, &operands)) + { + free_tokens(tokens + i, len - i); + return empty_stacks(operators, operands); + } + operators = stack_push(operators, tokens[i]); + } + op = 0; + } + } + if (!clear_operators(&operators, &operands)) + { + return empty_stacks(operators, operands); + } + struct ast *res = stack_peek(operands); + operands = stack_pop(operands); + if (!act) + { + return fix_root(res); + } + return res; +} diff --git a/myfind/myfind/src/shunting_yard.h b/myfind/myfind/src/shunting_yard.h new file mode 100644 index 0000000..4813e37 --- /dev/null +++ b/myfind/myfind/src/shunting_yard.h @@ -0,0 +1,10 @@ +#ifndef SHUNTING_YARD_H +#define SHUNTING_YARD_H + +// Returns NULL on token errors, or adress of ast root node on success +// Frees the array of tokens but not the tokens +struct ast *shunting_yard(struct ast **tokens, int len, int act); + +void ast_destroy(struct ast *ast); + +#endif /* ! SHUNTING_YARD_H*/ diff --git a/myfind/myfind/src/stack/stack.c b/myfind/myfind/src/stack/stack.c new file mode 100644 index 0000000..2bf8577 --- /dev/null +++ b/myfind/myfind/src/stack/stack.c @@ -0,0 +1,30 @@ +#include "stack.h" + +#include + +struct stack *stack_push(struct stack *s, struct ast *e) +{ + struct stack *new = malloc(sizeof(struct stack)); + new->token = e; + new->next = NULL; + + new->next = s; + return new; +} + +struct stack *stack_pop(struct stack *s) +{ + if (s == NULL) + { + return NULL; + } + + struct stack *res = s->next; + free(s); + return res; +} + +struct ast *stack_peek(struct stack *s) +{ + return s->token; +} diff --git a/myfind/myfind/src/stack/stack.h b/myfind/myfind/src/stack/stack.h new file mode 100644 index 0000000..2de43a6 --- /dev/null +++ b/myfind/myfind/src/stack/stack.h @@ -0,0 +1,15 @@ +#ifndef STACK_H +#define STACK_H + +#include "lexer.h" + +struct stack +{ + struct ast *token; + struct stack *next; +}; + +struct stack *stack_push(struct stack *s, struct ast *e); +struct stack *stack_pop(struct stack *s); +struct ast *stack_peek(struct stack *s); +#endif /* !STACK_H */ diff --git a/myfind/myfind/src/ugtests.c b/myfind/myfind/src/ugtests.c new file mode 100644 index 0000000..7d89b5e --- /dev/null +++ b/myfind/myfind/src/ugtests.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include + +int perminus(const char *p, struct stat *s) +{ + int u = ((s->st_mode & S_IRUSR) != 0) * 4 + + ((s->st_mode & S_IWUSR) != 0) * 2 + ((s->st_mode & S_IXUSR) != 0); + int g = ((s->st_mode & S_IRGRP) != 0) * 4 + + ((s->st_mode & S_IWGRP) != 0) * 2 + ((s->st_mode & S_IXGRP) != 0); + int o = ((s->st_mode & S_IROTH) != 0) * 4 + + ((s->st_mode & S_IWOTH) != 0) * 2 + ((s->st_mode & S_IXOTH) != 0); + return ((p[0] - '0') & u) == p[0] - '0' && ((p[1] - '0') & g) == p[1] - '0' + && ((p[2] - '0') & o) == p[2] - '0'; +} + +int permslash(const char *p, struct stat *s) +{ + int u = ((s->st_mode & S_IRUSR) != 0) * 4 + + ((s->st_mode & S_IWUSR) != 0) * 2 + ((s->st_mode & S_IXUSR) != 0); + int g = ((s->st_mode & S_IRGRP) != 0) * 4 + + ((s->st_mode & S_IWGRP) != 0) * 2 + ((s->st_mode & S_IXGRP) != 0); + int o = ((s->st_mode & S_IROTH) != 0) * 4 + + ((s->st_mode & S_IWOTH) != 0) * 2 + ((s->st_mode & S_IXOTH) != 0); + return (p[0] - '0') & u || (p[1] - '0') & g || (p[2] - '0') & o; +} + +int perm(const char *permission, const char *filename) +{ + struct stat s; + lstat(filename, &s); + if (permission[0] == '-') + return perminus(permission + 1, &s); + if (permission[0] == '/') + return permslash(permission + 1, &s); + int u = ((s.st_mode & S_IRUSR) != 0) * 4 + ((s.st_mode & S_IWUSR) != 0) * 2 + + ((s.st_mode & S_IXUSR) != 0); + int g = ((s.st_mode & S_IRGRP) != 0) * 4 + ((s.st_mode & S_IWGRP) != 0) * 2 + + ((s.st_mode & S_IXGRP) != 0); + int o = ((s.st_mode & S_IROTH) != 0) * 4 + ((s.st_mode & S_IWOTH) != 0) * 2 + + ((s.st_mode & S_IXOTH) != 0); + if (permission[0] - '0' != u) + return 0; + if (permission[1] - '0' != g) + return 0; + if (permission[2] - '0' != o) + return 0; + return 1; +} + +int user_belong(const char *user, const char *filename) +{ + struct passwd *p = getpwnam(user); + if (!p) + { + fprintf(stderr, "myfind: user %s not found\n", user); + return -1; + } + struct stat s; + lstat(filename, &s); + + if (s.st_uid != p->pw_uid) + return 0; + return 1; +} + +int group_belong(const char *group, const char *filename) +{ + struct group *g = getgrnam(group); + if (!g) + { + fprintf(stderr, "myfind: group %s not found\n", group); + return -1; + } + struct stat s; + lstat(filename, &s); + + if (s.st_gid != g->gr_gid) + return 0; + return 1; +} diff --git a/myfind/myfind/src/ugtests.h b/myfind/myfind/src/ugtests.h new file mode 100644 index 0000000..1d64411 --- /dev/null +++ b/myfind/myfind/src/ugtests.h @@ -0,0 +1,10 @@ +#ifndef UGTESTS_H +#define UGTESTS_H + +int perm(const char *permission, const char *filename); + +int user_belong(const char *user, const char *filename); + +int group_belong(const char *group, const char *filename); + +#endif // !UGTESTS_H diff --git a/myfind/myfind/tests/fun/tests.sh b/myfind/myfind/tests/fun/tests.sh new file mode 100755 index 0000000..f229a92 --- /dev/null +++ b/myfind/myfind/tests/fun/tests.sh @@ -0,0 +1,196 @@ +#!/bin/sh + +REF_OUT="ref.out" +TEST_OUT="test.out" + +testfiles() +{ + echo "Finding path '$@'..." + find "$@" > "$REF_OUT" + sed -i '/.*\.out/d' "$REF_OUT" + ./myfind "$@" > "$TEST_OUT" + [ $(echo "$?") -eq 0 ] && echo "Return value OK" + sed -i '/.*\.out/d' "$TEST_OUT" + diff "$REF_OUT" "$TEST_OUT" && echo "Success" +} + +testperm() +{ + echo "Finding perm '$@'..." + find "$@" > "$REF_OUT" + sed -i '/.*\.out/d' "$REF_OUT" + ./myfind "$@" > "$TEST_OUT" + [ $(echo "$?") -eq 0 ] && echo "Return value OK" + sed -i '/.*\.out/d' "$TEST_OUT" + diff "$REF_OUT" "$TEST_OUT" && echo "Success" +} + +testug() +{ + echo "Finding '$@'..." + find "$@" > "$REF_OUT" + sed -i '/.*\.out/d' "$REF_OUT" + ./myfind "$@" > "$TEST_OUT" + [ $(echo "$?") -eq 0 ] && echo "Return value OK" + sed -i '/.*\.out/d' "$TEST_OUT" + diff "$REF_OUT" "$TEST_OUT" && echo "Success" +} + +testflags() +{ + echo "Finding flags '$@'..." + find "$@" > "$REF_OUT" + sed -i '/.*\.out/d' "$REF_OUT" + ./myfind "$@" > "$TEST_OUT" + [ $(echo "$?") -eq 0 ] && echo "Return value OK" + sed -i '/.*\.out/d' "$TEST_OUT" + diff "$REF_OUT" "$TEST_OUT" && echo "Success" +} + +testboth() +{ + echo "Finding '$@'..." + find "$@" > "$REF_OUT" + sed -i '/.*\.out/d' "$REF_OUT" + ./myfind "$@" > "$TEST_OUT" + [ $(echo "$?") -eq 0 ] && echo "Return value OK" + sed -i '/.*\.out/d' "$TEST_OUT" + diff "$REF_OUT" "$TEST_OUT" && echo "Success" +} + +testerror() +{ + echo "Attempting to find '$@'..." + find "$@" > "$REF_OUT" + sed -i '/.*\.out/d' "$REF_OUT" + ./myfind "$@" > "$TEST_OUT" + ret=$(echo "$?") + ([ "$ret" -eq 1 ] && echo "Return value OK") || echo "Invalid return value " $ret + sed -i '/.*\.out/d' "$TEST_OUT" + diff "$REF_OUT" "$TEST_OUT" && echo "Success" +} + +clean() +{ + rm "$REF_OUT" "$TEST_OUT" +} + +# Files + +echo "Tests files:" +echo "======" + +testfiles +testfiles tests +testfiles src +testfiles src/// + +echo +echo "=============================================" +echo + +# Perms + +mkdir foo +touch foo/foo foo/bar foo/baz +chmod 123 foo/foo +chmod 777 foo/bar +chmod 644 foo/baz + +echo "Tests perm:" +echo "======" + +# Should only match foo/foo +testperm foo -perm 123 +# Should match foo/bar and foo/foo +testperm foo -perm -121 +# Should match foo/bar and foo/baz +testperm foo -perm /641 +# Should only match foo/bar +testperm foo -perm /060 +# Should only match foo/foo +chmod 644 foo/bar +chmod 644 foo/baz +testperm foo -perm -121 + +echo +echo "=============================================" +echo + +echo "Tests user:" +echo "======" + +testug foo -user marcellus +# sudo chown nobody foo/bar +testug foo -user marcellus +testug foo -user nobody + +echo +echo "=============================================" +echo + +echo "Tests group:" +echo "======" + +testug foo -group marcellus +# sudo chown :wheel foo/bar +testug foo -group marcellus +testug foo -group wheel + +# Clean +rm -rf foo + +echo +echo "=============================================" +echo + +# Flags + +echo "Tests flags:" +echo "======" + +testflags -type d +testflags -type f +testflags -name '*' +testflags -newer Makefile + +echo +echo "=============================================" +echo + +# Both + +echo "Tests both:" +echo "======" + +testboth tests -type d +testboth src/ -type f +testboth ../ -name '*.h' -a -type f +testboth ../ -name Makefile -o -newer Makefile + +echo +echo "=============================================" +echo + +# Errors + +echo "Error tests:" +echo "======" + +testerror -type ff +testerror -name +testerror -newer +testerror -type +testerror -name '*.c' -print -newer -o -print +mkdir foo +touch foo/foo foo/bar foo/baz +testerror foo -user dghdfkhg +rm -rf foo +testerror -name '*.c' -a -a -group marcellus +testerror -name '*.c' -o -a -group marcellus +testerror -name '*.c' -a -o -group marcellus +testerror -name '*.c' -o -o -group marcellus + +# Cleanup + +clean diff --git a/myfind/myfind/tests/unit/eval.c b/myfind/myfind/tests/unit/eval.c new file mode 100644 index 0000000..1debdbf --- /dev/null +++ b/myfind/myfind/tests/unit/eval.c @@ -0,0 +1,32 @@ +#include +#include + +#include "lexer.h" +#include "shunting_yard.h" +#include "myfind.h" + +TestSuite(ast_eval); + +Test(ast_eval, ast_eval_basic) +{ + char *argv[] = {"-print", "-name", "*.c"}; + int len = 3; + struct ast **tokens = lex(argv, &len); + struct ast *root = shunting_yard(tokens, len); + int ret = eval_expr(root, "src/myfind.c"); + cr_expect(ret == 1, "Expected return value 1, got %d", ret); + ast_destroy(root); + free(tokens); +} + +Test(ast_eval, ast_eval_basic2) +{ + char *argv[] = {"-print", "-name", "*.c", "-o", "-type", "f"}; + int len = 6; + struct ast **tokens = lex(argv, &len); + struct ast *root = shunting_yard(tokens, len); + int ret = eval_expr(root, "src/myfind.c"); + cr_expect(ret == 1, "Expected return value 1, got %d", ret); + ast_destroy(root); + free(tokens); +} diff --git a/myfind/myfind/tests/unit/lexing.c b/myfind/myfind/tests/unit/lexing.c new file mode 100644 index 0000000..dfa876a --- /dev/null +++ b/myfind/myfind/tests/unit/lexing.c @@ -0,0 +1,45 @@ +#include +#include + +#include "lexer.h" +#include "myfind.h" + +TestSuite(lexer); + +Test(lex, lex_basic) +{ + char *argv[] = {"-print", "-name", "*.c"}; + int len = 3; + struct ast **tokens = lex(argv, &len); + cr_assert(len == 2, "Expected size %d, got %d", 2, len); + enum type expected[] = {PRINT, NAME}; + for (int i = 0; i < len; i++) + { + cr_expect(tokens[i]->type == expected[i], "Expected %d, got %d", expected[i], tokens[i]->type); + free(tokens[i]); + } + free(tokens); +} + +Test(lex, lex_basic2) +{ + char *argv[] = {"-print", "-name", "*.c", "-o", "-type", "f"}; + int len = 6; + struct ast **tokens = lex(argv, &len); + cr_assert(len == 4, "Expected size %d, got %d", 4, len); + enum type expected[] = {PRINT, NAME, OR, TYPE}; + for (int i = 0; i < len; i++) + { + cr_expect(tokens[i]->type == expected[i], "Expected %d, got %d", expected[i], tokens[i]->type); + free(tokens[i]); + } + free(tokens); +} + +Test(lex, lex_error) +{ + char *argv[] = {"-print", "-name"}; + int len = 2; + struct ast **tokens = lex(argv, &len); + cr_expect(tokens == NULL, "Expected (null), got %p", tokens); +} diff --git a/myfind/myfind/tests/unit/shunting.c b/myfind/myfind/tests/unit/shunting.c new file mode 100644 index 0000000..e69437a --- /dev/null +++ b/myfind/myfind/tests/unit/shunting.c @@ -0,0 +1,30 @@ +#include +#include + +#include "lexer.h" +#include "shunting_yard.h" +#include "myfind.h" + +TestSuite(shunting_yard); + +Test(shunting_yard, shunting_basic) +{ + char *argv[] = {"-print", "-name", "*.c"}; + int len = 3; + struct ast **tokens = lex(argv, &len); + struct ast *root = shunting_yard(tokens, len); + cr_expect(root->type == AND, "Expected AND as root node, got %d", root->type); + ast_destroy(root); + free(tokens); +} + +Test(shunting_yard, shunting_basic2) +{ + char *argv[] = {"-print", "-name", "*.c", "-o", "-type", "f"}; + int len = 6; + struct ast **tokens = lex(argv, &len); + struct ast *root = shunting_yard(tokens, len); + cr_expect(root->type == OR, "Expected OR as root node, got %d", root->type); + ast_destroy(root); + free(tokens); +} diff --git a/myfind/simple_ls/Makefile b/myfind/simple_ls/Makefile new file mode 100644 index 0000000..49af11b --- /dev/null +++ b/myfind/simple_ls/Makefile @@ -0,0 +1,15 @@ +CC = gcc +CFLAGS = -std=c99 -pedantic -Werror -Wall -Wextra -Wvla + +SRC = simple_ls.c +OBJ = $(SRC:.c=.o) + +all: $(OBJ) + $(CC) -o simple_ls $(OBJ) + +$(OBJ): $(SRC) + +.PHONY: clean + +clean : + $(RM) $(OBJ) simple_ls diff --git a/myfind/simple_ls/simple_ls.c b/myfind/simple_ls/simple_ls.c new file mode 100644 index 0000000..084072d --- /dev/null +++ b/myfind/simple_ls/simple_ls.c @@ -0,0 +1,41 @@ +#include +#include + +int main(int argc, char **argv) +{ + if (argc > 2) + return 1; + + DIR *d; + if (argc == 1) + { + d = opendir("."); + if (!d) + { + fprintf(stderr, "simple_ls: .: No such file or directory\n"); + return 1; + } + } + else + { + d = opendir(argv[1]); + if (!d) + { + fprintf(stderr, "simple_ls: %s: No such file or directory\n", + argv[1]); + return 1; + } + } + + struct dirent *dir; + while ((dir = readdir(d))) + { + if (dir->d_name[0] != '.') + printf("%s\n", dir->d_name); + } + + if (closedir(d)) + return 1; + + return 0; +} diff --git a/myfind/simple_stat/Makefile b/myfind/simple_stat/Makefile new file mode 100644 index 0000000..f38f031 --- /dev/null +++ b/myfind/simple_stat/Makefile @@ -0,0 +1,15 @@ +CC = gcc +CFLAGS = -std=c99 -pedantic -Werror -Wall -Wextra -Wvla + +SRC = simple_stat.c +OBJ = $(SRC:.c=.o) + +all: $(OBJ) + $(CC) -o simple_stat $(OBJ) + +$(OBJ): $(SRC) + +.PHONY: clean + +clean : + $(RM) $(OBJ) simple_stat diff --git a/myfind/simple_stat/simple_stat.c b/myfind/simple_stat/simple_stat.c new file mode 100644 index 0000000..27065b6 --- /dev/null +++ b/myfind/simple_stat/simple_stat.c @@ -0,0 +1,31 @@ +#include +#include + +int main(int argc, char **argv) +{ + if (argc == 1) + return 0; + + struct stat s; + + for (int i = 1; i < argc; i++) + { + if (stat(argv[i], &s) != 0) + return 1; + printf("st_dev=%ld\n", s.st_dev); + printf("st_ino=%ld\n", s.st_ino); + printf("st_mode=%07o\n", s.st_mode); + printf("st_nlink=%ld\n", s.st_nlink); + printf("st_uid=%d\n", s.st_uid); + printf("st_gid=%d\n", s.st_gid); + printf("st_rdev=%ld\n", s.st_rdev); + printf("st_size=%ld\n", s.st_size); + printf("st_atime=%ld\n", s.st_atime); + printf("st_mtime=%ld\n", s.st_mtime); + printf("st_ctime=%ld\n", s.st_ctime); + printf("st_blksize=%ld\n", s.st_blksize); + printf("st_blocks=%ld\n", s.st_blocks); + } + + return 0; +} diff --git a/myfind/user_belong/Makefile b/myfind/user_belong/Makefile new file mode 100644 index 0000000..ca37a55 --- /dev/null +++ b/myfind/user_belong/Makefile @@ -0,0 +1,17 @@ +CC = gcc CFLAGS = -std = c99 - pedantic - Werror - Wall - Wextra + - Wvla + + SRC = user_belong.c OBJ = $(SRC + :.c =.o) + + all + : $(OBJ) $(CC) + - o user_belong $(OBJ) + + $(OBJ) + : $(SRC) + + .PHONY + : clean + + clean : $(RM) $(OBJ) user_belong diff --git a/myfind/user_belong/user_belong.c b/myfind/user_belong/user_belong.c new file mode 100644 index 0000000..bb7332a --- /dev/null +++ b/myfind/user_belong/user_belong.c @@ -0,0 +1,25 @@ +#include +#include +#include + +int main(int argc, char **argv) +{ + if (argc != 3) + return 2; + + struct passwd *p = getpwnam(argv[2]); + if (!p) + { + printf("user %s does not exist\n", argv[2]); + return 0; + } + struct stat s; + stat(argv[1], &s); + + printf("%s does ", argv[1]); + if (s.st_uid != p->pw_uid) + printf("not "); + printf("belong to user %s\n", argv[2]); + + return 0; +} -- cgit v1.2.3