Compare commits

...

33 Commits

Author SHA1 Message Date
GreekStapler 9452fea2ab fix: Somehow, test file changed line endings to CRLF. Changed it back to LF. 2023-01-27 22:24:54 +00:00
GreekStapler 10eb08b8e4 chore: Rebase to dev branch with binomial heap PR. 2023-01-27 22:23:23 +00:00
GreekStapler d1a8ba7737 refactor: Added SHA256 to package description file. 2023-01-27 22:17:57 +00:00
GreekStapler 2ce7a819a9 refactor: Decided to not return char** in function that creates the package description after all, now returns char*.
The method I was trying started to irk me when I thought of creating a test unit for it. Also fixed some other issues I found in the package_to_description function (SHA256SUM section still missing).
2023-01-27 22:17:57 +00:00
GreekStapler 2d6ac2ac19 fix: Took a very long time finding a very annyoing bug because realloc is the devil. 2023-01-27 22:17:57 +00:00
GreekStapler 6d731d788c refactor: Created function that will return the char ** (originally char *) that will be used to make the description file. Also removed free that was resulting in a crash. 2023-01-27 22:17:57 +00:00
GreekStapler a60c923547 refactor: Modified .PKGINFO parser not to rely on gotos and continues. Parser is now optimistic and assumes .PKGINFO file is valid.
After my changes to the macros, they still feel a bit hacky, but I'm content with them. I also changed the parser to assume the .PKGINFO files are always valid because they are automatically generated. The parser also assumes the same fields will always appear in the same fixed order. I made this change after checking how makepkg generated this file.
2023-01-27 22:17:57 +00:00
GreekStapler 276fa99e4b refactor: Add original .c package files by Jef. 2023-01-27 22:17:57 +00:00
Jef Roosens 379a05a7b6 Merge pull request 'Binomial heap' (#4) from Chewing_Bever/libvieter:min-heap into dev
Reviewed-on: vieter-v/libvieter#4
2023-01-27 22:32:02 +01:00
Jef Roosens 167611e6fa
test(heap): test insert after every pop just in case 2023-01-27 21:59:09 +01:00
Jef Roosens 5b2ce6acaa
feat(heap): thread-safety features 2023-01-27 21:23:32 +01:00
Jef Roosens d77b3e4fee
chore: some nitpicking 2023-01-27 20:50:23 +01:00
Jef Roosens ab418e57b3
test: also test with -O3 which can produce extra errors 2023-01-26 12:24:34 +01:00
Jef Roosens a6bdd39776
docs(heap): add readme 2023-01-26 11:03:41 +01:00
Jef Roosens 05b96d1fd6
fix(heap): finally fixed that pop bug 2023-01-26 10:21:30 +01:00
Jef Roosens 3ec2e76af9
refactor(heap): some better variable names; some more tests 2023-01-25 22:12:22 +01:00
Jef Roosens dc557f57ab
test(heap): some more tests to expose flaws 2023-01-25 20:49:18 +01:00
Jef Roosens 63100c5b99
test(heap): add random test that exposes some faults 2023-01-24 21:19:08 +01:00
Jef Roosens 6845e67cb6
feat(heap): possibly working pop 2023-01-24 21:02:29 +01:00
Jef Roosens 95d8c9972b
refactor(heap): combine tree and node into single struct 2023-01-24 17:22:11 +01:00
Jef Roosens 09c488aa0f
feat(heap): not quite working pop 2023-01-24 17:01:37 +01:00
Jef Roosens 6cf4eaaf0b
chore: separate target for each test binary 2023-01-24 14:06:46 +01:00
Jef Roosens 3c8c33b47a
fix(heap): some insert fixes 2023-01-24 12:07:30 +01:00
Jef Roosens c1ad26cf0c
feat(heap): initially working insert 2023-01-24 07:53:51 +01:00
Jef Roosens 16b78b8431 chore: improve ci lint 2023-01-22 13:02:11 +01:00
Jef Roosens 0c673a2751 chore: make lint job check for warnings 2023-01-22 12:46:09 +01:00
Jef Roosens d11d074960 chore: some reorganising 2023-01-22 12:35:55 +01:00
Jef Roosens 13a63d548c docs: added docstrings to public headers 2023-01-22 12:32:47 +01:00
Jef Roosens 8609769389 chore: better separate ci jobs 2023-01-22 12:17:56 +01:00
Jef Roosens 2cc974accc chore: updated readme 2023-01-22 11:56:11 +01:00
Jef Roosens ef625ed14e chore: allow tests to access internal methods 2023-01-22 10:10:17 +01:00
Jef Roosens 6823050c2f refactor(heap): properly organised code 2023-01-22 09:40:35 +01:00
Jef Roosens 050e99b413 feat(heap): code skeleton 2023-01-21 16:31:22 +01:00
27 changed files with 1588 additions and 39 deletions

View File

@ -0,0 +1,16 @@
variables:
&image 'git.rustybever.be/chewing_bever/c-devop:alpine3.17'
branches:
exclude: [ main ]
platform: linux/amd64
pipeline:
lint:
image: *image
pull: true
commands:
- make lint
- make objs CFLAGS='-Werror -fsyntax-only'
when:
event: [push, pull_request]

View File

@ -0,0 +1,20 @@
variables:
&image 'git.rustybever.be/chewing_bever/c-devop:alpine3.17'
branches:
exclude: [ main ]
platform: linux/amd64
depends_on:
- test
pipeline:
test:
image: *image
pull: true
commands:
- make test-mem
- make clean
- make test-mem CFLAGS='-O3 -Werror -Wall'
when:
event: [push, pull_request]

View File

@ -11,26 +11,12 @@ branches:
platform: ${PLATFORM} platform: ${PLATFORM}
pipeline: pipeline:
lint: build-and-test:
image: *image image: *image
pull: true pull: true
commands:
- make lint
when:
event: [push, pull_request]
build:
image: *image
commands:
- make
- make clean
- CFLAGS='-O3' make
when:
event: [push, pull_request]
test:
image: *image
commands: commands:
- make test - make test
- make clean
- make test CFLAGS='-O3 -Werror -Wall'
when: when:
event: [push, pull_request] event: [push, pull_request]

View File

@ -8,6 +8,8 @@ SRC_DIR ?= src
TEST_DIR ?= test TEST_DIR ?= test
INC_DIRS ?= include INC_DIRS ?= include
LIB := $(BUILD_DIR)/$(LIB_FILENAME)
SRCS != find '$(SRC_DIR)' -iname '*.c' SRCS != find '$(SRC_DIR)' -iname '*.c'
SRCS_H != find $(INC_DIRS) -iname '*.h' SRCS_H != find $(INC_DIRS) -iname '*.h'
SRCS_TEST != find '$(TEST_DIR)' -iname '*.c' SRCS_TEST != find '$(TEST_DIR)' -iname '*.c'
@ -17,6 +19,8 @@ OBJS_TEST := $(SRCS_TEST:%=$(BUILD_DIR)/%.o)
DEPS := $(SRCS:%=$(BUILD_DIR)/%.d) $(SRCS_TEST:%=$(BUILD_DIR)/%.d) DEPS := $(SRCS:%=$(BUILD_DIR)/%.d) $(SRCS_TEST:%=$(BUILD_DIR)/%.d)
BINS_TEST := $(OBJS_TEST:%.c.o=%) BINS_TEST := $(OBJS_TEST:%.c.o=%)
TARGETS_TEST := $(BINS_TEST:%=test-%)
TARGETS_MEM_TEST := $(BINS_TEST:%=test-mem-%)
INC_FLAGS := $(addprefix -I,$(INC_DIRS)) INC_FLAGS := $(addprefix -I,$(INC_DIRS))
@ -25,42 +29,66 @@ INC_FLAGS := $(addprefix -I,$(INC_DIRS))
# object file is also recompiled if only a header is changed. # object file is also recompiled if only a header is changed.
# -MP: generate a dummy target for every header file (according to the docs it # -MP: generate a dummy target for every header file (according to the docs it
# prevents some errors when removing header files) # prevents some errors when removing header files)
CFLAGS ?= -MMD -MP -Wall -Werror -Wextra CFLAGS ?= -MMD -MP -g
CFLAGS += $(INC_FLAGS) VIETERCFLAGS := $(INC_FLAGS) $(CFLAGS) -Wall -Wextra
.PHONY: all .PHONY: all
all: vieter all: vieter
# =====COMPILATION===== # =====COMPILATION=====
# Utility used by the CI to lint
.PHONY: objs
objs: $(OBJS)
.PHONY: vieter .PHONY: vieter
vieter: $(BUILD_DIR)/$(LIB_FILENAME) vieter: $(LIB)
$(BUILD_DIR)/$(LIB_FILENAME): $(OBJS) $(BUILD_DIR)/$(LIB_FILENAME): $(OBJS)
ar -rcs $@ $(OBJS) ar -rcs $@ $(OBJS)
$(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c $(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c
mkdir -p $(dir $@) mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(VIETERCFLAGS) -c $< -o $@
# =====TESTING===== # =====TESTING=====
.PHONY: test .PHONY: test
test: build-test test: $(TARGETS_TEST)
@ $(foreach bin,$(BINS_TEST),./$(bin);)
.PHONY: test-mem
test-mem: $(TARGETS_MEM_TEST)
.PHONY: $(TARGETS_TEST)
$(TARGETS_TEST): test-%: %
./$^
.PHONY: $(TARGETS_MEM_TEST)
$(TARGETS_MEM_TEST): test-mem-%: %
valgrind --tool=memcheck --error-exitcode=1 --track-origins=yes --leak-check=full ./$^
.PHONY: build-test .PHONY: build-test
build-test: $(BINS_TEST) build-test: $(BINS_TEST)
# For simplicity, we link every object file to each of the test files. This # For simplicity, we link every object file to each of the test files. This
# might be changed later if this starts to become too slow. # might be changed later if this starts to become too slow.
<<<<<<< HEAD
$(BINS_TEST): %: %.c.o $(LIB)
$(CC) \
$^ -o $@
=======
$(BINS_TEST): %: %.c.o $(OBJS) $(BINS_TEST): %: %.c.o $(OBJS)
$(CC) $^ -o $@ $(CC) $^ -larchive -o $@
>>>>>>> c94ab92 (refactor: Add libarchive link to test compilation area of the Makefile. Created test units with xcursor-dmz as test package.)
# Each test includes the test directory, which contains the acutest header file # Along with the include directory, each test includes $(TEST_DIR) (which
# contains the acutest.h header file), and the src directory of the module it's
# testing. This allows tests to access internal methods, which aren't publicly
# exposed.
$(BUILD_DIR)/$(TEST_DIR)/%.c.o: $(TEST_DIR)/%.c $(BUILD_DIR)/$(TEST_DIR)/%.c.o: $(TEST_DIR)/%.c
mkdir -p $(dir $@) mkdir -p $(dir $@)
$(CC) $(CFLAGS) -I$(TEST_DIR) -c $< -o $@ $(CC) $(VIETERCFLAGS) -I$(TEST_DIR) \
-I$(dir $(@:$(BUILD_DIR)/$(TEST_DIR)/%=$(SRC_DIR)/%)) \
-c $< -o $@
# =====MAINTENANCE===== # =====MAINTENANCE=====
.PHONY: lint .PHONY: lint

View File

@ -1,15 +1,12 @@
# libvieter # libvieter
This library powers part of Vieter, most noteably the sections that can easily This library powers part of Vieter, most notably the sections that can easily
be implemented in C (or just parts I want to implement in C because it's fun). be implemented in C (or just parts I want to implement in C because it's fun).
The goal of this library is to be completely self-contained, meaning any The goal of this library is to be as self-contained as possible; data
required data structures have to be implemented as well. It can only depend on structures should be implemented manually if possible.
the C standard libraries.
Currently it contains the following: See the [source code](src) for the list of modules.
* Cron expression parser & next time calculator
## Development ## Development
@ -18,13 +15,41 @@ Currently it contains the following:
Everything is handled by the provided Makefile. To compile the static library, Everything is handled by the provided Makefile. To compile the static library,
simply run `make`. simply run `make`.
### Project structure
Each module has its own subdirectory inside `src`, e.g. `src/cron`. This
directory contains the actual implementation of a module, along with any
internally used header files. Each internal function should be defined in a
header file, as to make testing these possible.
Each module should also have its own header file inside the `include`
directory. This header file defines the public API that the library exposes for
this specific module.
Any code in a module may only import internal headers from that module, along
with any of the public API header files. Modules should not depend on each
other's internal implementationns.
Each module should contain a README describing its contents.
All file names, function names... (even internals) should follow snake case
convention and have a prefix unique to that module, starting with `vieter_`.
For example, the `cron` modules uses the `vieter_cron_` prefix for everything.
Header files should only import what they explicitely need. If some function is
only used in a .c file, the import should be placed in the .c file instead.
### Testing ### Testing
This library uses [Acutest](https://github.com/mity/acutest) for its tests. This library uses [Acutest](https://github.com/mity/acutest) for its tests.
Tests should be placed in the `test` subdirectory, further divided into Tests should be placed in the `test` subdirectory, further divided into
directories that correspond those in `src`. Test files should begin with directories that correspond to those in `src`. Test files should begin with
`test_`, and their format should follow the expected format for Acutest. `test_`, and their format should follow the expected format for Acutest.
Each `test_` is compiled separately into a binary, linked with libvieter. A
test file can import any of the public API header files, along with any header
files defined in its respective module. This allows testing internal functions.
To run the tests, simply run `make test`. If you wish to only run a specific To run the tests, simply run `make test`. If you wish to only run a specific
test binary, you can find them in `build/test`. test binary, you can find them in `build/test`.

24
include/dynarray.h 100644
View File

@ -0,0 +1,24 @@
#ifndef VIETER_DYNARRAY
#define VIETER_DYNARRAY
#include <stdlib.h>
#include <string.h>
typedef struct dyn_array DynArray;
struct dyn_array {
char **array;
size_t capacity;
size_t size;
};
DynArray *dynarray_init(size_t initial_capacity);
void dynarray_add(DynArray *da, const char * s);
void dynarray_free(DynArray *da);
/**
* Convert a DynArray into an array by freeing all its surrounding components
* and returning the underlying array pointer.
*/
char **dynarray_convert(DynArray *da);
#endif

26
include/package.h 100644
View File

@ -0,0 +1,26 @@
#ifndef VIETER_PACKAGE
#define VIETER_PACKAGE
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include "archive.h"
#include "archive_entry.h"
#include "package_info.h"
#include "dynarray.h"
typedef struct pkg {
char *path;
PkgInfo *info;
DynArray *files;
int compression;
} Pkg;
Pkg *package_read_archive(const char *pkg_path);
void package_free(Pkg ** ptp);
char *package_to_description(Pkg *pkg);
#endif

View File

@ -0,0 +1,39 @@
#ifndef VIETER_PACKAGE_INFO
#define VIETER_PACKAGE_INFO
#define FREE_STRING(sp) if (sp != NULL) free(sp)
#include <stdint.h>
#include "dynarray.h"
typedef struct pkg_info {
char *name;
char *base;
char *version;
char *description;
int64_t size;
int64_t csize;
char *url;
char *arch;
int64_t build_date;
char *packager;
char *pgpsig;
int64_t pgpsigsize;
DynArray *groups;
DynArray *licenses;
DynArray *replaces;
DynArray *depends;
DynArray *conflicts;
DynArray *provides;
DynArray *optdepends;
DynArray *makedepends;
DynArray *checkdepends;
} PkgInfo;
PkgInfo *package_info_init();
void package_info_parse(PkgInfo *pkg_info, char *pkg_info_str);
void package_info_free(PkgInfo *pkg_info);
#endif

View File

@ -1,7 +1,6 @@
#ifndef VIETER_CRON #ifndef VIETER_CRON
#define VIETER_CRON #define VIETER_CRON
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -35,18 +34,38 @@ typedef struct vieter_cron_simple_time {
int minute; int minute;
} vieter_cron_simple_time; } vieter_cron_simple_time;
vieter_cron_expression *ce_init(); /*
* Allocate and initialize a new empty cron expression.
*/
vieter_cron_expression *vieter_cron_expr_init();
/*
* Deallocate a cron expression.
*/
void vieter_cron_expr_free(vieter_cron_expression *ce); void vieter_cron_expr_free(vieter_cron_expression *ce);
/*
* Given a cron expression and a reference time, calculate the next time after
* the reference time that this expression matches.
*/
void vieter_cron_expr_next(vieter_cron_simple_time *out, void vieter_cron_expr_next(vieter_cron_simple_time *out,
vieter_cron_expression *ce, vieter_cron_expression *ce,
vieter_cron_simple_time *ref); vieter_cron_simple_time *ref);
/*
* Convencience wrapper around vieter_cron_expr_next that uses the current time
* as the reference time.
*/
void vieter_cron_expr_next_from_now(vieter_cron_simple_time *out, void vieter_cron_expr_next_from_now(vieter_cron_simple_time *out,
vieter_cron_expression *ce); vieter_cron_expression *ce);
enum vieter_cron_parse_error vieter_cron_expr_parse(vieter_cron_expression *out, /*
const char *expression); * Try to parse a string into a cron expression. Note that the cron expression
* is updated in-place, meaning it can contain invalid information if the
* function returns an error. The cron expression should only be used if the
* function succeeded.
*/
vieter_cron_parse_error vieter_cron_expr_parse(vieter_cron_expression *out,
const char *expression);
#endif #endif

View File

@ -0,0 +1,62 @@
#ifndef VIETER_HEAP
#define VIETER_HEAP
#include <stdint.h>
typedef struct vieter_heap vieter_heap;
typedef enum vieter_heap_error {
vieter_heap_ok = 0,
vieter_heap_empty = 1
} vieter_heap_error;
/*
* Allocate and initialize an empty heap.
*/
vieter_heap *vieter_heap_init();
/*
* Deallocate a heap.
*/
void vieter_heap_free(vieter_heap *heap);
/*
* Return how many elements are currently in the heap.
*/
uint64_t vieter_heap_size(vieter_heap *heap);
/*
* Insert a new value into the heap.
*/
vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key,
void *data);
/*
* Remove the smallest element from the heap.
*/
vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap);
/*
* Get the smallest element in the heap without removing it.
*/
vieter_heap_error vieter_heap_peek(void **out, vieter_heap *heap);
/*
* Acquire a read lock on the heap. Return value is the result of
* pthread_rwlock_rdlock.
*/
int vieter_heap_rlock(vieter_heap *heap);
/*
* Acquire a write lock on the heap. Return value is the result of
* pthread_rwlock_wrlock.
*/
int vieter_heap_wlock(vieter_heap *heap);
/*
* Unlock the lock after having acquired it. Return value is the result of
* pthread_rwlock_unlock.
*/
int vieter_heap_unlock(vieter_heap *heap);
#endif

View File

@ -1,4 +1,8 @@
#include "vieter_cron.h" #include "vieter_cron.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
// This prefix is needed to properly compile // This prefix is needed to properly compile
const uint8_t parse_month_days[] = {31, 28, 31, 30, 31, 30, const uint8_t parse_month_days[] = {31, 28, 31, 30, 31, 30,

33
src/heap/README.md 100644
View File

@ -0,0 +1,33 @@
This min-heap implementation is a pretty standard binomial heap.
## Representation in memory
A heap consists of one or more binomial trees, each with a different order `k`
and `2^k` total nodes. This heap can contain `2^64 - 1` elements at most, which
is far more than your memory can contain, but it's still fun to mention.
A tree does not have its own memory structure; a node that's the root of a
binomial tree is simply called the tree.
Each node has the following layout:
```c
typedef struct vieter_heap_node {
uint64_t key;
void *data;
struct vieter_heap_node *largest_order;
union {
struct vieter_heap_node *next_tree;
struct vieter_heap_node *next_largest_order;
} ptr;
uint8_t order;
} vieter_heap_node;
```
Each node has a pointer to its child with the largest order (if the node's
order is `0`, this pointer will be NULL). Each non-root node has a pointer to
its sibling with the next-highest order. These pointers allow the children of a
binomial tree to be recombined into a new tree, once their root has been
pop'ed.
Roots point to the binomial tree in the heap with the next largest order.

View File

@ -0,0 +1,96 @@
#include "vieter_heap_internal.h"
#include <stdlib.h>
vieter_heap *vieter_heap_init() {
vieter_heap *heap = calloc(1, sizeof(vieter_heap));
pthread_rwlock_init(&heap->lock, NULL);
return heap;
}
uint64_t vieter_heap_size(vieter_heap *heap) {
uint64_t size = 0;
vieter_heap_node *tree = heap->tree;
while (tree != NULL) {
size |= (uint64_t)1 << tree->order;
tree = tree->ptr.next_tree;
}
return size;
}
void vieter_heap_free(vieter_heap *heap) {
vieter_heap_node *tree = heap->tree;
vieter_heap_node *next;
while (tree != NULL) {
next = tree->ptr.next_tree;
vieter_heap_tree_free(tree);
tree = next;
}
free(heap);
}
vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key,
void *data) {
vieter_heap_node *new_tree = vieter_heap_node_init();
new_tree->key = key;
new_tree->data = data;
new_tree->order = 0;
if (heap->tree == NULL) {
heap->tree = new_tree;
} else {
heap->tree = vieter_heap_tree_merge(heap->tree, new_tree);
}
return vieter_heap_ok;
}
vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap) {
if (heap->tree == NULL) {
return vieter_heap_empty;
}
heap->tree = vieter_heap_tree_pop(out, heap->tree);
return vieter_heap_ok;
}
vieter_heap_error vieter_heap_peek(void **out, vieter_heap *heap) {
if (heap->tree == NULL) {
return vieter_heap_empty;
}
vieter_heap_node *tree = heap->tree;
uint64_t smallest_key = tree->key;
*out = tree->data;
while (tree->ptr.next_tree != NULL) {
tree = tree->ptr.next_tree;
if (tree->key < smallest_key) {
smallest_key = tree->key;
*out = tree->data;
}
}
return vieter_heap_ok;
}
int vieter_heap_rlock(vieter_heap *heap) {
return pthread_rwlock_rdlock(&heap->lock);
}
int vieter_heap_wlock(vieter_heap *heap) {
return pthread_rwlock_wrlock(&heap->lock);
}
int vieter_heap_unlock(vieter_heap *heap) {
return pthread_rwlock_unlock(&heap->lock);
}

View File

@ -0,0 +1,8 @@
#include "vieter_heap.h"
#include "vieter_heap_tree.h"
#include <pthread.h>
struct vieter_heap {
vieter_heap_node *tree;
pthread_rwlock_t lock;
};

View File

@ -0,0 +1,192 @@
#include "vieter_heap_tree.h"
vieter_heap_node *vieter_heap_node_init() {
return calloc(1, sizeof(vieter_heap_node));
}
void vieter_heap_node_free(vieter_heap_node *node) { free(node); }
void vieter_heap_tree_free(vieter_heap_node *root) {
if (root->order == 0) {
goto end;
}
uint64_t size = 1;
vieter_heap_node **stack =
malloc(((uint64_t)1 << root->order) * sizeof(vieter_heap_node *));
stack[0] = root->largest_order;
vieter_heap_node *node;
while (size > 0) {
node = stack[size - 1];
size--;
if (node->largest_order != NULL) {
stack[size] = node->largest_order;
size++;
}
if (node->ptr.next_largest_order != NULL) {
stack[size] = node->ptr.next_largest_order;
size++;
}
vieter_heap_node_free(node);
}
free(stack);
end:
vieter_heap_node_free(root);
}
vieter_heap_node *vieter_heap_tree_merge_same_order(vieter_heap_node *root_a,
vieter_heap_node *root_b) {
vieter_heap_node *root, *child;
if (root_a->key <= root_b->key) {
root = root_a;
child = root_b;
} else {
root = root_b;
child = root_a;
}
child->ptr.next_largest_order = root->largest_order;
root->largest_order = child;
root->order++;
return root;
}
vieter_heap_node *vieter_heap_tree_merge(vieter_heap_node *target_tree,
vieter_heap_node *other_tree) {
vieter_heap_node *out = target_tree;
vieter_heap_node *next_other_tree, *next_target_tree;
vieter_heap_node *previous_target_tree = NULL;
while (target_tree != NULL && other_tree != NULL) {
if (target_tree->order == other_tree->order) {
next_other_tree = other_tree->ptr.next_tree;
next_target_tree = target_tree->ptr.next_tree;
target_tree = vieter_heap_tree_merge_same_order(target_tree, other_tree);
target_tree->ptr.next_tree = next_target_tree;
// If this merge produces a binomial tree whose size is already in
// target, it will be the next target. Therefore, we can merge target's
// trees until we no longer have a duplicate depth.
while (next_target_tree != NULL &&
next_target_tree->order == target_tree->order) {
next_target_tree = next_target_tree->ptr.next_tree;
target_tree = vieter_heap_tree_merge_same_order(
target_tree, target_tree->ptr.next_tree);
target_tree->ptr.next_tree = next_target_tree;
}
if (previous_target_tree != NULL) {
previous_target_tree->ptr.next_tree = target_tree;
} else {
out = target_tree;
}
other_tree = next_other_tree;
} else if (target_tree->order > other_tree->order) {
next_other_tree = other_tree->ptr.next_tree;
if (previous_target_tree == NULL) {
previous_target_tree = other_tree;
out = other_tree;
} else {
previous_target_tree->ptr.next_tree = other_tree;
// This single missing line right here broke this entire function for
// nearly a week.
previous_target_tree = other_tree;
}
other_tree->ptr.next_tree = target_tree;
other_tree = next_other_tree;
} else {
if (previous_target_tree == NULL) {
out = target_tree;
}
previous_target_tree = target_tree;
target_tree = target_tree->ptr.next_tree;
}
}
// Append final part of tree to target
if (target_tree == NULL) {
previous_target_tree->ptr.next_tree = other_tree;
}
return out;
}
vieter_heap_node *vieter_heap_tree_pop(void **out, vieter_heap_node *tree) {
vieter_heap_node *tree_before_smallest = NULL;
vieter_heap_node *previous_tree = NULL;
vieter_heap_node *original_root = tree;
uint64_t smallest_key = tree->key;
while (tree->ptr.next_tree != NULL) {
previous_tree = tree;
tree = tree->ptr.next_tree;
if (tree->key < smallest_key) {
smallest_key = tree->key;
tree_before_smallest = previous_tree;
}
}
vieter_heap_node *tree_to_pop;
if (tree_before_smallest != NULL) {
tree_to_pop = tree_before_smallest->ptr.next_tree;
tree_before_smallest->ptr.next_tree = tree_to_pop->ptr.next_tree;
} else {
tree_to_pop = original_root;
original_root = original_root->ptr.next_tree;
}
*out = tree_to_pop->data;
if (tree_to_pop->order == 0) {
vieter_heap_tree_free(tree_to_pop);
return original_root;
}
// Each child has a pointer to its sibling with the next largest order. If we
// want to convert this list of children into their own tree, these pointers
// have to be reversed.
previous_tree = tree_to_pop->largest_order;
vieter_heap_node_free(tree_to_pop);
tree = previous_tree->ptr.next_largest_order;
previous_tree->ptr.next_tree = NULL;
vieter_heap_node *next_tree;
while (tree != NULL) {
next_tree = tree->ptr.next_largest_order;
tree->ptr.next_tree = previous_tree;
previous_tree = tree;
tree = next_tree;
}
// original_root is zero if the heap only contained a single tree.
if (original_root != NULL) {
return vieter_heap_tree_merge(original_root, previous_tree);
} else {
return previous_tree;
}
}

View File

@ -0,0 +1,53 @@
#ifndef VIETER_HEAP_TREE
#define VIETER_HEAP_TREE
#include <stdint.h>
#include <stdlib.h>
typedef struct vieter_heap_node {
uint64_t key;
void *data;
struct vieter_heap_node *largest_order;
union {
// Roots point to next tree in the heap, other nodes point to their first
// neighbour.
struct vieter_heap_node *next_tree;
struct vieter_heap_node *next_largest_order;
} ptr;
uint8_t order;
} vieter_heap_node;
/*
* Allocate and initialize a heap node object.
*/
vieter_heap_node *vieter_heap_node_init();
/*
* Deallocate a node object.
*/
void vieter_heap_node_free(vieter_heap_node *node);
/*
* Deallocate a node's entire structure.
*/
void vieter_heap_tree_free(vieter_heap_node *root);
/*
* Given the roots of the smallest trees in two heaps, merge them into a single
* large heap.
*/
vieter_heap_node *vieter_heap_tree_merge(vieter_heap_node *root_a, vieter_heap_node *root_b);
/*
* Given the roots of two trees of the same order, merge them into a heap of one
* order larger.
*/
vieter_heap_node *vieter_heap_tree_merge_same_order(vieter_heap_node *root_a,
vieter_heap_node *root_b);
/*
* Remove the smallest element from the given heap.
*/
vieter_heap_node *vieter_heap_tree_pop(void **out, vieter_heap_node *root);
#endif

View File

@ -0,0 +1,50 @@
#include "dynarray.h"
DynArray *dynarray_init(size_t initial_capacity) {
DynArray *da = malloc(sizeof(DynArray));
da->size = 0;
da->capacity = initial_capacity;
return da;
}
void dynarray_add(DynArray *da, const char *s) {
// An empty dynarray does not have an allocated internal array yet
if (da->size == 0) {
da->array = malloc(sizeof(char*) * da->capacity);
}
// Double array size if it's full
else if (da->size == da->capacity) {
// if the realloc fails, access to memory in da->array is lost
da->array = realloc(da->array, sizeof(char*) * da->capacity * 2);
da->capacity *= 2;
}
da->array[da->size] = strdup(s);
da->size++;
}
void dynarray_free(DynArray *da) {
if (da == NULL) {
return;
}
if (da->array != NULL) {
for (size_t i = 0; i < da->size; i++) {
free(da->array[i]);
}
free(da->array);
}
free(da);
}
char **dynarray_convert(DynArray *da) {
char **array = da->array;
da->array = NULL;
dynarray_free(da);
return array;
}

View File

@ -0,0 +1,180 @@
#include "package.h"
#define SMALL_BUFF_SIZE 128
#define ADD_STRING(section, field) if (pkg_info->field != 0) { \
snprintf(aux, SMALL_BUFF_SIZE, section, pkg_info->field); \
if (buff_size < strlen(description) + SMALL_BUFF_SIZE + 1) { \
description = realloc(description, buff_size * 2); \
buff_size *= 2; \
} \
strcat(description, aux); \
}
#define ADD_ARRAY(section, field) i = 0; if (pkg_info->field != NULL) { \
snprintf(aux, SMALL_BUFF_SIZE, section, pkg_info->field->array[i]); i++; \
if (buff_size < strlen(description) + SMALL_BUFF_SIZE + 1) { \
description = realloc(description, buff_size * 2); \
buff_size *= 2; \
} \
strcat(description, aux); \
while (pkg_info->field->array[i] != NULL) { \
snprintf(aux, SMALL_BUFF_SIZE, "\n%s", pkg_info->field->array[i]); i++; \
if (buff_size < strlen(description) + SMALL_BUFF_SIZE + 1) { \
description = realloc(description, buff_size * 2); \
buff_size *= 2; \
} \
strcat(description, aux); \
} \
}
static char *ignored_names[5] = {
".BUILDINFO",
".INSTALL",
".MTREE",
".PKGINFO",
".CHANGELOG"
};
static size_t ignored_words_len = sizeof(ignored_names) / sizeof(char *);
Pkg *package_init() {
return calloc(sizeof(PkgInfo), 1);
}
Pkg *package_read_archive(const char *pkg_path) {
struct archive *a = archive_read_new();
struct archive_entry *entry = archive_entry_new();
// These three are the most commonly used compression methods
archive_read_support_filter_zstd(a);
archive_read_support_filter_gzip(a);
archive_read_support_filter_xz(a);
// Contents should always be a tarball
archive_read_support_format_tar(a);
// TODO where does this 10240 come from?
int r = archive_read_open_filename(a, pkg_path, 10240);
// Exit early if we weren't able to successfully open the archive for reading
if (r != ARCHIVE_OK) {
return NULL;
}
int compression_code = archive_filter_code(a, 0);
const char *path_name;
PkgInfo *pkg_info;
DynArray *files = dynarray_init(16);
dynarray_add(files, "%FILES%");
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
path_name = archive_entry_pathname(entry);
bool ignore = false;
for (size_t i = 0; i < ignored_words_len; i++) {
if (strcmp(path_name, ignored_names[i]) == 0) {
ignore = true;
break;
}
}
if (!ignore) {
dynarray_add(files, path_name);
}
if (strcmp(path_name, ".PKGINFO") == 0) {
// Read data of file into memory buffer
int size = archive_entry_size(entry);
char *buf = malloc(size);
archive_read_data(a, buf, size);
// Parse package info string into a struct
pkg_info = package_info_init();
package_info_parse(pkg_info, buf);
free(buf);
} else {
archive_read_data_skip(a);
}
}
// Get size of file
struct stat stats;
if (stat(pkg_path, &stats) != 0) {
return NULL;
}
pkg_info->csize = stats.st_size;
archive_read_free(a);
// Create final return value
Pkg *pkg = package_init();
pkg->path = strdup(pkg_path);
pkg->info = pkg_info;
pkg->files = files;
pkg->compression = compression_code;
return pkg;
}
void sha256sum(Pkg *pkg, char *res) {
char command[SMALL_BUFF_SIZE];
snprintf(command, SMALL_BUFF_SIZE, "sha256sum %s", pkg->path);
FILE *p = popen(command, "r");
fgets(res, 65, p);
pclose(p);
}
char *package_to_description(Pkg *pkg) {
PkgInfo *pkg_info = pkg->info;
size_t buff_size = 1024;
char aux[SMALL_BUFF_SIZE];
char *description = malloc(sizeof(char) * buff_size);
int i;
// special case for FILENAME
// assuming .pkg.tar.zst; other formats are valid, this should account for that
snprintf(aux, SMALL_BUFF_SIZE, "%%FILENAME%%\n%s-%s-%s.pkg.tar.zst", pkg_info->name, pkg_info->version,
pkg_info->arch);
strcat(description, aux);
ADD_STRING("\n\n%%NAME%%\n%s", name);
ADD_STRING("\n\n%%BASE%%\n%s", base);
ADD_STRING("\n\n%%VERSION%%\n%s", version);
ADD_STRING("\n\n%%DESC%%\n%s", description);
ADD_ARRAY("\n\n%%GROUPS%%\n%s", groups);
ADD_STRING("\n\n%%CSIZE%%\n%ld", csize);
ADD_STRING("\n\n%%ISIZE%%\n%ld", size);
char checksum[65];
sha256sum(pkg, checksum);
snprintf(aux, SMALL_BUFF_SIZE, "\n\n%%SHA256SUM%%\n%s", checksum);
if (buff_size < strlen(description) + SMALL_BUFF_SIZE + 1) {
description = realloc(description, buff_size * 2);
buff_size *= 2;
}
strcat(description, aux);
ADD_STRING("\n\n%%URL%%\n%s", url);
ADD_ARRAY("\n\n%%LICENSE%%\n%s", licenses);
ADD_STRING("\n\n%%ARCH%%\n%s", arch);
ADD_STRING("\n\n%%BUILDDATE%%\n%ld", build_date);
ADD_STRING("\n\n%%PACKAGER%%\n%s", packager);
ADD_ARRAY("\n\n%%REPLACES%%\n%s", replaces);
ADD_ARRAY("\n\n%%CONFLICTS%%\n%s", conflicts);
ADD_ARRAY("\n\n%%PROVIDES%%\n%s", provides);
ADD_ARRAY("\n\n%%DEPENDS%%\n%s", depends);
ADD_ARRAY("\n\n%%OPTDEPENDS%%\n%s", optdepends);
ADD_ARRAY("\n\n%%MAKEDEPENDS%%\n%s", makedepends);
ADD_ARRAY("\n\n%%CHECKDEPENDS%%\n%s", checkdepends);
snprintf(aux, SMALL_BUFF_SIZE, "\n\n");
strcat(description, aux);
return description;
}

View File

@ -0,0 +1,79 @@
#include <stdbool.h>
#include "package_info.h"
#define PKG_INFO_STRING(key_ptr, field) if ((value_ptr = strstr(value_ptr, key_ptr)) != NULL) { \
value_ptr += strlen(key_ptr);\
tail_ptr = strchr(value_ptr, '\n');\
tail_ptr[0] = '\0'; \
pkg_info->field = strdup(value_ptr); \
tail_ptr[0] = '\n'; \
} value_ptr = tail_ptr;
#define PKG_INFO_INT(key_ptr, field) value_ptr = strstr(value_ptr, key_ptr) + strlen(key_ptr);\
tail_ptr = strchr(value_ptr, '\n');\
tail_ptr[0] = '\0'; \
pkg_info->field = atoi(value_ptr); \
tail_ptr[0] = '\n'; \
value_ptr = tail_ptr;
#define PKG_INFO_ARRAY(key_ptr, field) while((value_ptr = strstr(value_ptr, key_ptr)) != NULL){ \
value_ptr = value_ptr + strlen(key_ptr);\
tail_ptr = strchr(value_ptr, '\n'); \
tail_ptr[0] = '\0'; \
if(pkg_info->field == NULL) { pkg_info->field = dynarray_init(4); } \
dynarray_add(pkg_info->field, value_ptr); \
tail_ptr[0] = '\n'; \
value_ptr = tail_ptr;\
} value_ptr = tail_ptr;
PkgInfo *package_info_init() {
return calloc(1, sizeof(PkgInfo));
}
void package_info_free(PkgInfo *pkg_info) {
FREE_STRING(pkg_info->name);
FREE_STRING(pkg_info->base);
FREE_STRING(pkg_info->version);
FREE_STRING(pkg_info->description);
FREE_STRING(pkg_info->url);
FREE_STRING(pkg_info->arch);
FREE_STRING(pkg_info->packager);
FREE_STRING(pkg_info->pgpsig);
dynarray_free(pkg_info->groups);
dynarray_free(pkg_info->licenses);
dynarray_free(pkg_info->replaces);
dynarray_free(pkg_info->depends);
dynarray_free(pkg_info->conflicts);
dynarray_free(pkg_info->provides);
dynarray_free(pkg_info->optdepends);
dynarray_free(pkg_info->makedepends);
dynarray_free(pkg_info->checkdepends);
free(pkg_info);
}
void package_info_parse(PkgInfo *pkg_info, char *pkg_info_str) {
char *value_ptr = pkg_info_str, *tail_ptr;
PKG_INFO_STRING("\npkgname = ", name);
PKG_INFO_STRING("\npkgbase = ", base);
PKG_INFO_STRING("\npkgver = ", version);
PKG_INFO_STRING("\npkgdesc = ", description);
PKG_INFO_STRING("\nurl = ", url);
PKG_INFO_INT("\nbuilddate = ", build_date);
PKG_INFO_STRING("\npackager = ", packager);
PKG_INFO_INT("\nsize = ", size);
PKG_INFO_STRING("\narch = ", arch);
PKG_INFO_ARRAY("\nlicense = ", licenses);
PKG_INFO_ARRAY("\nreplaces = ", replaces);
PKG_INFO_ARRAY("\ngroup = ", groups);
PKG_INFO_ARRAY("\nconflict = ", conflicts);
PKG_INFO_ARRAY("\nprovides = ", provides);
PKG_INFO_ARRAY("\ndepend = ", depends);
PKG_INFO_ARRAY("\noptdepend = ", optdepends);
PKG_INFO_ARRAY("\nmakedepend = ", makedepends);
PKG_INFO_ARRAY("\ncheckdepend = ", checkdepends);
}

View File

@ -0,0 +1,187 @@
#include "acutest.h"
#include "vieter_heap_internal.h"
#include <stdlib.h>
#define TEST_SIZE(heap, size) \
TEST_CHECK(vieter_heap_size(heap) == size); \
TEST_MSG("Size: %zu, expected: %lu", vieter_heap_size(heap), (uint64_t)size)
void test_init() {
vieter_heap *heap = vieter_heap_init();
TEST_CHECK(heap != NULL);
TEST_SIZE(heap, 0);
vieter_heap_free(heap);
}
void count_nodes(uint64_t *counter, vieter_heap_node *root) {
(*counter)++;
if (root->largest_order != NULL) {
count_nodes(counter, root->largest_order);
}
// This will also traverse the various trees
if (root->ptr.next_largest_order != NULL) {
count_nodes(counter, root->ptr.next_largest_order);
}
}
uint64_t count_nodes_heap(vieter_heap *heap) {
uint64_t counter = 0;
if (heap->tree != NULL) {
count_nodes(&counter, heap->tree);
}
return counter;
}
void test_insert() {
vieter_heap *heap = vieter_heap_init();
TEST_SIZE(heap, 0);
void *data;
for (uint64_t i = 50; i > 0; i--) {
vieter_heap_insert(heap, i, (void *)i);
TEST_SIZE(heap, (uint64_t)51 - i);
TEST_CHECK(count_nodes_heap(heap) == (uint64_t)51 - i);
data = 0;
TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok);
TEST_CHECK_(data == (void *)i, "%lX == %lX", (uint64_t)data, i);
}
vieter_heap_free(heap);
}
void test_insert_random() {
srand(1);
vieter_heap *heap = vieter_heap_init();
TEST_SIZE(heap, 0);
uint64_t num = rand();
uint64_t smallest = num;
void *data = NULL;
for (uint64_t i = 0; i < 5000; i++) {
vieter_heap_insert(heap, num, (void *)num);
TEST_SIZE(heap, i + 1);
TEST_CHECK(count_nodes_heap(heap) == (uint64_t)i + 1);
if (num < smallest) {
smallest = num;
}
TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok);
TEST_CHECK(data == (void *)smallest);
data = NULL;
num = rand();
}
vieter_heap_free(heap);
}
void test_pop() {
const uint64_t n = 500;
vieter_heap *heap = vieter_heap_init();
TEST_SIZE(heap, 0);
void *data;
for (uint64_t i = n; i > 0; i--) {
vieter_heap_insert(heap, i, (void *)i);
TEST_SIZE(heap, (uint64_t)n + 1 - i);
TEST_CHECK(count_nodes_heap(heap) == (uint64_t)n + 1 - i);
TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok);
TEST_CHECK(data == (void*)i);
}
data = NULL;
for (uint64_t i = 1; i <= n; i++) {
TEST_CHECK(vieter_heap_pop(&data, heap) == vieter_heap_ok);
TEST_CHECK(data == (void*)i);
TEST_SIZE(heap, (uint64_t)n - i);
}
vieter_heap_free(heap);
}
int uint64_t_compare(const void *a, const void *b) {
if ((*(uint64_t *)a) < (*(uint64_t *)b)) {
return -1;
} else if ((*(uint64_t *)a) > (*(uint64_t *)b)) {
return 1;
} else {
return 0;
}
}
void test_pop_random() {
const uint64_t n = 500;
srand(0);
vieter_heap *heap = vieter_heap_init();
uint64_t *numbers = malloc(n * sizeof(uint64_t));
uint64_t num;
for (uint64_t i = 0; i < n; i++) {
num = rand();
vieter_heap_insert(heap, num, (void *)num);
TEST_SIZE(heap, i + 1);
TEST_CHECK(count_nodes_heap(heap) == i + 1);
numbers[i] = num;
}
qsort(numbers, n, sizeof(uint64_t), uint64_t_compare);
void *data = NULL;
for (uint64_t i = 0; i < n; i++) {
TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok);
TEST_CHECK_(data == (void *)numbers[i], "peek %lx == %lx", (uint64_t)data, numbers[i]);
data = NULL;
TEST_CHECK(vieter_heap_pop(&data, heap) == vieter_heap_ok);
TEST_CHECK_(data == (void *)numbers[i], "pop %lx == %lx", (uint64_t)data, numbers[i]);
TEST_SIZE(heap, n - i - 1);
TEST_CHECK(count_nodes_heap(heap) == n - i - 1);
// Assure each size is also a valid heap after inserting
vieter_heap_insert(heap, numbers[i], (void *)numbers[i]);
TEST_SIZE(heap, n - i);
TEST_CHECK(count_nodes_heap(heap) == n - i);
data = NULL;
TEST_CHECK(vieter_heap_pop(&data, heap) == vieter_heap_ok);
TEST_CHECK_(data == (void *)numbers[i], "pop %lx == %lx", (uint64_t)data, numbers[i]);
TEST_SIZE(heap, n - i - 1);
TEST_CHECK(count_nodes_heap(heap) == n - i - 1);
}
vieter_heap_free(heap);
free(numbers);
}
TEST_LIST = {
{"init", test_init},
{"insert", test_insert},
{"insert random", test_insert_random},
{"pop", test_pop},
{"pop random", test_pop_random},
{NULL, NULL}
};

View File

@ -0,0 +1,28 @@
#include "acutest.h"
#include "vieter_heap.h"
#include "vieter_heap_tree.h"
#include <stdlib.h>
void test_merge_same_order() {
vieter_heap_node *root_a = vieter_heap_node_init();
root_a->key = 1;
root_a->order = 0;
vieter_heap_node *root_b = vieter_heap_node_init();
root_b->key = 2;
root_b->order = 0;
vieter_heap_node *merged = vieter_heap_tree_merge_same_order(root_a, root_b);
TEST_CHECK(merged == root_a);
TEST_CHECK(merged->key == 1);
TEST_CHECK(merged->largest_order == root_b);
TEST_CHECK(merged->ptr.next_largest_order == NULL);
vieter_heap_tree_free(merged);
}
TEST_LIST = {
{"merge same order", test_merge_same_order},
{NULL, NULL}
};

View File

@ -0,0 +1,22 @@
# Generated by makepkg 6.0.2
# using fakeroot version 1.30.1
pkgname = xcursor-dmz
pkgbase = xcursor-dmz
pkgver = 0.4.5-2
pkgdesc = Style neutral, scalable cursor theme
url = https://packages.debian.org/sid/dmz-cursor-theme
builddate = 1673751613
packager = Unknown Packager
size = 3469584
arch = any
license = MIT
replaces = test1
group = x11
conflict = test2
conflict = test3
provides = test4
depend = test5
depend = test6
optdepend = test7
makedepend = xorg-xcursorgen
checkdepend = test8

45
test/package/desc 100644
View File

@ -0,0 +1,45 @@
%FILENAME%
xcursor-dmz-0.4.5-2-any.pkg.tar.zst
%NAME%
xcursor-dmz
%BASE%
xcursor-dmz
%VERSION%
0.4.5-2
%DESC%
Style neutral, scalable cursor theme
%GROUPS%
x11
%CSIZE%
328282
%ISIZE%
3469584
%SHA256SUM%
4f4bce9e975334ed7775ff4ddf4d2e82e411d599802f6179a122f89149f53bfb
%URL%
https://packages.debian.org/sid/dmz-cursor-theme
%LICENSE%
MIT
%ARCH%
any
%BUILDDATE%
1673751613
%PACKAGER%
Unknown Packager
%MAKEDEPENDS%
xorg-xcursorgen

243
test/package/files 100644
View File

@ -0,0 +1,243 @@
%FILES%
usr/
usr/share/
usr/share/icons/
usr/share/icons/DMZ-Black/
usr/share/icons/DMZ-Black/cursor.theme
usr/share/icons/DMZ-Black/cursors/
usr/share/icons/DMZ-Black/cursors/00008160000006810000408080010102
usr/share/icons/DMZ-Black/cursors/028006030e0e7ebffc7f7070c0600140
usr/share/icons/DMZ-Black/cursors/03b6e0fcb3499374a867c041f52298f0
usr/share/icons/DMZ-Black/cursors/08e8e1c95fe2fc01f976f1e063a24ccd
usr/share/icons/DMZ-Black/cursors/1081e37283d90000800003c07f3ef6bf
usr/share/icons/DMZ-Black/cursors/14fef782d02440884392942c11205230
usr/share/icons/DMZ-Black/cursors/2870a09082c103050810ffdffffe0204
usr/share/icons/DMZ-Black/cursors/3085a0e285430894940527032f8b26df
usr/share/icons/DMZ-Black/cursors/3ecb610c1bf2410f44200f48c40d3599
usr/share/icons/DMZ-Black/cursors/4498f0e0c1937ffe01fd06f973665830
usr/share/icons/DMZ-Black/cursors/5c6cd98b3f3ebcb1f9c7f1c204630408
usr/share/icons/DMZ-Black/cursors/6407b0e94181790501fd1e167b474872
usr/share/icons/DMZ-Black/cursors/640fb0e74195791501fd1ed57b41487f
usr/share/icons/DMZ-Black/cursors/9081237383d90e509aa00f00170e968f
usr/share/icons/DMZ-Black/cursors/9d800788f1b08800ae810202380a0822
usr/share/icons/DMZ-Black/cursors/X_cursor
usr/share/icons/DMZ-Black/cursors/alias
usr/share/icons/DMZ-Black/cursors/arrow
usr/share/icons/DMZ-Black/cursors/bd_double_arrow
usr/share/icons/DMZ-Black/cursors/bottom_left_corner
usr/share/icons/DMZ-Black/cursors/bottom_right_corner
usr/share/icons/DMZ-Black/cursors/bottom_side
usr/share/icons/DMZ-Black/cursors/bottom_tee
usr/share/icons/DMZ-Black/cursors/c7088f0f3e6c8088236ef8e1e3e70000
usr/share/icons/DMZ-Black/cursors/circle
usr/share/icons/DMZ-Black/cursors/col-resize
usr/share/icons/DMZ-Black/cursors/color-picker
usr/share/icons/DMZ-Black/cursors/copy
usr/share/icons/DMZ-Black/cursors/cross
usr/share/icons/DMZ-Black/cursors/cross_reverse
usr/share/icons/DMZ-Black/cursors/crossed_circle
usr/share/icons/DMZ-Black/cursors/crosshair
usr/share/icons/DMZ-Black/cursors/d9ce0ab605698f320427677b458ad60b
usr/share/icons/DMZ-Black/cursors/default
usr/share/icons/DMZ-Black/cursors/diamond_cross
usr/share/icons/DMZ-Black/cursors/dnd-ask
usr/share/icons/DMZ-Black/cursors/dnd-copy
usr/share/icons/DMZ-Black/cursors/dnd-link
usr/share/icons/DMZ-Black/cursors/dnd-move
usr/share/icons/DMZ-Black/cursors/dnd-none
usr/share/icons/DMZ-Black/cursors/dot_box_mask
usr/share/icons/DMZ-Black/cursors/dotbox
usr/share/icons/DMZ-Black/cursors/double_arrow
usr/share/icons/DMZ-Black/cursors/draft_large
usr/share/icons/DMZ-Black/cursors/draft_small
usr/share/icons/DMZ-Black/cursors/draped_box
usr/share/icons/DMZ-Black/cursors/e-resize
usr/share/icons/DMZ-Black/cursors/e29285e634086352946a0e7090d73106
usr/share/icons/DMZ-Black/cursors/ew-resize
usr/share/icons/DMZ-Black/cursors/fcf1c3c7cd4491d801f1e1c78f100000
usr/share/icons/DMZ-Black/cursors/fd_double_arrow
usr/share/icons/DMZ-Black/cursors/fleur
usr/share/icons/DMZ-Black/cursors/grab
usr/share/icons/DMZ-Black/cursors/grabbing
usr/share/icons/DMZ-Black/cursors/h_double_arrow
usr/share/icons/DMZ-Black/cursors/hand
usr/share/icons/DMZ-Black/cursors/hand1
usr/share/icons/DMZ-Black/cursors/hand2
usr/share/icons/DMZ-Black/cursors/help
usr/share/icons/DMZ-Black/cursors/icon
usr/share/icons/DMZ-Black/cursors/left_ptr
usr/share/icons/DMZ-Black/cursors/left_ptr_help
usr/share/icons/DMZ-Black/cursors/left_ptr_watch
usr/share/icons/DMZ-Black/cursors/left_side
usr/share/icons/DMZ-Black/cursors/left_tee
usr/share/icons/DMZ-Black/cursors/link
usr/share/icons/DMZ-Black/cursors/ll_angle
usr/share/icons/DMZ-Black/cursors/lr_angle
usr/share/icons/DMZ-Black/cursors/move
usr/share/icons/DMZ-Black/cursors/n-resize
usr/share/icons/DMZ-Black/cursors/ne-resize
usr/share/icons/DMZ-Black/cursors/nesw-resize
usr/share/icons/DMZ-Black/cursors/not-allowed
usr/share/icons/DMZ-Black/cursors/ns-resize
usr/share/icons/DMZ-Black/cursors/nw-resize
usr/share/icons/DMZ-Black/cursors/nwse-resize
usr/share/icons/DMZ-Black/cursors/openhand
usr/share/icons/DMZ-Black/cursors/pencil
usr/share/icons/DMZ-Black/cursors/pirate
usr/share/icons/DMZ-Black/cursors/plus
usr/share/icons/DMZ-Black/cursors/pointer
usr/share/icons/DMZ-Black/cursors/progress
usr/share/icons/DMZ-Black/cursors/question_arrow
usr/share/icons/DMZ-Black/cursors/right_ptr
usr/share/icons/DMZ-Black/cursors/right_side
usr/share/icons/DMZ-Black/cursors/right_tee
usr/share/icons/DMZ-Black/cursors/row-resize
usr/share/icons/DMZ-Black/cursors/s-resize
usr/share/icons/DMZ-Black/cursors/sb_down_arrow
usr/share/icons/DMZ-Black/cursors/sb_h_double_arrow
usr/share/icons/DMZ-Black/cursors/sb_left_arrow
usr/share/icons/DMZ-Black/cursors/sb_right_arrow
usr/share/icons/DMZ-Black/cursors/sb_up_arrow
usr/share/icons/DMZ-Black/cursors/sb_v_double_arrow
usr/share/icons/DMZ-Black/cursors/se-resize
usr/share/icons/DMZ-Black/cursors/size_bdiag
usr/share/icons/DMZ-Black/cursors/size_fdiag
usr/share/icons/DMZ-Black/cursors/size_hor
usr/share/icons/DMZ-Black/cursors/size_ver
usr/share/icons/DMZ-Black/cursors/sw-resize
usr/share/icons/DMZ-Black/cursors/target
usr/share/icons/DMZ-Black/cursors/tcross
usr/share/icons/DMZ-Black/cursors/text
usr/share/icons/DMZ-Black/cursors/top_left_arrow
usr/share/icons/DMZ-Black/cursors/top_left_corner
usr/share/icons/DMZ-Black/cursors/top_right_corner
usr/share/icons/DMZ-Black/cursors/top_side
usr/share/icons/DMZ-Black/cursors/top_tee
usr/share/icons/DMZ-Black/cursors/ul_angle
usr/share/icons/DMZ-Black/cursors/ur_angle
usr/share/icons/DMZ-Black/cursors/v_double_arrow
usr/share/icons/DMZ-Black/cursors/w-resize
usr/share/icons/DMZ-Black/cursors/wait
usr/share/icons/DMZ-Black/cursors/watch
usr/share/icons/DMZ-Black/cursors/xterm
usr/share/icons/DMZ-White/
usr/share/icons/DMZ-White/cursor.theme
usr/share/icons/DMZ-White/cursors/
usr/share/icons/DMZ-White/cursors/00008160000006810000408080010102
usr/share/icons/DMZ-White/cursors/028006030e0e7ebffc7f7070c0600140
usr/share/icons/DMZ-White/cursors/03b6e0fcb3499374a867c041f52298f0
usr/share/icons/DMZ-White/cursors/08e8e1c95fe2fc01f976f1e063a24ccd
usr/share/icons/DMZ-White/cursors/1081e37283d90000800003c07f3ef6bf
usr/share/icons/DMZ-White/cursors/14fef782d02440884392942c11205230
usr/share/icons/DMZ-White/cursors/2870a09082c103050810ffdffffe0204
usr/share/icons/DMZ-White/cursors/3085a0e285430894940527032f8b26df
usr/share/icons/DMZ-White/cursors/3ecb610c1bf2410f44200f48c40d3599
usr/share/icons/DMZ-White/cursors/4498f0e0c1937ffe01fd06f973665830
usr/share/icons/DMZ-White/cursors/5c6cd98b3f3ebcb1f9c7f1c204630408
usr/share/icons/DMZ-White/cursors/6407b0e94181790501fd1e167b474872
usr/share/icons/DMZ-White/cursors/640fb0e74195791501fd1ed57b41487f
usr/share/icons/DMZ-White/cursors/9081237383d90e509aa00f00170e968f
usr/share/icons/DMZ-White/cursors/9d800788f1b08800ae810202380a0822
usr/share/icons/DMZ-White/cursors/X_cursor
usr/share/icons/DMZ-White/cursors/alias
usr/share/icons/DMZ-White/cursors/arrow
usr/share/icons/DMZ-White/cursors/bd_double_arrow
usr/share/icons/DMZ-White/cursors/bottom_left_corner
usr/share/icons/DMZ-White/cursors/bottom_right_corner
usr/share/icons/DMZ-White/cursors/bottom_side
usr/share/icons/DMZ-White/cursors/bottom_tee
usr/share/icons/DMZ-White/cursors/c7088f0f3e6c8088236ef8e1e3e70000
usr/share/icons/DMZ-White/cursors/circle
usr/share/icons/DMZ-White/cursors/col-resize
usr/share/icons/DMZ-White/cursors/color-picker
usr/share/icons/DMZ-White/cursors/copy
usr/share/icons/DMZ-White/cursors/cross
usr/share/icons/DMZ-White/cursors/cross_reverse
usr/share/icons/DMZ-White/cursors/crossed_circle
usr/share/icons/DMZ-White/cursors/crosshair
usr/share/icons/DMZ-White/cursors/d9ce0ab605698f320427677b458ad60b
usr/share/icons/DMZ-White/cursors/default
usr/share/icons/DMZ-White/cursors/diamond_cross
usr/share/icons/DMZ-White/cursors/dnd-ask
usr/share/icons/DMZ-White/cursors/dnd-copy
usr/share/icons/DMZ-White/cursors/dnd-link
usr/share/icons/DMZ-White/cursors/dnd-move
usr/share/icons/DMZ-White/cursors/dnd-none
usr/share/icons/DMZ-White/cursors/dot_box_mask
usr/share/icons/DMZ-White/cursors/dotbox
usr/share/icons/DMZ-White/cursors/double_arrow
usr/share/icons/DMZ-White/cursors/draft_large
usr/share/icons/DMZ-White/cursors/draft_small
usr/share/icons/DMZ-White/cursors/draped_box
usr/share/icons/DMZ-White/cursors/e-resize
usr/share/icons/DMZ-White/cursors/e29285e634086352946a0e7090d73106
usr/share/icons/DMZ-White/cursors/ew-resize
usr/share/icons/DMZ-White/cursors/fcf1c3c7cd4491d801f1e1c78f100000
usr/share/icons/DMZ-White/cursors/fd_double_arrow
usr/share/icons/DMZ-White/cursors/fleur
usr/share/icons/DMZ-White/cursors/grab
usr/share/icons/DMZ-White/cursors/grabbing
usr/share/icons/DMZ-White/cursors/h_double_arrow
usr/share/icons/DMZ-White/cursors/hand
usr/share/icons/DMZ-White/cursors/hand1
usr/share/icons/DMZ-White/cursors/hand2
usr/share/icons/DMZ-White/cursors/help
usr/share/icons/DMZ-White/cursors/icon
usr/share/icons/DMZ-White/cursors/left_ptr
usr/share/icons/DMZ-White/cursors/left_ptr_help
usr/share/icons/DMZ-White/cursors/left_ptr_watch
usr/share/icons/DMZ-White/cursors/left_side
usr/share/icons/DMZ-White/cursors/left_tee
usr/share/icons/DMZ-White/cursors/link
usr/share/icons/DMZ-White/cursors/ll_angle
usr/share/icons/DMZ-White/cursors/lr_angle
usr/share/icons/DMZ-White/cursors/move
usr/share/icons/DMZ-White/cursors/n-resize
usr/share/icons/DMZ-White/cursors/ne-resize
usr/share/icons/DMZ-White/cursors/nesw-resize
usr/share/icons/DMZ-White/cursors/not-allowed
usr/share/icons/DMZ-White/cursors/ns-resize
usr/share/icons/DMZ-White/cursors/nw-resize
usr/share/icons/DMZ-White/cursors/nwse-resize
usr/share/icons/DMZ-White/cursors/openhand
usr/share/icons/DMZ-White/cursors/pencil
usr/share/icons/DMZ-White/cursors/pirate
usr/share/icons/DMZ-White/cursors/plus
usr/share/icons/DMZ-White/cursors/pointer
usr/share/icons/DMZ-White/cursors/progress
usr/share/icons/DMZ-White/cursors/question_arrow
usr/share/icons/DMZ-White/cursors/right_ptr
usr/share/icons/DMZ-White/cursors/right_side
usr/share/icons/DMZ-White/cursors/right_tee
usr/share/icons/DMZ-White/cursors/row-resize
usr/share/icons/DMZ-White/cursors/s-resize
usr/share/icons/DMZ-White/cursors/sb_down_arrow
usr/share/icons/DMZ-White/cursors/sb_h_double_arrow
usr/share/icons/DMZ-White/cursors/sb_left_arrow
usr/share/icons/DMZ-White/cursors/sb_right_arrow
usr/share/icons/DMZ-White/cursors/sb_up_arrow
usr/share/icons/DMZ-White/cursors/sb_v_double_arrow
usr/share/icons/DMZ-White/cursors/se-resize
usr/share/icons/DMZ-White/cursors/size_bdiag
usr/share/icons/DMZ-White/cursors/size_fdiag
usr/share/icons/DMZ-White/cursors/size_hor
usr/share/icons/DMZ-White/cursors/size_ver
usr/share/icons/DMZ-White/cursors/sw-resize
usr/share/icons/DMZ-White/cursors/target
usr/share/icons/DMZ-White/cursors/tcross
usr/share/icons/DMZ-White/cursors/text
usr/share/icons/DMZ-White/cursors/top_left_arrow
usr/share/icons/DMZ-White/cursors/top_left_corner
usr/share/icons/DMZ-White/cursors/top_right_corner
usr/share/icons/DMZ-White/cursors/top_side
usr/share/icons/DMZ-White/cursors/top_tee
usr/share/icons/DMZ-White/cursors/ul_angle
usr/share/icons/DMZ-White/cursors/ur_angle
usr/share/icons/DMZ-White/cursors/v_double_arrow
usr/share/icons/DMZ-White/cursors/w-resize
usr/share/icons/DMZ-White/cursors/wait
usr/share/icons/DMZ-White/cursors/watch
usr/share/icons/DMZ-White/cursors/xterm
usr/share/licenses/
usr/share/licenses/xcursor-dmz/
usr/share/licenses/xcursor-dmz/LICENSE

View File

@ -0,0 +1,84 @@
#include "acutest.h"
#include "package.h"
void test_pkg_info_parse() {
FILE *f = fopen("./test/package/.PKGINFO", "r");
TEST_ASSERT_(f != NULL, "could not find test .PKGINFO file in ./test/package ");
fseek(f, 0L, SEEK_END);
size_t size = ftell(f);
fflush(stdout);
rewind(f);
char *pkg_info_str = malloc(size);
fread(pkg_info_str, 1, size, f);
fclose(f);
PkgInfo *pkg_info = package_info_init();
package_info_parse(pkg_info, pkg_info_str);
TEST_CHECK(!strcmp(pkg_info->name, "xcursor-dmz"));
TEST_CHECK(!strcmp(pkg_info->base, "xcursor-dmz"));
TEST_CHECK(!strcmp(pkg_info->version, "0.4.5-2"));
TEST_CHECK(!strcmp(pkg_info->description, "Style neutral, scalable cursor theme"));
TEST_CHECK(!strcmp(pkg_info->url, "https://packages.debian.org/sid/dmz-cursor-theme"));
TEST_CHECK(pkg_info->build_date == 1673751613);
TEST_CHECK(!strcmp(pkg_info->packager, "Unknown Packager"));
TEST_CHECK(pkg_info->size == 3469584);
TEST_CHECK(!strcmp(pkg_info->arch, "any"));
TEST_CHECK(!strcmp(pkg_info->licenses->array[0], "MIT"));
TEST_CHECK(!strcmp(pkg_info->replaces->array[0], "test1"));
TEST_CHECK(!strcmp(pkg_info->groups->array[0], "x11"));
TEST_CHECK(!strcmp(pkg_info->conflicts->array[0], "test2"));
TEST_CHECK(!strcmp(pkg_info->conflicts->array[1], "test3"));
TEST_CHECK(!strcmp(pkg_info->provides->array[0], "test4"));
TEST_CHECK(!strcmp(pkg_info->depends->array[0], "test5"));
TEST_CHECK(!strcmp(pkg_info->depends->array[1], "test6"));
TEST_CHECK(!strcmp(pkg_info->optdepends->array[0], "test7"));
TEST_CHECK(!strcmp(pkg_info->makedepends->array[0], "xorg-xcursorgen"));
TEST_CHECK(!strcmp(pkg_info->checkdepends->array[0], "test8"));
}
void test_pkg_read_archive_files() {
Pkg *pkg = package_read_archive("./test/package/xcursor-dmz-0.4.5-2-any.pkg.tar.zst");
TEST_ASSERT_(pkg != NULL, "failure parsing pkg archive");
FILE *f = fopen("./test/package/files", "r");
TEST_ASSERT_(f != NULL, "could not find test files file in ./test/package");
char buff[128];
size_t i = 0;
while ((fgets(buff, 128, f)) != NULL || i < pkg->files->size) {
if (buff[strlen(buff) - 1] == '\n') {
buff[strlen(buff) - 1] = '\0';
}
TEST_CHECK_(!strcmp(pkg->files->array[i], buff), "%s != %s", pkg->files->array[i], buff);
i++;
}
TEST_CHECK(pkg->compression = 14);
}
void test_pkg_read_archive_desc() {
Pkg *pkg = package_read_archive("./test/package/xcursor-dmz-0.4.5-2-any.pkg.tar.zst");
TEST_ASSERT_(pkg != NULL, "failure parsing pkg archive");
char *description = package_to_description(pkg);
FILE *f = fopen("./test/package/desc", "r");
TEST_ASSERT_(f != NULL, "could not find test desc file in ./test/package");
fseek(f, 0, SEEK_END);
size_t size = ftell(f);
rewind(f);
char *desc = malloc(size);
fread(desc, 1, size, f);
fclose(f);
TEST_CHECK(!strcmp(description, desc));
}
TEST_LIST = {
{"pkg_info_valid_parse", test_pkg_info_parse},
{"pkg_read_archive_files", test_pkg_read_archive_files},
{"pkg_read_archive_desc", test_pkg_read_archive_desc},
{NULL, NULL}
};