libropkg: implement package manifest parsing

This commit is contained in:
2025-08-06 22:07:47 +01:00
parent f35ade11d6
commit 6275836e40
5 changed files with 588 additions and 2 deletions

View File

@@ -0,0 +1,87 @@
#ifndef ROPKG_MANIFEST_H_
#define ROPKG_MANIFEST_H_
#include <blue/core/error.h>
#include <ropkg/misc.h>
#include <ropkg/status.h>
#include <stdio.h>
enum ropkg_manifest_value_type {
ROPKG_MANIFEST_VALUE_T_NONE = 0,
ROPKG_MANIFEST_VALUE_T_STRING,
ROPKG_MANIFEST_VALUE_T_INT,
};
enum ropkg_manifest_value_id {
ROPKG_MANIFEST_VALUE_N_NONE = 0,
ROPKG_MANIFEST_VALUE_N_NAME,
ROPKG_MANIFEST_VALUE_N_ARCHITECTURE,
ROPKG_MANIFEST_VALUE_N_VERSION,
ROPKG_MANIFEST_VALUE_N_PRIORITY,
ROPKG_MANIFEST_VALUE_N_CATEGORY,
ROPKG_MANIFEST_VALUE_N_MAINTAINER,
ROPKG_MANIFEST_VALUE_N_PROVIDES,
ROPKG_MANIFEST_VALUE_N_DEPENDS,
ROPKG_MANIFEST_VALUE_N_RECOMMENDS,
ROPKG_MANIFEST_VALUE_N_SUGGESTS,
ROPKG_MANIFEST_VALUE_N_CONFLICTS,
ROPKG_MANIFEST_VALUE_N_ENHANCES,
ROPKG_MANIFEST_VALUE_N_INSTALLED_SIZE,
ROPKG_MANIFEST_VALUE_N_DESCRIPTION,
};
struct ropkg_manifest;
struct ropkg_manifest_value;
ROPKG_API enum ropkg_status ropkg_manifest_create(struct ropkg_manifest **out);
ROPKG_API void ropkg_manifest_destroy(struct ropkg_manifest *manifest);
ROPKG_API b_result ropkg_manifest_parse(FILE *fp, struct ropkg_manifest *dest);
ROPKG_API enum ropkg_status ropkg_manifest_get_by_name(
struct ropkg_manifest *manifest,
const char *name,
struct ropkg_manifest_value **out);
ROPKG_API enum ropkg_status ropkg_manifest_get_by_id(
struct ropkg_manifest *manifest,
enum ropkg_manifest_value_id id,
struct ropkg_manifest_value **out);
ROPKG_API enum ropkg_status ropkg_manifest_put(
struct ropkg_manifest *manifest,
struct ropkg_manifest_value *value);
ROPKG_API const struct ropkg_manifest_value *ropkg_manifest_get_first_value(
const struct ropkg_manifest *manifest);
ROPKG_API const struct ropkg_manifest_value *ropkg_manifest_get_next_value(
const struct ropkg_manifest *manifest,
const struct ropkg_manifest_value *value);
ROPKG_API enum ropkg_status ropkg_manifest_value_create_int_with_name(
size_t value,
const char *name,
struct ropkg_manifest_value **out);
ROPKG_API enum ropkg_status ropkg_manifest_value_create_int_with_id(
size_t value,
enum ropkg_manifest_value_id id,
struct ropkg_manifest_value **out);
ROPKG_API enum ropkg_status ropkg_manifest_value_create_string_with_name(
const char *value,
const char *name,
struct ropkg_manifest_value **out);
ROPKG_API enum ropkg_status ropkg_manifest_value_create_string_with_id(
const char *value,
enum ropkg_manifest_value_id id,
struct ropkg_manifest_value **out);
ROPKG_API void ropkg_manifest_value_destroy(struct ropkg_manifest_value *value);
ROPKG_API enum ropkg_manifest_value_type ropkg_manifest_value_get_type(
const struct ropkg_manifest_value *value);
ROPKG_API const char *ropkg_manifest_value_get_name(
const struct ropkg_manifest_value *value);
ROPKG_API enum ropkg_manifest_value_id ropkg_manifest_value_get_id(
const struct ropkg_manifest_value *value);
ROPKG_API const char *ropkg_manifest_value_get_string(
const struct ropkg_manifest_value *value);
ROPKG_API size_t
ropkg_manifest_value_get_int(const struct ropkg_manifest_value *value);
#endif

View File

@@ -4,11 +4,12 @@
#include <blue/core/error.h> #include <blue/core/error.h>
#include <ropkg/misc.h> #include <ropkg/misc.h>
#define ROPKG_ERROR_VENDOR (ropkg_error_vendor()) #define ROPKG_ERROR_VENDOR (ropkg_error_vendor())
#define ROPKG_RESULT_SUCCESS B_RESULT_SUCCESS
#define ROPKG_RESULT_ERR(code) \ #define ROPKG_RESULT_ERR(code) \
b_error_with_code(ROPKG_ERROR_VENDOR, ROPKG_ERR_##code) b_error_with_code(ROPKG_ERROR_VENDOR, ROPKG_ERR_##code)
#define ROPKG_RESULT_SUCCESS B_RESULT_SUCCESS #define ROPKG_RESULT_STATUS(code) b_error_with_code(ROPKG_ERROR_VENDOR, code)
enum ropkg_status { enum ropkg_status {
ROPKG_SUCCESS = 0, ROPKG_SUCCESS = 0,
@@ -18,11 +19,17 @@ enum ropkg_status {
ROPKG_ERR_BAD_STATE, ROPKG_ERR_BAD_STATE,
ROPKG_ERR_INTERNAL_FAILURE, ROPKG_ERR_INTERNAL_FAILURE,
ROPKG_ERR_IO_FAILURE, ROPKG_ERR_IO_FAILURE,
ROPKG_ERR_NO_ENTRY,
ROPKG_ERR_NO_DATA,
ROPKG_ERR_NAME_EXISTS,
ROPKG_ERR_DIR_OPEN_FAILED, ROPKG_ERR_DIR_OPEN_FAILED,
ROPKG_ERR_INSTANCE_DIR_CREATE_FAILED, ROPKG_ERR_INSTANCE_DIR_CREATE_FAILED,
ROPKG_ERR_FILE_OPEN_FAILED, ROPKG_ERR_FILE_OPEN_FAILED,
ROPKG_ERR_INVALID_MANIFEST_FORMAT,
ROPKG_ERR_INVALID_VERSION_FORMAT, ROPKG_ERR_INVALID_VERSION_FORMAT,
ROPKG_ERR_INVALID_PKG_EXPR,
ROPKG_ERR_INVALID_PKG_FILE,
}; };
enum ropkg_error_msg { enum ropkg_error_msg {

450
libropkg/manifest.c Normal file
View File

@@ -0,0 +1,450 @@
#include "manifest.h"
#include <blue/object/string.h>
#include <ropkg/manifest.h>
#include <stdlib.h>
#include <string.h>
static const char *value_id_names[] = {
[ROPKG_MANIFEST_VALUE_N_NAME] = "Name",
[ROPKG_MANIFEST_VALUE_N_ARCHITECTURE] = "Architecture",
[ROPKG_MANIFEST_VALUE_N_VERSION] = "Version",
[ROPKG_MANIFEST_VALUE_N_PRIORITY] = "Priority",
[ROPKG_MANIFEST_VALUE_N_CATEGORY] = "Category",
[ROPKG_MANIFEST_VALUE_N_MAINTAINER] = "Maintainer",
[ROPKG_MANIFEST_VALUE_N_PROVIDES] = "Provides",
[ROPKG_MANIFEST_VALUE_N_DEPENDS] = "Depends",
[ROPKG_MANIFEST_VALUE_N_RECOMMENDS] = "Recommends",
[ROPKG_MANIFEST_VALUE_N_SUGGESTS] = "Suggests",
[ROPKG_MANIFEST_VALUE_N_CONFLICTS] = "Conflicts",
[ROPKG_MANIFEST_VALUE_N_ENHANCES] = "Enhances",
[ROPKG_MANIFEST_VALUE_N_INSTALLED_SIZE] = "Installed-Size",
[ROPKG_MANIFEST_VALUE_N_DESCRIPTION] = "Description",
};
static const size_t nr_value_id_names
= sizeof value_id_names / sizeof value_id_names[0];
static enum ropkg_manifest_value_id value_id_from_string(const char *s)
{
for (size_t i = 0; i < nr_value_id_names; i++) {
const char *name = value_id_names[i];
if (name && !strcmp(name, s)) {
return i;
}
}
return ROPKG_MANIFEST_VALUE_N_NONE;
}
enum ropkg_status ropkg_manifest_create(struct ropkg_manifest **out)
{
struct ropkg_manifest *manifest = malloc(sizeof *manifest);
if (!manifest) {
return ROPKG_ERR_NO_MEMORY;
}
memset(manifest, 0x0, sizeof *manifest);
*out = manifest;
return ROPKG_SUCCESS;
}
void ropkg_manifest_destroy(struct ropkg_manifest *manifest)
{
b_queue_entry *entry = b_queue_pop_back(&manifest->m_values);
while (entry) {
struct ropkg_manifest_value *value
= b_unbox(struct ropkg_manifest_value, entry, v_entry);
ropkg_manifest_value_destroy(value);
entry = b_queue_pop_back(&manifest->m_values);
}
free(manifest);
}
static b_result create_manifest_value(
b_string *name,
b_string *value,
struct ropkg_manifest_value **out)
{
const char *name_cstr = b_string_ptr(name);
enum ropkg_manifest_value_id id = value_id_from_string(name_cstr);
const char *value_cstr = b_string_ptr(value);
char *ep;
size_t v = strtoull(value_cstr, &ep, 10);
struct ropkg_manifest_value *result = NULL;
enum ropkg_status status = ROPKG_SUCCESS;
if (*ep == 0) {
if (id != ROPKG_MANIFEST_VALUE_N_NONE) {
status = ropkg_manifest_value_create_int_with_id(
v,
id,
&result);
} else {
status = ropkg_manifest_value_create_int_with_name(
v,
name_cstr,
&result);
}
} else {
if (id != ROPKG_MANIFEST_VALUE_N_NONE) {
status = ropkg_manifest_value_create_string_with_id(
value_cstr,
id,
&result);
} else {
status = ropkg_manifest_value_create_string_with_name(
value_cstr,
name_cstr,
&result);
}
}
if (status != ROPKG_SUCCESS) {
return b_error_with_code(ROPKG_ERROR_VENDOR, status);
}
*out = result;
return B_RESULT_SUCCESS;
}
b_result ropkg_manifest_parse(FILE *fp, struct ropkg_manifest *dest)
{
enum parser_state {
PARSER_STATE_NAME,
PARSER_STATE_VALUE,
} parser_state = PARSER_STATE_NAME;
enum ropkg_status status = ROPKG_SUCCESS;
b_result result = B_RESULT_SUCCESS;
b_string *name = b_string_create();
b_string *value_string = b_string_create();
struct ropkg_manifest_value *value = NULL;
char s[2] = {0};
bool done = false;
while (!done) {
char c = fgetc(fp);
switch (c) {
case ':':
if (parser_state == PARSER_STATE_NAME) {
parser_state = PARSER_STATE_VALUE;
continue;
}
s[0] = c;
b_string_append_cstr(value_string, s);
continue;
case '\n':
if (parser_state == PARSER_STATE_NAME) {
done = true;
break;
}
result = create_manifest_value(
name,
value_string,
&value);
if (b_result_is_error(result)) {
done = true;
break;
}
status = ropkg_manifest_put(dest, value);
if (status != ROPKG_SUCCESS) {
result = b_error_with_code(
ROPKG_ERROR_VENDOR,
status);
done = true;
break;
}
b_string_clear(name);
b_string_clear(value_string);
parser_state = PARSER_STATE_NAME;
break;
case ' ':
case '\t':
if (parser_state == PARSER_STATE_NAME
&& b_string_get_size(name, 0) == 0) {
break;
}
if (parser_state == PARSER_STATE_VALUE
&& b_string_get_size(value_string, 0) == 0) {
break;
}
default:
s[0] = c;
if (parser_state == PARSER_STATE_NAME) {
b_string_append_cstr(name, s);
} else {
b_string_append_cstr(value_string, s);
}
break;
}
}
b_string_release(name);
b_string_release(value_string);
return result;
}
enum ropkg_status ropkg_manifest_get_by_name(
struct ropkg_manifest *manifest,
const char *name,
struct ropkg_manifest_value **out)
{
b_queue_iterator it;
b_queue_foreach(&it, &manifest->m_values)
{
struct ropkg_manifest_value *value = b_unbox(
struct ropkg_manifest_value,
it.entry,
v_entry);
if (value->v_name && !strcmp(value->v_name, name)) {
*out = value;
return ROPKG_SUCCESS;
}
}
return ROPKG_ERR_NO_ENTRY;
}
enum ropkg_status ropkg_manifest_get_by_id(
struct ropkg_manifest *manifest,
enum ropkg_manifest_value_id id,
struct ropkg_manifest_value **out)
{
b_queue_iterator it;
b_queue_foreach(&it, &manifest->m_values)
{
struct ropkg_manifest_value *value = b_unbox(
struct ropkg_manifest_value,
it.entry,
v_entry);
if (value->v_id != ROPKG_MANIFEST_VALUE_N_NONE
&& value->v_id == id) {
*out = value;
return ROPKG_SUCCESS;
}
}
return ROPKG_ERR_NO_ENTRY;
}
enum ropkg_status ropkg_manifest_put(
struct ropkg_manifest *manifest,
struct ropkg_manifest_value *value)
{
enum ropkg_status status = ROPKG_ERR_NO_ENTRY;
struct ropkg_manifest_value *tmp;
if (value->v_name) {
status = ropkg_manifest_get_by_name(
manifest,
value->v_name,
&tmp);
} else if (value->v_id != ROPKG_MANIFEST_VALUE_N_NONE) {
status = ropkg_manifest_get_by_id(manifest, value->v_id, &tmp);
} else {
return ROPKG_ERR_INVALID_ARGUMENT;
}
if (status == ROPKG_SUCCESS) {
return ROPKG_ERR_NAME_EXISTS;
}
b_queue_push_back(&manifest->m_values, &value->v_entry);
return ROPKG_SUCCESS;
}
const struct ropkg_manifest_value *ropkg_manifest_get_first_value(
const struct ropkg_manifest *manifest)
{
b_queue_entry *entry = b_queue_first(&manifest->m_values);
if (!entry) {
return NULL;
}
return b_unbox(struct ropkg_manifest_value, entry, v_entry);
}
const struct ropkg_manifest_value *ropkg_manifest_get_next_value(
const struct ropkg_manifest *manifest,
const struct ropkg_manifest_value *value)
{
b_queue_entry *entry = b_queue_next(&value->v_entry);
if (!entry) {
return NULL;
}
return b_unbox(struct ropkg_manifest_value, entry, v_entry);
}
enum ropkg_status ropkg_manifest_value_create_int_with_name(
size_t value,
const char *name,
struct ropkg_manifest_value **out)
{
struct ropkg_manifest_value *vp = malloc(sizeof *vp);
if (!vp) {
return ROPKG_ERR_NO_MEMORY;
}
memset(vp, 0x0, sizeof *vp);
vp->v_type = ROPKG_MANIFEST_VALUE_T_INT;
vp->v_name = b_strdup(name);
if (!vp->v_name) {
free(vp);
return ROPKG_ERR_NO_MEMORY;
}
vp->v_value.i = value;
*out = vp;
return ROPKG_SUCCESS;
}
enum ropkg_status ropkg_manifest_value_create_int_with_id(
size_t value,
enum ropkg_manifest_value_id id,
struct ropkg_manifest_value **out)
{
struct ropkg_manifest_value *vp = malloc(sizeof *vp);
if (!vp) {
return ROPKG_ERR_NO_MEMORY;
}
memset(vp, 0x0, sizeof *vp);
vp->v_type = ROPKG_MANIFEST_VALUE_T_INT;
vp->v_id = id;
vp->v_value.i = value;
*out = vp;
return ROPKG_SUCCESS;
}
enum ropkg_status ropkg_manifest_value_create_string_with_name(
const char *value,
const char *name,
struct ropkg_manifest_value **out)
{
struct ropkg_manifest_value *vp = malloc(sizeof *vp);
if (!vp) {
return ROPKG_ERR_NO_MEMORY;
}
memset(vp, 0x0, sizeof *vp);
vp->v_type = ROPKG_MANIFEST_VALUE_T_STRING;
vp->v_name = b_strdup(vp->v_name);
if (!vp->v_name) {
free(vp);
return ROPKG_ERR_NO_MEMORY;
}
vp->v_value.s = b_strdup(value);
if (!vp->v_value.s) {
free(vp);
return ROPKG_ERR_NO_MEMORY;
}
*out = vp;
return ROPKG_SUCCESS;
}
enum ropkg_status ropkg_manifest_value_create_string_with_id(
const char *value,
enum ropkg_manifest_value_id id,
struct ropkg_manifest_value **out)
{
struct ropkg_manifest_value *vp = malloc(sizeof *vp);
if (!vp) {
return ROPKG_ERR_NO_MEMORY;
}
memset(vp, 0x0, sizeof *vp);
vp->v_type = ROPKG_MANIFEST_VALUE_T_STRING;
vp->v_id = id;
vp->v_value.s = b_strdup(value);
if (!vp->v_value.s) {
free(vp);
return ROPKG_ERR_NO_MEMORY;
}
*out = vp;
return ROPKG_SUCCESS;
}
void ropkg_manifest_value_destroy(struct ropkg_manifest_value *value)
{
if (value->v_type == ROPKG_MANIFEST_VALUE_T_STRING
&& value->v_value.s) {
free(value->v_value.s);
}
if (value->v_name) {
free(value->v_name);
}
free(value);
}
enum ropkg_manifest_value_type ropkg_manifest_value_get_type(
const struct ropkg_manifest_value *value)
{
return value->v_type;
}
const char *ropkg_manifest_value_get_name(
const struct ropkg_manifest_value *value)
{
if (value->v_name) {
return value->v_name;
}
if (value->v_id == ROPKG_MANIFEST_VALUE_N_NONE) {
return NULL;
}
return value_id_names[value->v_id];
}
enum ropkg_manifest_value_id ropkg_manifest_value_get_id(
const struct ropkg_manifest_value *value)
{
return value->v_id;
}
const char *ropkg_manifest_value_get_string(
const struct ropkg_manifest_value *value)
{
if (value->v_type != ROPKG_MANIFEST_VALUE_T_STRING) {
return NULL;
}
return value->v_value.s;
}
size_t ropkg_manifest_value_get_int(const struct ropkg_manifest_value *value)
{
if (value->v_type != ROPKG_MANIFEST_VALUE_T_INT) {
return 0;
}
return value->v_value.i;
}

24
libropkg/manifest.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef _MANIFEST_H_
#define _MANIFEST_H_
#include <blue/core/queue.h>
#include <ropkg/manifest.h>
#include <stddef.h>
struct ropkg_manifest_value {
enum ropkg_manifest_value_type v_type;
enum ropkg_manifest_value_id v_id;
char *v_name;
b_queue_entry v_entry;
union {
size_t i;
char *s;
} v_value;
};
struct ropkg_manifest {
b_queue m_values;
};
#endif

View File

@@ -18,6 +18,14 @@ static const b_error_definition ropkg_errors[] = {
"INTERNAL_FAILURE", "INTERNAL_FAILURE",
"Internal failure"), "Internal failure"),
B_ERROR_DEFINITION(ROPKG_ERR_IO_FAILURE, "IO_FAILURE", "I/O failure"), B_ERROR_DEFINITION(ROPKG_ERR_IO_FAILURE, "IO_FAILURE", "I/O failure"),
B_ERROR_DEFINITION(
ROPKG_ERR_NO_ENTRY,
"NO_ENTRY",
"Name does not exist"),
B_ERROR_DEFINITION(
ROPKG_ERR_NAME_EXISTS,
"NAME_EXISTS",
"Name already exist"),
B_ERROR_DEFINITION_TEMPLATE( B_ERROR_DEFINITION_TEMPLATE(
ROPKG_ERR_DIR_OPEN_FAILED, ROPKG_ERR_DIR_OPEN_FAILED,
@@ -51,10 +59,20 @@ static const b_error_definition ropkg_errors[] = {
B_ERROR_TEMPLATE_PARAM_STRING, B_ERROR_TEMPLATE_PARAM_STRING,
"%s")), "%s")),
B_ERROR_DEFINITION(
ROPKG_ERR_INVALID_MANIFEST_FORMAT,
"INVALID_MANIFSET_FORMAT",
"Invalid manifest format"),
B_ERROR_DEFINITION( B_ERROR_DEFINITION(
ROPKG_ERR_INVALID_VERSION_FORMAT, ROPKG_ERR_INVALID_VERSION_FORMAT,
"INVALID_VERSION_FORMAT", "INVALID_VERSION_FORMAT",
"Invalid version format"), "Invalid version format"),
B_ERROR_DEFINITION(
ROPKG_ERR_INVALID_PKG_EXPR,
"INVALID_PKG_EXPR",
"Invalid package expression"),
}; };
static const b_error_msg ropkg_error_msg[] = { static const b_error_msg ropkg_error_msg[] = {