Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e66b47a
core/Fix indexer error handling
ThatHackerDudeFromCyberspace Aug 6, 2026
5619540
core/Fix package hook path issues
ThatHackerDudeFromCyberspace Aug 6, 2026
4f8dd72
core/Fix uninstall hook issues
ThatHackerDudeFromCyberspace Aug 6, 2026
6be0bda
core/Implement more package install error checks
ThatHackerDudeFromCyberspace Aug 6, 2026
0db2c6d
Implement new test suite
ThatHackerDudeFromCyberspace Aug 6, 2026
1a26187
cli/Fix cleanup issue
ThatHackerDudeFromCyberspace Aug 6, 2026
b4aca6d
CI/Implement tests as separate action
ThatHackerDudeFromCyberspace Aug 6, 2026
0c913ed
CI/Try to fix issues when compiling gnu23
ThatHackerDudeFromCyberspace Aug 6, 2026
cdf78ed
CI/Try to fix issues when compiling gnu23
ThatHackerDudeFromCyberspace Aug 6, 2026
2103c35
CI/Use newer Ubuntu
ThatHackerDudeFromCyberspace Aug 6, 2026
7a8b6ec
CI/Fix more test issues
ThatHackerDudeFromCyberspace Aug 6, 2026
6fb72ba
tests/Improve test portability
ThatHackerDudeFromCyberspace Aug 6, 2026
9802657
Update fbink
ThatHackerDudeFromCyberspace Aug 6, 2026
8af58be
Improve test logging
ThatHackerDudeFromCyberspace Aug 6, 2026
75c9356
CI/Upload builddir in test CI
ThatHackerDudeFromCyberspace Aug 6, 2026
6a21562
CI/Implement assert_hd so that tests can run in non-debug builds
ThatHackerDudeFromCyberspace Aug 6, 2026
bff39c6
CI/Update tests workflow
ThatHackerDudeFromCyberspace Aug 6, 2026
193fb70
core/Implement file hashing when downloading
ThatHackerDudeFromCyberspace Aug 7, 2026
1212a93
core/Bump manifest and KPM version
ThatHackerDudeFromCyberspace Aug 7, 2026
2e04dc5
core/Fix zstd compilation issues
ThatHackerDudeFromCyberspace Aug 7, 2026
ffa767f
core/Fix hash debug logging issues
ThatHackerDudeFromCyberspace Aug 7, 2026
2fa4127
Cleanup and document SimpleGET API
ThatHackerDudeFromCyberspace Sep 6, 2026
aabafa2
Implement KPKG 3 file loader
ThatHackerDudeFromCyberspace Sep 6, 2026
08820ee
Add more kpkg 3 helper functions
ThatHackerDudeFromCyberspace Sep 6, 2026
b8f9f19
core/Implement more KPKG functions + fix KPKG 3 format bugs
ThatHackerDudeFromCyberspace Sep 7, 2026
215736c
Fix compilation errors on older GCC
ThatHackerDudeFromCyberspace Sep 7, 2026
53f211c
cli: expand GitHub owner/repo shorthand in add-repo
zampierilucas Oct 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:

jobs:
build:
runs-on: ubuntu-latest
runs-on: ubuntu-26.04

steps:
- uses: actions/checkout@v6
Expand Down
33 changes: 33 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Tests

on:
push:
pull_request:
workflow_dispatch:
workflow_call:

jobs:
build:
runs-on: ubuntu-26.04

steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Install Meson
shell: bash
run: |
sudo apt update
sudo apt install gcc-15 g++-15 python3 python3-pip python3-setuptools python3-wheel ninja-build
pip3 install meson
- name: Build
shell: bash
run: |
meson setup --reconfigure --buildtype=release builddir
cd builddir
meson test
- uses: actions/upload-artifact@v4
if: always()
with:
name: testlog.txt
path: builddir/meson-logs/testlog.txt
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ builddir/
docs/
subprojects/*
!subprojects/*.wrap
!subprojects/packagefiles
build/
19 changes: 15 additions & 4 deletions cli/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,25 @@ int main(int argc, char* argv[])
Repository repository;
for (int i = command_index+1; i < argc; i++)
{
if ((error = KPM_AddRepository(&kpm, argv[i], &repository, &kpm_io)) != KPM_OK)
kpm_io.log(KPM_VERBOSITY_ERROR, "Could not add repository '%s' (%i: %s)", argv[i], error, KPM_ErrorToString(error));
char* url;
char* slash = strchr(argv[i], '/');
if (strpbrk(argv[i], ".:") == NULL && slash != NULL)
{
char* path = strchr(slash+1, '/');
int repo_len = path != NULL ? path - argv[i] : strlen(argv[i]);
url = asprintf_hd("https://raw.githubusercontent.com/%.*s/HEAD%s.json", repo_len, argv[i], path != NULL ? path : "/manifest");
}
else
url = strdup(argv[i]);

if ((error = KPM_AddRepository(&kpm, url, &repository, &kpm_io)) != KPM_OK)
kpm_io.log(KPM_VERBOSITY_ERROR, "Could not add repository '%s' (%i: %s)", url, error, KPM_ErrorToString(error));
else
{
kpm_io.log(KPM_VERBOSITY_INFO, "Added repository '%s'", repository.id);
KPM_FreeRepository(&repository);
}

free(url);
}

if (error == KPM_OK)
Expand Down Expand Up @@ -380,7 +391,7 @@ kpm_io.log(KPM_VERBOSITY_INFO, "usage: kpm [--help, -h] [--fbink] [-y] {version
version:\n\
Returns the current version of the KPM library and CLI\n\
add-repo:\n\
Add a repository from one or more URLs\n\
Add a repository from one or more URLs, or GitHub owner/repo[/path] shorthands (path defaults to manifest, .json is appended)\n\
remove-repo:\n\
Remove one or more repositories given one or more IDs\n\
list-repo:\n\
Expand Down
12 changes: 11 additions & 1 deletion cli/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ if KPM_PLATFORM == ''
KPM_PLATFORM = host_machine.cpu_family()
endif

subproject('fbink', default_options: ['bitmap=enabled', 'button-scan=disabled', 'fonts=disabled', 'image=disabled', 'inputlib=disabled', 'opentype=disabled', 'fbdepth=disabled', 'target=' + meson.get_external_property('target', 'Linux')])
subproject('fbink', default_options: [
'bitmap=enabled',
'button-scan=disabled',
'fonts=disabled',
'image=disabled',
'inputlib=disabled',
'opentype=disabled',
'fbink=disabled',
'fbdepth=disabled',
'target=' + meson.get_external_property('target', 'Linux')
])
fbink_dep = dependency('fbink')
executable('kpm', sources, include_directories: include_dir, dependencies: [kpm_dep, fbink_dep], build_rpath: kmc_lib_dir, install_rpath: kmc_lib_dir)
8 changes: 5 additions & 3 deletions kpm-helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
logging.basicConfig(format="[%(levelname)s] %(message)s", level=logging.INFO)
logger = logging.getLogger(__name__)

KPM_MANIFEST_VERSION = 2
KPM_MANIFEST_VERSION = 3
valid_supported_platforms = [
"kindle",
"kindle5",
Expand Down Expand Up @@ -103,9 +103,9 @@ def __init__(

# @TODO
if manifest_version != KPM_MANIFEST_VERSION:
if manifest_version["manifest_version"] == 1:
if manifest_version["manifest_version"] < KPM_MANIFEST_VERSION:
logger.warning(
"Manifest v1 is deprecated. Please upgrade to manifest v2."
f"Manifest v{manifest_version['manifest_version']} is deprecated. Please upgrade to manifest v{KPM_MANIFEST_VERSION}."
)
else:
logger.error(
Expand Down Expand Up @@ -248,6 +248,8 @@ def pack(self, output_path: str, compression: int = 5):
file = tarfile.open(packageFilename, "w:")
elif self.manifest_version >= 2:
file = tarfile.open(packageFilename, "w:gz", compresslevel=compression)
elif self.manifest_version >= 3:
file = tarfile.open(packageFilename, "w:zst", level=compression)
else:
file = tarfile.open(
packageFilename, "w:xz", preset=compression
Expand Down
22 changes: 18 additions & 4 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,32 @@ add_global_arguments('-DKPM_PKG_PATH="' + get_option('pkg_path') + '"', language

kmc_lib_dir = '/var/local/kmc/lib'

subproject('zstd', default_options: [
'default_library=static',
'legacy_level=7',
'debug_level=0',
'static_runtime=true',
'bin_programs=false',
'bin_tests=false',
'bin_contrib=false',
'zlib=enabled',
'lzma=disabled',
'lz4=disabled',
'c_args=-lrt',
'cpp_args=-lrt'
])
subproject('liblzma', default_options: ['default_library=static'])
subproject('zlib', default_options: ['default_library=static', 'tests=disabled'])
subproject('sqlite3', default_options: ['default_library=static']) # PW2 sqlite3 is unusable it straight up doesn't compile I need to fix this
subproject('cjson', default_options: ['tests=false', 'default_library=static'])
subproject('libarchive', default_options: [
'default_library=static',
'openssl=disabled',
'zlib=enabled',
'zlib=disabled',
'bz2lib=disabled',
'libb2=disabled',
'iconv=disabled',
'lz4=disabled',
'zstd=disabled',
'zstd=enabled',
'lzma=enabled',
'lzo2=disabled',
'cng=disabled',
Expand All @@ -35,7 +48,7 @@ subproject('libarchive', default_options: [

])
subproject('google-brotli', default_options: ['default_library=static']) # Needed for cURL brotli support
subproject('openssl', default_options: ['default_library=static', 'build_cli=false']) # Needed for cURL HTTPS support
openssl = subproject('openssl', default_options: ['default_library=static', 'build_cli=false']) # Needed for cURL HTTPS support
subproject('nghttp2', default_options: ['default_library=static']) # Needed for cURL HTTP/2 support
curl = subproject('curl', default_options: [
'tool=disabled',
Expand Down Expand Up @@ -85,6 +98,7 @@ sqlite3_dep = dependency('sqlite3', static: true, default_options: [
curl_dep = curl.get_variable('curl_dep') # Get static libcurl
libcjson_dep = dependency('libcjson', static: true)
libarchive_dep = dependency('libarchive', static: true)
libcrypto_dep = dependency('libcrypto', static: true)

subdir('./stubs')
subdir('./src')
Expand Down
3 changes: 2 additions & 1 deletion src/include/internal_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ void mkdir_r(char* path, __mode_t mode);
void rmdir_r(char* path);

char* vasprintf_hd(const char* format, va_list args);
char* asprintf_hd(const char* format, ...) __attribute__((format(printf, 1, 2)));
char* asprintf_hd(const char* format, ...) __attribute__((format(printf, 1, 2)));
char* get_realpath(const char* location);
85 changes: 85 additions & 0 deletions src/include/kpm/kpkg.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#pragma once

#include "kpm/semver.h"
#include <stddef.h>
#include <stdint.h>

#define KPKG_MAGIC "KPKG"
#define KPKG_MAGIC_LENGTH 4
#define KPKG_VERSION 3

typedef enum {
KPKG_OK,
} KPKG_Result;

typedef enum {
KINDLEANY = 0,
KINDLE = 1 << 0,
KINDLE5 = 1 << 1,
KINDLEPW2 = 1 << 2,
KINDLEHF = 1 << 3
} KPKG_SupportedPlatforms;

struct KPKG_Header
{
char magic[KPKG_MAGIC_LENGTH]; // "KPKG"
uint8_t version; // Format version
SemVer package_version; // Version of the package
uint8_t supported_platforms; // Bitfield of KPKG_SupportedPlatforms
uint32_t dependency_count;
uint32_t file_count;
uint64_t strings_size; // String section size
};
typedef struct KPKG_Header KPKG_Header;

struct KPKG_DependencyEntry
{
uint64_t id; // Pointer in string section
SemVer min_version;
SemVer max_version;
};
typedef struct KPKG_DependencyEntry KPKG_DependencyEntry;

struct KPKG_FileEntry
{
uint64_t path; // Pointer in string section
uint8_t checksum[16];
uint32_t file_mode;
uint64_t offset; // Pointer in file data section
};
typedef struct KPKG_FileEntry KPKG_FileEntry;

struct KPKG_File
{
char* location; // The URL/path to the file
void* internal_file; // The memory mapped file or NULL
size_t map_size; // If file is local and memory mapped, this will indicate the length
KPKG_Header* header; // Local storage of header (always present)
KPKG_DependencyEntry* dependencies;
KPKG_FileEntry* files;
char* strings;
};
typedef struct KPKG_File KPKG_File;

/**
* @brief Load a KPKG_File from the specified location
*
* @param location The URL or path to the KPKG file
* @return KPKG_File* A pointer to the KPKG_File file object if valid, otherwise NULL
*/
KPKG_File* KPKG_LoadFile(const char* location);

void KPKG_CloseFile(KPKG_File* file);

/**
* @brief Validate a KPKG_File object such that none of the pointers are OOB, note: this function assumes that the entry and string arrays are not malformed.
*
* @param kpkg_file The file object to validate
* @return true
* @return false
*/
bool KPKG_ValidateFile(KPKG_File* kpkg_file);

char* KPKG_GetString(KPKG_File* kpkg_file, uint64_t offset);
KPKG_DependencyEntry* KPKG_GetDependencies(KPKG_File* kpkg_file, size_t* count);
KPKG_FileEntry* KPKG_GetFileEntries(KPKG_File* kpkg_file, size_t* count);
7 changes: 3 additions & 4 deletions src/include/kpm/kpm.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,15 @@
*/

#include <sqlite3.h>
#include <stddef.h>
#include <sys/types.h>

#include "semver.h"

#define KPM_MANIFEST_VERSION 2
#define KPM_MANIFEST_VERSION 3

#define KPM_VERSION_MAJOR 0
#define KPM_VERSION_MINOR 2
#define KPM_VERSION_PATCH 3
#define KPM_VERSION_MINOR 3
#define KPM_VERSION_PATCH 0

typedef enum
{
Expand Down
7 changes: 4 additions & 3 deletions src/include/kpm/semver.h
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
#pragma once

#include <limits.h>
#include <stdint.h>

#define VERSION_MAX UINT_MAX
#define SEMVER_MAX { .major = UINT_MAX, .minor = UINT_MAX, .patch = UINT_MAX }

struct SemVer
{
unsigned int major;
unsigned int minor;
unsigned int patch;
uint32_t major;
uint32_t minor;
uint32_t patch;
};
typedef struct SemVer SemVer;

Expand Down
34 changes: 27 additions & 7 deletions src/include/simpleGET.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,35 @@

struct SimpleGETRequest
{
CURL* curl;
long response_code;
char* buffer;
size_t size;
CURL* curl; /**< The internal CURL object backing this request */
long response_code; /**< The received response code */
unsigned char* buffer; /**< The buffer of data from this request */
size_t size; /**< The current size of the buffer */
size_t max_size; /**< The maximum size of the response before the request is terminated */
};
typedef struct SimpleGETRequest SimpleGETRequest;

size_t SimpleGET_Callback(char* ptr, size_t size, size_t nmemb, void* userdata);
void SimpleGET_Initialise(SimpleGETRequest* request, const char* url);

/**
* @brief Create a SimpleGETRequest
*
* @param url The URL to request
* @return SimpleGETRequest* A pointer to the created SimpleGETRequest object
*/
SimpleGETRequest* SimpleGET_Initialise(const char* url);

/**
* @brief Frees a SimpleGETRequest object
*
* @param request The SimpleGetRequest object to free
*/
void SimpleGET_Cleanup(SimpleGETRequest* request);
CURLcode SimpleGET_Perform(SimpleGETRequest* request);
long SimpleGET_GetResponseCode(SimpleGETRequest* request);

/**
* @brief Perform the request
*
* @param request The SimpleGETRequest to perform
* @return CURLcode The internal CURL result of the request, given by curl_easy_perform
*/
CURLcode SimpleGET_Perform(SimpleGETRequest* request);
Loading