summaryrefslogtreecommitdiff
path: root/myfind
diff options
context:
space:
mode:
Diffstat (limited to 'myfind')
-rw-r--r--myfind/ast_evaluation/Makefile9
-rw-r--r--myfind/ast_evaluation/ast_evaluation.c29
-rw-r--r--myfind/ast_evaluation/expression.h28
-rw-r--r--myfind/ast_evaluation/main.c24
-rw-r--r--myfind/ast_evaluation/memory.c36
-rw-r--r--myfind/ast_evaluation/memory.h10
-rw-r--r--myfind/ast_evaluation/parser.c258
-rw-r--r--myfind/ast_evaluation/parser.h9
-rw-r--r--myfind/display_perm/Makefile15
-rw-r--r--myfind/display_perm/display_perm.c25
-rw-r--r--myfind/group_belong/Makefile17
-rw-r--r--myfind/group_belong/group_belong.c25
-rw-r--r--myfind/is_newer/Makefile15
-rw-r--r--myfind/is_newer/is_newer.c29
-rw-r--r--myfind/myexecvp/Makefile17
-rw-r--r--myfind/myexecvp/myexecvp.c26
-rw-r--r--myfind/myfind/Makefile22
-rw-r--r--myfind/myfind/src/ast_evaluation.c37
-rw-r--r--myfind/myfind/src/lexer.c205
-rw-r--r--myfind/myfind/src/lexer.h44
-rw-r--r--myfind/myfind/src/myfind.c63
-rw-r--r--myfind/myfind/src/myfind.h9
-rw-r--r--myfind/myfind/src/print.c75
-rw-r--r--myfind/myfind/src/shunting_yard.c177
-rw-r--r--myfind/myfind/src/shunting_yard.h10
-rw-r--r--myfind/myfind/src/stack/stack.c30
-rw-r--r--myfind/myfind/src/stack/stack.h15
-rw-r--r--myfind/myfind/src/ugtests.c82
-rw-r--r--myfind/myfind/src/ugtests.h10
-rwxr-xr-xmyfind/myfind/tests/fun/tests.sh196
-rw-r--r--myfind/myfind/tests/unit/eval.c32
-rw-r--r--myfind/myfind/tests/unit/lexing.c45
-rw-r--r--myfind/myfind/tests/unit/shunting.c30
-rw-r--r--myfind/simple_ls/Makefile15
-rw-r--r--myfind/simple_ls/simple_ls.c41
-rw-r--r--myfind/simple_stat/Makefile15
-rw-r--r--myfind/simple_stat/simple_stat.c31
-rw-r--r--myfind/user_belong/Makefile17
-rw-r--r--myfind/user_belong/user_belong.c25
39 files changed, 1798 insertions, 0 deletions
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 <err.h>
+#include <stdio.h>
+
+#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 <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#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> ([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 <err.h>
+#include <stdlib.h>
+
+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 <stddef.h>
+
+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 <assert.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 <stdio.h>
+#include <sys/stat.h>
+
+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 <grp.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+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 <stdio.h>
+#include <sys/stat.h>
+
+#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 <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+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 <stdio.h>
+
+#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 <fnmatch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#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 <stddef.h>
+
+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 <stdlib.h>
+
+#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 <dirent.h>
+#include <fnmatch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#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 <stdio.h>
+#include <stdlib.h>
+
+#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 <stdlib.h>
+
+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 <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+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 <criterion/criterion.h>
+#include <criterion/assert.h>
+
+#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 <criterion/criterion.h>
+#include <criterion/assert.h>
+
+#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 <criterion/criterion.h>
+#include <criterion/assert.h>
+
+#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 <dirent.h>
+#include <stdio.h>
+
+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 <stdio.h>
+#include <sys/stat.h>
+
+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 <pwd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+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;
+}