640 lines
12 KiB
C
640 lines
12 KiB
C
#include "pkg-expr.h"
|
|
|
|
#include <blue/core/error.h>
|
|
#include <blue/object/string.h>
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
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;
|
|
}
|