#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; }