497 lines
9.6 KiB
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);
|
|
}
|
|
}
|