Compare commits

...

5 Commits

11 changed files with 398 additions and 4 deletions

View File

@ -5,6 +5,6 @@ root = true
end_of_line = lf end_of_line = lf
insert_final_newline = true insert_final_newline = true
[*.v] [*.{v,c,h}]
# vfmt wants it :( # vfmt wants it :(
indent_style = tab indent_style = tab

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
*.c vieter.c
/data/ /data/
# Build artifacts # Build artifacts

View File

@ -1,9 +1,10 @@
# =====CONFIG===== # =====CONFIG=====
SRC_DIR := src SRC_DIR := src
SOURCES != find '$(SRC_DIR)' -iname '*.v' SOURCES != find '$(SRC_DIR)' -type f \( -iname '*.v' -or -iname '*.c' -or -iname '*.h' \)
V_PATH ?= v V_PATH ?= v
V := $(V_PATH) -showcc -gc boehm -W -d use_openssl # We need to use GCC because TCC doesn't like the way we use C bindings
V := $(V_PATH) -showcc -gc boehm -W -d use_openssl -cc gcc
all: vieter all: vieter

View File

@ -0,0 +1,55 @@
#include "dynarray.h"
struct dyn_array {
char **array;
size_t size;
size_t capacity;
};
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) {
da->array = realloc(da->array, 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,19 @@
#ifndef VIETER_DYNARRAY
#define VIETER_DYNARRAY
#include <stdlib.h>
#include <string.h>
typedef struct dyn_array DynArray;
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

View File

@ -0,0 +1,98 @@
#include "package.h"
static char *ignored_names[5] = {
".BUILDINFO",
".INSTALL",
".MTREE",
".PKGINFO",
".CHANGELOG"
};
static int 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);
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);
archive_entry_free(entry);
// 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;
}
char *package_to_description(Pkg *pkg) {
}

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,139 @@
#include <stdbool.h>
#include "package_info.h"
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);
}
/**
* Advance the pointer until all spaces are skipped.
*/
static inline char *trim_spaces_front(char *ptr) {
while (ptr[0] == ' ') {
ptr++;
}
return ptr;
}
/**
* Given a string pointer in the middle of a string, move over all spaces in the
* given direction. The final space is replaced with a NULL-byte.
*/
static inline void trim_spaces_back(char *ptr) {
if (ptr[0] != ' ') {
return;
}
while (ptr[-1] == ' ') {
ptr--;
}
ptr[0] = '\0';
}
#define PKG_INFO_STRING(key, field) if (strcmp(key_ptr, key) == 0) { pkg_info->field = strdup(value_ptr); goto advance; }
#define PKG_INFO_INT(key, field) if (strcmp(key_ptr, key) == 0) { pkg_info->field = atoi(value_ptr); goto advance; }
#define PKG_INFO_ARRAY(key, field) if (strcmp(key_ptr, key) == 0) { \
if (pkg_info->field == NULL) { pkg_info->field = dynarray_init(4); } \
dynarray_add(pkg_info->field, value_ptr); goto advance; \
}
int package_info_parse(PkgInfo *pkg_info, char *pkg_info_str) {
char *offset_ptr, *equals_ptr, *key_ptr, *value_ptr;
bool end = false;
// Iterate over all lines in file
while (!end) {
// This pointer will always point to the final character of the
// current line, be it the position of a newline or the NULL byte at
// the end of the entire string
offset_ptr = strchr(pkg_info_str, '\n');
// We replace the newline with a NULL byte. Now we know the line runs
// until the next NULL byte.
if (offset_ptr != NULL) {
offset_ptr[0] = '\0';
} else {
// Advance pointer to the NULL byte of the string
offset_ptr = pkg_info_str + 1;
while (*offset_ptr != '\0') {
offset_ptr++;
}
end = true;
}
// Skip comment lines
if (pkg_info_str[0] == '#') {
goto advance;
}
equals_ptr = strchr(pkg_info_str, '=');
// If a line doesn't contain an equals sign, the file is invalid
if (equals_ptr == NULL) {
return 1;
}
// Trim whitespace from key
key_ptr = trim_spaces_front(pkg_info_str);
trim_spaces_back(equals_ptr - 1);
// Trim spaces from value
value_ptr = trim_spaces_front(equals_ptr + 1);
trim_spaces_back(offset_ptr - 1);
// Match key
PKG_INFO_STRING("pkgname", name);
PKG_INFO_STRING("pkgbase", base);
PKG_INFO_STRING("pkgver", version);
PKG_INFO_STRING("pkgdesc", description);
PKG_INFO_INT("size", size);
PKG_INFO_STRING("url", url);
PKG_INFO_STRING("arch", arch);
PKG_INFO_INT("builddate", build_date);
PKG_INFO_STRING("packager", packager);
PKG_INFO_STRING("pgpsig", pgpsig);
PKG_INFO_INT("pgpsigsize", pgpsigsize);
PKG_INFO_ARRAY("group", groups);
PKG_INFO_ARRAY("license", licenses);
PKG_INFO_ARRAY("replaces", replaces);
PKG_INFO_ARRAY("depend", depends);
PKG_INFO_ARRAY("optdepend", optdepends);
PKG_INFO_ARRAY("makedepend", makedepends);
PKG_INFO_ARRAY("checkdepend", checkdepends);
advance:
pkg_info_str = offset_ptr + 1;
continue;
}
return 0;
}

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();
int package_info_parse(PkgInfo *pkg_info, char *pkg_info_str);
void package_info_free(PkgInfo *pkg_info);
#endif

View File

@ -0,0 +1,14 @@
module package
#flag -I @VMODROOT/c
// We need to specify *every* C file here. Otherwise, Vieter doesn't compile.
#flag @VMODROOT/c/package.o
#flag @VMODROOT/c/package_info.o
#flag @VMODROOT/c/dynarray.o
#include "package.h"
struct C.Pkg{}
fn C.package_read_archive(pkg_path &char) &C.pkg

View File

@ -0,0 +1,3 @@
Module{
name: 'package'
}