diff --git a/libropkg/include/ropkg/pkg-expr.h b/libropkg/include/ropkg/pkg-expr.h new file mode 100644 index 0000000..5c3d391 --- /dev/null +++ b/libropkg/include/ropkg/pkg-expr.h @@ -0,0 +1,51 @@ +#ifndef ROPKG_PKG_EXPR_H_ +#define ROPKG_PKG_EXPR_H_ + +#include +#include + +enum ropkg_pkg_expr_type { + ROPKG_PKG_EXPR_NODE_PKG, + ROPKG_PKG_EXPR_NODE_AND, + ROPKG_PKG_EXPR_NODE_OR, + ROPKG_PKG_EXPR_NODE_GROUP, +}; + +enum ropkg_comparison_op; + +struct ropkg_version; + +struct ropkg_pkg_expr; +struct ropkg_pkg_expr_pkg; +struct ropkg_pkg_expr_and; +struct ropkg_pkg_expr_or; + +ROPKG_API void ropkg_pkg_expr_destroy(struct ropkg_pkg_expr *expr); + +ROPKG_API b_result +ropkg_pkg_expr_parse(const char *s, struct ropkg_pkg_expr **out); + +ROPKG_API enum ropkg_pkg_expr_type ropkg_pkg_expr_get_type( + const struct ropkg_pkg_expr *expr); + +ROPKG_API struct ropkg_pkg_expr_pkg *ropkg_pkg_expr_pkg_create(void); +ROPKG_API const char *ropkg_pkg_expr_pkg_get_name( + const struct ropkg_pkg_expr_pkg *expr); +ROPKG_API enum ropkg_comparison_op ropkg_pkg_expr_pkg_get_version_predicate( + const struct ropkg_pkg_expr_pkg *expr); +ROPKG_API const struct ropkg_version *ropkg_pkg_expr_pkg_get_version( + const struct ropkg_pkg_expr_pkg *expr); + +ROPKG_API struct ropkg_pkg_expr_and *ropkg_pkg_expr_and_create(void); +ROPKG_API const struct ropkg_pkg_expr *ropkg_pkg_expr_and_get_left_node( + const struct ropkg_pkg_expr_and *expr); +ROPKG_API const struct ropkg_pkg_expr *ropkg_pkg_expr_and_get_right_node( + const struct ropkg_pkg_expr_and *expr); + +ROPKG_API struct ropkg_pkg_expr_or *ropkg_pkg_expr_or_create(void); +ROPKG_API const struct ropkg_pkg_expr *ropkg_pkg_expr_or_get_left_node( + const struct ropkg_pkg_expr_or *expr); +ROPKG_API const struct ropkg_pkg_expr *ropkg_pkg_expr_or_get_right_node( + const struct ropkg_pkg_expr_or *expr); + +#endif diff --git a/libropkg/pkg-expr.c b/libropkg/pkg-expr.c new file mode 100644 index 0000000..ec1cd95 --- /dev/null +++ b/libropkg/pkg-expr.c @@ -0,0 +1,639 @@ +#include "pkg-expr.h" + +#include +#include +#include +#include +#include + +enum parse_state { + PARSE_NONE = 0, + PARSE_PKG_NAME, + PARSE_PKG_MID, + PARSE_PKG_VERSION, + PARSE_PKG_VERSION_PREDICATE, + PARSE_PKG_VERSION_MID, + PARSE_PKG_VERSION_ID, + PARSE_PKG_END, + + PARSE_AND_SEP, + PARSE_OR_SEP, +}; + +struct parse_ctx { + const char *ctx_str; + b_string *ctx_temp; + enum parse_state ctx_state; + size_t ctx_i; + b_queue ctx_stack, ctx_queue; +}; + +static struct ropkg_pkg_expr *parse_ctx_peek_stack(struct parse_ctx *ctx) +{ + b_queue_entry *entry = b_queue_last(&ctx->ctx_stack); + if (!entry) { + return NULL; + } + + return b_unbox(struct ropkg_pkg_expr, entry, expr_entry); +} + +static struct ropkg_pkg_expr *parse_ctx_pop(struct parse_ctx *ctx) +{ + b_queue_entry *entry = b_queue_pop_back(&ctx->ctx_stack); + if (!entry) { + return NULL; + } + + return b_unbox(struct ropkg_pkg_expr, entry, expr_entry); +} + +static void parse_ctx_push(struct parse_ctx *ctx, struct ropkg_pkg_expr *expr) +{ + b_queue_push_back(&ctx->ctx_stack, &expr->expr_entry); +} + +static struct ropkg_pkg_expr *parse_ctx_dequeue(struct parse_ctx *ctx) +{ + b_queue_entry *entry = b_queue_pop_front(&ctx->ctx_queue); + if (!entry) { + return NULL; + } + + return b_unbox(struct ropkg_pkg_expr, entry, expr_entry); +} + +static void parse_ctx_enqueue( + struct parse_ctx *ctx, + struct ropkg_pkg_expr *expr) +{ + b_queue_push_back(&ctx->ctx_queue, &expr->expr_entry); +} + +static char parse_ctx_peek(struct parse_ctx *ctx) +{ + return ctx->ctx_str[ctx->ctx_i]; +} + +static char parse_ctx_advance(struct parse_ctx *ctx) +{ + if (!ctx->ctx_str[ctx->ctx_i]) { + return 0; + } + + ctx->ctx_i++; + + while (1) { + char c = parse_ctx_peek(ctx); + if (isspace(c)) { + ctx->ctx_i++; + continue; + } + + break; + } + + return ctx->ctx_str[ctx->ctx_i]; +} + +static void parse_ctx_cleanup(struct parse_ctx *ctx) +{ + if (ctx->ctx_temp) { + b_string_release(ctx->ctx_temp); + } + + struct ropkg_pkg_expr *expr = parse_ctx_dequeue(ctx); + while (expr) { + ropkg_pkg_expr_destroy(expr); + expr = parse_ctx_dequeue(ctx); + } + + expr = parse_ctx_pop(ctx); + while (expr) { + ropkg_pkg_expr_destroy(expr); + expr = parse_ctx_pop(ctx); + } +} + +enum ropkg_status ropkg_pkg_expr_create(struct ropkg_pkg_expr **out) +{ + return ROPKG_SUCCESS; +} + +void ropkg_pkg_expr_destroy(struct ropkg_pkg_expr *expr) +{ + b_queue q = B_QUEUE_INIT; + b_queue_push_back(&q, &expr->expr_entry); + + while (1) { + b_queue_entry *entry = b_queue_pop_front(&q); + if (!entry) { + break; + } + + expr = b_unbox(struct ropkg_pkg_expr, entry, expr_entry); + + switch (expr->expr_type) { + case ROPKG_PKG_EXPR_NODE_PKG: { + struct ropkg_pkg_expr_pkg *pkg_node + = (struct ropkg_pkg_expr_pkg *)expr; + if (pkg_node->pkg_name) { + free(pkg_node->pkg_name); + } + + if (pkg_node->pkg_version) { + ropkg_version_destroy(pkg_node->pkg_version); + } + + break; + case ROPKG_PKG_EXPR_NODE_AND: { + struct ropkg_pkg_expr_and *and_node + = (struct ropkg_pkg_expr_and *)expr; + if (and_node->and_left) { + b_queue_push_back( + &q, + &and_node->and_left->expr_entry); + } + + if (and_node->and_right) { + b_queue_push_back( + &q, + &and_node->and_right->expr_entry); + } + break; + } + case ROPKG_PKG_EXPR_NODE_OR: { + struct ropkg_pkg_expr_or *or_node + = (struct ropkg_pkg_expr_or *)expr; + if (or_node->or_left) { + b_queue_push_back( + &q, + &or_node->or_left->expr_entry); + } + + if (or_node->or_right) { + b_queue_push_back( + &q, + &or_node->or_right->expr_entry); + } + break; + } + default: + break; + } + } + + free(expr); + } +} + +static b_result parse_pkg_name( + struct parse_ctx *ctx, + struct ropkg_pkg_expr_pkg *pkg) +{ + b_string_clear(ctx->ctx_temp); + + while (1) { + char c = parse_ctx_peek(ctx); + if (!isalnum(c) && c != '+' && c != '-' && c != '_') { + break; + } + + char s[] = {c, 0}; + b_string_append_cstr(ctx->ctx_temp, s); + parse_ctx_advance(ctx); + } + + pkg->pkg_name = b_string_steal(ctx->ctx_temp); + return B_RESULT_SUCCESS; +} + +static b_result parse_pkg_version_predicate( + struct parse_ctx *ctx, + struct ropkg_pkg_expr_pkg *pkg) +{ + b_string_clear(ctx->ctx_temp); + + while (1) { + char c = parse_ctx_peek(ctx); + if (!ispunct(c)) { + break; + } + + char s[] = {c, 0}; + b_string_append_cstr(ctx->ctx_temp, s); + parse_ctx_advance(ctx); + } + + const char *pred = b_string_ptr(ctx->ctx_temp); + if (!strcmp(pred, ">=")) { + pkg->pkg_version_predicate = ROPKG_OP_GREATER_EQUAL; + } else if (!strcmp(pred, ">")) { + pkg->pkg_version_predicate = ROPKG_OP_GREATER_THAN; + } else if (!strcmp(pred, "<=")) { + pkg->pkg_version_predicate = ROPKG_OP_LESS_EQUAL; + } else if (!strcmp(pred, "<")) { + pkg->pkg_version_predicate = ROPKG_OP_LESS_THAN; + } else if (!strcmp(pred, "==") || !strcmp(pred, "=")) { + pkg->pkg_version_predicate = ROPKG_OP_EQUAL; + } else { + return b_error_with_code( + ROPKG_ERROR_VENDOR, + ROPKG_ERR_INVALID_PKG_EXPR); + } + + return B_RESULT_SUCCESS; +} + +static b_result parse_pkg_version_id( + struct parse_ctx *ctx, + struct ropkg_pkg_expr_pkg *pkg) +{ + b_string_clear(ctx->ctx_temp); + while (1) { + char c = parse_ctx_peek(ctx); + if (!isalnum(c) && c != '.' && c != '-' && c != '~') { + break; + } + + char s[] = {c, 0}; + b_string_append_cstr(ctx->ctx_temp, s); + parse_ctx_advance(ctx); + } + + struct ropkg_version *version = NULL; + enum ropkg_status status = ropkg_version_create(&version); + if (status != ROPKG_SUCCESS) { + return b_error_with_code(ROPKG_ERROR_VENDOR, status); + } + + b_result result + = ropkg_version_parse(version, b_string_ptr(ctx->ctx_temp)); + if (b_result_is_error(result)) { + ropkg_version_destroy(version); + version = NULL; + } + + pkg->pkg_version = version; + return b_result_propagate(result); +} + +static b_result parse_pkg_version( + struct parse_ctx *ctx, + struct ropkg_pkg_expr_pkg *pkg) +{ + parse_ctx_advance(ctx); + + b_result result = B_RESULT_SUCCESS; + char c = parse_ctx_peek(ctx); + if (c == '>' || c == '<' || c == '=') { + result = parse_pkg_version_predicate(ctx, pkg); + } + + if (b_result_is_error(result)) { + return result; + } + + c = parse_ctx_peek(ctx); + + if (isalnum(c)) { + result = parse_pkg_version_id(ctx, pkg); + } else { + result = b_error_with_code( + ROPKG_ERROR_VENDOR, + ROPKG_ERR_INVALID_PKG_EXPR); + } + + c = parse_ctx_peek(ctx); + if (c == ')') { + parse_ctx_advance(ctx); + } else { + result = b_error_with_code( + ROPKG_ERROR_VENDOR, + ROPKG_ERR_INVALID_PKG_EXPR); + } + + return result; +} + +static b_result parse_pkg(struct parse_ctx *ctx) +{ + struct ropkg_pkg_expr_pkg *pkg = ropkg_pkg_expr_pkg_create(); + + b_result result = B_RESULT_SUCCESS; + char c = parse_ctx_peek(ctx); + if (isalpha(c)) { + result = parse_pkg_name(ctx, pkg); + } + + if (b_result_is_error(result)) { + ropkg_pkg_expr_destroy((struct ropkg_pkg_expr *)pkg); + return b_result_propagate(result); + } + + c = parse_ctx_peek(ctx); + if (c == '(') { + result = parse_pkg_version(ctx, pkg); + } + + if (b_result_is_error(result)) { + ropkg_pkg_expr_destroy((struct ropkg_pkg_expr *)pkg); + return b_result_propagate(result); + } + + parse_ctx_enqueue(ctx, &pkg->pkg_base); + + return result; +} + +static b_result parse_and(struct parse_ctx *ctx) +{ + parse_ctx_advance(ctx); + + struct ropkg_pkg_expr_and *and_node = ropkg_pkg_expr_and_create(); + + while (1) { + struct ropkg_pkg_expr *top = parse_ctx_peek_stack(ctx); + if (!top) { + break; + } + + if (top->expr_type == ROPKG_PKG_EXPR_NODE_GROUP) { + break; + } + + if (top->expr_type < and_node->and_base.expr_type) { + break; + } + + top = parse_ctx_pop(ctx); + parse_ctx_enqueue(ctx, top); + } + + parse_ctx_push(ctx, &and_node->and_base); + return B_RESULT_SUCCESS; +} + +static b_result parse_or(struct parse_ctx *ctx) +{ + parse_ctx_advance(ctx); + + struct ropkg_pkg_expr_or *or_node = ropkg_pkg_expr_or_create(); + + while (1) { + struct ropkg_pkg_expr *top = parse_ctx_peek_stack(ctx); + if (!top) { + break; + } + + if (top->expr_type < or_node->or_base.expr_type) { + break; + } + + top = parse_ctx_pop(ctx); + parse_ctx_enqueue(ctx, top); + } + + parse_ctx_push(ctx, &or_node->or_base); + return B_RESULT_SUCCESS; +} + +static b_result parse_group_start(struct parse_ctx *ctx) +{ + parse_ctx_advance(ctx); + + struct ropkg_pkg_expr *group_node = malloc(sizeof *group_node); + if (!group_node) { + return b_error_with_code( + ROPKG_ERROR_VENDOR, + ROPKG_ERR_NO_MEMORY); + } + + memset(group_node, 0x0, sizeof *group_node); + + group_node->expr_type = ROPKG_PKG_EXPR_NODE_GROUP; + + parse_ctx_push(ctx, group_node); + return B_RESULT_SUCCESS; +} + +static b_result parse_group_end(struct parse_ctx *ctx) +{ + parse_ctx_advance(ctx); + + bool done = false; + + while (!done) { + struct ropkg_pkg_expr *expr = parse_ctx_pop(ctx); + + if (!expr) { + return b_error_with_code( + ROPKG_ERROR_VENDOR, + ROPKG_ERR_INVALID_PKG_EXPR); + } + + switch (expr->expr_type) { + case ROPKG_PKG_EXPR_NODE_GROUP: + free(expr); + done = true; + break; + default: + parse_ctx_enqueue(ctx, expr); + break; + } + } + + return B_RESULT_SUCCESS; +} + +b_result ropkg_pkg_expr_parse(const char *s, struct ropkg_pkg_expr **out) +{ + while (isspace(*s)) { + s++; + } + + struct parse_ctx ctx = { + .ctx_str = s, + .ctx_temp = b_string_create(), + }; + + b_result result = B_RESULT_SUCCESS; + + while (!b_result_is_error(result)) { + char c = parse_ctx_peek(&ctx); + if (c == 0) { + break; + } + + if (c == '(') { + result = parse_group_start(&ctx); + } else if (c == ')') { + result = parse_group_end(&ctx); + } else if (c == ',') { + result = parse_and(&ctx); + } else if (c == '|') { + result = parse_or(&ctx); + } else if (isalpha(c)) { + result = parse_pkg(&ctx); + } else { + result = b_error_with_code( + ROPKG_ERROR_VENDOR, + ROPKG_ERR_INVALID_PKG_EXPR); + break; + } + } + + if (b_result_is_error(result)) { + parse_ctx_cleanup(&ctx); + return result; + } + + struct ropkg_pkg_expr *expr = parse_ctx_pop(&ctx); + while (expr) { + parse_ctx_enqueue(&ctx, expr); + expr = parse_ctx_pop(&ctx); + } + + while (1) { + expr = parse_ctx_dequeue(&ctx); + if (!expr) { + break; + } + + if (expr->expr_type == ROPKG_PKG_EXPR_NODE_PKG) { + parse_ctx_push(&ctx, expr); + continue; + } + + if (expr->expr_type == ROPKG_PKG_EXPR_NODE_GROUP) { + parse_ctx_cleanup(&ctx); + return b_error_with_code( + ROPKG_ERROR_VENDOR, + ROPKG_ERR_INVALID_PKG_EXPR); + } + + struct ropkg_pkg_expr *left = NULL, *right = NULL; + right = parse_ctx_pop(&ctx); + left = parse_ctx_pop(&ctx); + if (!left || !right) { + parse_ctx_cleanup(&ctx); + return b_error_with_code( + ROPKG_ERROR_VENDOR, + ROPKG_ERR_INVALID_PKG_EXPR); + } + + switch (expr->expr_type) { + case ROPKG_PKG_EXPR_NODE_AND: { + struct ropkg_pkg_expr_and *and_node + = (struct ropkg_pkg_expr_and *)expr; + and_node->and_left = left; + and_node->and_right = right; + break; + } + case ROPKG_PKG_EXPR_NODE_OR: { + struct ropkg_pkg_expr_or *or_node + = (struct ropkg_pkg_expr_or *)expr; + or_node->or_left = left; + or_node->or_right = right; + break; + } + default: + break; + } + + parse_ctx_push(&ctx, expr); + } + + *out = parse_ctx_pop(&ctx); + parse_ctx_cleanup(&ctx); + return result; +} + +enum ropkg_pkg_expr_type ropkg_pkg_expr_get_type( + const struct ropkg_pkg_expr *expr) +{ + return expr->expr_type; +} + +struct ropkg_pkg_expr_pkg *ropkg_pkg_expr_pkg_create(void) +{ + struct ropkg_pkg_expr_pkg *out = malloc(sizeof *out); + if (!out) { + return NULL; + } + + memset(out, 0x0, sizeof *out); + + out->pkg_base.expr_type = ROPKG_PKG_EXPR_NODE_PKG; + + return out; +} + +const char *ropkg_pkg_expr_pkg_get_name(const struct ropkg_pkg_expr_pkg *expr) +{ + return expr->pkg_name; +} + +enum ropkg_comparison_op ropkg_pkg_expr_pkg_get_version_predicate( + const struct ropkg_pkg_expr_pkg *expr) +{ + return expr->pkg_version_predicate; +} + +const struct ropkg_version *ropkg_pkg_expr_pkg_get_version( + const struct ropkg_pkg_expr_pkg *expr) +{ + return expr->pkg_version; +} + +struct ropkg_pkg_expr_and *ropkg_pkg_expr_and_create(void) +{ + struct ropkg_pkg_expr_and *out = malloc(sizeof *out); + if (!out) { + return NULL; + } + + memset(out, 0x0, sizeof *out); + + out->and_base.expr_type = ROPKG_PKG_EXPR_NODE_AND; + + return out; +} + +const struct ropkg_pkg_expr *ropkg_pkg_expr_and_get_left_node( + const struct ropkg_pkg_expr_and *expr) +{ + return expr->and_left; +} + +const struct ropkg_pkg_expr *ropkg_pkg_expr_and_get_right_node( + const struct ropkg_pkg_expr_and *expr) +{ + return expr->and_right; +} + +struct ropkg_pkg_expr_or *ropkg_pkg_expr_or_create(void) +{ + struct ropkg_pkg_expr_or *out = malloc(sizeof *out); + if (!out) { + return NULL; + } + + memset(out, 0x0, sizeof *out); + + out->or_base.expr_type = ROPKG_PKG_EXPR_NODE_OR; + + return out; +} + +const struct ropkg_pkg_expr *ropkg_pkg_expr_or_get_left_node( + const struct ropkg_pkg_expr_or *expr) +{ + return expr->or_left; +} + +const struct ropkg_pkg_expr *ropkg_pkg_expr_or_get_right_node( + const struct ropkg_pkg_expr_or *expr) +{ + return expr->or_right; +} diff --git a/libropkg/pkg-expr.h b/libropkg/pkg-expr.h new file mode 100644 index 0000000..dd206b8 --- /dev/null +++ b/libropkg/pkg-expr.h @@ -0,0 +1,29 @@ +#ifndef _PKG_EXPR_H_ +#define _PKG_EXPR_H_ + +#include +#include + +struct ropkg_pkg_expr { + enum ropkg_pkg_expr_type expr_type; + b_queue_entry expr_entry; +}; + +struct ropkg_pkg_expr_pkg { + struct ropkg_pkg_expr pkg_base; + char *pkg_name; + enum ropkg_comparison_op pkg_version_predicate; + struct ropkg_version *pkg_version; +}; + +struct ropkg_pkg_expr_and { + struct ropkg_pkg_expr and_base; + struct ropkg_pkg_expr *and_left, *and_right; +}; + +struct ropkg_pkg_expr_or { + struct ropkg_pkg_expr or_base; + struct ropkg_pkg_expr *or_left, *or_right; +}; + +#endif