This commit is contained in:
kento2 2026-09-04 19:59:24 +02:00
commit 55385f8857
69 changed files with 1125 additions and 0 deletions

18
README Normal file
View file

@ -0,0 +1,18 @@
depends: https://github.com/arp242/toml-c
expected usage:
(1) pkg_add /path/to/source/code/with/template/file
(2) pkg_add name of source template in source repo
We need a template format that specifies compiletime
dependencies in addition to the standart rpm spec fields
such as name, description, build script, etc..
In the case of (2), it must also specify an URL to get the
source code from.
in the end it should produce an rpm package and install it
(with dnf). referenced packages(like in dependencies) should
use the name from fedoras package list.
If a source template depends on another package that only
exists as a source template, pkg_add must make sure to
compile that first.

9
builder/README Normal file
View file

@ -0,0 +1,9 @@
build steps:
- create rootfs(empty fs)
- fetch all files(including dependencies) + cache
- extract/install files/deps
- chroot into rootfs
impl details:
- use queues for fetch and extract with parallel workers
- rootfs

10
builder/run Executable file
View file

@ -0,0 +1,10 @@
#!/bin/sh
mkdir -p rootfs
podman run \
-v ./rootfs:/data \
docker.io/fedora \
dnf5 \
--installroot /data \
--repofrompath "bootstrap,https://ftp.uni-stuttgart.de/fedora/releases/44/Everything/x86_64/os/" \
--assumeyes \
install basesystem ed

12
conf.strff Normal file
View file

@ -0,0 +1,12 @@
name=pkg_add
version=0.1
makedeps=muon samurai
[build]
muon setup build
samu -C build
[install]
install -d /usr/local/bin
install -m755 pkg_add /usr/local/bin

11
example.source-template Normal file
View file

@ -0,0 +1,11 @@
name=lr
ver=2.0.1
descr=List files, recursively
build() {
make
}
install() {
make install
}

10
fetcher/README Normal file
View file

@ -0,0 +1,10 @@
idea:
given a list of URL-to-path pairs, it downloads them in
parallel to the specified path.
implementation:
These pairs are added to a queue(FIFO) along with an
attempt counter. Multiple worker processes are spawned
that each handle an entry in a queue.
workers write to an error pipe and to a out pipe.

11
fetcher/a.txt Normal file
View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>lab 0x13</title>
</head>
<body>
<h1>welcome to the lab!</h1>
<p><a href="https://git.lab0x13.site/explore/repos">git</a></p>
<p><a href="intellij-colorscheme.icls">intellij colorscheme</a>
inspired by <a href="https://hank.bond/posts/highlighting-my-code-based-on-how-much-i-care/">this blog post</a></p>
</html>

5
fetcher/fetcher-worker.c Normal file
View file

@ -0,0 +1,5 @@
/*
* stdin serves as a queue in the form of
* URL outputDir
* where outputDir is the directory to save the downloaded
* file in.

60
fetcher/fetcher.c Normal file
View file

@ -0,0 +1,60 @@
#include <unistd.h>
#include <libowfat/errmsg.h>
#include <libowfat/buffer.h>
#include <sys/wait.h>
#include "fetcher.h"
#define dieusage() die(100, "usage: ", argv0, " [-v] [-j jobs] URL...\n\
(-v means verbose)")
int verbose = 0;
int jobs = 5;
int queuepipe[2];
/* merged stdout of workers */
int errpipe[2];
/* merged stderr of workers */
int outpipe[2];
int main(int argc, char **argv)
{
errmsg_iam(*argv);
int i;
for (i = 1; i < argc; i++) {
if (argv[i][0] != '-') break;
if (argv[i][1] == '-' && !argv[i][2]) break;
switch (argv[i][1]) {
case 'j':
i++;
jobs = atoi(argv[i]);
if (jobs < 1)
die(100, "value for -n must be >= 1");
break;
case 'v':
verbose = 1;
break;
default: dieusage();
}
}
argc -= i;
argv += i;
if (argc < 1) dieusage();
if (pipe(errpipe)<0
|| pipe(outpipe)<0
) diesys(111, "pipe");
if (verbose) {
buffer_puts(buffer_2, "Using ");
buffer_putlong(buffer_2, jobs);
buffer_puts(buffer_2, " parallel jobs\n");
buffer_flush(buffer_2);
}
char **fetchcmd = (char *[]){"curl", "-o", "a.txt", "https://lab0x13.site/index.html", NULL};
pid_t pid = spawnchild(fetchcmd, outpipe[1], errpipe[1]);
if (pid<0)
exit(111);
waitpid(pid, NULL, 0);
exit(0);
}

13
fetcher/fetcher.h Normal file
View file

@ -0,0 +1,13 @@
#ifndef FETCHER_H
#define FETCHER_H
#include <sys/types.h>
/*
* execs args[0] with args. output is connected to outfd
* and errfd.
* return: pid of child process or -1 on error.
*/
pid_t spawnchild(char **args, int outfd, int errfd);
#endif

26
fetcher/meson.build Normal file
View file

@ -0,0 +1,26 @@
project(
'fetcher', 'c',
default_options: [
'c_std=c99',
'warning_level=2',
],
)
cc = meson.get_compiler('c')
deps = [
cc.find_library('libowfat'),
dependency('libtoml'),
]
tmpdir = get_option('tmpdir')
add_project_arguments('-DTMPDIR="'+tmpdir+'"', language: 'c')
executable('fetcher', files(
'fetcher.c',
'spawnchild.c',
), dependencies:deps)
executable('fetcher-worker', files(
'fetcher-worker.c',
'spawnchild.c',
), dependencies:deps)

1
fetcher/meson.options Normal file
View file

@ -0,0 +1 @@
option('tmpdir', type : 'string', value : '/tmp', description : 'writable directory for temporary files')

41
fetcher/spawnchild.c Normal file
View file

@ -0,0 +1,41 @@
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <libowfat/errmsg.h>
#include <libowfat/buffer.h>
static void carpsysargs(char *args[])
{
buffer_puts(buffer_2, "failed to exec");
while (*args) {
buffer_puts(buffer_2, " \'");
buffer_puts(buffer_2, *args);
buffer_puts(buffer_2, "'");
args++;
}
buffer_puts(buffer_2, ": ");
buffer_puts(buffer_2, strerror(errno));
buffer_puts(buffer_2, "\n");
buffer_flush(buffer_2);
}
pid_t spawnchild(char *args[], int infd, int outfd, int errfd)
{
pid_t pid = fork();
switch (pid) {
case -1: carpsys("fork: "); return -1;
case 0:
// TODO pipe
if (dup2(1, outfd)<0
|| dup2(2, errfd)<0
) {
carpsys("dup2");
return -1;
}
execvp(args[0], args);
carpsysargs(args);
return -1;
default:
return pid;
}
}

23
meson.build Normal file
View file

@ -0,0 +1,23 @@
project(
'pkg_add', 'c',
default_options: [
'c_std=c99',
'warning_level=2',
],
)
cc = meson.get_compiler('c')
deps = [
cc.find_library('libowfat'),
dependency('libtoml'),
]
executable('pkg_add', files(
'pkg_add.c',
'strff/strff_open.c',
'strff/strff_close.c',
'strff/strff_seek.c',
'strff/strff_read.c',
'strff/strff_read_sa.c',
'strff/strff_get.c',
), dependencies:deps)

Binary file not shown.

Binary file not shown.

Binary file not shown.

17
pkin/README Normal file
View file

@ -0,0 +1,17 @@
pkin is the binary package management frontend.
think of this as something like debian's apt or alpine's apk.
source package templates specify compile-time dependencies,
i.e. packages that are required to be present in the build
environment, e.g. gcc and glibc to compile a c project.
repositories
------------
goal:
- lazy pkg building
- lazy metadata
-
deps: aria2
makedeps: meson(or muon), ninja(or samu), a c compiler

2
pkin/asset/example.a Normal file
View file

@ -0,0 +1,2 @@
lr 1.0 foo https://lab0x13.site/index.html
asd 1.0 asd http://kentoj/index.html

2
pkin/asset/example.b Normal file
View file

@ -0,0 +1,2 @@
lsasdr 1.0 folo
lasd 1.0 asdl

8
pkin/command.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef PKIN_SUBCOMMAND_H
#define PKIN_SUBCOMMAND_H
void command_search(int argc, char **argv);
void command_fetch(int argc, char **argv);
#endif

22
pkin/doc/pkin-repo.7.scd Normal file
View file

@ -0,0 +1,22 @@
pkin-repo(8)
# DESCRIPTION
Every repository really just consists of an indexfile.
The indexfile is used for querying/filtering available packages for e.g
pkin-search(8).
The indexfile plain text file where each line presents a package's most
crucial metadata: name, version and description.
These fields are tab-separated.
```
foo 1.0 foo's description
foo 1.0 foo's description
```
# LIMITATIONS
- it's hard to add fields to the indexfile with this format
- the format is not versioned. changing the format requires
changing the filename

24
pkin/meson.build Normal file
View file

@ -0,0 +1,24 @@
project(
'pkin', 'c',
default_options: [
'c_std=c99',
'warning_level=2',
],
)
cc = meson.get_compiler('c')
deps = [
cc.find_library('libowfat'),
]
executable('pkin', files(
'./pkin.c',
'./pkin-search.c',
'./pkin-fetch.c',
'./pkin/pkin_repo_iter_init.c',
'./pkin/pkin_repo_iter_read.c',
'./pkin/pkin_pkg_fetch_prepare.c',
'./pkin/pkin_pkg_fetch_add.c',
'./pkin/pkin_pkg_fetch_start.c',
'./pkin_internal/pkin__spawn.c',
), dependencies:deps)

54
pkin/pkin-fetch.c Normal file
View file

@ -0,0 +1,54 @@
#include <libowfat/errmsg.h>
#include <libowfat/str.h>
#include <unistd.h>
#include "../pkin.h"
#include "command.h"
#define dieusage() die(100, "usage: ", argv0, " PACKGE")
static int find_package(char *name, pkin_pkgmeta *pkgmeta)
{
pkin_repo_iter iter;
pkin_repo_iter_init(&iter);
while (pkin_repo_iter_read(&iter, pkgmeta))
if (str_equal(pkgmeta->name, name))
return 1;
return 0;
}
static void run(int argc, char **argv)
{
int i;
pkin_pkgmeta pkgmeta;
pkin_fetch_queue queue;
if (!pkin_fetch_prepare(&queue))
die(111, "failed to create package fetch queue");
for (i = 0; i < argc; i++) {
if (!find_package(argv[i], &pkgmeta))
die(100, "package '", argv[i], "' not found ");
pkin_fetch_add(&queue, pkgmeta.url, pkgmeta.name);
}
if (!pkin_fetch_start(&queue))
die(111, "failed fetching packages");
}
void command_fetch(int argc, char **argv)
{
int i;
errmsg_iam("pkin-fetch");
for (i = 1; i < argc; i++) {
if (argv[i][0] != '-') break;
if (argv[i][1] == '-' && !argv[i][2]) break;
switch (argv[i][1]) {
default: dieusage();
}
}
argc -= i;
argv += i;
run(argc, argv);
}

54
pkin/pkin-search.c Normal file
View file

@ -0,0 +1,54 @@
#include <libowfat/errmsg.h>
#include <libowfat/str.h>
#include <unistd.h>
#include "../pkin.h"
#include "command.h"
#define dieusage() die(100, "usage: ",argv0, " PATTERN...")
static void print_meta(pkin_pkgmeta *pkgmeta)
{
buffer_puts(buffer_1, pkgmeta->name);
buffer_puts(buffer_1, "-");
buffer_puts(buffer_1, pkgmeta->version);
buffer_puts(buffer_1, ": ");
buffer_puts(buffer_1, pkgmeta->descr);
buffer_puts(buffer_1, "\n");
}
static void run(int argc, char **argv)
{
pkin_repo_iter iter;
pkin_pkgmeta pkgmeta;
int i;
pkin_repo_iter_init(&iter);
while (pkin_repo_iter_read(&iter, &pkgmeta)) {
int matches = 1;
for (i = 0; i < argc; i++) {
if (!strstr(pkgmeta.name, argv[i]) && !strstr(pkgmeta.descr, argv[i]))
matches = 0;
}
if (matches)
print_meta(&pkgmeta);
}
if (buffer_flush(buffer_1) < 0)
carpsys("write to stdout");
}
void command_search(int argc, char **argv)
{
int i;
errmsg_iam("pkin-search");
for (i = 1; i < argc; i++) {
if (argv[i][0] != '-') break;
if (argv[i][1] == '-' && !argv[i][2]) break;
switch (argv[i][1]) {
default: dieusage();
}
}
argc -= i;
argv += i;
run(argc, argv);
}

90
pkin/pkin.c Normal file
View file

@ -0,0 +1,90 @@
#include <libowfat/array.h>
#include <libowfat/str.h>
#include <libowfat/byte.h>
#include <libowfat/errmsg.h>
#include <string.h>
#include "../pkin.h"
#include "command.h"
#define dienoarg(OPT) \
do { \
carp("missing argument for -"OPT); \
dieusage(); \
} while(0)
int pkin_verbose = 0;
array pkin_repos;
static void add_repo(char *path, size_t pathlen)
{
char **repo;
repo = array_allocate(&pkin_repos, sizeof(char *), array_length(&pkin_repos, sizeof(char *)));
if (!repo) diesys(111, "failed allocating repository entry");
*repo = malloc(pathlen + 1);
if (!*repo) diesys(111, "failed allocating repository");
byte_copy(*repo, pathlen, path);
(*repo)[pathlen] = 0;
if (pkin_verbose) {
buffer_puts(buffer_2, "added repository ");
buffer_put(buffer_2, path, pathlen);
buffer_putsflush(buffer_2, "\n");
}
}
/* imports repositories defined by the PKIN_REPOS env var */
static void env_import_repos(void) {
char *repos;
size_t l;
repos = getenv("PKIN_REPOS");
if (!repos) {
carp("PKIN_REPOS not defined");
return;
}
for (;;) {
l = str_chr(repos, ':');
add_repo(repos, l);
if (l == str_len(repos)) break;
repos += l + 1;
}
}
static void dieusage()
{
die(100, "usage: ",argv0, " [-r repository]... [-v] COMMAND\n"
" -v for verbose\n"
"Commands: search, fetch");
}
int main(int argc, char **argv)
{
int i;
errmsg_iam(*argv);
for (i = 1; i < argc; i++) {
if (argv[i][0] != '-') break;
if (argv[i][1] == '-' && !argv[i][2]) break;
switch (argv[i][1]) {
case 'r':
if (!argv[++i]) dienoarg("r");
add_repo(argv[i], str_len(argv[i]));
break;
case 'v':
pkin_verbose = 1;
break;
default: dieusage();
}
}
argc -= i;
argv += i;
if (argc == 0) dieusage();
env_import_repos();
if (str_equal(*argv, "search"))
command_search(argc, argv);
else if (str_equal(*argv, "fetch"))
command_fetch(argc, argv);
else dieusage();
}

65
pkin/pkin.h Normal file
View file

@ -0,0 +1,65 @@
#ifndef PKIN_REPO_H
#define PKIN_REPO_H
/*
* each repo is a directory with a repo-index json file.
*/
#include <libowfat/stralloc.h>
#include <libowfat/array.h>
#include <libowfat/buffer.h>
#include <limits.h>
#include <jansson.h>
#define PKIN_PKGMETA_NAME_MAX 128
#define PKIN_PKGMETA_DESCR_MAX 256
#define PKIN_PKGMETA_VERSION_MAX 256
#define PKIN_PKGMETA_URL_MAX 2048
extern int pkin_verbose;
extern array pkin_repos; /* of char * */
typedef struct {
char name[PKIN_PKGMETA_NAME_MAX];
char descr[PKIN_PKGMETA_DESCR_MAX];
char version[PKIN_PKGMETA_VERSION_MAX];
char url[PKIN_PKGMETA_URL_MAX]; /* url to download binary package from */
} pkin_pkgmeta;
typedef struct {
buffer b;
int fd;
char buf[4096];
stralloc line;
size_t repos_i;
size_t repos_len;
} pkin_repo_iter;
/* parlallel querying would be nice */
/*
* initialize a pkin_repo_query.
*/
void pkin_repo_iter_init(pkin_repo_iter *);
/*
* gets the next pkgmeta.
* return: 1 if found, 0 if no next
*/
int pkin_repo_iter_read(pkin_repo_iter *, pkin_pkgmeta *res);
typedef struct {
buffer writeb;
char writebuf[1024];
int pipefd[2];
} pkin_fetch_queue;
int pkin_fetch_prepare(pkin_fetch_queue *queue);
void pkin_fetch_add(pkin_fetch_queue *queue, char *url, char *output);
/*
* downloads the binary package from url to the given
* directory and writes the resulting path to rest
*/
int pkin_fetch_start(pkin_fetch_queue *queue);
#endif

View file

@ -0,0 +1,2 @@
int pkin_pkg_fetch(char *url, char *dir, stralloc *result);

View file

@ -0,0 +1,8 @@
#include "../pkin.h"
void pkin_fetch_add(pkin_fetch_queue *queue, char *url, char *output)
{
buffer_puts(&queue->writeb, url);
buffer_puts(&queue->writeb, "\n -o ");
buffer_puts(&queue->writeb, output);
}

View file

@ -0,0 +1,15 @@
#include <libowfat/errmsg.h>
#include <unistd.h>
#include "../pkin.h"
int pkin_fetch_prepare(pkin_fetch_queue *queue)
{
if (pipe(queue->pipefd) < 0) {
carpsys("pipe");
return 0;
}
buffer_init_write(&queue->writeb, queue->pipefd[1],
queue->writebuf, sizeof(queue->writebuf));
return 1;
}

View file

@ -0,0 +1,27 @@
#include <libowfat/errmsg.h>
#include <sys/wait.h>
#include "../pkin.h"
#include "../pkin_internal.h"
int pkin_fetch_start(pkin_fetch_queue *queue)
{
char **fetchcmd;
pid_t pid;
if (buffer_flush(&queue->writeb) < 0) {
carpsys("write to pipe");
return 0;
}
fetchcmd = (char *[]){"aria2c", "-i", "-", NULL};
pid = pkin__spawn(fetchcmd, queue->pipefd[0], 1, 2);
if (pid < 0) {
carp("failed spawning aria2c to fetch files");
return 0;
}
if (waitpid(pid, NULL, 0) < 0) {
carp("waitpid");
return 0;
}
return 1;
}

View file

@ -0,0 +1,9 @@
#include "../pkin.h"
void pkin_repo_iter_init(pkin_repo_iter *iter)
{
iter->fd = -1;
iter->repos_i = 0;
iter->repos_len = array_length(&pkin_repos, sizeof(char *));
stralloc_init(&iter->line);
}

View file

@ -0,0 +1,106 @@
#include <libowfat/array.h>
#include <libowfat/buffer.h>
#include <libowfat/errmsg.h>
#include <libowfat/byte.h>
#include <libowfat/str.h>
#include <libowfat/open.h>
#include <libowfat/stralloc.h>
#include <unistd.h>
#include "../pkin.h"
static int parse_field(char *line, char *field, size_t fieldmax)
{
size_t l;
l = str_chr(line, '\t');
if (l == 0) return 0;
if (l >= fieldmax) {
carp("field ", field, " is too long ");
return 0;
}
byte_copy(field, l, line);
field[l] = 0;
return l;
}
static int parse_pkgmeta(char *line, pkin_pkgmeta *res)
{
size_t l;
l = parse_field(line, res->name, PKIN_PKGMETA_NAME_MAX);
if (l == 0) goto skip;
line += l+1;
l = parse_field(line, res->version, PKIN_PKGMETA_VERSION_MAX);
if (l == 0) goto skip;
line += l+1;
l = parse_field(line, res->descr, PKIN_PKGMETA_DESCR_MAX);
if (l == 0) goto skip;
line += l+1;
l = parse_field(line, res->url, PKIN_PKGMETA_URL_MAX);
if (l == 0) goto skip;
return 1;
skip:
carp("skipping malformed pkgmeta: '", line, "'");
return 0;
}
static int repo_iter_read(char *repo, pkin_repo_iter *iter, pkin_pkgmeta *res)
{
pkin_pkgmeta pkgmeta;
int r;
if (iter->fd < 0) {
iter->fd = open_read(repo);
if (iter->fd < 0) {
carpsys("open_read repo indexfile ", repo);
return 0;
}
buffer_init_read(&iter->b, iter->fd, iter->buf, sizeof(iter->buf));
}
r = buffer_getnewline_sa(&iter->b, &iter->line);
if (r == 0) return 0;
if (r < 0) {
carpsys("failed to read ", repo);
goto notfound;
}
stralloc_chomp(&iter->line);
stralloc_0(&iter->line);
if (!parse_pkgmeta(iter->line.s, &pkgmeta)) {
carp("bad pkgmeta in ", repo, ": '", iter->line.s, "'");
goto notfound;
}
*res = pkgmeta;
return 1;
notfound:
buffer_close(&iter->b);
close(iter->fd);
iter->fd = -1;
return 0;
}
int pkin_repo_iter_read(pkin_repo_iter *iter, pkin_pkgmeta *res)
{
char **repo;
int found;
for (; iter->repos_i < iter->repos_len; iter->repos_i++) {
repo = array_get(&pkin_repos, sizeof(char *), iter->repos_i);
found = repo_iter_read(*repo, iter, res);
if (found)
return 1;
}
stralloc_free(&iter->line);
if (iter->fd >= 0) {
close(iter->fd);
buffer_close(&iter->b);
iter->fd = -1;
}
return 0;
}

8
pkin/pkin_internal.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef PKIN_INTERNAL
#define PKIN_INTERNAL
#include <sys/types.h>
pid_t pkin__spawn(char *args[], int infd, int outfd, int errfd);
#endif

View file

@ -0,0 +1,43 @@
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <libowfat/errmsg.h>
#include <libowfat/buffer.h>
static void carpsysargs(char *args[])
{
buffer_puts(buffer_2, "failed to exec");
while (*args) {
buffer_puts(buffer_2, " \'");
buffer_puts(buffer_2, *args);
buffer_puts(buffer_2, "'");
args++;
}
buffer_puts(buffer_2, ": ");
buffer_puts(buffer_2, strerror(errno));
buffer_puts(buffer_2, "\n");
buffer_flush(buffer_2);
}
pid_t pkin__spawn(char *args[], int infd, int outfd, int errfd)
{
pid_t pid = fork();
switch (pid) {
case -1: carpsys("fork: "); return -1;
case 0:
// TODO pipe
if (dup2(0, infd)<0
|| dup2(1, outfd)<0
|| dup2(2, errfd)<0
) {
carpsys("dup2");
return -1;
}
execvp(args[0], args);
carpsysargs(args);
return -1;
default:
return pid;
}
}

51
strff.h Normal file
View file

@ -0,0 +1,51 @@
/* string file format */
#ifndef STRFF_H
#define STRFF_H
#include <libowfat/stralloc.h>
#include <libowfat/buffer.h>
enum strff_type {
STRFF_TYPE_NONE = 0,
STRFF_TYPE_BRACKET,
STRFF_TYPE_EQUAL
};
struct strff {
buffer b;
char buf[1024];
int fd;
enum strff_type fieldtype;
};
void strff_open(struct strff *, int fd);
void strff_close(struct strff *);
/*
* seeks for a field with the specified key in the file.
* sets cursor position ofc.
* strff_read can be used to get the value.
* key may not contain '['.
* if key not found, sets errno to ENOKEY
* return: 1 if found, 0 on error.
*/
int strff_seek(struct strff *, char *key);
/*
* reads n bytes into buf.
* return: bytes read, 0 on end-of-field, -1 on error.
*/
int strff_read(struct strff *, size_t buflen, char buf[buflen]);
/*
* same as strff_read but reads the whole value into sa
* return: 1 on success, 0 on error.
*/
int strff_read_sa(struct strff *, stralloc *sa);
/*
* wrapper around strff_seek and strff_read_sa.
*/
int strff_get(struct strff *, char *key, stralloc *sa);
#endif

6
strff/strff_close.c Normal file
View file

@ -0,0 +1,6 @@
#include "../strff.h"
void strff_close(struct strff *strff)
{
buffer_close(&strff->b);
}

8
strff/strff_get.c Normal file
View file

@ -0,0 +1,8 @@
#include "../strff.h"
int strff_get(struct strff *strff, char *key, stralloc *sa)
{
if (!strff_seek(strff, key))
return 0;
return strff_read_sa(strff, sa);
}

8
strff/strff_open.c Normal file
View file

@ -0,0 +1,8 @@
#include "../strff.h"
#include <libowfat/byte.h>
void strff_open(struct strff *strff, int fd)
{
byte_zero(strff, sizeof *strff);
strff->fd = fd;
}

44
strff/strff_read.c Normal file
View file

@ -0,0 +1,44 @@
#include "../strff.h"
#include <errno.h>
int strff_read(struct strff *strff, size_t buflen, char buf[buflen])
{
size_t nread;
int r;
char c;
int isnewline;
if (strff->fieldtype == STRFF_TYPE_NONE) {
errno = EINVAL; /* cannot read NONE field */
return -1;
}
isnewline = 1;
nread = 0;
while (nread < buflen) {
r = buffer_peekc(&strff->b, &c);
if (r < 0) return -1;
if (r == 0) {
if (isnewline && nread > 0)
nread--; /* remove last \n */
break;
}
if (strff->fieldtype == STRFF_TYPE_EQUAL && c == '\n')
break;
else if (strff->fieldtype == STRFF_TYPE_BRACKET) {
if (isnewline && c == '[') {
if (nread > 0)
nread--; /* remove last \n */
break;
}
isnewline = 0;
if (c == '\n')
isnewline = 1;
}
buf[nread++] = c;
strff->b.p++;
}
return nread;
}

15
strff/strff_read_sa.c Normal file
View file

@ -0,0 +1,15 @@
#include "../strff.h"
#include <libowfat/stralloc.h>
int strff_read_sa(struct strff *strff, stralloc *sa)
{
int n;
char buf[1024];
for (;;) {
n = strff_read(strff, sizeof buf, buf);
if (n == 0) break;
if (n < 0) return 0;
if (!stralloc_catb(sa, buf, n)) return 0;
}
return 1;
}

124
strff/strff_seek.c Normal file
View file

@ -0,0 +1,124 @@
#include "../strff.h"
#include <libowfat/buffer.h>
#include <libowfat/stralloc.h>
#include <libowfat/errmsg.h>
#include <libowfat/str.h>
#include <libowfat/byte.h>
#include <unistd.h>
#include <alloca.h>
#include <errno.h>
/*
keys can be defined either with key= or with [key].
The first is the equalform, the second the bracketform.
There can be no keyforms after the bracketform.
*/
static int skip_to_next_line(buffer *b)
{
int r;
char c;
for (;;) {
r = buffer_getc(b, &c);
if (r == 0 || c == '\n') break;
if (r < 0) return 0;
}
return 1;
}
/*
* advances the cursor until it is positioned at the value of a
* field. It will look for both the equalform and bracketform of
* the field, and returns STRFF_TYPE_BRACKET or STRFF_TYPE_EQUAL
* accordingly. If no such field is found, it returns STRFF_TYPE_NONE.
* On error, -1 is returned.
*/
static int seek_field(buffer *b, char *equalform, char *bracketform)
{
size_t linemax, linelen;
char *line;
int r;
/* bracketform is longer */
linemax = str_len(bracketform) + 1;
line = alloca(linemax);
linelen = 0;
for (;;) {
r = buffer_getc(b, line + linelen);
if (r < 0) return -1;
if (r == 0) break;
linelen++;
if (byte_starts(line, linelen, bracketform)) {
return STRFF_TYPE_BRACKET;
}
if (line[0] == '[') {
/* equalform cannot come after a bracketform */
equalform = NULL;
}
if (equalform && byte_starts(line, linelen, equalform)){
return STRFF_TYPE_EQUAL;
}
if (line[linelen-1] == '\n') {
/* reset line */
linelen = 0;
continue;
}
if (linelen == linemax) {
if (!skip_to_next_line(b))
return -1;
linelen = 0;
}
}
/* not found */
return STRFF_TYPE_NONE;
}
static int to_equalform(char *key, char *result)
{
size_t l;
l = str_copy(result, key);
result[l++] = '=';
result[l++] = 0;
return 1;
}
static int to_bracketform(char *key, char *result)
{
size_t l;
l = 0;
result[l++] = '[';
l += str_copy(result + l, key);
result[l++] = ']';
result[l++] = '\n';
result[l++] = 0;
return 1;
}
int strff_seek(struct strff *strff, char *key) {
char *equalform, *bracketform;
int fieldtype;
if (str_chr(key, '[') != str_len(key)) {
errno = EINVAL; /* key cannot contain '[' */
return 0;
}
equalform = alloca(str_len(key) + 2);
to_equalform(key, equalform);
bracketform = alloca(str_len(key) + 4);
to_bracketform(key, bracketform);
buffer_init(&strff->b, read, strff->fd, strff->buf, sizeof strff->buf);
lseek(strff->fd, 0, SEEK_SET);
fieldtype = seek_field(&strff->b, equalform, bracketform);
if (fieldtype < 0) return 0;
strff->fieldtype = fieldtype;
if (fieldtype == STRFF_TYPE_NONE) {
errno = ENOKEY;
return 0;
}
return 1;
}

63
strff_test.c Normal file
View file

@ -0,0 +1,63 @@
#include <libowfat/open.h>
#include <libowfat/errmsg.h>
#include <libowfat/stralloc.h>
#include "strff.h"
typedef struct {
stralloc name;
stralloc version;
stralloc makedeps;
stralloc buildcmd;
stralloc installcmd;
} pkg;
static struct strff cfgff;
static void pkg_load(pkg *pkg, char *path)
{
int fd;
fd = open_read(path);
if (fd < 0)
diesys(111, "open_read ", path);
stralloc_init(&pkg->name);
stralloc_init(&pkg->version);
stralloc_init(&pkg->makedeps);
stralloc_init(&pkg->buildcmd);
stralloc_init(&pkg->installcmd);
strff_open(&cfgff, fd);
if (!strff_get(&cfgff, "name", &pkg->name))
diesys(111, "cannot get value for name");
if (!strff_get(&cfgff, "version", &pkg->version))
diesys(111, "cannot get value for version");
if (!strff_get(&cfgff, "makedeps", &pkg->makedeps))
diesys(111, "cannot get value for makedeps");
if (!strff_get(&cfgff, "build", &pkg->buildcmd))
diesys(111, "cannot get value for buildcmd");
if (!strff_get(&cfgff, "install", &pkg->installcmd))
diesys(111, "cannot get value for installcmd");
}
int main()
{
pkg pkg;
pkg_load(&pkg, "./conf.strff");
buffer_puts(buffer_1, "name is '");
buffer_put(buffer_1, pkg.name.s, pkg.name.len);
buffer_puts(buffer_1, "', ");
buffer_puts(buffer_1, "version is '");
buffer_put(buffer_1, pkg.version.s, pkg.version.len);
buffer_puts(buffer_1, "', ");
buffer_puts(buffer_1, "makedeps is '");
buffer_put(buffer_1, pkg.makedeps.s, pkg.makedeps.len);
buffer_puts(buffer_1, "', ");
buffer_puts(buffer_1, "buildcmd is '");
buffer_put(buffer_1, pkg.buildcmd.s, pkg.buildcmd.len);
buffer_puts(buffer_1, "', ");
buffer_puts(buffer_1, "installcmd is '");
buffer_put(buffer_1, pkg.installcmd.s, pkg.installcmd.len);
buffer_putsflush(buffer_1, "'\n");
return 0;
}