Files
ropkg/ropkg/package/create.c

497 lines
9.6 KiB
C

#include "../commands.h"
#include <blue/cmd.h>
#include <blue/compress/cstream.h>
#include <blue/compress/function.h>
#include <blue/core/error.h>
#include <blue/io/directory.h>
#include <blue/term/print.h>
#include <errno.h>
#include <ropkg/manifest.h>
#include <ropkg/writer.h>
#include <stdio.h>
#include <stdlib.h>
enum {
OPT_OUTPATH,
OPT_OUTPATH_PATH,
OPT_MANIFEST,
OPT_MANIFEST_PATH,
OPT_NEWS,
OPT_NEWS_PATH,
// OPT_CONTROL,
// OPT_CONTROL_PATH,
OPT_PARAMETER,
OPT_PARAMETER_NAME,
OPT_PARAMETER_VALUE,
OPT_SCRIPT,
OPT_SCRIPT_NAME,
OPT_SCRIPT_PATH,
ARG_SOURCE_DIR,
};
static int add_file_to_archive_ex(
struct ropkg_writer *writer,
b_directory *src_dir,
const b_path *src,
const char *dest)
{
b_file *fp;
b_result result = b_file_open(
src_dir,
src,
B_FILE_READ_ONLY | B_FILE_BINARY,
&fp);
if (!fp) {
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;
}
struct ropkg_writer_file_info file = {0};
b_file_size(fp, &file.f_length);
ropkg_writer_begin_file(writer, dest, &file);
char buf[4096];
size_t offset = 0;
int c;
while (1) {
size_t r;
b_status status = b_file_read(fp, offset, sizeof buf, buf, &r);
if (!B_OK(status)) {
break;
}
size_t w;
char s = c;
ropkg_writer_write(writer, buf, r, &w);
offset += r;
if (r < sizeof buf) {
break;
}
}
b_file_release(fp);
ropkg_writer_end_file(writer);
return 0;
}
static int add_file_to_archive(
struct ropkg_writer *writer,
const char *src,
const char *dest)
{
b_path *src_path = b_path_create_from_cstr(src);
int ret = add_file_to_archive_ex(writer, NULL, src_path, dest);
b_path_release(src_path);
return ret;
}
static int add_data_archive(
const b_arglist *args,
struct b_cstream *stream,
struct ropkg_writer *pkg)
{
const char *data_path_cstr;
b_status bstatus = b_arglist_get_string(
args,
B_COMMAND_INVALID_ID,
ARG_SOURCE_DIR,
0,
&data_path_cstr);
if (!B_OK(bstatus)) {
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;
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;
}
struct ropkg_writer *data;
struct ropkg_writer_file_info file = {0};
ropkg_writer_begin_file(pkg, ROPKG_PATH_DATA ".zst", &file);
b_cstream_begin_compressed_section(stream, NULL);
enum ropkg_status status = ropkg_writer_open(stream, &data);
int ret = 0;
b_directory_iterator it;
b_directory_foreach(&it, data_dir, B_DIRECTORY_ITERATE_PARENT_FIRST)
{
if (!(it.info.attrib & B_FILE_ATTRIB_NORMAL)) {
continue;
}
ret = add_file_to_archive_ex(
data,
data_dir,
it.filepath,
b_path_ptr(it.filepath));
if (ret != 0) {
break;
}
}
ropkg_writer_close(data);
b_cstream_end_compressed_section(stream, NULL, NULL);
ropkg_writer_end_file(pkg);
return ret;
}
static int add_control_archive(
const b_arglist *args,
b_cstream *stream,
struct ropkg_writer *pkg)
{
int ret;
struct ropkg_writer *control;
struct ropkg_writer_file_info file = {0};
ropkg_writer_begin_file(pkg, ROPKG_PATH_CONTROL ".zst", &file);
b_cstream_begin_compressed_section(stream, NULL);
enum ropkg_status status = ropkg_writer_open(stream, &control);
b_arglist_option_iterator it;
b_arglist_option_foreach_filtered(&it, args, OPT_SCRIPT)
{
struct b_arglist_value *type, *path;
b_arglist_option_get_value(it.opt, OPT_SCRIPT_NAME, 0, &type);
b_arglist_option_get_value(it.opt, OPT_SCRIPT_PATH, 0, &path);
ret = add_file_to_archive(
control,
path->val_str,
type->val_str);
if (ret != 0) {
ropkg_writer_close(control);
return ret;
}
}
ropkg_writer_close(control);
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,
b_cstream *stream,
struct ropkg_writer *pkg)
{
const char *manifest_path, *news_path;
b_status bstatus = b_arglist_get_string(
args,
OPT_MANIFEST,
OPT_MANIFEST_PATH,
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);
b_cstream_begin_compressed_section(stream, NULL);
enum ropkg_status status = ropkg_writer_open(stream, &meta);
bstatus = b_arglist_get_string(
args,
OPT_NEWS,
OPT_NEWS_PATH,
1,
&news_path);
if (B_OK(bstatus)) {
ret = add_file_to_archive(meta, news_path, "news");
if (ret != 0) {
ropkg_writer_close(meta);
return ret;
}
}
ret = add_file_to_archive(meta, manifest_path, "manifest");
if (ret != 0) {
ropkg_writer_close(meta);
return ret;
}
ropkg_writer_close(meta);
b_cstream_end_compressed_section(stream, NULL, NULL);
ropkg_writer_end_file(pkg);
return 0;
}
static int create(
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_writer *pkg, *subpkg;
enum ropkg_status status;
const char *out_path;
b_status bstatus = b_arglist_get_string(
opt,
OPT_OUTPATH,
OPT_OUTPATH_PATH,
0,
&out_path);
if (!B_OK(bstatus)) {
b_arglist_report_missing_option(opt, OPT_OUTPATH);
return -1;
}
FILE *fp = fopen(out_path, "wb");
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(cstream, &pkg);
int ret;
ret = add_data_archive(opt, cstream, pkg);
if (ret != 0) {
goto end;
}
ret = add_control_archive(opt, cstream, pkg);
if (ret != 0) {
goto end;
}
ret = add_meta_archive(opt, cstream, pkg);
if (ret != 0) {
goto end;
}
end:
ropkg_writer_close(pkg);
b_cstream_close(cstream);
b_stream_close(fp_stream);
fclose(fp);
return ret;
}
B_COMMAND(CMD_PACKAGE_CREATE, CMD_PACKAGE)
{
B_COMMAND_NAME("create");
B_COMMAND_SHORT_NAME('C');
B_COMMAND_DESC("create a Rosetta package from a directory");
B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT);
B_COMMAND_FUNCTION(create);
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_MANIFEST)
{
B_OPTION_SHORT_NAME('m');
B_OPTION_LONG_NAME("manifest");
B_OPTION_DESC(
"the path to a manifest file describing the "
"package to be created.");
B_OPTION_ARG(OPT_MANIFEST_PATH)
{
B_ARG_NAME("path");
B_ARG_NR_VALUES(1);
}
}
B_COMMAND_OPTION(OPT_NEWS)
{
B_OPTION_SHORT_NAME('n');
B_OPTION_LONG_NAME("news");
B_OPTION_DESC(
"the path to a news file to be included in the "
"package.");
B_OPTION_ARG(OPT_NEWS_PATH)
{
B_ARG_NAME("path");
B_ARG_NR_VALUES(1);
}
}
#if 0
B_COMMAND_OPTION(OPT_CONTROL)
{
B_OPTION_SHORT_NAME('c');
B_OPTION_LONG_NAME("control");
B_OPTION_DESC(
"the path to a control directory containing package "
"scripts. this option cannot be used with "
"-s/--script.");
B_OPTION_ARG(OPT_CONTROL_PATH)
{
B_ARG_NAME("path");
B_ARG_NR_VALUES(1);
}
}
#endif
B_COMMAND_OPTION(OPT_SCRIPT)
{
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 "
"package (un)installation process.");
B_OPTION_ARG(OPT_SCRIPT_NAME)
{
B_ARG_NAME("event");
B_ARG_NR_VALUES(1);
B_ARG_ALLOWED_VALUES(
"pre-install",
"post-install",
"pre-uninstall",
"post-uninstall");
}
B_OPTION_ARG(OPT_SCRIPT_PATH)
{
B_ARG_NAME("path");
B_ARG_NR_VALUES(1);
}
}
B_COMMAND_OPTION(OPT_PARAMETER)
{
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 "
"reference "
"of the form ${EXAMPLE} in the package "
"manifest will "
"be "
"replaced with the specified parameter value.");
B_OPTION_ARG(OPT_PARAMETER_NAME)
{
B_ARG_NAME("name");
B_ARG_NR_VALUES(1);
}
B_OPTION_ARG(OPT_PARAMETER_VALUE)
{
B_ARG_NAME("value");
B_ARG_NR_VALUES(1);
}
}
B_COMMAND_ARG(ARG_SOURCE_DIR)
{
B_ARG_NAME("source-dir");
B_ARG_DESC(
"the directory to package. the specified "
"directory "
"will become the root of the newly created "
"package.");
B_ARG_NR_VALUES(1);
}
B_COMMAND_USAGE()
{
B_COMMAND_USAGE_OPT(OPT_OUTPATH);
B_COMMAND_USAGE_OPT_PLACEHOLDER();
B_COMMAND_USAGE_ARG(ARG_SOURCE_DIR);
}
}