diff --git a/ropkg/CMakeLists.txt b/ropkg/CMakeLists.txt index 840b56a..153cf9f 100644 --- a/ropkg/CMakeLists.txt +++ b/ropkg/CMakeLists.txt @@ -1,4 +1,4 @@ -file(GLOB sources *.c *.h) +file(GLOB_RECURSE sources *.c *.h) add_executable(ropkg ${sources}) target_link_libraries(ropkg diff --git a/ropkg/build.c b/ropkg/build.c deleted file mode 100644 index 6d21f38..0000000 --- a/ropkg/build.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "commands.h" - -#include - -enum { - OPT_OUTPATH, - OPT_OUTPATH_PATH, - - ARG_RECIPE, -}; - -static int build( - const b_command *self, - const b_arglist *opt, - const b_array *args) -{ - return 0; -} - -B_COMMAND(CMD_BUILD, CMD_ROOT) -{ - B_COMMAND_NAME("build"); - B_COMMAND_SHORT_NAME('B'); - B_COMMAND_DESC("build a Rosetta package from a recipe"); - B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); - B_COMMAND_FUNCTION(build); - - B_COMMAND_HELP_OPTION(); - - B_COMMAND_OPTION(OPT_OUTPATH) - { - B_OPTION_SHORT_NAME('o'); - B_OPTION_LONG_NAME("out"); - B_OPTION_DESC("the path to save the new package file to"); - - B_OPTION_ARG(OPT_OUTPATH_PATH) - { - B_ARG_NAME("path"); - B_ARG_NR_VALUES(1); - } - } - - B_COMMAND_ARG(ARG_RECIPE) - { - B_ARG_NAME("recipe"); - B_ARG_DESC("the recipe to build the package from."); - B_ARG_NR_VALUES(1); - } - - B_COMMAND_USAGE() - { - B_COMMAND_USAGE_OPT(OPT_OUTPATH); - B_COMMAND_USAGE_ARG(ARG_RECIPE); - } -} diff --git a/ropkg/commands.h b/ropkg/commands.h index 48750de..744e7cf 100644 --- a/ropkg/commands.h +++ b/ropkg/commands.h @@ -3,12 +3,29 @@ enum { CMD_ROOT, - CMD_CREATE, - CMD_BUILD, - CMD_QUERY, - CMD_EXTRACT, - CMD_INSTALL, - CMD_COMPARE_VERSION, + + CMD_PACKAGE, + CMD_PACKAGE_CREATE, + CMD_PACKAGE_BUILD, + CMD_PACKAGE_EXTRACT, + CMD_PACKAGE_INSTALL, + + CMD_PACKAGE_QUERY, + CMD_PACKAGE_QUERY_README, + CMD_PACKAGE_QUERY_MANIFEST, + CMD_PACKAGE_QUERY_SUMMARY, + CMD_PACKAGE_QUERY_LIST, + + CMD_VERSION, + CMD_VERSION_COMPARE, + + CMD_MANIFEST, + CMD_MANIFEST_CREATE, + CMD_MANIFEST_VALIDATE, + + CMD_NEWS, + CMD_NEWS_CREATE, + CMD_NEWS_VALIDATE, }; #endif diff --git a/ropkg/main.c b/ropkg/main.c index e4924fa..cd8c881 100644 --- a/ropkg/main.c +++ b/ropkg/main.c @@ -1,19 +1,29 @@ #include "commands.h" #include +#include +#include B_COMMAND(CMD_ROOT, B_COMMAND_INVALID_ID) { B_COMMAND_NAME("ropkg"); B_COMMAND_DESC( "Rosetta package manipulation tool. This tool is used to " - "create, build, and otherwise manipulate individual Rosetta " - "package files."); + "create, build, and otherwise manipulate individual Rosetta " + "package files."); B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); B_COMMAND_HELP_OPTION(); + + B_COMMAND_USAGE() + { + B_COMMAND_USAGE_COMMAND_PLACEHOLDER(); + } } int main(int argc, const char **argv) { + b_set_error_report_function( + b_enhanced_error_reporter, + B_ERROR_REPORT_ALL); return b_command_dispatch(CMD_ROOT, argc, argv); } diff --git a/ropkg/manifest/create.c b/ropkg/manifest/create.c new file mode 100644 index 0000000..ae88aa3 --- /dev/null +++ b/ropkg/manifest/create.c @@ -0,0 +1,106 @@ +#include "../commands.h" + +#include +#include +#include +#include + +enum { + OPT_HEADERS, + OPT_HEADERS_PATH, + OPT_README, + OPT_README_PATH, + OPT_OUTPATH, + OPT_OUTPATH_PATH, +}; + +static int create( + const b_command *self, + const b_arglist *opt, + const b_array *args) +{ + const char *headers_path = NULL, *readme_path = NULL, *out_path = NULL; + b_status status = b_arglist_get_string( + opt, + OPT_HEADERS, + OPT_HEADERS_PATH, + 0, + &headers_path); + if (!B_OK(status)) { + b_arglist_report_missing_option(opt, OPT_HEADERS); + return -1; + } + + status = b_arglist_get_string( + opt, + OPT_README, + OPT_README_PATH, + 0, + &headers_path); + if (!B_OK(status)) { + b_arglist_report_missing_option(opt, OPT_README); + return -1; + } + + b_arglist_get_string(opt, OPT_OUTPATH, OPT_OUTPATH_PATH, 0, &out_path); + + FILE *headers, *readme, *out; + + return 0; +} + +B_COMMAND(CMD_MANIFEST_CREATE, CMD_MANIFEST) +{ + B_COMMAND_NAME("create"); + B_COMMAND_SHORT_NAME('C'); + B_COMMAND_DESC("create a manifest file."); + B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); + B_COMMAND_FUNCTION(create); + + B_COMMAND_HELP_OPTION(); + + B_COMMAND_OPTION(OPT_HEADERS) + { + B_OPTION_LONG_NAME("headers"); + B_OPTION_SHORT_NAME('H'); + B_OPTION_DESC( + "The path to a json file describing the package " + "manifest headers."); + + B_OPTION_ARG(OPT_HEADERS_PATH) + { + B_ARG_NAME("path"); + B_ARG_NR_VALUES(1); + } + } + + B_COMMAND_OPTION(OPT_README) + { + B_OPTION_LONG_NAME("readme"); + B_OPTION_SHORT_NAME('R'); + B_OPTION_DESC( + "The path to a plaintext file describing the package."); + + B_OPTION_ARG(OPT_README_PATH) + { + B_ARG_NAME("path"); + B_ARG_NR_VALUES(1); + } + } + + B_COMMAND_OPTION(OPT_OUTPATH) + { + B_OPTION_LONG_NAME("out"); + B_OPTION_SHORT_NAME('o'); + B_OPTION_DESC( + "The path to write the new manifest file to. If no " + "path is specified, the manifest will be written to " + "standard out."); + + B_OPTION_ARG(OPT_OUTPATH_PATH) + { + B_ARG_NAME("path"); + B_ARG_NR_VALUES(1); + } + } +} diff --git a/ropkg/manifest/root.c b/ropkg/manifest/root.c new file mode 100644 index 0000000..0cee90e --- /dev/null +++ b/ropkg/manifest/root.c @@ -0,0 +1,19 @@ +#include "../commands.h" + +#include +#include +#include + +B_COMMAND(CMD_MANIFEST, CMD_ROOT) +{ + B_COMMAND_NAME("manifest"); + B_COMMAND_SHORT_NAME('M'); + B_COMMAND_DESC("Package manifest inspection and manipulation."); + B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); + B_COMMAND_HELP_OPTION(); + + B_COMMAND_USAGE() + { + B_COMMAND_USAGE_COMMAND_PLACEHOLDER(); + } +} diff --git a/ropkg/manifest/validate.c b/ropkg/manifest/validate.c new file mode 100644 index 0000000..f70d5e8 --- /dev/null +++ b/ropkg/manifest/validate.c @@ -0,0 +1,307 @@ +#include "../commands.h" + +#include +#include +#include +#include +#include +#include +#include + +enum { + ARG_MANIFEST +}; + +static void print_pkg_expr(const struct ropkg_pkg_expr *expr); + +static void print_pkg_expr_and(const struct ropkg_pkg_expr *expr) +{ + int count = 0; + const struct ropkg_pkg_expr_and *expr_and + = (const struct ropkg_pkg_expr_and *)expr; + const struct ropkg_pkg_expr *cur + = ropkg_pkg_expr_and_get_left_node(expr_and); + + printf("("); + print_pkg_expr(cur); + printf(")"); + + printf(" AND "); + + cur = ropkg_pkg_expr_and_get_right_node(expr_and); + printf("("); + print_pkg_expr(cur); + printf(")"); +} + +static void print_pkg_expr_or(const struct ropkg_pkg_expr *expr) +{ + int count = 0; + const struct ropkg_pkg_expr_or *expr_or + = (const struct ropkg_pkg_expr_or *)expr; + const struct ropkg_pkg_expr *cur + = ropkg_pkg_expr_or_get_left_node(expr_or); + + printf("("); + print_pkg_expr(cur); + printf(")"); + + printf(" OR "); + + cur = ropkg_pkg_expr_or_get_right_node(expr_or); + printf("("); + print_pkg_expr(cur); + printf(")"); +} + +static void print_pkg_expr_pkg(const struct ropkg_pkg_expr *expr) +{ + const struct ropkg_pkg_expr_pkg *pkg + = (const struct ropkg_pkg_expr_pkg *)expr; + printf("%s", ropkg_pkg_expr_pkg_get_name(pkg)); + + const struct ropkg_version *version + = ropkg_pkg_expr_pkg_get_version(pkg); + if (version) { + char s[128]; + ropkg_version_to_string(version, s, sizeof s); + + printf("["); + + switch (ropkg_pkg_expr_pkg_get_version_predicate(pkg)) { + case ROPKG_OP_LESS_THAN: + printf("<"); + break; + case ROPKG_OP_LESS_EQUAL: + printf("<="); + break; + case ROPKG_OP_GREATER_THAN: + printf(">"); + break; + case ROPKG_OP_GREATER_EQUAL: + printf(">="); + break; + case ROPKG_OP_EQUAL: + printf("="); + break; + default: + break; + } + + printf("%s", s); + + printf("]"); + } +} + +static void print_pkg_expr(const struct ropkg_pkg_expr *expr) +{ + enum ropkg_pkg_expr_type type = ropkg_pkg_expr_get_type(expr); + + switch (type) { + case ROPKG_PKG_EXPR_NODE_PKG: + print_pkg_expr_pkg(expr); + break; + case ROPKG_PKG_EXPR_NODE_AND: + print_pkg_expr_and(expr); + break; + case ROPKG_PKG_EXPR_NODE_OR: + print_pkg_expr_or(expr); + break; + default: + break; + } +} + +static b_result validate_pkg_expr_value( + const struct ropkg_manifest_value *value) +{ + const char *value_str = ropkg_manifest_value_get_string(value); + if (!value_str) { + return b_error_with_code( + ROPKG_ERROR_VENDOR, + ROPKG_ERR_INVALID_MANIFEST_FORMAT); + } + + struct ropkg_pkg_expr *pkg_expr = NULL; + b_result result = ropkg_pkg_expr_parse(value_str, &pkg_expr); + if (b_result_is_error(result)) { + return b_result_propagate(result); + } + + print_pkg_expr(pkg_expr); + + return B_RESULT_SUCCESS; +} + +static b_result validate_version_value(const struct ropkg_manifest_value *value) +{ + const char *value_str = ropkg_manifest_value_get_string(value); + if (!value_str) { + return b_error_with_code( + ROPKG_ERROR_VENDOR, + ROPKG_ERR_INVALID_MANIFEST_FORMAT); + } + + 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, value_str); + if (b_result_is_error(result)) { + return b_result_propagate(result); + } + + char temp[128]; + ropkg_version_to_string(version, temp, sizeof temp); + ropkg_version_destroy(version); + + printf("%s", temp); + + return B_RESULT_SUCCESS; +} + +static b_result validate_string_value(const struct ropkg_manifest_value *value) +{ + const char *value_str = ropkg_manifest_value_get_string(value); + if (!value_str) { + return b_error_with_code( + ROPKG_ERROR_VENDOR, + ROPKG_ERR_INVALID_MANIFEST_FORMAT); + } + + printf("%s", value_str); + + return B_RESULT_SUCCESS; +} + +static b_result validate_int_value(const struct ropkg_manifest_value *value) +{ + if (ropkg_manifest_value_get_type(value) + != ROPKG_MANIFEST_VALUE_T_INT) { + return b_error_with_code( + ROPKG_ERROR_VENDOR, + ROPKG_ERR_INVALID_MANIFEST_FORMAT); + } + + printf("%zu", ropkg_manifest_value_get_int(value)); + + return B_RESULT_SUCCESS; + return B_RESULT_SUCCESS; +} + +static int validate( + const b_command *self, + const b_arglist *opt, + const b_array *args) +{ + const char *manifest_path; + b_status status = b_arglist_get_string( + opt, + B_COMMAND_INVALID_ID, + ARG_MANIFEST, + 0, + &manifest_path); + if (!B_OK(status)) { + b_arglist_report_missing_args( + opt, + B_COMMAND_INVALID_ID, + ARG_MANIFEST, + 0); + return -1; + } + + FILE *fp = fopen(manifest_path, "r"); + if (!fp) { + b_throw_error_code(ROPKG_ERROR_VENDOR, ROPKG_ERR_NO_ENTRY); + return -1; + } + + struct ropkg_manifest *manifest; + enum ropkg_status status2 = ropkg_manifest_create(&manifest); + if (status2 != ROPKG_SUCCESS) { + b_throw_error_code(ROPKG_ERROR_VENDOR, status2); + return -1; + } + + b_result result = ropkg_manifest_parse(fp, manifest); + if (b_result_is_error(result)) { + b_throw(result); + return -1; + } + + const struct ropkg_manifest_value *value + = ropkg_manifest_get_first_value(manifest); + while (value && b_result_is_success(result)) { + const char *name = ropkg_manifest_value_get_name(value); + enum ropkg_manifest_value_type type + = ropkg_manifest_value_get_type(value); + enum ropkg_manifest_value_id id + = ropkg_manifest_value_get_id(value); + + if (name) { + printf("%s ", name); + } + + if (id != ROPKG_MANIFEST_VALUE_N_NONE) { + printf("[%d] ", id); + } + + printf(": "); + + switch (id) { + case ROPKG_MANIFEST_VALUE_N_DEPENDS: + case ROPKG_MANIFEST_VALUE_N_PROVIDES: + case ROPKG_MANIFEST_VALUE_N_RECOMMENDS: + case ROPKG_MANIFEST_VALUE_N_SUGGESTS: + case ROPKG_MANIFEST_VALUE_N_CONFLICTS: + case ROPKG_MANIFEST_VALUE_N_ENHANCES: + result = validate_pkg_expr_value(value); + break; + case ROPKG_MANIFEST_VALUE_N_INSTALLED_SIZE: + result = validate_int_value(value); + break; + case ROPKG_MANIFEST_VALUE_N_VERSION: + result = validate_version_value(value); + break; + default: + result = validate_string_value(value); + break; + } + + printf("\n"); + value = ropkg_manifest_get_next_value(manifest, value); + } + + if (b_result_is_error(result)) { + b_throw(result); + return -1; + } + + return 0; +} + +B_COMMAND(CMD_MANIFEST_VALIDATE, CMD_MANIFEST) +{ + B_COMMAND_NAME("validate"); + B_COMMAND_SHORT_NAME('V'); + B_COMMAND_DESC("check a manifest file for errors."); + B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); + B_COMMAND_FUNCTION(validate); + + B_COMMAND_ARG(ARG_MANIFEST) + { + B_ARG_NAME("manifest-path"); + B_ARG_DESC("the manifest file to validate"); + B_ARG_NR_VALUES(1); + } + + B_COMMAND_HELP_OPTION(); + + B_COMMAND_USAGE() + { + B_COMMAND_USAGE_ARG(ARG_MANIFEST); + } +} diff --git a/ropkg/news/root.c b/ropkg/news/root.c new file mode 100644 index 0000000..2f935e3 --- /dev/null +++ b/ropkg/news/root.c @@ -0,0 +1,19 @@ +#include "../commands.h" + +#include +#include +#include + +B_COMMAND(CMD_NEWS, CMD_ROOT) +{ + B_COMMAND_NAME("news"); + B_COMMAND_SHORT_NAME('N'); + B_COMMAND_DESC("News file inspection and manipulation."); + B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); + B_COMMAND_HELP_OPTION(); + + B_COMMAND_USAGE() + { + B_COMMAND_USAGE_COMMAND_PLACEHOLDER(); + } +} diff --git a/ropkg/package/build.c b/ropkg/package/build.c new file mode 100644 index 0000000..7fe7480 --- /dev/null +++ b/ropkg/package/build.c @@ -0,0 +1,102 @@ +#include "../commands.h" + +#include + +enum { + OPT_OUTPATH, + OPT_OUTPATH_PATH, + + OPT_SOURCE_DIRECTORY, + OPT_SOURCE_DIRECTORY_PATH, + + OPT_EXT_SOURCE_DIRECTORY, + OPT_EXT_SOURCE_DIRECTORY_NAME, + OPT_EXT_SOURCE_DIRECTORY_PATH, + + ARG_RECIPE, +}; + +static int build( + const b_command *self, + const b_arglist *opt, + const b_array *args) +{ + return 0; +} + +B_COMMAND(CMD_PACKAGE_BUILD, CMD_PACKAGE) +{ + B_COMMAND_NAME("build"); + B_COMMAND_SHORT_NAME('B'); + B_COMMAND_DESC("build a Rosetta package from a recipe"); + B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); + B_COMMAND_FUNCTION(build); + + B_COMMAND_HELP_OPTION(); + + B_COMMAND_OPTION(OPT_OUTPATH) + { + B_OPTION_SHORT_NAME('o'); + B_OPTION_LONG_NAME("out"); + B_OPTION_DESC("the path to save the new package file to"); + + B_OPTION_ARG(OPT_OUTPATH_PATH) + { + B_ARG_NAME("path"); + B_ARG_NR_VALUES(1); + } + } + + B_COMMAND_OPTION(OPT_SOURCE_DIRECTORY) + { + B_OPTION_SHORT_NAME('s'); + B_OPTION_LONG_NAME("source-dir"); + B_OPTION_DESC( + "the source directory to build the package from. if no " + "source directory is specified, the recipe will " + "download the sources automatically."); + + B_OPTION_ARG(OPT_SOURCE_DIRECTORY_PATH) + { + B_ARG_NAME("path"); + B_ARG_NR_VALUES(1); + } + } + + B_COMMAND_OPTION(OPT_EXT_SOURCE_DIRECTORY) + { + B_OPTION_SHORT_NAME('S'); + B_OPTION_LONG_NAME("named-source-dir"); + B_OPTION_DESC( + "a source directory to build the package from. if no " + "source directory is specified, the recipe will " + "download the sources automatically. use this option " + "if the recipe requires more than one separate source " + "directory."); + + B_OPTION_ARG(OPT_EXT_SOURCE_DIRECTORY_NAME) + { + B_ARG_NAME("name"); + B_ARG_NR_VALUES(1); + } + + B_OPTION_ARG(OPT_EXT_SOURCE_DIRECTORY_PATH) + { + B_ARG_NAME("path"); + B_ARG_NR_VALUES(1); + } + } + + B_COMMAND_ARG(ARG_RECIPE) + { + B_ARG_NAME("recipe"); + B_ARG_DESC("the recipe to build the package from."); + B_ARG_NR_VALUES(1); + } + + B_COMMAND_USAGE() + { + B_COMMAND_USAGE_OPT(OPT_OUTPATH); + B_COMMAND_USAGE_ARG(ARG_RECIPE); + } +} diff --git a/ropkg/create.c b/ropkg/package/create.c similarity index 72% rename from ropkg/create.c rename to ropkg/package/create.c index 3ede1a2..2388a27 100644 --- a/ropkg/create.c +++ b/ropkg/package/create.c @@ -1,11 +1,13 @@ -#include "commands.h" +#include "../commands.h" #include +#include +#include +#include #include #include #include -#include -#include +#include #include #include #include @@ -41,14 +43,17 @@ static int add_file_to_archive_ex( const char *dest) { b_file *fp; - b_status status = b_file_open( + b_result result = b_file_open( src_dir, src, B_FILE_READ_ONLY | B_FILE_BINARY, &fp); if (!fp) { - b_err("cannot open file '%s'", src); - b_i("reason: %s", b_status_to_string(status)); + b_throw_error_with_template_caused_by_error( + ROPKG_ERROR_VENDOR, + ROPKG_ERR_DIR_OPEN_FAILED, + result, + B_ERROR_PARAM("filepath", b_path_ptr(src))); return -1; } @@ -63,7 +68,7 @@ static int add_file_to_archive_ex( int c; while (1) { size_t r; - status = b_file_read(fp, offset, sizeof buf, buf, &r); + b_status status = b_file_read(fp, offset, sizeof buf, buf, &r); if (!B_OK(status)) { break; } @@ -97,7 +102,7 @@ static int add_file_to_archive( static int add_data_archive( const b_arglist *args, - struct ropkg_stream *stream, + struct b_cstream *stream, struct ropkg_writer *pkg) { const char *data_path_cstr; @@ -108,16 +113,23 @@ static int add_data_archive( 0, &data_path_cstr); if (!B_OK(bstatus)) { - b_arglist_report_missing_option(args, OPT_MANIFEST); + b_arglist_report_missing_args( + args, + B_COMMAND_INVALID_ID, + ARG_SOURCE_DIR, + 0); return -1; } b_path *data_path = b_path_create_from_cstr(data_path_cstr); b_directory *data_dir; - bstatus = b_directory_open(NULL, data_path, &data_dir); - if (!B_OK(bstatus)) { - b_err("cannot open directory '%s'", data_path_cstr); - b_i("reason: %s", b_status_to_string(bstatus)); + b_result result = b_directory_open(NULL, data_path, 0, &data_dir); + if (b_result_is_error(result)) { + b_throw_error_with_template_caused_by_error( + ROPKG_ERROR_VENDOR, + ROPKG_ERR_DIR_OPEN_FAILED, + result, + B_ERROR_PARAM("filepath", data_path_cstr)); return -1; } @@ -125,7 +137,7 @@ static int add_data_archive( struct ropkg_writer_file_info file = {0}; ropkg_writer_begin_file(pkg, ROPKG_PATH_DATA ".zst", &file); - ropkg_stream_begin_compressed_section(stream); + b_cstream_begin_compressed_section(stream, NULL); enum ropkg_status status = ropkg_writer_open(stream, &data); int ret = 0; @@ -148,7 +160,7 @@ static int add_data_archive( } ropkg_writer_close(data); - ropkg_stream_end_compressed_section(stream, NULL, NULL); + b_cstream_end_compressed_section(stream, NULL, NULL); ropkg_writer_end_file(pkg); return ret; @@ -156,7 +168,7 @@ static int add_data_archive( static int add_control_archive( const b_arglist *args, - struct ropkg_stream *stream, + b_cstream *stream, struct ropkg_writer *pkg) { int ret; @@ -165,7 +177,7 @@ static int add_control_archive( struct ropkg_writer_file_info file = {0}; ropkg_writer_begin_file(pkg, ROPKG_PATH_CONTROL ".zst", &file); - ropkg_stream_begin_compressed_section(stream); + b_cstream_begin_compressed_section(stream, NULL); enum ropkg_status status = ropkg_writer_open(stream, &control); b_arglist_option_iterator it; @@ -188,16 +200,40 @@ static int add_control_archive( ropkg_writer_close(control); - ropkg_stream_end_compressed_section(stream, NULL, NULL); + b_cstream_end_compressed_section(stream, NULL, NULL); ropkg_writer_end_file(pkg); return 0; } +static b_result validate_manifest(const char *path) +{ + FILE *fp = fopen(path, "r"); + if (!fp) { + return b_error_with_template( + ROPKG_ERROR_VENDOR, + ROPKG_ERR_FILE_OPEN_FAILED, + B_ERROR_PARAM("filepath", path)); + } + + struct ropkg_manifest *manifest; + enum ropkg_status status = ropkg_manifest_create(&manifest); + if (status != ROPKG_SUCCESS) { + return b_error_with_code(ROPKG_ERROR_VENDOR, status); + } + + b_result result = ropkg_manifest_parse(fp, manifest); + + ropkg_manifest_destroy(manifest); + fclose(fp); + + return b_result_propagate(result); +} + static int add_meta_archive( const b_arglist *args, - struct ropkg_stream *stream, + b_cstream *stream, struct ropkg_writer *pkg) { const char *manifest_path, *news_path; @@ -205,20 +241,26 @@ static int add_meta_archive( args, OPT_MANIFEST, OPT_MANIFEST_PATH, - 1, + 0, &manifest_path); if (!B_OK(bstatus)) { b_arglist_report_missing_option(args, OPT_MANIFEST); return -1; } + b_result result = validate_manifest(manifest_path); + if (b_result_is_error(result)) { + b_throw(result); + return -1; + } + int ret; struct ropkg_writer *meta; struct ropkg_writer_file_info file = {0}; ropkg_writer_begin_file(pkg, ROPKG_PATH_META ".zst", &file); - ropkg_stream_begin_compressed_section(stream); + b_cstream_begin_compressed_section(stream, NULL); enum ropkg_status status = ropkg_writer_open(stream, &meta); bstatus = b_arglist_get_string( @@ -243,7 +285,7 @@ static int add_meta_archive( ropkg_writer_close(meta); - ropkg_stream_end_compressed_section(stream, NULL, NULL); + b_cstream_end_compressed_section(stream, NULL, NULL); ropkg_writer_end_file(pkg); @@ -255,8 +297,8 @@ static int create( const b_arglist *opt, const b_array *args) { - const struct ropkg_compression_function *zstd - = ropkg_compression_function_for_type(ROPKG_COMPRESSION_ZSTD); + const b_compression_function *zstd + = b_compression_function_get_by_id(B_COMPRESSOR_FUNCTION_ZSTD); struct ropkg_writer *pkg, *subpkg; enum ropkg_status status; @@ -265,7 +307,7 @@ static int create( opt, OPT_OUTPATH, OPT_OUTPATH_PATH, - 1, + 0, &out_path); if (!B_OK(bstatus)) { b_arglist_report_missing_option(opt, OPT_OUTPATH); @@ -273,37 +315,45 @@ static int create( } FILE *fp = fopen(out_path, "wb"); - struct ropkg_stream *stream; - status = ropkg_stream_open(fp, zstd, ROPKG_STREAM_WRITE, &stream); + b_stream *fp_stream = b_stream_open_fp(fp); + b_cstream *cstream; + bstatus = b_cstream_open( + fp_stream, + zstd, + B_COMPRESSION_MODE_COMPRESS, + &cstream); - status = ropkg_writer_open(stream, &pkg); + status = ropkg_writer_open(cstream, &pkg); int ret; - ret = add_data_archive(opt, stream, pkg); + ret = add_data_archive(opt, cstream, pkg); if (ret != 0) { goto end; } - ret = add_control_archive(opt, stream, pkg); + ret = add_control_archive(opt, cstream, pkg); if (ret != 0) { goto end; } - ret = add_meta_archive(opt, stream, pkg); + ret = add_meta_archive(opt, cstream, pkg); if (ret != 0) { goto end; } end: ropkg_writer_close(pkg); - ropkg_stream_close(stream); + + b_cstream_close(cstream); + b_stream_close(fp_stream); + fclose(fp); return ret; } -B_COMMAND(CMD_CREATE, CMD_ROOT) +B_COMMAND(CMD_PACKAGE_CREATE, CMD_PACKAGE) { B_COMMAND_NAME("create"); B_COMMAND_SHORT_NAME('C'); @@ -376,7 +426,8 @@ B_COMMAND(CMD_CREATE, CMD_ROOT) B_OPTION_SHORT_NAME('s'); B_OPTION_LONG_NAME("script"); B_OPTION_DESC( - "a control script to be run at a certain point in the " + "a control script to be run at a certain point " + "in the " "package (un)installation process."); B_OPTION_ARG(OPT_SCRIPT_NAME) @@ -402,10 +453,13 @@ B_COMMAND(CMD_CREATE, CMD_ROOT) B_OPTION_SHORT_NAME('p'); B_OPTION_LONG_NAME("parameter"); B_OPTION_DESC( - "a parameter to use when creating the new file. " - "if a parameter EXAMPLE is defined, any variable " + "a parameter to use when creating the new " + "file. " + "if a parameter EXAMPLE is defined, any " + "variable " "reference " - "of the form ${EXAMPLE} in the package manifest will " + "of the form ${EXAMPLE} in the package " + "manifest will " "be " "replaced with the specified parameter value."); @@ -426,8 +480,10 @@ B_COMMAND(CMD_CREATE, CMD_ROOT) { B_ARG_NAME("source-dir"); B_ARG_DESC( - "the directory to package. the specified directory " - "will become the root of the newly created package."); + "the directory to package. the specified " + "directory " + "will become the root of the newly created " + "package."); B_ARG_NR_VALUES(1); } diff --git a/ropkg/extract.c b/ropkg/package/extract.c similarity index 87% rename from ropkg/extract.c rename to ropkg/package/extract.c index 08d67dd..290142e 100644 --- a/ropkg/extract.c +++ b/ropkg/package/extract.c @@ -1,4 +1,4 @@ -#include "commands.h" +#include "../commands.h" #include @@ -17,12 +17,12 @@ static int extract( return 0; } -B_COMMAND(CMD_EXTRACT, CMD_ROOT) +B_COMMAND(CMD_PACKAGE_EXTRACT, CMD_PACKAGE) { B_COMMAND_NAME("extract"); B_COMMAND_SHORT_NAME('X'); B_COMMAND_DESC("extract the contents of a Rosetta package"); - B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); + // B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); B_COMMAND_FUNCTION(extract); B_COMMAND_HELP_OPTION(); diff --git a/ropkg/install.c b/ropkg/package/install.c similarity index 93% rename from ropkg/install.c rename to ropkg/package/install.c index 628b5cb..5e57340 100644 --- a/ropkg/install.c +++ b/ropkg/package/install.c @@ -1,4 +1,4 @@ -#include "commands.h" +#include "../commands.h" #include @@ -17,7 +17,7 @@ static int install( return 0; } -B_COMMAND(CMD_INSTALL, CMD_ROOT) +B_COMMAND(CMD_PACKAGE_INSTALL, CMD_PACKAGE) { B_COMMAND_NAME("install"); B_COMMAND_SHORT_NAME('I'); diff --git a/ropkg/package/query/list.c b/ropkg/package/query/list.c new file mode 100644 index 0000000..3c2b5df --- /dev/null +++ b/ropkg/package/query/list.c @@ -0,0 +1,193 @@ +#include "../../commands.h" + +#include +#include +#include +#include +#include +#include + +enum { + OPT_DATA = 0x01u, + OPT_META = 0x02u, + OPT_CONTROL = 0x04u, + + ARG_PACKAGE_PATH, +}; + +static int print_file_list( + int section, + bool show_header, + struct ropkg_reader *pkg) +{ + if (show_header) { + switch (section) { + case OPT_DATA: + printf("data:\n"); + break; + case OPT_META: + printf("meta:\n"); + break; + case OPT_CONTROL: + printf("control:\n"); + break; + default: + break; + } + } + + enum ropkg_status status = ROPKG_SUCCESS; + + while (!ropkg_reader_eof(pkg)) { + if (show_header) { + printf(" "); + } + + const struct ropkg_file_info *file + = ropkg_reader_current_file(pkg); + printf("%s\n", file->f_filename); + status = ropkg_reader_move_next(pkg); + } + + return 0; +} + +static int query_list( + const b_command *self, + const b_arglist *opt, + const b_array *args) +{ + const b_compression_function *zstd + = b_compression_function_get_by_id(B_COMPRESSOR_FUNCTION_ZSTD); + struct ropkg_reader *pkg; + enum ropkg_status status; + + const char *in_path; + b_status bstatus = b_arglist_get_string( + opt, + B_COMMAND_INVALID_ID, + ARG_PACKAGE_PATH, + 0, + &in_path); + if (!B_OK(bstatus)) { + b_arglist_report_missing_args( + opt, + B_COMMAND_INVALID_ID, + ARG_PACKAGE_PATH, + 0); + return -1; + } + + int sections = 0; + if (b_arglist_get_count(opt, OPT_DATA, B_COMMAND_INVALID_ID) > 0) { + sections |= OPT_DATA; + } + if (b_arglist_get_count(opt, OPT_META, B_COMMAND_INVALID_ID) > 0) { + sections |= OPT_META; + } + if (b_arglist_get_count(opt, OPT_CONTROL, B_COMMAND_INVALID_ID) > 0) { + sections |= OPT_CONTROL; + } + + if (sections == 0) { + sections = OPT_DATA; + } + + bool show_headers = b_popcountl(sections) > 1; + + FILE *fp = fopen(in_path, "rb"); + b_stream *fp_stream = b_stream_open_fp(fp); + b_cstream *cstream; + bstatus = b_cstream_open( + fp_stream, + zstd, + B_COMPRESSION_MODE_DECOMPRESS, + &cstream); + + status = ropkg_reader_open(cstream, &pkg); + if (status != ROPKG_SUCCESS) { + printf("cannot open package\n"); + return -1; + } + + while (!ropkg_reader_eof(pkg)) { + const struct ropkg_file_info *file + = ropkg_reader_current_file(pkg); + + int section = -1; + if (!strncmp(file->f_filename, "data.tar", 8)) { + section = OPT_DATA; + } else if (!strncmp(file->f_filename, "meta.tar", 8)) { + section = OPT_META; + } else if (!strncmp(file->f_filename, "control.tar", 11)) { + section = OPT_CONTROL; + } + + if (section == -1 || (section & sections) != section) { + status = ropkg_reader_move_next(pkg); + continue; + } + + struct ropkg_reader *subpkg = NULL; + b_cstream_begin_compressed_section(cstream, NULL); + status = ropkg_reader_open(cstream, &subpkg); + print_file_list(section, show_headers, subpkg); + ropkg_reader_close(subpkg); + b_cstream_end_compressed_section(cstream, NULL, NULL); + + status = ropkg_reader_move_next(pkg); + } + + return 0; +} + +B_COMMAND(CMD_PACKAGE_QUERY_LIST, CMD_PACKAGE_QUERY) +{ + B_COMMAND_NAME("list"); + B_COMMAND_SHORT_NAME('L'); + B_COMMAND_DESC("list the contents of a package"); + B_COMMAND_FUNCTION(query_list); + + B_COMMAND_HELP_OPTION(); + + B_COMMAND_ARG(ARG_PACKAGE_PATH) + { + B_ARG_NAME("package"); + B_ARG_DESC("the package to query."); + B_ARG_NR_VALUES(1); + } + + B_COMMAND_OPTION(OPT_DATA) + { + B_OPTION_SHORT_NAME('d'); + B_OPTION_LONG_NAME("data"); + B_OPTION_DESC( + "list all data files contained within a " + "package. if no " + "options are specified, this behaviour is the " + "default."); + } + + B_COMMAND_OPTION(OPT_META) + { + B_OPTION_SHORT_NAME('m'); + B_OPTION_LONG_NAME("meta"); + B_OPTION_DESC( + "list all metadata files contained within a " + "package."); + } + + B_COMMAND_OPTION(OPT_CONTROL) + { + B_OPTION_SHORT_NAME('c'); + B_OPTION_LONG_NAME("control"); + B_OPTION_DESC( + "list all control files contained within a " + "package."); + } + + B_COMMAND_USAGE() + { + B_COMMAND_USAGE_ARG(ARG_PACKAGE_PATH); + } +} diff --git a/ropkg/package/query/manifest.c b/ropkg/package/query/manifest.c new file mode 100644 index 0000000..835ea2b --- /dev/null +++ b/ropkg/package/query/manifest.c @@ -0,0 +1,38 @@ +#include "../../commands.h" + +#include + +enum { + ARG_PACKAGE_PATH, +}; + +static int query_manifest( + const b_command *self, + const b_arglist *opt, + const b_array *args) +{ + return 0; +} + +B_COMMAND(CMD_PACKAGE_QUERY_MANIFEST, CMD_PACKAGE_QUERY) +{ + B_COMMAND_NAME("manifest"); + B_COMMAND_SHORT_NAME('M'); + B_COMMAND_DESC("print the manifest of a package"); + B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); + B_COMMAND_FUNCTION(query_manifest); + + B_COMMAND_HELP_OPTION(); + + B_COMMAND_ARG(ARG_PACKAGE_PATH) + { + B_ARG_NAME("package"); + B_ARG_DESC("the package to query."); + B_ARG_NR_VALUES(1); + } + + B_COMMAND_USAGE() + { + B_COMMAND_USAGE_ARG(ARG_PACKAGE_PATH); + } +} diff --git a/ropkg/package/query/readme.c b/ropkg/package/query/readme.c new file mode 100644 index 0000000..0f9aa2f --- /dev/null +++ b/ropkg/package/query/readme.c @@ -0,0 +1,38 @@ +#include "../../commands.h" + +#include + +enum { + ARG_PACKAGE_PATH, +}; + +static int query_readme( + const b_command *self, + const b_arglist *opt, + const b_array *args) +{ + return 0; +} + +B_COMMAND(CMD_PACKAGE_QUERY_README, CMD_PACKAGE_QUERY) +{ + B_COMMAND_NAME("readme"); + B_COMMAND_SHORT_NAME('R'); + B_COMMAND_DESC("print the readme of a package"); + B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); + B_COMMAND_FUNCTION(query_readme); + + B_COMMAND_HELP_OPTION(); + + B_COMMAND_ARG(ARG_PACKAGE_PATH) + { + B_ARG_NAME("package"); + B_ARG_DESC("the package to query."); + B_ARG_NR_VALUES(1); + } + + B_COMMAND_USAGE() + { + B_COMMAND_USAGE_ARG(ARG_PACKAGE_PATH); + } +} diff --git a/ropkg/package/query/root.c b/ropkg/package/query/root.c new file mode 100644 index 0000000..6f77318 --- /dev/null +++ b/ropkg/package/query/root.c @@ -0,0 +1,18 @@ +#include "../../commands.h" + +#include + +B_COMMAND(CMD_PACKAGE_QUERY, CMD_PACKAGE) +{ + B_COMMAND_NAME("query"); + B_COMMAND_SHORT_NAME('Q'); + B_COMMAND_DESC("query information about a Rosetta package"); + B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); + + B_COMMAND_HELP_OPTION(); + + B_COMMAND_USAGE() + { + B_COMMAND_USAGE_COMMAND_PLACEHOLDER(); + } +} diff --git a/ropkg/package/query/summary.c b/ropkg/package/query/summary.c new file mode 100644 index 0000000..66dbd23 --- /dev/null +++ b/ropkg/package/query/summary.c @@ -0,0 +1,99 @@ +#include "../../commands.h" + +#include +#include +#include +#include +#include +#include +#include + +enum { + ARG_PACKAGE_PATH, +}; + +static int query_summary( + const b_command *self, + const b_arglist *opt, + const b_array *args) +{ + const b_compression_function *zstd + = b_compression_function_get_by_id(B_COMPRESSOR_FUNCTION_ZSTD); + struct ropkg_reader *pkg; + enum ropkg_status status; + + const char *in_path; + b_status bstatus = b_arglist_get_string( + opt, + B_COMMAND_INVALID_ID, + ARG_PACKAGE_PATH, + 0, + &in_path); + + if (!B_OK(bstatus)) { + b_arglist_report_missing_args( + opt, + B_COMMAND_INVALID_ID, + ARG_PACKAGE_PATH, + 0); + return -1; + } + + FILE *fp = fopen(in_path, "rb"); + b_stream *fp_stream = b_stream_open_fp(fp); + b_cstream *cstream; + bstatus = b_cstream_open( + fp_stream, + zstd, + B_COMPRESSION_MODE_DECOMPRESS, + &cstream); + + status = ropkg_reader_open(cstream, &pkg); + if (status != ROPKG_SUCCESS) { + printf("cannot open package\n"); + return -1; + } + + b_directory *dest; + b_directory_open( + NULL, + B_RV_PATH("data"), + B_DIRECTORY_OPEN_CREATE | B_DIRECTORY_OPEN_DELETE_ON_CLOSE, + &dest); + + b_result result = ropkg_extract_metadata(pkg, dest); + if (b_result_is_error(result)) { + b_throw(result); + } + + b_directory_release(dest); + ropkg_reader_close(pkg); + b_cstream_close(cstream); + b_stream_close(fp_stream); + fclose(fp); + + return 0; +} + +B_COMMAND(CMD_PACKAGE_QUERY_SUMMARY, CMD_PACKAGE_QUERY) +{ + B_COMMAND_NAME("summary"); + B_COMMAND_SHORT_NAME('S'); + B_COMMAND_DESC("print a summary of information about a package"); + B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); + B_COMMAND_FUNCTION(query_summary); + + B_COMMAND_HELP_OPTION(); + + B_COMMAND_ARG(ARG_PACKAGE_PATH) + { + B_ARG_NAME("package"); + B_ARG_DESC("the package to query."); + B_ARG_NR_VALUES(1); + } + + B_COMMAND_USAGE() + { + B_COMMAND_USAGE_ARG(ARG_PACKAGE_PATH); + } +} diff --git a/ropkg/package/root.c b/ropkg/package/root.c new file mode 100644 index 0000000..834b3fa --- /dev/null +++ b/ropkg/package/root.c @@ -0,0 +1,19 @@ +#include "../commands.h" + +#include +#include +#include + +B_COMMAND(CMD_PACKAGE, CMD_ROOT) +{ + B_COMMAND_NAME("package"); + B_COMMAND_SHORT_NAME('P'); + B_COMMAND_DESC("Package inspection and manipulation."); + B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); + B_COMMAND_HELP_OPTION(); + + B_COMMAND_USAGE() + { + B_COMMAND_USAGE_COMMAND_PLACEHOLDER(); + } +} diff --git a/ropkg/query.c b/ropkg/query.c deleted file mode 100644 index 3ba863f..0000000 --- a/ropkg/query.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "commands.h" - -#include - -enum { - ARG_PACKAGE, -}; - -static int query( - const b_command *self, - const b_arglist *opt, - const b_array *args) -{ - return 0; -} - -B_COMMAND(CMD_QUERY, CMD_ROOT) -{ - B_COMMAND_NAME("query"); - B_COMMAND_SHORT_NAME('Q'); - B_COMMAND_DESC("query information about a Rosetta package"); - B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); - B_COMMAND_FUNCTION(query); - - B_COMMAND_HELP_OPTION(); - - B_COMMAND_ARG(ARG_PACKAGE) - { - B_ARG_NAME("package"); - B_ARG_DESC("the package to query."); - B_ARG_NR_VALUES(1); - } - - B_COMMAND_USAGE() - { - B_COMMAND_USAGE_ARG(ARG_PACKAGE); - } -} diff --git a/ropkg/compare-version.c b/ropkg/version/compare.c similarity index 97% rename from ropkg/compare-version.c rename to ropkg/version/compare.c index b3cd74f..123186d 100644 --- a/ropkg/compare-version.c +++ b/ropkg/version/compare.c @@ -1,4 +1,4 @@ -#include "commands.h" +#include "../commands.h" #include "ropkg/version.h" #include @@ -175,9 +175,10 @@ end: return ret; } -B_COMMAND(CMD_COMPARE_VERSION, CMD_ROOT) +B_COMMAND(CMD_VERSION_COMPARE, CMD_VERSION) { - B_COMMAND_NAME("compare-version"); + B_COMMAND_NAME("compare"); + B_COMMAND_SHORT_NAME('C'); B_COMMAND_DESC("compare two package version identifiers."); B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); B_COMMAND_FUNCTION(compare_version); diff --git a/ropkg/version/root.c b/ropkg/version/root.c new file mode 100644 index 0000000..c42eca2 --- /dev/null +++ b/ropkg/version/root.c @@ -0,0 +1,19 @@ +#include "../commands.h" + +#include +#include +#include + +B_COMMAND(CMD_VERSION, CMD_ROOT) +{ + B_COMMAND_NAME("version"); + B_COMMAND_SHORT_NAME('V'); + B_COMMAND_DESC("Version identifier inspection and manipulation."); + B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); + B_COMMAND_HELP_OPTION(); + + B_COMMAND_USAGE() + { + B_COMMAND_USAGE_COMMAND_PLACEHOLDER(); + } +}