Compare commits

...

10 Commits

55 changed files with 3335 additions and 158 deletions

View File

@@ -7,8 +7,7 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
find_package(Bluelib COMPONENTS Core Object Io Term Cmd REQUIRED) find_package(Bluelib COMPONENTS Core Object Io Term Cmd Compress REQUIRED)
find_package(ZSTD REQUIRED)
add_subdirectory(libropkg) add_subdirectory(libropkg)
add_subdirectory(ropkg) add_subdirectory(ropkg)

View File

@@ -49,7 +49,7 @@ if (Bluelib_STATIC)
set(_lib_suffix "-s") set(_lib_suffix "-s")
endif () endif ()
set(supported_components Core Object Term Cmd Io Serial) set(supported_components Core Object Term Cmd Io Serial Compress)
set(components ${Bluelib_FIND_COMPONENTS}) set(components ${Bluelib_FIND_COMPONENTS})
string(REPLACE ";" ", " supported_components_string_list "${supported_components}") string(REPLACE ";" ", " supported_components_string_list "${supported_components}")
@@ -177,5 +177,13 @@ if (Bluelib_FOUND)
target_link_libraries(Bluelib::Io INTERFACE Bluelib::Core Bluelib::Object) target_link_libraries(Bluelib::Io INTERFACE Bluelib::Core Bluelib::Object)
endif () endif ()
if ("${component}" STREQUAL "Compress")
if (NOT TARGET Bluelib::Core)
message(FATAL_ERROR "Bluelib: Module 'Compress' depends on 'Core', which was not specified in find_package()")
endif ()
target_link_libraries(Bluelib::Compress INTERFACE Bluelib::Core Bluelib::Object)
endif ()
endforeach (component) endforeach (component)
endif() endif()

31
doc/package-manifest.json Normal file
View File

@@ -0,0 +1,31 @@
{
"Name": "sample-package",
"Architecture": "amd64",
"Version": "1.2.5-2",
"Priority": "optional",
"Category": "misc",
"Maintainer": "John Doe <john@sample.com>",
"Provides": [
{ "Name": "sample-api", "Version": "1.2.5" }
],
"Depends": [
{ "Name": "libc", "Version": "2.0", "Predicate": ">=" },
{ "Name": "libstdc++", "Version": "2.3", "Predicate": ">=" },
{ "Name": "libc", "Version": "2.0", "Predicate": ">=" },
{
"One-Of": [
{ "Name": "libs1", "Version": "1.0", "Predicate": ">=" },
{ "Name": "libs2", "Version": "1.2", "Predicate": ">=" }
]
}
],
"Recommends": [ "sample-cli" ],
"Suggests": [
{ "One-Of": [ "sample-gtk", "sample-qt" ] },
{ "Name": "libc", "Version": "2.0", "Predicate": ">=" }
],
"Conflicts": [ "example-package" ],
"Enhances": [ "basic-package" ],
"Description": "A sample package",
"Installed-Size": 3245
}

25
doc/package-readme.txt Normal file
View File

@@ -0,0 +1,25 @@
+------------------------------------------------------------------------------+
| A Sample Package |
+------------------------------------------------------------------------------+
This is a sample package to demonstrate the layout and features of a Rosetta
package.
1 Main Package Layout
=====================
2 The Payload Package
=====================
3 The Control Package
=====================
4 The Meta Package
==================
vim: shiftwidth=3 expandtab

124
doc/package-versions.txt Normal file
View File

@@ -0,0 +1,124 @@
+------------------------------------------------------------------------------+
| Rosetta Package Manager Documentation |
| ..................................... |
| Package Version Format and Comparison |
+------------------------------------------------------------------------------+
This document describes the format of Rosetta package version indicators, and
the way in which they are compared.
1 Permitted Characters
======================
The following characters can be used in a package version identifier:
- lowercase letters [a-z]
- digits [0-9]
- the following punctuation: - . ~
2 Version String Format
=======================
The package version identifier should have the following layout:
[release-phase]<upstream-version>[~version-version-phase[version-release-phase-revision]][-package-revision]
Components surrounded by [square brackets] is optional, while components
surrounded by <triangular brackets> are required. If a component name
is preceded by a punctuation mark, the value of that component in a package
version identifier must also be preceded by that punctuation mark.
The different version identifier components are defined as follows:
- release-phase (optional) indicates the release stage of the upstream
software. allowable values are: alpha, beta
- upstream-version (required) indicates the version number of the upstream
release. this is made up of one to five integers >= 0 separated by full
stops.
- version-release-phase (optional) indicates the release stage of this
particular version of the upstream software. allowable values are:
alpha, beta, rc.
- version-release-phase-revision (optional) indicates the revision of the
upstream package version within a given pre-release-version. must be
a positive non-zero integer.
- package-revision (optional) indicates different versions of a particular
package that contain the same version of the upstream software.
Some examples of package versions include:
- 1.0.0
First revision of package for upstream release 1.0.0
- beta1.7
First revision of package for upstream beta pre-release 1.7
- 0.6-2
Second revision of package for upstream release 0.6
- 1.2~beta2
First revision of package for the second beta pre-release of upstream
version 1.2
- 5.15~rc1-2
Second revision of package for the first release-candidate pre-release of
upstream version 5.15
3 Version Comparison
====================
There are a number of rules that govern how package version identifiers are
compared.
3.1 Comparison Procedure
------------------------
When comparing two version numbers, the five components are compared left to
right using the following procedure:
1. release-phase is compared according to Release Phase Precedence. If the
two packages have different release-phase values, the one with higher
precedence is considered the newer package. If one package does not have
a release-phase, a release-phase of 'release' is assumed, which has the
highest precedence.
2. Each digit in upstream-version is compared left-to-right. If one package
version has fewer digits than the other, the missing digits are assumed to
be zero. If one package has a version digit greater than the other when
read left-to-right, it is considered the newer package.
3. version-release-phase is compared according to Release Phase Precedence.
If the two packages have different version-release-phase values, the
one with higher precedence is considered the newer package. If a package
does not have a version-release-phase, a value of 'release' (with the
highest precedence) is assumed, and step 4 is skipped.
4. The package with the greater version-release-phase-revision is considered
the newer package. If a package does not have a
version-release-phase-revision, a value of one is assumed.
5. The package with the greater package-revision is considered the newer
package. If a package does not have a package-revision, a value of one is
assumed.
If each of the five components of two package versions is determined to be
equal according to the above procedure, the two packages are considered to
be equal in version.
3.2 Release Phase Precedence
--------------------------
Release phases have a defined order of precedence which is used during
comparisons. This order, in decending level of precedence, is:
1. release (cannot be specified directly, but is used implicitly if no
release phase is specified).
2. rc
3. beta
4. alpha
vim: shiftwidth=3 expandtab

9
doc/release.json Normal file
View File

@@ -0,0 +1,9 @@
{
"mango-kernel": {
"0.1": {
}
},
"rosetta-system": {
"0.1": {
}
}

View File

@@ -0,0 +1,3 @@
#!/bin/bash
echo "Hello, world!"

View File

@@ -0,0 +1,3 @@
#!/bin/bash
echo "Hello, world!"

View File

@@ -0,0 +1,3 @@
#!/bin/bash
echo "Hello, world!"

View File

@@ -0,0 +1,3 @@
#!/bin/bash
echo "Hello, world!"

View File

@@ -0,0 +1,22 @@
#!/bin/bash
payload_name=payload.tar.zst
control_name=control.tar.zst
news_name=news.tar.zst
manifest_name=manifest.json.zst
payload_dest=$TMPDIR/$payload_name
control_dest=$TMPDIR/$control_name
news_dest=$TMPDIR/$news_name
manifest_dest=$TMPDIR/$manifest_name
pkg_dest=sample-package_0.1_amd64.ropkg
rm -rf $manifest_dest
tar cf $payload_dest --zstd -C payload .
tar cf $control_dest --zstd -C control .
tar cf $news_dest --zstd -C news .
zstd manifest.json -o $manifest_dest
tar cf $pkg_dest -C $TMPDIR $payload_name $control_name $news_name $manifest_name

View File

@@ -0,0 +1,17 @@
Name: sample-package
Architecture: amd64
Version: 1.2.5-2
Priority: optional
Category: misc
Maintainer: John Doe <john@sample.com>
Provides: sample-api (= 1.2.5)
Depends: libc (>= 2.0), libstdc++ (>= 2.3), libs1 (>= 1.0) | libs2 (>= 1.2)
Recommends: sample-cli
Suggests: sample-gtk | sample-qt, sample-server
Conflicts: example-package
Enhances: basic-package
Description: A sample package
Installed-Size: 3245
This is a sample package that demonstrates the structure of a Rosetta package,
as well as the layout of the associated manifest and news files.

View File

@@ -0,0 +1,110 @@
PublishDate: 2025-07-07 09:00:00
Importance: Normal
Title: Welcome to the Rosetta Package Manager
Category: General
+------------------------------------------------------------------------------+
| Welcome to the Rosetta Package Manager! |
+------------------------------------------------------------------------------+
This is a sample news item to introduce you to the Rosetta package manager.
These news items can be used to provide information and updates to those who
use your repository.
1 News File Location
====================
News items come from four different sources:
- Repository-wide news items, located in /news
- Channel-related news items, located in /channel/<channel-id>/news
- Component-related news items, located in
/channel/<channel-id>/component/<component-id>/news
- Package-related news items, included in individual package files.
Specific news items can be published in different locations depending on who
the news is relevant for. For example, if there is some critical issue that
affects all users of a repository, /news would be the perfect place to use.
If, instead, the issue only affects users of a particular channel (maybe
you have a channel for each release of your operating system),
/channel/<channel-id>/news would be more appropriate. It's important that
news is targeted to the appropriate audience so that users don't have to sift
through news that isn't relevant to them.
2 News File Format
==================
News items have a specific plain-text format, which can be seen in this file.
The file begins with a set of headers that describe certain attributes of the
news, followed by the news content and an optional footer.
2.1 Header And Footer
---------------------
These headers include:
- PublishDate: The date and time on which the news item was published. This
is used by clients to determine which news items have been released since
the user last checked the news. This is always in UTC.
- Title: The title of the news item.
- Category: The particular category that the news item is relevant to.
Possible values include:
* General
* Security
- Importance: How important it is that the user reads this news.
Possible values include:
* Low
* Normal
* High
* Critical
The user has to go out of their way to read Low and Normal news items, while
High and Critical news items will be shown automatically when the user next
interacts with the relevant part of the repo.
Immediately following these headers are two line-feed characters. This
denotes the end of the header and the start of the content of the news files.
With two line feeds, there should be exactly one blank line visible between
the last header line and the first content line.
At the end of the file is a line with 5 asterisk (*) characters. This denotes
the end of the of the news file's content and the start of the footer. Beyond
this point, you can put extra data, such as the vim formatting commands
visible in this file, that you don't want shown to the user. If no footer is
required, the 5-asterisk marker can be omitted, and the news content will
simply end where the file ends.
2.2 Content
-----------
Between the header and footer is the actual news content that is shown to the
user. This section can be formatted in any way you prefer; any plaintext is
acceptable. This news file has been formated in a particular way to provide
example formatting that you may wish to use. It features a page header,
section and sub-section headings, and list formatting. All paragraphs
are indented with three spaces. The gap between a list item marker
and list item text is two spaces, with any following lines in that particular
list item indented with three spaces. There is one blank line between each
paragraph, and two blank lines between the last line in one section and
the heading of the next.
The only formatting rule that should be adhered to is that, like code, lines
should be kept to no longer than 80 characters. This is due to the fact that
the vast majority of users will be viewing news items within their terminal
as they are using the ropkg commands, and 80 cells is the standard width for
terminal displays.
Don't forget that, if your text editor supports in-line formatting directives
like vim, you can store these directives in the page footer so that they
aren't shown to the user.
*****
vim: shiftwidth=3 expandtab

View File

Binary file not shown.

46
doc/sample.recipe Normal file
View File

@@ -0,0 +1,46 @@
import ropkg
package_manifest = {
'Name': 'rosequartz',
'Version': '1.0',
'Maintainers': [ 'Max Wash <max@doorstuck.net>' ],
'Arch': ropkg.system.arch(),
'Platform': ropkg.system.platform_name(),
'Description': 'Cross platform C and C++ runtime',
'Depends': [ 'libc:1.0', 'libm:1.0'],
'License': '3-Clause BSD',
'Website': 'http://doorstuck.net',
'SourceWebsite': 'https://gitlab.com/doorstuck/rosequartz'
}
def get_sources():
ropkg.git.clone_repo('rosequartz', 'https://gitlab.com/doorstuck/rosequartz.git')
def configure():
ropkg.cmake.configure_project('rosequartz')
def build():
ropkg.build.build_project('rosequartz')
def package():
ropkg.pkg.build_package('rosequartz', package_manifest)
recipe = {
'name': 'rosequartz',
'steps': {
'get-src': get_sources,
'configure': configure,
'build': build,
'package': package,
}
}
ropkg.run_recipe(recipe)
# vim: ft=python

View File

@@ -6,10 +6,9 @@ set_target_properties(libropkg PROPERTIES
OUTPUT_NAME ropkg) OUTPUT_NAME ropkg)
target_link_libraries(libropkg target_link_libraries(libropkg
Bluelib::Io Bluelib::Io
${ZSTD_LIBRARY}) Bluelib::Compress)
target_include_directories(libropkg PUBLIC target_include_directories(libropkg PUBLIC
include/ include/)
${ZSTD_INCLUDE_DIR})
target_compile_definitions(libropkg PRIVATE target_compile_definitions(libropkg PRIVATE
LIBROPKG_EXPORT=1 LIBROPKG_EXPORT=1
LIBROPKG_STATIC=0) LIBROPKG_STATIC=0)

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

@@ -0,0 +1,13 @@
#ifndef ROPKG_PACKAGE_H_
#define ROPKG_PACKAGE_H_
#include <blue/core/error.h>
#include <blue/io/directory.h>
#include <ropkg/misc.h>
struct ropkg_reader;
ROPKG_API b_result
ropkg_extract_metadata(struct ropkg_reader *reader, b_directory *dest);
#endif

View File

@@ -0,0 +1,51 @@
#ifndef ROPKG_PKG_EXPR_H_
#define ROPKG_PKG_EXPR_H_
#include <ropkg/misc.h>
#include <ropkg/status.h>
enum ropkg_pkg_expr_type {
ROPKG_PKG_EXPR_NODE_PKG,
ROPKG_PKG_EXPR_NODE_AND,
ROPKG_PKG_EXPR_NODE_OR,
ROPKG_PKG_EXPR_NODE_GROUP,
};
enum ropkg_comparison_op;
struct ropkg_version;
struct ropkg_pkg_expr;
struct ropkg_pkg_expr_pkg;
struct ropkg_pkg_expr_and;
struct ropkg_pkg_expr_or;
ROPKG_API void ropkg_pkg_expr_destroy(struct ropkg_pkg_expr *expr);
ROPKG_API b_result
ropkg_pkg_expr_parse(const char *s, struct ropkg_pkg_expr **out);
ROPKG_API enum ropkg_pkg_expr_type ropkg_pkg_expr_get_type(
const struct ropkg_pkg_expr *expr);
ROPKG_API struct ropkg_pkg_expr_pkg *ropkg_pkg_expr_pkg_create(void);
ROPKG_API const char *ropkg_pkg_expr_pkg_get_name(
const struct ropkg_pkg_expr_pkg *expr);
ROPKG_API enum ropkg_comparison_op ropkg_pkg_expr_pkg_get_version_predicate(
const struct ropkg_pkg_expr_pkg *expr);
ROPKG_API const struct ropkg_version *ropkg_pkg_expr_pkg_get_version(
const struct ropkg_pkg_expr_pkg *expr);
ROPKG_API struct ropkg_pkg_expr_and *ropkg_pkg_expr_and_create(void);
ROPKG_API const struct ropkg_pkg_expr *ropkg_pkg_expr_and_get_left_node(
const struct ropkg_pkg_expr_and *expr);
ROPKG_API const struct ropkg_pkg_expr *ropkg_pkg_expr_and_get_right_node(
const struct ropkg_pkg_expr_and *expr);
ROPKG_API struct ropkg_pkg_expr_or *ropkg_pkg_expr_or_create(void);
ROPKG_API const struct ropkg_pkg_expr *ropkg_pkg_expr_or_get_left_node(
const struct ropkg_pkg_expr_or *expr);
ROPKG_API const struct ropkg_pkg_expr *ropkg_pkg_expr_or_get_right_node(
const struct ropkg_pkg_expr_or *expr);
#endif

View File

@@ -0,0 +1,33 @@
#ifndef ROPKG_READER_H_
#define ROPKG_READER_H_
#include <ropkg/misc.h>
#include <ropkg/status.h>
struct ropkg_reader;
struct b_cstream;
struct ropkg_file_info {
char f_filename[255];
int f_mode;
int f_uid, f_gid;
unsigned long f_filesize;
unsigned long f_mtime;
int f_type;
char f_linkname[100];
};
ROPKG_API enum ropkg_status ropkg_reader_open(
struct b_cstream *fp,
struct ropkg_reader **out);
ROPKG_API enum ropkg_status ropkg_reader_close(struct ropkg_reader *pkg);
ROPKG_API const struct ropkg_file_info *ropkg_reader_current_file(
struct ropkg_reader *reader);
ROPKG_API enum ropkg_status ropkg_reader_move_next(struct ropkg_reader *pkg);
ROPKG_API enum ropkg_status ropkg_reader_move_to_file(
struct ropkg_reader *pkg,
const char *name);
ROPKG_API bool ropkg_reader_eof(const struct ropkg_reader *pkg);
#endif

View File

@@ -6,9 +6,10 @@
#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

132
libropkg/package.c Normal file
View File

@@ -0,0 +1,132 @@
#include "reader.h"
#include <blue/core/error.h>
#include <blue/io/directory.h>
#include <ropkg/reader.h>
static b_result extract_file(
struct ropkg_reader *pkg,
const char *path_cstr,
b_directory *dest)
{
b_path *path = b_path_create_from_cstr(path_cstr);
if (!path) {
return ROPKG_RESULT_ERR(NO_MEMORY);
}
b_path *dir_path = NULL;
b_path_get_directory(path, &dir_path);
if (!dir_path) {
b_path_release(path);
return ROPKG_RESULT_ERR(NO_MEMORY);
}
b_result result = B_RESULT_SUCCESS;
if (b_path_length(dir_path) > 0) {
b_directory *parent_dir;
result = b_directory_open(
dest,
dir_path,
B_DIRECTORY_OPEN_CREATE_INTERMEDIATE,
&parent_dir);
b_directory_release(parent_dir);
}
if (b_result_is_error(result)) {
goto end;
}
b_file *dest_file = NULL;
result = b_file_open(
dest,
path,
B_FILE_WRITE_ONLY | B_FILE_CREATE | B_FILE_BINARY,
&dest_file);
if (b_result_is_error(result)) {
goto end;
}
char data[] = "Hello";
size_t nr_written = 0;
b_file_write(dest_file, 0, sizeof data, data, &nr_written);
b_file_release(dest_file);
end:
b_path_release(path);
b_path_release(dir_path);
return result;
}
static b_result extract_subpackage(
struct ropkg_reader *pkg,
const char *name,
b_directory *dest)
{
b_directory *dest_subdir;
b_result result = b_directory_open(
dest,
B_RV_PATH(name),
B_DIRECTORY_OPEN_CREATE,
&dest_subdir);
if (b_result_is_error(result)) {
return result;
}
b_cstream_begin_compressed_section(pkg->r_stream, NULL);
struct ropkg_reader *subpkg = NULL;
enum ropkg_status status = ropkg_reader_open(pkg->r_stream, &subpkg);
if (status != ROPKG_SUCCESS) {
result = ROPKG_RESULT_STATUS(status);
goto end;
}
while (!ropkg_reader_eof(subpkg)) {
const struct ropkg_file_info *file
= ropkg_reader_current_file(subpkg);
result = extract_file(subpkg, file->f_filename, dest_subdir);
if (b_result_is_error(result)) {
break;
}
status = ropkg_reader_move_next(subpkg);
if (status != ROPKG_SUCCESS) {
result = ROPKG_RESULT_STATUS(status);
break;
}
}
end:
b_cstream_end_compressed_section(pkg->r_stream, NULL, NULL);
return result;
}
b_result ropkg_extract_metadata(struct ropkg_reader *pkg, b_directory *dest)
{
b_result result = B_RESULT_SUCCESS;
while (!ropkg_reader_eof(pkg)) {
const struct ropkg_file_info *file
= ropkg_reader_current_file(pkg);
int section = -1;
if (!strncmp(file->f_filename, "meta.tar", 8)) {
result = extract_subpackage(pkg, "meta", dest);
} else if (!strncmp(file->f_filename, "control.tar", 11)) {
result = extract_subpackage(pkg, "control", dest);
}
if (b_result_is_error(result)) {
break;
}
enum ropkg_status status = ropkg_reader_move_next(pkg);
if (status != ROPKG_SUCCESS) {
result = ROPKG_RESULT_STATUS(status);
break;
}
}
return result;
}

639
libropkg/pkg-expr.c Normal file
View File

@@ -0,0 +1,639 @@
#include "pkg-expr.h"
#include <blue/core/error.h>
#include <blue/object/string.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
enum parse_state {
PARSE_NONE = 0,
PARSE_PKG_NAME,
PARSE_PKG_MID,
PARSE_PKG_VERSION,
PARSE_PKG_VERSION_PREDICATE,
PARSE_PKG_VERSION_MID,
PARSE_PKG_VERSION_ID,
PARSE_PKG_END,
PARSE_AND_SEP,
PARSE_OR_SEP,
};
struct parse_ctx {
const char *ctx_str;
b_string *ctx_temp;
enum parse_state ctx_state;
size_t ctx_i;
b_queue ctx_stack, ctx_queue;
};
static struct ropkg_pkg_expr *parse_ctx_peek_stack(struct parse_ctx *ctx)
{
b_queue_entry *entry = b_queue_last(&ctx->ctx_stack);
if (!entry) {
return NULL;
}
return b_unbox(struct ropkg_pkg_expr, entry, expr_entry);
}
static struct ropkg_pkg_expr *parse_ctx_pop(struct parse_ctx *ctx)
{
b_queue_entry *entry = b_queue_pop_back(&ctx->ctx_stack);
if (!entry) {
return NULL;
}
return b_unbox(struct ropkg_pkg_expr, entry, expr_entry);
}
static void parse_ctx_push(struct parse_ctx *ctx, struct ropkg_pkg_expr *expr)
{
b_queue_push_back(&ctx->ctx_stack, &expr->expr_entry);
}
static struct ropkg_pkg_expr *parse_ctx_dequeue(struct parse_ctx *ctx)
{
b_queue_entry *entry = b_queue_pop_front(&ctx->ctx_queue);
if (!entry) {
return NULL;
}
return b_unbox(struct ropkg_pkg_expr, entry, expr_entry);
}
static void parse_ctx_enqueue(
struct parse_ctx *ctx,
struct ropkg_pkg_expr *expr)
{
b_queue_push_back(&ctx->ctx_queue, &expr->expr_entry);
}
static char parse_ctx_peek(struct parse_ctx *ctx)
{
return ctx->ctx_str[ctx->ctx_i];
}
static char parse_ctx_advance(struct parse_ctx *ctx)
{
if (!ctx->ctx_str[ctx->ctx_i]) {
return 0;
}
ctx->ctx_i++;
while (1) {
char c = parse_ctx_peek(ctx);
if (isspace(c)) {
ctx->ctx_i++;
continue;
}
break;
}
return ctx->ctx_str[ctx->ctx_i];
}
static void parse_ctx_cleanup(struct parse_ctx *ctx)
{
if (ctx->ctx_temp) {
b_string_release(ctx->ctx_temp);
}
struct ropkg_pkg_expr *expr = parse_ctx_dequeue(ctx);
while (expr) {
ropkg_pkg_expr_destroy(expr);
expr = parse_ctx_dequeue(ctx);
}
expr = parse_ctx_pop(ctx);
while (expr) {
ropkg_pkg_expr_destroy(expr);
expr = parse_ctx_pop(ctx);
}
}
enum ropkg_status ropkg_pkg_expr_create(struct ropkg_pkg_expr **out)
{
return ROPKG_SUCCESS;
}
void ropkg_pkg_expr_destroy(struct ropkg_pkg_expr *expr)
{
b_queue q = B_QUEUE_INIT;
b_queue_push_back(&q, &expr->expr_entry);
while (1) {
b_queue_entry *entry = b_queue_pop_front(&q);
if (!entry) {
break;
}
expr = b_unbox(struct ropkg_pkg_expr, entry, expr_entry);
switch (expr->expr_type) {
case ROPKG_PKG_EXPR_NODE_PKG: {
struct ropkg_pkg_expr_pkg *pkg_node
= (struct ropkg_pkg_expr_pkg *)expr;
if (pkg_node->pkg_name) {
free(pkg_node->pkg_name);
}
if (pkg_node->pkg_version) {
ropkg_version_destroy(pkg_node->pkg_version);
}
break;
case ROPKG_PKG_EXPR_NODE_AND: {
struct ropkg_pkg_expr_and *and_node
= (struct ropkg_pkg_expr_and *)expr;
if (and_node->and_left) {
b_queue_push_back(
&q,
&and_node->and_left->expr_entry);
}
if (and_node->and_right) {
b_queue_push_back(
&q,
&and_node->and_right->expr_entry);
}
break;
}
case ROPKG_PKG_EXPR_NODE_OR: {
struct ropkg_pkg_expr_or *or_node
= (struct ropkg_pkg_expr_or *)expr;
if (or_node->or_left) {
b_queue_push_back(
&q,
&or_node->or_left->expr_entry);
}
if (or_node->or_right) {
b_queue_push_back(
&q,
&or_node->or_right->expr_entry);
}
break;
}
default:
break;
}
}
free(expr);
}
}
static b_result parse_pkg_name(
struct parse_ctx *ctx,
struct ropkg_pkg_expr_pkg *pkg)
{
b_string_clear(ctx->ctx_temp);
while (1) {
char c = parse_ctx_peek(ctx);
if (!isalnum(c) && c != '+' && c != '-' && c != '_') {
break;
}
char s[] = {c, 0};
b_string_append_cstr(ctx->ctx_temp, s);
parse_ctx_advance(ctx);
}
pkg->pkg_name = b_string_steal(ctx->ctx_temp);
return B_RESULT_SUCCESS;
}
static b_result parse_pkg_version_predicate(
struct parse_ctx *ctx,
struct ropkg_pkg_expr_pkg *pkg)
{
b_string_clear(ctx->ctx_temp);
while (1) {
char c = parse_ctx_peek(ctx);
if (!ispunct(c)) {
break;
}
char s[] = {c, 0};
b_string_append_cstr(ctx->ctx_temp, s);
parse_ctx_advance(ctx);
}
const char *pred = b_string_ptr(ctx->ctx_temp);
if (!strcmp(pred, ">=")) {
pkg->pkg_version_predicate = ROPKG_OP_GREATER_EQUAL;
} else if (!strcmp(pred, ">")) {
pkg->pkg_version_predicate = ROPKG_OP_GREATER_THAN;
} else if (!strcmp(pred, "<=")) {
pkg->pkg_version_predicate = ROPKG_OP_LESS_EQUAL;
} else if (!strcmp(pred, "<")) {
pkg->pkg_version_predicate = ROPKG_OP_LESS_THAN;
} else if (!strcmp(pred, "==") || !strcmp(pred, "=")) {
pkg->pkg_version_predicate = ROPKG_OP_EQUAL;
} else {
return b_error_with_code(
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_PKG_EXPR);
}
return B_RESULT_SUCCESS;
}
static b_result parse_pkg_version_id(
struct parse_ctx *ctx,
struct ropkg_pkg_expr_pkg *pkg)
{
b_string_clear(ctx->ctx_temp);
while (1) {
char c = parse_ctx_peek(ctx);
if (!isalnum(c) && c != '.' && c != '-' && c != '~') {
break;
}
char s[] = {c, 0};
b_string_append_cstr(ctx->ctx_temp, s);
parse_ctx_advance(ctx);
}
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, b_string_ptr(ctx->ctx_temp));
if (b_result_is_error(result)) {
ropkg_version_destroy(version);
version = NULL;
}
pkg->pkg_version = version;
return b_result_propagate(result);
}
static b_result parse_pkg_version(
struct parse_ctx *ctx,
struct ropkg_pkg_expr_pkg *pkg)
{
parse_ctx_advance(ctx);
b_result result = B_RESULT_SUCCESS;
char c = parse_ctx_peek(ctx);
if (c == '>' || c == '<' || c == '=') {
result = parse_pkg_version_predicate(ctx, pkg);
}
if (b_result_is_error(result)) {
return result;
}
c = parse_ctx_peek(ctx);
if (isalnum(c)) {
result = parse_pkg_version_id(ctx, pkg);
} else {
result = b_error_with_code(
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_PKG_EXPR);
}
c = parse_ctx_peek(ctx);
if (c == ')') {
parse_ctx_advance(ctx);
} else {
result = b_error_with_code(
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_PKG_EXPR);
}
return result;
}
static b_result parse_pkg(struct parse_ctx *ctx)
{
struct ropkg_pkg_expr_pkg *pkg = ropkg_pkg_expr_pkg_create();
b_result result = B_RESULT_SUCCESS;
char c = parse_ctx_peek(ctx);
if (isalpha(c)) {
result = parse_pkg_name(ctx, pkg);
}
if (b_result_is_error(result)) {
ropkg_pkg_expr_destroy((struct ropkg_pkg_expr *)pkg);
return b_result_propagate(result);
}
c = parse_ctx_peek(ctx);
if (c == '(') {
result = parse_pkg_version(ctx, pkg);
}
if (b_result_is_error(result)) {
ropkg_pkg_expr_destroy((struct ropkg_pkg_expr *)pkg);
return b_result_propagate(result);
}
parse_ctx_enqueue(ctx, &pkg->pkg_base);
return result;
}
static b_result parse_and(struct parse_ctx *ctx)
{
parse_ctx_advance(ctx);
struct ropkg_pkg_expr_and *and_node = ropkg_pkg_expr_and_create();
while (1) {
struct ropkg_pkg_expr *top = parse_ctx_peek_stack(ctx);
if (!top) {
break;
}
if (top->expr_type == ROPKG_PKG_EXPR_NODE_GROUP) {
break;
}
if (top->expr_type < and_node->and_base.expr_type) {
break;
}
top = parse_ctx_pop(ctx);
parse_ctx_enqueue(ctx, top);
}
parse_ctx_push(ctx, &and_node->and_base);
return B_RESULT_SUCCESS;
}
static b_result parse_or(struct parse_ctx *ctx)
{
parse_ctx_advance(ctx);
struct ropkg_pkg_expr_or *or_node = ropkg_pkg_expr_or_create();
while (1) {
struct ropkg_pkg_expr *top = parse_ctx_peek_stack(ctx);
if (!top) {
break;
}
if (top->expr_type < or_node->or_base.expr_type) {
break;
}
top = parse_ctx_pop(ctx);
parse_ctx_enqueue(ctx, top);
}
parse_ctx_push(ctx, &or_node->or_base);
return B_RESULT_SUCCESS;
}
static b_result parse_group_start(struct parse_ctx *ctx)
{
parse_ctx_advance(ctx);
struct ropkg_pkg_expr *group_node = malloc(sizeof *group_node);
if (!group_node) {
return b_error_with_code(
ROPKG_ERROR_VENDOR,
ROPKG_ERR_NO_MEMORY);
}
memset(group_node, 0x0, sizeof *group_node);
group_node->expr_type = ROPKG_PKG_EXPR_NODE_GROUP;
parse_ctx_push(ctx, group_node);
return B_RESULT_SUCCESS;
}
static b_result parse_group_end(struct parse_ctx *ctx)
{
parse_ctx_advance(ctx);
bool done = false;
while (!done) {
struct ropkg_pkg_expr *expr = parse_ctx_pop(ctx);
if (!expr) {
return b_error_with_code(
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_PKG_EXPR);
}
switch (expr->expr_type) {
case ROPKG_PKG_EXPR_NODE_GROUP:
free(expr);
done = true;
break;
default:
parse_ctx_enqueue(ctx, expr);
break;
}
}
return B_RESULT_SUCCESS;
}
b_result ropkg_pkg_expr_parse(const char *s, struct ropkg_pkg_expr **out)
{
while (isspace(*s)) {
s++;
}
struct parse_ctx ctx = {
.ctx_str = s,
.ctx_temp = b_string_create(),
};
b_result result = B_RESULT_SUCCESS;
while (!b_result_is_error(result)) {
char c = parse_ctx_peek(&ctx);
if (c == 0) {
break;
}
if (c == '(') {
result = parse_group_start(&ctx);
} else if (c == ')') {
result = parse_group_end(&ctx);
} else if (c == ',') {
result = parse_and(&ctx);
} else if (c == '|') {
result = parse_or(&ctx);
} else if (isalpha(c)) {
result = parse_pkg(&ctx);
} else {
result = b_error_with_code(
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_PKG_EXPR);
break;
}
}
if (b_result_is_error(result)) {
parse_ctx_cleanup(&ctx);
return result;
}
struct ropkg_pkg_expr *expr = parse_ctx_pop(&ctx);
while (expr) {
parse_ctx_enqueue(&ctx, expr);
expr = parse_ctx_pop(&ctx);
}
while (1) {
expr = parse_ctx_dequeue(&ctx);
if (!expr) {
break;
}
if (expr->expr_type == ROPKG_PKG_EXPR_NODE_PKG) {
parse_ctx_push(&ctx, expr);
continue;
}
if (expr->expr_type == ROPKG_PKG_EXPR_NODE_GROUP) {
parse_ctx_cleanup(&ctx);
return b_error_with_code(
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_PKG_EXPR);
}
struct ropkg_pkg_expr *left = NULL, *right = NULL;
right = parse_ctx_pop(&ctx);
left = parse_ctx_pop(&ctx);
if (!left || !right) {
parse_ctx_cleanup(&ctx);
return b_error_with_code(
ROPKG_ERROR_VENDOR,
ROPKG_ERR_INVALID_PKG_EXPR);
}
switch (expr->expr_type) {
case ROPKG_PKG_EXPR_NODE_AND: {
struct ropkg_pkg_expr_and *and_node
= (struct ropkg_pkg_expr_and *)expr;
and_node->and_left = left;
and_node->and_right = right;
break;
}
case ROPKG_PKG_EXPR_NODE_OR: {
struct ropkg_pkg_expr_or *or_node
= (struct ropkg_pkg_expr_or *)expr;
or_node->or_left = left;
or_node->or_right = right;
break;
}
default:
break;
}
parse_ctx_push(&ctx, expr);
}
*out = parse_ctx_pop(&ctx);
parse_ctx_cleanup(&ctx);
return result;
}
enum ropkg_pkg_expr_type ropkg_pkg_expr_get_type(
const struct ropkg_pkg_expr *expr)
{
return expr->expr_type;
}
struct ropkg_pkg_expr_pkg *ropkg_pkg_expr_pkg_create(void)
{
struct ropkg_pkg_expr_pkg *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(out, 0x0, sizeof *out);
out->pkg_base.expr_type = ROPKG_PKG_EXPR_NODE_PKG;
return out;
}
const char *ropkg_pkg_expr_pkg_get_name(const struct ropkg_pkg_expr_pkg *expr)
{
return expr->pkg_name;
}
enum ropkg_comparison_op ropkg_pkg_expr_pkg_get_version_predicate(
const struct ropkg_pkg_expr_pkg *expr)
{
return expr->pkg_version_predicate;
}
const struct ropkg_version *ropkg_pkg_expr_pkg_get_version(
const struct ropkg_pkg_expr_pkg *expr)
{
return expr->pkg_version;
}
struct ropkg_pkg_expr_and *ropkg_pkg_expr_and_create(void)
{
struct ropkg_pkg_expr_and *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(out, 0x0, sizeof *out);
out->and_base.expr_type = ROPKG_PKG_EXPR_NODE_AND;
return out;
}
const struct ropkg_pkg_expr *ropkg_pkg_expr_and_get_left_node(
const struct ropkg_pkg_expr_and *expr)
{
return expr->and_left;
}
const struct ropkg_pkg_expr *ropkg_pkg_expr_and_get_right_node(
const struct ropkg_pkg_expr_and *expr)
{
return expr->and_right;
}
struct ropkg_pkg_expr_or *ropkg_pkg_expr_or_create(void)
{
struct ropkg_pkg_expr_or *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(out, 0x0, sizeof *out);
out->or_base.expr_type = ROPKG_PKG_EXPR_NODE_OR;
return out;
}
const struct ropkg_pkg_expr *ropkg_pkg_expr_or_get_left_node(
const struct ropkg_pkg_expr_or *expr)
{
return expr->or_left;
}
const struct ropkg_pkg_expr *ropkg_pkg_expr_or_get_right_node(
const struct ropkg_pkg_expr_or *expr)
{
return expr->or_right;
}

29
libropkg/pkg-expr.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef _PKG_EXPR_H_
#define _PKG_EXPR_H_
#include <ropkg/pkg-expr.h>
#include <ropkg/version.h>
struct ropkg_pkg_expr {
enum ropkg_pkg_expr_type expr_type;
b_queue_entry expr_entry;
};
struct ropkg_pkg_expr_pkg {
struct ropkg_pkg_expr pkg_base;
char *pkg_name;
enum ropkg_comparison_op pkg_version_predicate;
struct ropkg_version *pkg_version;
};
struct ropkg_pkg_expr_and {
struct ropkg_pkg_expr and_base;
struct ropkg_pkg_expr *and_left, *and_right;
};
struct ropkg_pkg_expr_or {
struct ropkg_pkg_expr or_base;
struct ropkg_pkg_expr *or_left, *or_right;
};
#endif

239
libropkg/reader.c Normal file
View File

@@ -0,0 +1,239 @@
#include "reader.h"
#include <ropkg/reader.h>
#include <stdlib.h>
#include <string.h>
static struct ustar_header null_header = {0};
static void copy_string(
const char *src,
size_t src_max,
char *dest,
size_t dest_max)
{
size_t start = strlen(dest);
size_t max = b_min(size_t, src_max, dest_max - start);
for (size_t i = 0; i < max; i++) {
dest[start + i] = src[i];
if (!src[i]) {
break;
}
}
}
static long long from_octal(const char *s, size_t len)
{
long at = (long)len - 2;
long long value = 0;
long mul = 1;
while (at >= 0) {
char c = s[at];
if (c == 0 || c == ' ') {
break;
}
int v = c - '0';
value += (v * mul);
mul *= 8;
at--;
}
return value;
}
static enum ropkg_status read_file_header(struct ropkg_reader *reader)
{
if (b_cstream_in_compressed_section(reader->r_stream)) {
b_cstream_tx_bytes_uncompressed(
reader->r_stream,
&reader->r_cur_header_offset);
} else {
b_cstream_tx_bytes(
reader->r_stream,
&reader->r_cur_header_offset);
}
size_t nr_read = 0;
b_status status = b_cstream_read(
reader->r_stream,
&reader->r_cur_header,
sizeof reader->r_cur_header,
&nr_read);
if (!B_OK(status)) {
return ROPKG_ERR_IO_FAILURE;
}
if (nr_read != sizeof reader->r_cur_header) {
return ROPKG_ERR_INVALID_PKG_FILE;
}
memset(&reader->f_cur_file, 0x0, sizeof reader->f_cur_file);
copy_string(
reader->r_cur_header.tar_filename_prefix,
sizeof reader->r_cur_header.tar_filename_prefix,
reader->f_cur_file.f_filename,
sizeof reader->f_cur_file.f_filename);
copy_string(
reader->r_cur_header.tar_filename,
sizeof reader->r_cur_header.tar_filename,
reader->f_cur_file.f_filename,
sizeof reader->f_cur_file.f_filename);
copy_string(
reader->r_cur_header.tar_linkname,
sizeof reader->r_cur_header.tar_linkname,
reader->f_cur_file.f_linkname,
sizeof reader->f_cur_file.f_linkname);
reader->f_cur_file.f_mode = from_octal(
reader->r_cur_header.tar_mode,
sizeof reader->r_cur_header.tar_mode);
reader->f_cur_file.f_uid = from_octal(
reader->r_cur_header.tar_uid,
sizeof reader->r_cur_header.tar_uid);
reader->f_cur_file.f_gid = from_octal(
reader->r_cur_header.tar_gid,
sizeof reader->r_cur_header.tar_gid);
reader->f_cur_file.f_filesize = from_octal(
reader->r_cur_header.tar_filesize,
sizeof reader->r_cur_header.tar_filesize);
reader->f_cur_file.f_mtime = from_octal(
reader->r_cur_header.tar_mtime,
sizeof reader->r_cur_header.tar_mtime);
reader->f_cur_file.f_type = reader->r_cur_header.tar_type[0];
return ROPKG_SUCCESS;
}
enum ropkg_status ropkg_reader_open(b_cstream *fp, struct ropkg_reader **out)
{
struct ropkg_reader *reader = malloc(sizeof *reader);
if (!reader) {
return ROPKG_ERR_NO_MEMORY;
}
memset(reader, 0x0, sizeof *reader);
reader->r_stream = fp;
enum ropkg_status status = read_file_header(reader);
if (status != ROPKG_SUCCESS) {
free(reader);
return status;
}
if (!memcmp(&reader->r_cur_header, &null_header, sizeof null_header)) {
b_cstream_skip(reader->r_stream, sizeof null_header, NULL);
reader->r_eof = true;
}
*out = reader;
return ROPKG_SUCCESS;
}
enum ropkg_status ropkg_reader_close(struct ropkg_reader *pkg)
{
free(pkg);
return ROPKG_SUCCESS;
}
const struct ropkg_file_info *ropkg_reader_current_file(
struct ropkg_reader *reader)
{
return &reader->f_cur_file;
}
enum ropkg_status ropkg_reader_move_next(struct ropkg_reader *pkg)
{
if (pkg->r_eof) {
return ROPKG_ERR_NO_DATA;
}
size_t pos = 0;
if (b_cstream_in_compressed_section(pkg->r_stream)) {
b_cstream_tx_bytes_uncompressed(pkg->r_stream, &pos);
} else {
b_cstream_tx_bytes(pkg->r_stream, &pos);
}
size_t end = pkg->r_cur_header_offset + sizeof pkg->r_cur_header
+ pkg->f_cur_file.f_filesize;
if ((end % 512) != 0) {
end += 512 - (end % 512);
}
size_t skip = end - pos;
size_t nr_skipped = 0;
b_cstream_skip(pkg->r_stream, skip, &nr_skipped);
if (nr_skipped != skip) {
pkg->r_eof = true;
return ROPKG_ERR_INVALID_PKG_FILE;
}
enum ropkg_status status = read_file_header(pkg);
if (status != ROPKG_SUCCESS) {
return status;
}
if (!memcmp(&pkg->r_cur_header, &null_header, sizeof null_header)) {
b_cstream_skip(pkg->r_stream, sizeof null_header, NULL);
pkg->r_eof = true;
}
return ROPKG_SUCCESS;
}
static bool compare_name(const char *target, const char *candidate)
{
bool result = true;
for (size_t i = 0;; i++) {
if (target[i] == '*') {
break;
}
if (target[i] != candidate[i]) {
result = false;
break;
}
if (candidate[i] == 0 || target[i] == 0) {
break;
}
}
return result;
}
enum ropkg_status ropkg_reader_move_to_file(
struct ropkg_reader *pkg,
const char *name)
{
enum ropkg_status status = ROPKG_ERR_NO_ENTRY;
while (!ropkg_reader_eof(pkg)) {
struct ropkg_file_info *file = &pkg->f_cur_file;
bool match = compare_name(name, file->f_filename);
if (match) {
return ROPKG_SUCCESS;
}
status = ropkg_reader_move_next(pkg);
if (status != ROPKG_SUCCESS) {
return status;
}
}
return ROPKG_ERR_NO_ENTRY;
}
bool ropkg_reader_eof(const struct ropkg_reader *pkg)
{
return pkg->r_eof;
}

17
libropkg/reader.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef _READER_H_
#define _READER_H_
#include "tar.h"
#include <blue/compress/cstream.h>
#include <ropkg/reader.h>
struct ropkg_reader {
b_cstream *r_stream;
struct ustar_header r_cur_header;
size_t r_cur_header_offset;
struct ropkg_file_info f_cur_file;
bool r_eof;
};
#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[] = {

View File

@@ -1,4 +1,6 @@
file(GLOB_RECURSE sources *.c *.h) file(GLOB_RECURSE sources *.c *.h)
add_executable(ropam ${sources}) add_executable(ropam ${sources})
target_link_libraries(ropam Bluelib::Core Bluelib::Object Bluelib::Io Bluelib::Term Bluelib::Cmd) target_link_libraries(ropam
libropkg
Bluelib::Core Bluelib::Object Bluelib::Io Bluelib::Term Bluelib::Cmd)

60
ropam/bootstrap.c Normal file
View File

@@ -0,0 +1,60 @@
#include "commands.h"
#include <blue/cmd.h>
#include <ropkg/instance.h>
enum {
ARG_PATH,
};
static int bootstrap(
const b_command *self,
const b_arglist *opt,
const b_array *args)
{
const char *root_path = NULL;
b_status status = b_arglist_get_string(
opt,
B_COMMAND_INVALID_ID,
ARG_PATH,
0,
&root_path);
if (!B_OK(status)) {
b_arglist_report_missing_args(
opt,
B_COMMAND_INVALID_ID,
ARG_PATH,
0);
return -1;
}
struct ropkg_instance *inst;
b_result result = ropkg_instance_bootstrap(root_path, &inst);
if (b_result_is_error(result)) {
b_throw(result);
return -1;
}
return 0;
}
B_COMMAND(CMD_BOOTSTRAP, CMD_ROOT)
{
B_COMMAND_NAME("bootstrap");
B_COMMAND_DESC(
"initialise a new Rosetta package manager instance. use this "
"command to prepare a sysroot for the installation of Rosetta "
"packages.");
B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT);
B_COMMAND_FUNCTION(bootstrap);
B_COMMAND_ARG(ARG_PATH)
{
B_ARG_NAME("sysroot");
B_ARG_DESC(
"the path to the system root directory to initialise.");
B_ARG_NR_VALUES(1);
}
B_COMMAND_HELP_OPTION();
}

View File

@@ -6,6 +6,7 @@ enum {
CMD_SYNC, CMD_SYNC,
CMD_REMOVE, CMD_REMOVE,
CMD_QUERY, CMD_QUERY,
CMD_BOOTSTRAP,
CMD_REPO, CMD_REPO,
CMD_REPO_ADD, CMD_REPO_ADD,

View File

@@ -1,6 +1,8 @@
#include "commands.h" #include "commands.h"
#include <blue/cmd.h> #include <blue/cmd.h>
#include <blue/core/error.h>
#include <blue/term/print.h>
B_COMMAND(CMD_ROOT, B_COMMAND_INVALID_ID) B_COMMAND(CMD_ROOT, B_COMMAND_INVALID_ID)
{ {
@@ -28,5 +30,8 @@ B_COMMAND(CMD_ROOT, B_COMMAND_INVALID_ID)
int main(int argc, const char **argv) 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); return b_command_dispatch(CMD_ROOT, argc, argv);
} }

View File

@@ -1,4 +1,4 @@
file(GLOB sources *.c *.h) file(GLOB_RECURSE sources *.c *.h)
add_executable(ropkg ${sources}) add_executable(ropkg ${sources})
target_link_libraries(ropkg target_link_libraries(ropkg

View File

@@ -1,55 +0,0 @@
#include "commands.h"
#include <blue/cmd.h>
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);
}
}

View File

@@ -3,12 +3,29 @@
enum { enum {
CMD_ROOT, CMD_ROOT,
CMD_CREATE,
CMD_BUILD, CMD_PACKAGE,
CMD_QUERY, CMD_PACKAGE_CREATE,
CMD_EXTRACT, CMD_PACKAGE_BUILD,
CMD_INSTALL, CMD_PACKAGE_EXTRACT,
CMD_COMPARE_VERSION, 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 #endif

View File

@@ -1,6 +1,8 @@
#include "commands.h" #include "commands.h"
#include <blue/cmd.h> #include <blue/cmd.h>
#include <blue/core/error.h>
#include <blue/term/print.h>
B_COMMAND(CMD_ROOT, B_COMMAND_INVALID_ID) B_COMMAND(CMD_ROOT, B_COMMAND_INVALID_ID)
{ {
@@ -11,9 +13,17 @@ B_COMMAND(CMD_ROOT, B_COMMAND_INVALID_ID)
"package files."); "package files.");
B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT);
B_COMMAND_HELP_OPTION(); B_COMMAND_HELP_OPTION();
B_COMMAND_USAGE()
{
B_COMMAND_USAGE_COMMAND_PLACEHOLDER();
}
} }
int main(int argc, const char **argv) 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); return b_command_dispatch(CMD_ROOT, argc, argv);
} }

106
ropkg/manifest/create.c Normal file
View File

@@ -0,0 +1,106 @@
#include "../commands.h"
#include <blue/cmd.h>
#include <ropkg/pkg-expr.h>
#include <ropkg/version.h>
#include <stdio.h>
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);
}
}
}

19
ropkg/manifest/root.c Normal file
View File

@@ -0,0 +1,19 @@
#include "../commands.h"
#include <blue/cmd.h>
#include <blue/core/error.h>
#include <blue/term/print.h>
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();
}
}

307
ropkg/manifest/validate.c Normal file
View File

@@ -0,0 +1,307 @@
#include "../commands.h"
#include <blue/cmd.h>
#include <blue/core/error.h>
#include <ropkg/manifest.h>
#include <ropkg/pkg-expr.h>
#include <ropkg/status.h>
#include <ropkg/version.h>
#include <stdio.h>
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);
}
}

19
ropkg/news/root.c Normal file
View File

@@ -0,0 +1,19 @@
#include "../commands.h"
#include <blue/cmd.h>
#include <blue/core/error.h>
#include <blue/term/print.h>
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();
}
}

102
ropkg/package/build.c Normal file
View File

@@ -0,0 +1,102 @@
#include "../commands.h"
#include <blue/cmd.h>
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);
}
}

View File

@@ -1,11 +1,13 @@
#include "commands.h" #include "../commands.h"
#include <blue/cmd.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/io/directory.h>
#include <blue/term/print.h> #include <blue/term/print.h>
#include <errno.h> #include <errno.h>
#include <ropkg/compress.h> #include <ropkg/manifest.h>
#include <ropkg/stream.h>
#include <ropkg/writer.h> #include <ropkg/writer.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -41,14 +43,17 @@ static int add_file_to_archive_ex(
const char *dest) const char *dest)
{ {
b_file *fp; b_file *fp;
b_status status = b_file_open( b_result result = b_file_open(
src_dir, src_dir,
src, src,
B_FILE_READ_ONLY | B_FILE_BINARY, B_FILE_READ_ONLY | B_FILE_BINARY,
&fp); &fp);
if (!fp) { if (!fp) {
b_err("cannot open file '%s'", src); b_throw_error_with_template_caused_by_error(
b_i("reason: %s", b_status_to_string(status)); ROPKG_ERROR_VENDOR,
ROPKG_ERR_DIR_OPEN_FAILED,
result,
B_ERROR_PARAM("filepath", b_path_ptr(src)));
return -1; return -1;
} }
@@ -63,7 +68,7 @@ static int add_file_to_archive_ex(
int c; int c;
while (1) { while (1) {
size_t r; 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)) { if (!B_OK(status)) {
break; break;
} }
@@ -97,7 +102,7 @@ static int add_file_to_archive(
static int add_data_archive( static int add_data_archive(
const b_arglist *args, const b_arglist *args,
struct ropkg_stream *stream, struct b_cstream *stream,
struct ropkg_writer *pkg) struct ropkg_writer *pkg)
{ {
const char *data_path_cstr; const char *data_path_cstr;
@@ -108,16 +113,23 @@ static int add_data_archive(
0, 0,
&data_path_cstr); &data_path_cstr);
if (!B_OK(bstatus)) { 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; return -1;
} }
b_path *data_path = b_path_create_from_cstr(data_path_cstr); b_path *data_path = b_path_create_from_cstr(data_path_cstr);
b_directory *data_dir; b_directory *data_dir;
bstatus = b_directory_open(NULL, data_path, &data_dir); b_result result = b_directory_open(NULL, data_path, 0, &data_dir);
if (!B_OK(bstatus)) { if (b_result_is_error(result)) {
b_err("cannot open directory '%s'", data_path_cstr); b_throw_error_with_template_caused_by_error(
b_i("reason: %s", b_status_to_string(bstatus)); ROPKG_ERROR_VENDOR,
ROPKG_ERR_DIR_OPEN_FAILED,
result,
B_ERROR_PARAM("filepath", data_path_cstr));
return -1; return -1;
} }
@@ -125,7 +137,7 @@ static int add_data_archive(
struct ropkg_writer_file_info file = {0}; struct ropkg_writer_file_info file = {0};
ropkg_writer_begin_file(pkg, ROPKG_PATH_DATA ".zst", &file); 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); enum ropkg_status status = ropkg_writer_open(stream, &data);
int ret = 0; int ret = 0;
@@ -148,7 +160,7 @@ static int add_data_archive(
} }
ropkg_writer_close(data); 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); ropkg_writer_end_file(pkg);
return ret; return ret;
@@ -156,7 +168,7 @@ static int add_data_archive(
static int add_control_archive( static int add_control_archive(
const b_arglist *args, const b_arglist *args,
struct ropkg_stream *stream, b_cstream *stream,
struct ropkg_writer *pkg) struct ropkg_writer *pkg)
{ {
int ret; int ret;
@@ -165,7 +177,7 @@ static int add_control_archive(
struct ropkg_writer_file_info file = {0}; struct ropkg_writer_file_info file = {0};
ropkg_writer_begin_file(pkg, ROPKG_PATH_CONTROL ".zst", &file); 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); enum ropkg_status status = ropkg_writer_open(stream, &control);
b_arglist_option_iterator it; b_arglist_option_iterator it;
@@ -188,16 +200,40 @@ static int add_control_archive(
ropkg_writer_close(control); 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); ropkg_writer_end_file(pkg);
return 0; 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( static int add_meta_archive(
const b_arglist *args, const b_arglist *args,
struct ropkg_stream *stream, b_cstream *stream,
struct ropkg_writer *pkg) struct ropkg_writer *pkg)
{ {
const char *manifest_path, *news_path; const char *manifest_path, *news_path;
@@ -205,20 +241,26 @@ static int add_meta_archive(
args, args,
OPT_MANIFEST, OPT_MANIFEST,
OPT_MANIFEST_PATH, OPT_MANIFEST_PATH,
1, 0,
&manifest_path); &manifest_path);
if (!B_OK(bstatus)) { if (!B_OK(bstatus)) {
b_arglist_report_missing_option(args, OPT_MANIFEST); b_arglist_report_missing_option(args, OPT_MANIFEST);
return -1; return -1;
} }
b_result result = validate_manifest(manifest_path);
if (b_result_is_error(result)) {
b_throw(result);
return -1;
}
int ret; int ret;
struct ropkg_writer *meta; struct ropkg_writer *meta;
struct ropkg_writer_file_info file = {0}; struct ropkg_writer_file_info file = {0};
ropkg_writer_begin_file(pkg, ROPKG_PATH_META ".zst", &file); 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); enum ropkg_status status = ropkg_writer_open(stream, &meta);
bstatus = b_arglist_get_string( bstatus = b_arglist_get_string(
@@ -243,7 +285,7 @@ static int add_meta_archive(
ropkg_writer_close(meta); 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); ropkg_writer_end_file(pkg);
@@ -255,8 +297,8 @@ static int create(
const b_arglist *opt, const b_arglist *opt,
const b_array *args) const b_array *args)
{ {
const struct ropkg_compression_function *zstd const b_compression_function *zstd
= ropkg_compression_function_for_type(ROPKG_COMPRESSION_ZSTD); = b_compression_function_get_by_id(B_COMPRESSOR_FUNCTION_ZSTD);
struct ropkg_writer *pkg, *subpkg; struct ropkg_writer *pkg, *subpkg;
enum ropkg_status status; enum ropkg_status status;
@@ -265,7 +307,7 @@ static int create(
opt, opt,
OPT_OUTPATH, OPT_OUTPATH,
OPT_OUTPATH_PATH, OPT_OUTPATH_PATH,
1, 0,
&out_path); &out_path);
if (!B_OK(bstatus)) { if (!B_OK(bstatus)) {
b_arglist_report_missing_option(opt, OPT_OUTPATH); b_arglist_report_missing_option(opt, OPT_OUTPATH);
@@ -273,37 +315,45 @@ static int create(
} }
FILE *fp = fopen(out_path, "wb"); FILE *fp = fopen(out_path, "wb");
struct ropkg_stream *stream; b_stream *fp_stream = b_stream_open_fp(fp);
status = ropkg_stream_open(fp, zstd, ROPKG_STREAM_WRITE, &stream); 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; int ret;
ret = add_data_archive(opt, stream, pkg); ret = add_data_archive(opt, cstream, pkg);
if (ret != 0) { if (ret != 0) {
goto end; goto end;
} }
ret = add_control_archive(opt, stream, pkg); ret = add_control_archive(opt, cstream, pkg);
if (ret != 0) { if (ret != 0) {
goto end; goto end;
} }
ret = add_meta_archive(opt, stream, pkg); ret = add_meta_archive(opt, cstream, pkg);
if (ret != 0) { if (ret != 0) {
goto end; goto end;
} }
end: end:
ropkg_writer_close(pkg); ropkg_writer_close(pkg);
ropkg_stream_close(stream);
b_cstream_close(cstream);
b_stream_close(fp_stream);
fclose(fp); fclose(fp);
return ret; return ret;
} }
B_COMMAND(CMD_CREATE, CMD_ROOT) B_COMMAND(CMD_PACKAGE_CREATE, CMD_PACKAGE)
{ {
B_COMMAND_NAME("create"); B_COMMAND_NAME("create");
B_COMMAND_SHORT_NAME('C'); B_COMMAND_SHORT_NAME('C');
@@ -376,7 +426,8 @@ B_COMMAND(CMD_CREATE, CMD_ROOT)
B_OPTION_SHORT_NAME('s'); B_OPTION_SHORT_NAME('s');
B_OPTION_LONG_NAME("script"); B_OPTION_LONG_NAME("script");
B_OPTION_DESC( 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."); "package (un)installation process.");
B_OPTION_ARG(OPT_SCRIPT_NAME) B_OPTION_ARG(OPT_SCRIPT_NAME)
@@ -402,10 +453,13 @@ B_COMMAND(CMD_CREATE, CMD_ROOT)
B_OPTION_SHORT_NAME('p'); B_OPTION_SHORT_NAME('p');
B_OPTION_LONG_NAME("parameter"); B_OPTION_LONG_NAME("parameter");
B_OPTION_DESC( B_OPTION_DESC(
"a parameter to use when creating the new file. " "a parameter to use when creating the new "
"if a parameter EXAMPLE is defined, any variable " "file. "
"if a parameter EXAMPLE is defined, any "
"variable "
"reference " "reference "
"of the form ${EXAMPLE} in the package manifest will " "of the form ${EXAMPLE} in the package "
"manifest will "
"be " "be "
"replaced with the specified parameter value."); "replaced with the specified parameter value.");
@@ -426,8 +480,10 @@ B_COMMAND(CMD_CREATE, CMD_ROOT)
{ {
B_ARG_NAME("source-dir"); B_ARG_NAME("source-dir");
B_ARG_DESC( B_ARG_DESC(
"the directory to package. the specified directory " "the directory to package. the specified "
"will become the root of the newly created package."); "directory "
"will become the root of the newly created "
"package.");
B_ARG_NR_VALUES(1); B_ARG_NR_VALUES(1);
} }

View File

@@ -1,4 +1,4 @@
#include "commands.h" #include "../commands.h"
#include <blue/cmd.h> #include <blue/cmd.h>
@@ -17,12 +17,12 @@ static int extract(
return 0; return 0;
} }
B_COMMAND(CMD_EXTRACT, CMD_ROOT) B_COMMAND(CMD_PACKAGE_EXTRACT, CMD_PACKAGE)
{ {
B_COMMAND_NAME("extract"); B_COMMAND_NAME("extract");
B_COMMAND_SHORT_NAME('X'); B_COMMAND_SHORT_NAME('X');
B_COMMAND_DESC("extract the contents of a Rosetta package"); 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_FUNCTION(extract);
B_COMMAND_HELP_OPTION(); B_COMMAND_HELP_OPTION();

View File

@@ -1,4 +1,4 @@
#include "commands.h" #include "../commands.h"
#include <blue/cmd.h> #include <blue/cmd.h>
@@ -17,7 +17,7 @@ static int install(
return 0; return 0;
} }
B_COMMAND(CMD_INSTALL, CMD_ROOT) B_COMMAND(CMD_PACKAGE_INSTALL, CMD_PACKAGE)
{ {
B_COMMAND_NAME("install"); B_COMMAND_NAME("install");
B_COMMAND_SHORT_NAME('I'); B_COMMAND_SHORT_NAME('I');

193
ropkg/package/query/list.c Normal file
View File

@@ -0,0 +1,193 @@
#include "../../commands.h"
#include <blue/cmd.h>
#include <blue/compress/cstream.h>
#include <blue/compress/function.h>
#include <blue/core/bitop.h>
#include <ropkg/manifest.h>
#include <ropkg/reader.h>
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);
}
}

View File

@@ -0,0 +1,38 @@
#include "../../commands.h"
#include <blue/cmd.h>
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);
}
}

View File

@@ -0,0 +1,38 @@
#include "../../commands.h"
#include <blue/cmd.h>
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);
}
}

View File

@@ -0,0 +1,18 @@
#include "../../commands.h"
#include <blue/cmd.h>
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();
}
}

View File

@@ -0,0 +1,99 @@
#include "../../commands.h"
#include <blue/cmd.h>
#include <blue/compress/cstream.h>
#include <blue/compress/function.h>
#include <blue/io/directory.h>
#include <ropkg/manifest.h>
#include <ropkg/package.h>
#include <ropkg/reader.h>
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);
}
}

19
ropkg/package/root.c Normal file
View File

@@ -0,0 +1,19 @@
#include "../commands.h"
#include <blue/cmd.h>
#include <blue/core/error.h>
#include <blue/term/print.h>
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();
}
}

View File

@@ -1,38 +0,0 @@
#include "commands.h"
#include <blue/cmd.h>
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);
}
}

View File

@@ -1,4 +1,4 @@
#include "commands.h" #include "../commands.h"
#include "ropkg/version.h" #include "ropkg/version.h"
#include <blue/cmd.h> #include <blue/cmd.h>
@@ -175,9 +175,10 @@ end:
return ret; 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_DESC("compare two package version identifiers.");
B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT);
B_COMMAND_FUNCTION(compare_version); B_COMMAND_FUNCTION(compare_version);

19
ropkg/version/root.c Normal file
View File

@@ -0,0 +1,19 @@
#include "../commands.h"
#include <blue/cmd.h>
#include <blue/core/error.h>
#include <blue/term/print.h>
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();
}
}