ropkg: implement create command
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
file(GLOB sources *.c *.h)
|
||||
|
||||
add_executable(ropkg ${sources})
|
||||
target_link_libraries(ropkg Bluelib::Core Bluelib::Object Bluelib::Io Bluelib::Term Bluelib::Cmd)
|
||||
target_link_libraries(ropkg
|
||||
libropkg
|
||||
Bluelib::Core Bluelib::Object Bluelib::Io Bluelib::Term Bluelib::Cmd)
|
||||
|
||||
464
ropkg/create.c
464
ropkg/create.c
@@ -1,6 +1,14 @@
|
||||
#include "commands.h"
|
||||
|
||||
#include <blue/cmd.h>
|
||||
#include <blue/io/directory.h>
|
||||
#include <blue/term/print.h>
|
||||
#include <errno.h>
|
||||
#include <ropkg/compress.h>
|
||||
#include <ropkg/stream.h>
|
||||
#include <ropkg/writer.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
enum {
|
||||
OPT_OUTPATH,
|
||||
@@ -9,19 +17,290 @@ enum {
|
||||
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_status status = 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));
|
||||
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;
|
||||
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 ropkg_stream *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_option(args, OPT_MANIFEST);
|
||||
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));
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct ropkg_writer *data;
|
||||
struct ropkg_writer_file_info file = {0};
|
||||
ropkg_writer_begin_file(pkg, ROPKG_PATH_DATA ".zst", &file);
|
||||
|
||||
ropkg_stream_begin_compressed_section(stream);
|
||||
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);
|
||||
ropkg_stream_end_compressed_section(stream, NULL, NULL);
|
||||
ropkg_writer_end_file(pkg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int add_control_archive(
|
||||
const b_arglist *args,
|
||||
struct ropkg_stream *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);
|
||||
|
||||
ropkg_stream_begin_compressed_section(stream);
|
||||
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);
|
||||
|
||||
ropkg_stream_end_compressed_section(stream, NULL, NULL);
|
||||
|
||||
ropkg_writer_end_file(pkg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_meta_archive(
|
||||
const b_arglist *args,
|
||||
struct ropkg_stream *stream,
|
||||
struct ropkg_writer *pkg)
|
||||
{
|
||||
const char *manifest_path, *news_path;
|
||||
b_status bstatus = b_arglist_get_string(
|
||||
args,
|
||||
OPT_MANIFEST,
|
||||
OPT_MANIFEST_PATH,
|
||||
1,
|
||||
&manifest_path);
|
||||
if (!B_OK(bstatus)) {
|
||||
b_arglist_report_missing_option(args, OPT_MANIFEST);
|
||||
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);
|
||||
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);
|
||||
|
||||
ropkg_stream_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)
|
||||
{
|
||||
return 0;
|
||||
const struct ropkg_compression_function *zstd
|
||||
= ropkg_compression_function_for_type(ROPKG_COMPRESSION_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,
|
||||
1,
|
||||
&out_path);
|
||||
if (!B_OK(bstatus)) {
|
||||
b_arglist_report_missing_option(opt, OPT_OUTPATH);
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *fp = fopen(out_path, "wb");
|
||||
struct ropkg_stream *stream;
|
||||
status = ropkg_stream_open(fp, zstd, ROPKG_STREAM_WRITE, &stream);
|
||||
|
||||
status = ropkg_writer_open(stream, &pkg);
|
||||
|
||||
int ret;
|
||||
|
||||
ret = add_data_archive(opt, stream, pkg);
|
||||
if (ret != 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = add_control_archive(opt, stream, pkg);
|
||||
if (ret != 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = add_meta_archive(opt, stream, pkg);
|
||||
if (ret != 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
ropkg_writer_close(pkg);
|
||||
ropkg_stream_close(stream);
|
||||
fclose(fp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
B_COMMAND(CMD_CREATE, CMD_ROOT)
|
||||
@@ -53,10 +332,7 @@ B_COMMAND(CMD_CREATE, CMD_ROOT)
|
||||
B_OPTION_LONG_NAME("manifest");
|
||||
B_OPTION_DESC(
|
||||
"the path to a manifest file describing the "
|
||||
"package to be created. if no manifest is specified, "
|
||||
"the package binary directory will be searched for a "
|
||||
"manifest");
|
||||
|
||||
"package to be created.");
|
||||
B_OPTION_ARG(OPT_MANIFEST_PATH)
|
||||
{
|
||||
B_ARG_NAME("path");
|
||||
@@ -64,6 +340,63 @@ B_COMMAND(CMD_CREATE, CMD_ROOT)
|
||||
}
|
||||
}
|
||||
|
||||
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');
|
||||
@@ -101,126 +434,7 @@ B_COMMAND(CMD_CREATE, CMD_ROOT)
|
||||
B_COMMAND_USAGE()
|
||||
{
|
||||
B_COMMAND_USAGE_OPT(OPT_OUTPATH);
|
||||
B_COMMAND_USAGE_ARG(ARG_SOURCE_DIR);
|
||||
}
|
||||
|
||||
B_COMMAND_USAGE()
|
||||
{
|
||||
B_COMMAND_USAGE_OPT(OPT_MANIFEST);
|
||||
B_COMMAND_USAGE_OPT(OPT_OUTPATH);
|
||||
B_COMMAND_USAGE_ARG(ARG_SOURCE_DIR);
|
||||
}
|
||||
}
|
||||
#include "commands.h"
|
||||
|
||||
#include <blue/cmd.h>
|
||||
|
||||
enum {
|
||||
OPT_OUTPATH,
|
||||
OPT_OUTPATH_PATH,
|
||||
|
||||
OPT_MANIFEST,
|
||||
OPT_MANIFEST_PATH,
|
||||
|
||||
OPT_PARAMETER,
|
||||
OPT_PARAMETER_NAME,
|
||||
OPT_PARAMETER_VALUE,
|
||||
|
||||
ARG_SOURCE_DIR,
|
||||
};
|
||||
|
||||
static int create(
|
||||
const b_command *self,
|
||||
const b_arglist *opt,
|
||||
const b_array *args)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
B_COMMAND(CMD_CREATE, CMD_ROOT)
|
||||
{
|
||||
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. if no manifest is specified, "
|
||||
"the package binary directory will be searched for a "
|
||||
"manifest");
|
||||
|
||||
B_OPTION_ARG(OPT_MANIFEST_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_ARG(ARG_SOURCE_DIR);
|
||||
}
|
||||
|
||||
B_COMMAND_USAGE()
|
||||
{
|
||||
B_COMMAND_USAGE_OPT(OPT_MANIFEST);
|
||||
B_COMMAND_USAGE_OPT(OPT_OUTPATH);
|
||||
B_COMMAND_USAGE_OPT_PLACEHOLDER();
|
||||
B_COMMAND_USAGE_ARG(ARG_SOURCE_DIR);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user