aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.orig84
-rw-r--r--config/init.lua307
-rw-r--r--config/types.lua35
-rw-r--r--include/add_repo.h28
-rw-r--r--include/banned.h67
-rw-r--r--include/build.h28
-rw-r--r--include/cli.h (renamed from include/debug.h)11
-rw-r--r--include/common.h (renamed from include/globs.h)39
-rw-r--r--include/fetch.h28
-rw-r--r--include/files.h34
-rw-r--r--include/help.h8
-rw-r--r--include/is_updated.h28
-rw-r--r--include/lua_state.h16
-rw-r--r--include/parse_args.h26
-rw-r--r--include/pkg.h37
-rw-r--r--include/pkgit_lua.h77
-rw-r--r--include/search.h27
-rw-r--r--include/state.h45
-rw-r--r--include/str.h14
-rw-r--r--src/add_repo.c56
-rw-r--r--src/build.c170
-rw-r--r--src/check.c43
-rw-r--r--src/cli.c246
-rw-r--r--src/debug.c35
-rw-r--r--src/fetch.c95
-rw-r--r--src/files.c246
-rw-r--r--src/globs.c64
-rw-r--r--src/help.c7
-rw-r--r--src/is_updated.c48
-rw-r--r--src/log.c2
-rw-r--r--src/lua_globs.c213
-rw-r--r--src/lua_state.c146
-rw-r--r--src/lua_vars.c66
-rw-r--r--src/main.c28
-rw-r--r--src/parse_args.c206
-rw-r--r--src/pkg_create.c140
-rw-r--r--src/pkg_exists.c56
-rw-r--r--src/pkg_free.c10
-rw-r--r--src/pkg_install.c213
-rw-r--r--src/pkg_remove.c189
-rw-r--r--src/pkg_update.c174
-rw-r--r--src/search.c70
-rw-r--r--src/state.c (renamed from include/check.h)19
-rw-r--r--src/str.c34
44 files changed, 668 insertions, 2847 deletions
diff --git a/Makefile.orig b/Makefile.orig
deleted file mode 100644
index adacc56..0000000
--- a/Makefile.orig
+++ /dev/null
@@ -1,84 +0,0 @@
-# pkgit - package it!
-
-# Copyright (C) 2026 dacctal
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-.PHONY: default debug install defconfig clean
-
-CC ?= clang
-RM = rm -f
-PREFIX ?= /usr/local
-OBJDIR = obj
-SRCS = $(wildcard src/*.c)
-OBJS = $(SRCS:src/%.c=$(OBJDIR)/%.o)
-CFLAGS += $(shell pkg-config --cflags luajit) -I./include -Wno-format-truncation -std=c99
-
-# pretty print for compilation. To enable verbose (show commands), run with
-# `make V=1`
-ifeq ($(strip $(V)),)
- E = @echo
- Q = @
-else
- E = @\#
- Q =
-endif
-
-default: pkgit
-
-pkgit: $(OBJS)
- $(E) " LINK " $@
- $(Q) ${CC} -o $@ $^ $(shell pkg-config --libs luajit) $(shell pkg-config --libs libgit2) $(LDFLAGS) -g
-
-$(OBJDIR):
- @mkdir -p $(OBJDIR)
-
-$(OBJDIR)/%.o: src/%.c | $(OBJDIR)
- $(E) " CC " $@
- $(Q) ${CC} $(CFLAGS) -c -o $@ $<
-
-gdb: CFLAGS += -g -O0 -Wall -Wextra -Werror -Wvla -pedantic
-gdb: pkgit
-
-debug: CFLAGS += -g -O0 -Wall -Wextra -Werror -Wvla -pedantic -fsanitize=address
-debug: LDFLAGS += -fsanitize=address
-debug: pkgit
-
-install: pkgit
- $(E) " INSTALL" ${DESTDIR}${PREFIX}
- $(Q) install -d ${DESTDIR}${PREFIX}/bin
- $(Q) install -m 755 pkgit ${DESTDIR}${PREFIX}/bin/pkgit
- $(Q) install -d ${DESTDIR}${PREFIX}/share/man/man1
- $(Q) install -m 644 docs/pkgit.1 ${DESTDIR}${PREFIX}/share/man/man1/pkgit.1
-
-uninstall: ${DESTDIR}${PREFIX}/bin/pkgit
- $(E) " UNINSTALL" ${DESTDIR}${PREFIX}
- $(Q) ${RM} ${DESTDIR}${PREFIX}/bin/pkgit
- $(Q) ${RM} ${DESTDIR}${PREFIX}/share/man/man1/pkgit.1
-
-defconfig:
- $(Q) @if [ "$$(id -u)" -eq 0 ]; then dir=/etc/pkgit; else dir=~/.config/pkgit; fi; \
- $(Q) echo "installing default config to $$dir ..."; \
- $(Q) mkdir -p $$dir; \
- $(Q) cp -r ./config/* $$dir/; \
- $(Q) echo "default config installed"
-
-rmconfig:
- $(Q) @if [ "$$(id -u)" -eq 0 ]; then dir=/etc/pkgit; else dir=~/.config/pkgit; fi; \
- $(Q) echo "removing config from $$dir ..."; \
- $(Q) rm -rf $$dir; \
- $(Q) echo "config successfully removed"
-
-clean:
- $(E) " CLEAN"
- $(Q) ${RM} -r $(OBJDIR) pkgit
diff --git a/config/init.lua b/config/init.lua
index 0557a89..2a9dce2 100644
--- a/config/init.lua
+++ b/config/init.lua
@@ -1,247 +1,70 @@
---[[ introduction
-
- this is the pkgit configuration template.
- every configuration file is written in lua,
- the embeddable high-level programming language.
-
- for info on how to write lua, check out
- this website for documentation:
- https://www.lua.org/pil/contents.html
-
- all variables that are
- set by default are designed to
- work out of the box. be sure to
- keep the global variables global,
- as they need to be global in
- order for them to work with
- each other effectively.
-
- `local install_directories`, for
- example, will not work well in
- most cases.
-
-]]
-
---[[ prefix
-
- the prefix variable is necessary
- for custom install paths, which
- bldit maintainers should be
- respecting with their recipes.
-
- if you want to install packages
- without root, it's easist to
- create a home variable and
- append that to your prefix.
-
-]]
-local home = os.getenv("HOME")
-prefix = home.."/.local"
-
---[[ install_directories
-
- install_directories is an essential
- table that pkgit needs in order to
- function. this is how it determines
- where you install your packages to.
-
- it's most convenient when combined
- with `prefix` which allows for a
- more dynamic definition of your
- install directories.
-
-]]
-install_directories = {
- bin = prefix.."/bin", -- binaries (executables)
- -- the value of `bin` should also be set in your shell's $PATH
- include = prefix.."/include", -- C headers
- lib = prefix.."/lib", -- libraries (shared objects)
- src = prefix.."/share/pkgit", -- source code
+local pkgit = require("pkgit")
+local cfg = {}
+
+local prefix = os.getenv("HOME") .. "/.local"
+cfg.dirs = {
+ prefix = prefix,
+ src = prefix .. "/share/pkgit",
+ bin = prefix .. "/bin",
+ lib = prefix .. "/lib",
+ include = prefix .. "/include",
}
---[[ repositories
-
- this is where all your repos need to
- end up. every repo has a name, which
- can be written out normally
- (`name = {...}`), or for some repos,
- it might be necessary to wrap it in
- quotes (`["name"] = {...}`).
- that name can be used in a pkgit
- command to be installed.
- ( `pkgit --install [repo]` )
-
- each of these repos must be structured
- as a lua table. the structure is
- demonstrated below.
-
-]]
-repositories = {
- pkgit = {
- url = "https://git.symlinx.net/pkgit",
- --[[ repo example
-
- no targets table or dependencies are actually needed in
- this specific case, pkgit has a bldit.lua which guarantees
- that it will compile & install (as long as `install_directories`
- and `prefix` are set up).
-
- if you do make a targets table here, it will take priority
- over the bldit.lua in the repo, and your build system
- autodetection config ( `build_systems = {...}` )
-
- more about `targets` in the global `build_systems` table.
-
- ]]
- --dependencies = {
- -- name = {
- -- url = {...},
- -- version = "...",
- -- target = "...",
- -- },
- --},
- --targets = {
- -- default = {
- -- build = function()
- -- return os.execute("make")
- --
- -- -- ^ make sure to return the exit code of os.execute ^ --
- --
- -- end,
- -- install = function()
- -- return os.execute("make install PREFIX="..prefix)
- -- end,
- -- uninstall = function()
- -- return os.execute("make uninstall PREFIX="..prefix)
- -- end,
- -- }
- --}
- },
+cfg.pkgs = {
+ ["gmp"] = {},
+ ["mpfr"] = {},
+ ["mpc"] = {},
+ ["gcc"] = {
+ url = "https://ftp.gnu.org/gnu/gcc/gcc-16.1.0/gcc-16.1.0.tar.xz",
+ version = "16.1.0",
+ fetcher = cfg.fetchers.tarball,
+ dependencies = {
+ "gmp", "mpfr", "mpc"
+ }
+ },
+ ["luajit"] = {
+ url = "https://luajit.org/git/luajit.git",
+ version = "v2.1",
+ fetcher = cfg.fetchers.git,
+ dependencies = {
+ "gcc"
+ },
+ },
+ ["pkgit"] = {
+ url = "https://git.symlinx.net/pkgit",
+ version = "HEAD",
+ fetcher = cfg.fetchers.git,
+ dependencies = {
+ "luajit",
+ },
+ opts = {},
+ recipe = cfg.recipes.make,
+ }
}
---[[ build_systems
-
- this table contains all of the automatic
- build system detection necessary for pkgit
- to work without bldit.lua or `repositories = {...}`.
-
- this works by creating a table named after the
- filename associated with the build system that
- is found in the root directory of a package.
-
- by associating a filename with a build system,
- pkgit can dynamically compile and install any
- package, given that they use the build system
- according to the standard (or otherwise
- generally popular) usage guidelines.
-
-]]
-build_systems = {
- ["Makefile"] = {
- --[[ targets
-
- briefly mentioned above in `repositories`,
- the `targets` subtable is a standard that's
- consistent across all the different methods
- to build a package using pkgit; meaning
- you can write the same `targets` subtable
- in `repositories.<pkg>`, `bldit.lua`, and
- right here in `build_systems`.
-
- this table comes with two templates;
- `default` and `quiet`. the first is pretty
- self-explanatory; this is what you'd define
- as the default intended behavior of the
- build system.
-
- `quiet` is similar to `default`, in the sense
- that it inherits the default intended behavior,
- except that it does so while printing nothing
- to the terminal. instead, ideally, it would
- output its logs to a temporary file accessible
- by the user running the command.
-
- this is why the default logs in this example
- are located in `/tmp` and not `/var/log`,
- because the user typically has full access to
- `/tmp`, and normally not `/var/log`.
-
- ]]
- targets = {
- default = {
- build = function()
- return os.execute("make")
- end,
- install = function()
- return os.execute("make install PREFIX="..prefix)
- end,
- uninstall = function()
- return os.execute("make uninstall PREFIX="..prefix)
- end,
- },
- quiet = {
- build = function()
- return os.execute("make &>/tmp/pkgit_build.log")
- end,
- install = function()
- return os.execute("make install PREFIX="..prefix.." &>/tmp/pkgit_build.log")
- end,
- uninstall = function()
- return os.execute("make uninstall PREFIX="..prefix.." &>/tmp/pkgit_build.log")
- end,
- },
- }
- },
- ["meson.build"] = {
- targets = {
- default = {
- build = function()
- return os.execute("meson setup build --prefix "..prefix.." && meson compile -C build")
- end,
- install = function()
- return os.execute("cd build && meson install")
- end,
- uninstall = function()
- return os.execute("cd build && ninja uninstall")
- end,
- },
- quiet = {
- build = function()
- return os.execute("meson setup build --prefix "..prefix.." &>/tmp/pkgit_build.log && meson compile -C build &>/tmp/pkgit_build.log")
- end,
- install = function()
- return os.execute("cd build && meson install &>/tmp/pkgit_build.log")
- end,
- uninstall = function()
- return os.execute("cd build && ninja uninstall &>/tmp/pkgit_build.log")
- end,
- },
- }
- },
- ["CMakeLists.txt"] = {
- targets = {
- default = {
- build = function()
- return os.execute("cmake -B build && cmake --build build")
- end,
- install = function()
- return os.execute("cmake --build . --target install")
- end,
- uninstall = function()
- return os.execute("xargs rm < install_manifest.txt")
- end,
- },
- quiet = {
- build = function()
- return os.execute("cmake -B build &>/tmp/pkgit_build.log && cmake --build build &>/tmp/pkgit_build.log")
- end,
- install = function()
- return os.execute("cmake --build . --target install &>/tmp/pkgit_build.log")
- end,
- uninstall = function()
- return os.execute("xargs rm < install_manifest.txt &>/tmp/pkgit_build.log")
- end,
- },
- }
- },
+cfg.fetchers = {
+ git = {
+ get_source = function(url, version, target_dir) return 0 end,
+ get_version = function(url, target_dir) return "HEAD" end,
+ },
+ tarball = {
+ get_source = function(url, version, target_dir) return 0 end,
+ get_version = function(url, target_dir) return "" end,
+ }
+}
+cfg.fetchers.default = cfg.fetchers.git
+
+cfg.recipes = {}
+cfg.recipes.make = {
+ build = function(bldit_build)
+ return pkgit.run({ "make" })
+ end,
+ install = function(bldit_install)
+ return pkgit.run({ "make install PREFIX=" .. prefix })
+ end,
+ remove = function(bldit_remove)
+ return pkgit.run({ "make uninstall PREFIX=" .. prefix })
+ end,
}
+
+return cfg
diff --git a/config/types.lua b/config/types.lua
new file mode 100644
index 0000000..10a444d
--- /dev/null
+++ b/config/types.lua
@@ -0,0 +1,35 @@
+---@class (exact) Dirs
+---@field prefix string
+---@field bin string
+---@field include string
+---@field lib string
+---@field src string
+
+---@class (exact) Recipe
+---@field setup (fun(bldit_setup: fun()): number)?
+---@field build fun(bldit_build: fun()): number
+---@field install fun(bldit_install: fun()): number
+---@field remove fun(bldit_remove: fun()): number
+---@field on_update (fun(bldit_on_update: fun()): number)?
+
+---@type Recipe
+---@alias GetSourcesFn fun(url: string, version: string, target_dir: string): number
+---@alias GetVersionFn fun(url: string, target_dir: string): string
+
+---@class (exact) Fetcher
+---@field get_sources GetSourcesFn
+---@field get_version GetVersionFn
+
+---@class (exact) Package
+---@field url string
+---@field version string?
+---@field fetcher Fetcher | nil
+---@field dependencies { [string]: Package } | "bldit" | nil
+---@field opts table?
+---@field recipe Recipe | "bldit" | nil
+
+---@class (exact) Config
+---@field dirs Dirs
+---@field fetchers { [string]: Fetcher }
+---@field recipes { [string]: Recipe }
+---@field pkgs { [string]: Package }
diff --git a/include/add_repo.h b/include/add_repo.h
deleted file mode 100644
index 48a73fa..0000000
--- a/include/add_repo.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef PKGIT_ADD_H
-#define PKGIT_ADD_H
-
-#include "pkg.h"
-
-void add_repo(package_t *pkg);
-
-#endif
diff --git a/include/banned.h b/include/banned.h
deleted file mode 100644
index 3ea7dd4..0000000
--- a/include/banned.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef PKGIT_BANNED_H
-#define PKGIT_BANNED_H
-
-/*
- * This header lists functions that have been banned from our code base,
- * because they're too easy to misuse (and even if used correctly,
- * complicate audits). Including this header turns them into compile-time
- * errors.
- */
-
-#define BANNED(func) sorry_##func##_is_a_banned_function
-
-#undef strcpy
-#define strcpy(x,y) BANNED(strcpy)
-#undef strcat
-#define strcat(x,y) BANNED(strcat)
-#undef strncpy
-#define strncpy(x,y,n) BANNED(strncpy)
-#undef strncat
-#define strncat(x,y,n) BANNED(strncat)
-#undef strtok
-#define strtok(x,y) BANNED(strtok)
-#undef strtok_r
-#define strtok_r(x,y,z) BANNED(strtok_r)
-
-#undef sprintf
-#undef vsprintf
-#define sprintf(...) BANNED(sprintf)
-#define vsprintf(...) BANNED(vsprintf)
-
-#undef gmtime
-#define gmtime(t) BANNED(gmtime)
-#undef localtime
-#define localtime(t) BANNED(localtime)
-#undef ctime
-#define ctime(t) BANNED(ctime)
-#undef ctime_r
-#define ctime_r(t, buf) BANNED(ctime_r)
-#undef asctime
-#define asctime(t) BANNED(asctime)
-#undef asctime_r
-#define asctime_r(t, buf) BANNED(asctime_r)
-
-#undef mktemp
-#define mktemp(x) BANNED(mktemp)
-
-#endif /* BANNED_H */
diff --git a/include/build.h b/include/build.h
deleted file mode 100644
index 7dcb53a..0000000
--- a/include/build.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef PKGIT_BUILD_H
-#define PKGIT_BUILD_H
-
-#include "pkg.h"
-
-bool build(package_t *pkg);
-
-#endif
diff --git a/include/debug.h b/include/cli.h
index e74a099..602fa6d 100644
--- a/include/debug.h
+++ b/include/cli.h
@@ -18,10 +18,15 @@
*/
+#ifndef PKGIT_CLI_H
+#define PKGIT_CLI_H
-#ifndef PKGIT_DEBUG_H
-#define PKGIT_DEBUG_H
+typedef enum {
+ CLI_FAIL = 0,
+ CLI_SUCCESS = 1,
+ CLI_HELP_CALLED = 2,
+} cli_result_t;
-void debug_print_inst_dirs(void);
+cli_result_t cli(int argc, char **argv);
#endif
diff --git a/include/globs.h b/include/common.h
index 20c10f5..a77718b 100644
--- a/include/globs.h
+++ b/include/common.h
@@ -1,28 +1,24 @@
/*
pkgit - package it!
-
+
Copyright (C) 2026 dacctal
-
+
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
-
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-#ifndef PKGIT_GLOBALS_H
-#define PKGIT_GLOBALS_H
-
-#include <stdlib.h>
-
-#include "str.h"
+#ifndef PKGIT_COMMON_H
+#define PKGIT_COMMON_H
#define VERSION "1.2.0"
#define RED "\x1b[0;31m"
@@ -68,7 +64,7 @@
panic("allocation of `%s` failed", #ptr); \
} while (0)
-#define panic(...) \
+#define panic(...) \
do { \
char *tmp = strrchr(__FILE__, '/'); \
eprintf(BOLD RED "panic:" COLOR_RESET " line %d, func \"%s\" in file " \
@@ -106,25 +102,4 @@
#define MAX_DIRS 100
#define MAX_PATH_LEN 1024
-typedef struct {
- bool verbose, force;
-} cli_flags_t;
-
-typedef struct {
- str dir, name, content, repos;
- bool is_root_config;
-} config_t;
-
-typedef struct {
- str prefix, bin, lib, include, src;
-} install_dirs_t;
-
-extern cli_flags_t flags;
-extern config_t cfg;
-extern install_dirs_t inst_dirs;
-extern str new_arg_str;
-
-void init_vars(void);
-void free_vars(void);
-
#endif
diff --git a/include/fetch.h b/include/fetch.h
deleted file mode 100644
index d392098..0000000
--- a/include/fetch.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef PKGIT_FETCH_H
-#define PKGIT_FETCH_H
-
-#include "pkg.h"
-
-bool fetch(package_t *pkg);
-
-#endif
diff --git a/include/files.h b/include/files.h
deleted file mode 100644
index 9a17c88..0000000
--- a/include/files.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef PKGIT_FILES_H
-#define PKGIT_FILES_H
-
-#include <stdbool.h>
-
-#include "str.h"
-
-bool file_exists(const char *path);
-bool is_directory(const char *path);
-str cmd_out(const char *cmd);
-void cpdir(const char *src_path, const char *dst_path);
-int remove_tree(const char *path);
-
-#endif
diff --git a/include/help.h b/include/help.h
index c194be4..28fb539 100644
--- a/include/help.h
+++ b/include/help.h
@@ -18,9 +18,9 @@
*/
-#ifndef HELP_H
-#define HELP_H
+#ifndef PKGIT_HELP_H
+#define PKGIT_HELP_H
-void help(void);
+void print_help(void);
-#endif \ No newline at end of file
+#endif
diff --git a/include/is_updated.h b/include/is_updated.h
deleted file mode 100644
index 16f998f..0000000
--- a/include/is_updated.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef PKGIT_IS_UPDATED_H
-#define PKGIT_IS_UPDATED_H
-
-#include <stdbool.h>
-#include "str.h"
-bool is_updated(str *src);
-
-#endif
diff --git a/include/lua_state.h b/include/lua_state.h
new file mode 100644
index 0000000..49f35e4
--- /dev/null
+++ b/include/lua_state.h
@@ -0,0 +1,16 @@
+#ifndef PKGIT_LUA_STATE_H
+#define PKGIT_LUA_STATE_H
+
+#include <lauxlib.h>
+#include <lua.h>
+#include <lualib.h>
+#include <stdbool.h>
+
+extern lua_State *L;
+extern bool L_is_loaded;
+
+bool init_lua_state(void);
+bool init_lua_config(void);
+void free_lua_state(void);
+
+#endif
diff --git a/include/parse_args.h b/include/parse_args.h
deleted file mode 100644
index d68c37f..0000000
--- a/include/parse_args.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef PKGIT_CLA_PARSE_H
-#define PKGIT_CLA_PARSE_H
-
-void parse_args(int argc, char **argv);
-
-#endif \ No newline at end of file
diff --git a/include/pkg.h b/include/pkg.h
index 5ad88b2..47d1918 100644
--- a/include/pkg.h
+++ b/include/pkg.h
@@ -1,33 +1,24 @@
-/*
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-
#ifndef PKGIT_PKG_H
#define PKGIT_PKG_H
#include "str.h"
-typedef struct {
- str name, url, version, target, src;
+struct package_t;
+typedef bool (*package_hook_cb_t)(struct package_t *self);
+typedef str (*package_version_cb_t)(struct package_t *self);
+
+typedef struct package_t {
+ str name, version, url, src;
+ unsigned int dep_count;
bool is_local;
+ package_hook_cb_t setup, build, install, rem, on_update, fetch_sources;
+ package_version_cb_t fetch_latest_version;
} package_t;
-package_t pkg_create(str *arg);
-void pkg_free(package_t *pkg);
+bool setup(package_t *self), build(package_t *self),
+ install(package_t *self), rem(package_t *self),
+ on_update(package_t *self),
+ fetch_sources(package_t *self);
+str fetch_latest_version(package_t *self);
#endif
diff --git a/include/pkgit_lua.h b/include/pkgit_lua.h
deleted file mode 100644
index a9a6adf..0000000
--- a/include/pkgit_lua.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef PKGIT_LUA_H
-#define PKGIT_LUA_H
-
-#include <lua.h>
-#include <lauxlib.h>
-#include <lualib.h>
-#include <stdbool.h>
-
-#include "pkg.h"
-
-extern lua_State *L;
-extern lua_State *B;
-extern bool config_loaded;
-extern bool bldit_loaded;
-
-// init
-void push_lua_path(lua_State *L, const char *new_path);
-void init_lua_state(void);
-void init_bldit_state(void);
-void free_lua_state(void);
-void free_bldit_state(void);
-lua_State *get_lua_state(void);
-lua_State *get_bldit_state(void);
-
-// gobal vars
-void init_install_directories(void);
-void init_prefix_directory(void);
-
-// helpers
-void lua_isnt_type(char* variable, char* type);
-void bldit_isnt_type(char* variable, char* type);
-bool lua_try_function(lua_State *L, char *lua_file, char *fname);
-bool lua_try_table(lua_State *L, char *lua_file, char *tname);
-bool pkg_exists(str *name);
-str pkg_get_url(str *name);
-str bldit_getver(void);
-str bldit_pkg_getver(void);
-bool is_bldit_usable(void);
-
-// install
-void install_dependencies(lua_State *L);
-void pkg_install(package_t *pkg);
-
-// remove
-bool repo_uninstall(package_t *pkg);
-bool bldit_uninstall(package_t *pkg);
-bool config_uninstall(package_t *pkg);
-void pkg_remove(package_t *pkg);
-
-// update
-void pkg_update(package_t *pkg);
-void all_update(void);
-
-// declare
-void declare(void);
-
-#endif
diff --git a/include/search.h b/include/search.h
deleted file mode 100644
index 233537b..0000000
--- a/include/search.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef PKGIT_SEARCH_H
-#define PKGIT_SEARCH_H
-
-void list_installed(void);
-void search(char* arg);
-
-#endif
diff --git a/include/state.h b/include/state.h
new file mode 100644
index 0000000..9075270
--- /dev/null
+++ b/include/state.h
@@ -0,0 +1,45 @@
+/*
+
+ pkgit - package it!
+
+ Copyright (C) 2026 dacctal
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <stdbool.h>
+
+#include "str.h"
+
+typedef struct {
+ bool force, quiet;
+} cli_flags_t;
+
+typedef struct inst_dirs_t {
+ str prefix, src, bin, lib, include;
+} inst_dirs_t;
+
+typedef struct config_t {
+ inst_dirs_t inst_dirs;
+ str dir, init_path, autogenerated_path;
+ bool is_root;
+} user_config_t;
+
+typedef struct {
+ str prefix, bin, lib, include, src;
+} install_dirs_t;
+
+extern cli_flags_t flags;
+extern user_config_t config;
+extern install_dirs_t inst_dirs;
diff --git a/include/str.h b/include/str.h
index 3a4aefb..0e8ae59 100644
--- a/include/str.h
+++ b/include/str.h
@@ -74,6 +74,12 @@ str str_new_with_capacity(size_t cap);
str str_from_cstr(const char *s);
/**
+ * Adopts an existing heap-allocated C string, reusing its buffer in the
+ * resultant str.
+ */
+str str_adopt(char *s);
+
+/**
* Equivalent to str_from_cstr
*/
str mstr(const char *s);
@@ -182,6 +188,12 @@ void str_append_cstr(str *src, const char *new_cstr);
void str_append_str_slc(str *src, const str_slc new_slc);
/**
+ * Appends a new formatted section of the string, with the same calling
+ * style as str_append into an existing string buffer.
+ */
+void str_append_format(str *dest, const char *format, ...);
+
+/**
* Removes the last character from src, returning it.
*/
char str_pop_last(str *src);
@@ -444,7 +456,7 @@ str_slc str_slc_from_cstr(const char *s);
/**
* Equivalent to str_slc_from_cstr.
*/
-str_slc mstrslc(const char *s);
+str_slc mslc(const char *s);
str str_slc_concat(const str_slc lhs, const str_slc rhs);
diff --git a/src/add_repo.c b/src/add_repo.c
deleted file mode 100644
index 1c41f3b..0000000
--- a/src/add_repo.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-
-#include "add_repo.h"
-
-#include "files.h"
-#include "globs.h"
-
-void add_repo(package_t *pkg) {
- bool is_previous_repos = false;
- char rfile_line[1024];
- char rfile_contents[8192] = {0};
-
- if (file_exists(cfg.repos.data)) {
- FILE *rfile = fopen(cfg.repos.data, "r");
- if (rfile) {
- while (fgets(rfile_line, sizeof(rfile_line), rfile)) {
- strcat(rfile_contents, rfile_line);
- }
- fclose(rfile);
- is_previous_repos = true;
- }
- }
-
- char *previous_repos = is_previous_repos ? rfile_contents : "";
-
- FILE *wfile = fopen(cfg.repos.data, "w");
- if (wfile) {
- fprintf(wfile,
- "%srepositories[\"%s\"] = { url = \"%s\" }\n",
- previous_repos, pkg->name.data, pkg->url.data
- );
- fclose(wfile);
- }
-}
diff --git a/src/build.c b/src/build.c
deleted file mode 100644
index 1974846..0000000
--- a/src/build.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#include <stdio.h>
-#include <unistd.h>
-
-#include "build.h"
-
-#include "globs.h"
-#include "log.h"
-#include "lua.h"
-#include "pkg.h"
-#include "pkgit_lua.h"
-
-bool target_build(lua_State *L, char *lua_file, str *target) {
- if (!lua_try_table(L, lua_file, target->data))
- return false;
- if (lua_try_table(L, lua_file, "dependencies")) {
- lua_pushnil(L);
- install_dependencies(L);
- }
- lua_pop(L, 1);
- if (!lua_try_function(L, lua_file, "build"))
- return false;
- lua_pop(L, 1);
- return true;
-}
-
-bool repo_build(package_t *pkg) {
- lua_getglobal(L, "repositories");
- if (!lua_try_table(L, "init.lua", pkg->name.data)) {
- lua_pop(L, 2);
- return false;
- }
- if (lua_try_table(L, "init.lua", "dependencies")) {
- lua_pushnil(L);
- install_dependencies(L);
- }
- lua_pop(L, 1);
- if (!lua_try_table(L, "init.lua", "targets")) {
- lua_pop(L, 3);
- return false;
- }
- bool target_success = target_build(L, "init.lua", &pkg->target);
- lua_pop(L, 4);
- return target_success;
-}
-
-bool bldit(package_t *pkg) {
- init_bldit_state();
- if (!bldit_loaded)
- return false;
- lua_getglobal(B, "dependencies");
- if (!lua_istable(B, -1)) {
- bldit_isnt_type("dependencies", "table");
- lua_pop(B, 1);
- } else {
- lua_pushnil(B);
- install_dependencies(B);
- lua_pop(B, 1);
- }
- lua_getglobal(B, "targets");
- if (!lua_istable(B, -1)) {
- bldit_isnt_type("targets", "table");
- lua_pop(B, 1);
- lua_close(B);
- B = NULL;
- bldit_loaded = false;
- return false;
- }
- bool target_success = target_build(B, "bldit.lua", &pkg->target);
- lua_pop(B, 1);
- return target_success;
-}
-
-bool config_build(package_t *pkg) {
- lua_getglobal(L, "build_systems");
- if (!lua_istable(L, -1)) {
- lua_isnt_type("build_systems", "table");
- lua_pop(L, 1);
- return false;
- }
- bool target_success = false;
- lua_pushnil(L);
- while (lua_next(L, -2) != 0) {
- str key = mstr(lua_tostring(L, -2));
- if (!lua_istable(L, -1)) {
- lua_pop(L, 1);
- str_free(&key);
- continue;
- }
- str file_path = str_format("%.*s/%s", str_fmt(&pkg->src), key.data);
- if (access(file_path.data, F_OK) != 0) {
- lua_pop(L, 1);
- str_free(&file_path);
- str_free(&key);
- continue;
- }
- str_free(&file_path);
- str_free(&key);
- if (!lua_try_table(L, "init.lua", "targets"))
- continue;
- target_success = target_build(L, "init.lua", &pkg->target);
- if (target_success)
- break;
- lua_pop(L, 1);
- }
- lua_pop(L, 1);
- return target_success;
-}
-
-bool build_loop(package_t *pkg) {
- if (flags.verbose)
- log_info("attempting init.lua: 'repositories.%.*s.build'",
- str_fmt(&pkg->name));
- if (repo_build(pkg)) {
- return true;
- }
- if (flags.verbose)
- log_warn("failed init.lua: 'repositories.%.*s.build'",
- str_fmt(&pkg->name));
-
- if (flags.verbose)
- log_info("attempting bldit.lua");
- if (bldit(pkg)) {
- return true;
- }
- if (flags.verbose)
- log_warn("failed bldit.lua");
-
- if (flags.verbose)
- log_info("attempting init.lua: 'build_systems'");
- if (config_build(pkg)) {
- return true;
- }
- if (flags.verbose)
- log_warn("failed init.lua: 'build_systems'");
- return false;
-}
-
-bool build(package_t *pkg) {
- char cwd[MAX_PATH_LEN];
- if (getcwd(cwd, sizeof(cwd)) == NULL) {
- log_error("getcwd failed");
- return false;
- }
- if (!str_equal_cstr(&pkg->src, cwd) && !pkg->is_local)
- chdir(pkg->src.data);
-
- if (build_loop(pkg))
- return true;
- log_error("no usable build system was found for %.*s", str_fmt(&pkg->name));
- return false;
-}
diff --git a/src/check.c b/src/check.c
deleted file mode 100644
index 366f8a7..0000000
--- a/src/check.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#include <stdio.h>
-#include <unistd.h>
-
-#include "check.h"
-
-void check(void) {
- const char *frame_top = " (c _c)";
- const char *frame1_bot = "_/ \\-";
- const char *frame2_bot = "-/ \\_";
-
- printf("\033[2J\033[H");
- printf("Unfortunately due to budget issues, we could not afford a progress bar. Enjoy this instead:\n\n");
-
- for (int i = 0; i < 16; i++) {
- printf("%s\n%s\n", frame_top, i % 2 == 0 ? frame1_bot : frame2_bot);
- for (int j = 0; j <= i; j++) printf(".");
- printf("\n");
- fflush(stdout);
- sleep(1);
- if (i < 15) printf("\033[3A");
- }
- printf("\n");
- printf("Dependencies resolved! (this does nothing)\n");
-}
diff --git a/src/cli.c b/src/cli.c
new file mode 100644
index 0000000..c4499ff
--- /dev/null
+++ b/src/cli.c
@@ -0,0 +1,246 @@
+/*
+
+ pkgit - package it!
+
+ Copyright (C) 2026 dacctal
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "cli.h"
+#include "common.h"
+#include "help.h"
+#include "log.h"
+#include "state.h"
+#include "str.h"
+
+#define COMMAND(large, small) \
+ (!strcmp(argv[i], large) || !strcmp(argv[i], small))
+
+#define NOT_ENOUGH_ARGS(arg, next) \
+ log_error("Not enough arguments! Try: pkgit %s [%s]", (arg), (next))
+
+void cmd_add(char **argv, int i) {
+ if (argv[i + 1]) {
+ str arg = mstr(argv[i + 1]);
+ if (str_is_valid(&arg))
+ str_free(&arg);
+ } else {
+ NOT_ENOUGH_ARGS(argv[i], "url");
+ }
+}
+
+void cmd_build(int argc, char **argv, int i) {
+ if (argv[i + 1]) {
+ for (int j = i + 1; j < argc; j++) {
+ if (argv[j][0] == '-')
+ continue;
+ str arg = mstr(argv[j]);
+ if (str_is_valid(&arg))
+ str_free(&arg);
+ }
+ } else {
+ str arg = mstr(".");
+ if (str_is_valid(&arg))
+ str_free(&arg);
+ }
+}
+
+void cmd_install(int argc, char **argv, int i) {
+ if (argv[i + 1]) {
+ for (int j = i + 1; j < argc; j++) {
+ if (argv[j][0] == '-')
+ continue;
+ str arg = mstr(argv[j]);
+ if (str_is_valid(&arg))
+ str_free(&arg);
+ }
+ } else {
+ NOT_ENOUGH_ARGS(argv[i], "url/pkg");
+ }
+}
+
+void cmd_update(int argc, char **argv, int i) {
+ if (argv[i + 1]) {
+ for (int j = i + 1; j < argc; j++) {
+ if (argv[j][0] == '-')
+ continue;
+ str arg = mstr(argv[j]);
+ if (str_is_valid(&arg))
+ str_free(&arg);
+ }
+ } else {
+ }
+}
+
+void cmd_remove(int argc, char **argv, int i) {
+ if (argv[i + 1]) {
+ for (int j = i + 1; j < argc; j++) {
+ if (argv[j][0] == '-')
+ continue;
+ str arg = mstr(argv[j]);
+ if (str_is_valid(&arg))
+ str_free(&arg);
+ }
+ } else {
+ NOT_ENOUGH_ARGS(argv[i], "url/pkg");
+ }
+}
+
+void cmd_search(int argc, char **argv, int i) {
+ if (argv[i + 1]) {
+ for (int j = i + 1; j < argc; j++) {
+ if (argv[j][0] == '-')
+ continue;
+ }
+ } else {
+ }
+}
+
+void flags_mod(char **argv, int i) {
+ for (size_t j = 1; j < strlen(argv[i]); j++) {
+ switch (argv[i][j]) {
+ case 'q':
+ flags.quiet = true;
+ break;
+ case 'f':
+ flags.force = true;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+cli_result_t flags_cmd(int argc, char **argv, int i) {
+ for (size_t j = 1; j < strlen(argv[i]); j++) {
+ switch (argv[i][j]) {
+ case 'a':
+ cmd_add(argv, i);
+ break;
+ case 'b':
+ cmd_build(argc, argv, i);
+ break;
+ case 'c':
+ // check();
+ break;
+ case 'd':
+ // declare();
+ break;
+ case 'i':
+ cmd_install(argc, argv, i);
+ break;
+ case 'r':
+ cmd_remove(argc, argv, i);
+ break;
+ case 'u':
+ cmd_update(argc, argv, i);
+ break;
+ case 'l':
+ // list_installed();
+ break;
+ case 's':
+ cmd_search(argc, argv, i);
+ break;
+ case 'v':
+ printf("%s\n", VERSION);
+ break;
+ case 'h':
+ print_help();
+ return CLI_HELP_CALLED;
+ break;
+ default:
+ break;
+ }
+ }
+ return CLI_SUCCESS;
+}
+
+cli_result_t parse_flags(int argc, char **argv) {
+ for (int i = 1; i < argc; i++) {
+ if (argv[i][0] != '-')
+ continue;
+ if (argv[i][1] == '-') {
+ if (COMMAND("--quiet", "-q"))
+ flags.quiet = true;
+ if (COMMAND("--force", "-f"))
+ flags.force = true;
+ } else {
+ flags_mod(argv, i);
+ cli_result_t status = flags_cmd(argc, argv, i);
+ if (status != CLI_SUCCESS)
+ return status;
+ }
+ }
+ return CLI_SUCCESS;
+}
+
+cli_result_t parse_cmds(int argc, char **argv) {
+ for (int i = 1; i < argc; i++) {
+ if (COMMAND("--add", "a")) {
+ cmd_add(argv, i);
+ }
+ if (COMMAND("--build", "b")) {
+ cmd_build(argc, argv, i);
+ }
+ if (COMMAND("--install", "i")) {
+ cmd_install(argc, argv, i);
+ }
+ if (COMMAND("--remove", "r")) {
+ cmd_remove(argc, argv, i);
+ }
+ if (COMMAND("--update", "u")) {
+ cmd_update(argc, argv, i);
+ }
+ if (COMMAND("--declare", "d")) {
+ // declare();
+ }
+ if (COMMAND("--list", "l")) {
+ // list_installed();
+ }
+ if (COMMAND("--search", "s")) {
+ cmd_search(argc, argv, i);
+ }
+ if (COMMAND("--version", "v")) {
+ printf(VERSION "\n");
+ }
+ if (COMMAND("--help", "h")) {
+ print_help();
+ return CLI_HELP_CALLED;
+ }
+ if (COMMAND("--check", "c")) {
+ // check();
+ }
+ }
+ return CLI_SUCCESS;
+}
+
+cli_result_t cli(int argc, char **argv) {
+ flags.force = false;
+ flags.quiet = false;
+ if (argc == 1) {
+ print_help();
+ return CLI_HELP_CALLED;
+ }
+ if (!parse_flags(argc, argv))
+ return CLI_FAIL;
+
+ if (!parse_cmds(argc, argv))
+ return CLI_FAIL;
+
+ return CLI_SUCCESS;
+}
diff --git a/src/debug.c b/src/debug.c
deleted file mode 100644
index 0a580df..0000000
--- a/src/debug.c
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-
-#include "globs.h"
-
-void debug_print_inst_dirs(void) {
- printf("PREFIX:\t");
- str_println(&inst_dirs.prefix);
- printf("ID_BIN:\t");
- str_println(&inst_dirs.bin);
- printf("ID_LIB:\t");
- str_println(&inst_dirs.lib);
- printf("ID_INCLUDE:\t");
- str_println(&inst_dirs.include);
- printf("ID_SRC:\t");
- str_println(&inst_dirs.src);
-}
diff --git a/src/fetch.c b/src/fetch.c
deleted file mode 100644
index 2666f78..0000000
--- a/src/fetch.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#include <fcntl.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include "fetch.h"
-#include "files.h"
-#include "globs.h"
-#include "log.h"
-#include "str.h"
-
-bool fetch(package_t *pkg) {
- if (pkg->url.data == NULL || pkg->src.data == NULL) {
- if (flags.verbose)
- log_warn("invalid pkg: url or src is NULL");
- return false;
- }
-
- str s = str_dupe(&pkg->src);
- s.len -= pkg->version.len + 1;
- s.data[s.len] = 0;
-
- if (is_directory(s.data) && flags.force) {
- if (remove_tree(s.data)) {
- log_error("could not remove %.*s", str_fmt(&s));
- return false;
- }
- }
-
- str_free(&s);
-
- pid_t pid = fork();
- if (pid < 0) {
- log_error("fork failed");
- return false;
- }
-
- if (pid == 0) {
- if (!flags.verbose) {
- int nullfd = open("/dev/null", O_WRONLY);
- if (nullfd >= 0) {
- dup2(nullfd, STDOUT_FILENO);
- dup2(nullfd, STDERR_FILENO);
- close(nullfd);
- }
- }
-
- const char *argv[12];
- int i = 0;
- argv[i++] = "git";
- argv[i++] = "-c";
- argv[i++] = "advice.detachedHead=false";
- argv[i++] = "clone";
- if (pkg->version.data != NULL &&
- !str_equal_cstr(&pkg->version, "HEAD")) {
- argv[i++] = "--branch";
- argv[i++] = pkg->version.data;
- }
-
- argv[i++] = "--recursive";
- argv[i++] = pkg->url.data;
- argv[i++] = pkg->src.data;
- argv[i] = NULL;
- execvp("git", (char *const *)argv);
- _exit(127);
- }
- int status;
- waitpid(pid, &status, 0);
-
- bool result = (WEXITSTATUS(status) == 0);
- if (!result) {
- log_error("git clone failed");
- }
-
- return result;
-}
diff --git a/src/files.c b/src/files.c
deleted file mode 100644
index 0ce64b4..0000000
--- a/src/files.c
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#define _XOPEN_SOURCE 700
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <ftw.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include "files.h"
-#include "log.h"
-#include "str.h"
-
-bool file_exists(const char *path) {
- struct stat buffer;
- return (stat(path, &buffer) == 0);
-}
-
-bool is_directory(const char *path) {
- struct stat statbuf;
- if (stat(path, &statbuf) != 0) {
- return false;
- }
- return S_ISDIR(statbuf.st_mode);
-}
-
-FILE *popen(const char *command, const char *type);
-int pclose(FILE *stream);
-
-str cmd_out(const char *cmd) {
- FILE *pipe = popen(cmd, "r");
- if (!pipe)
- return mstr("");
-
- char buffer[128];
- size_t total_size = 0;
- size_t capacity = 256;
- char *result = malloc(capacity);
- if (!result) {
- pclose(pipe);
- return mstr("");
- }
- result[0] = '\0';
-
- while (fgets(buffer, sizeof(buffer), pipe) != NULL) {
- size_t len = strlen(buffer);
- // Ensure enough space for the new chunk + 1 byte for '\0'
- if (total_size + len + 1 > capacity) {
- capacity *= 2;
- char *new_result = realloc(result, capacity);
- if (!new_result) {
- free(result);
- pclose(pipe);
- return mstr("");
- }
- result = new_result;
- }
-
- snprintf(result + total_size, capacity - total_size, "%s", buffer);
- total_size += len;
- }
-
- pclose(pipe);
- str str_result = mstr(result);
- free(result);
- return str_result;
-}
-
-const char *get_filename_ext(const char *filename) {
- const char *dot = strrchr(filename, '.');
- if (!dot || dot == filename)
- return "";
- return dot + 1;
-}
-
-static int write_all(int fd, const void *buf, size_t size) {
- while (size) {
- ssize_t written = write(fd, buf, size);
- if (written < 0) {
- return -1;
- }
-
- size -= written;
- buf = (const char *)buf + written;
- }
-
- return 0;
-}
-
-static int copy_file_content(int src_fd, int dst_fd) {
- char buf[4096];
- ssize_t readed;
- while ((readed = read(src_fd, buf, sizeof(buf))) > 0) {
- if (write_all(dst_fd, buf, readed) < 0) {
- perror("write_all");
- return -1;
- }
- }
-
- if (readed < 0) {
- perror("read");
- return -1;
- }
-
- return 0;
-}
-
-static int copy_at(int src_dir_fd, const char *src_path, int dst_dir_fd,
- const char *dst_path);
-
-static int copy_dir_content(int src_fd, int dst_fd) {
- int ret = -1;
- // duplicate the descriptor to prevent closedir from closing original one
- src_fd = dup(src_fd);
- if (src_fd < 0) {
- perror("dup");
- goto cleanup0;
- }
-
- DIR *src_dir = fdopendir(src_fd);
- if (!src_dir) {
- perror("fdopendir");
- close(src_fd);
- goto cleanup0;
- }
-
- for (struct dirent *ent; (errno = 0, ent = readdir(src_dir));) {
- if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
- continue;
- }
-
- if (copy_at(src_fd, ent->d_name, dst_fd, ent->d_name) < 0) {
- goto cleanup1;
- }
- }
-
- if (errno != 0) {
- perror("readdir");
- goto cleanup1;
- }
-
- ret = 0;
-
-cleanup1:
- closedir(src_dir);
-cleanup0:
- return ret;
-}
-
-static int copy_at(int src_dir_fd, const char *src_path, int dst_dir_fd,
- const char *dst_path) {
- int ret = -1;
- int src_fd = openat(src_dir_fd, src_path, O_NOFOLLOW | O_RDONLY);
- if (src_fd < 0) {
- perror("openat");
- goto cleanup0;
- }
-
- struct stat stat;
- if (fstat(src_fd, &stat) < 0) {
- perror("fstat");
- goto cleanup1;
- }
-
- int dst_oflag = O_NOFOLLOW;
- if (S_ISDIR(stat.st_mode)) {
- if (mkdirat(dst_dir_fd, dst_path, stat.st_mode) < 0) {
- perror("mkdirat");
- goto cleanup1;
- }
-
- dst_oflag |= O_RDONLY | O_DIRECTORY;
- } else {
- dst_oflag |= O_WRONLY | O_CREAT | O_EXCL;
- }
-
- int dst_fd = openat(dst_dir_fd, dst_path, dst_oflag, stat.st_mode);
- if (dst_fd < 0) {
- perror("openat");
- goto cleanup1;
- }
-
- switch (stat.st_mode & S_IFMT) {
- case S_IFDIR:
- ret = copy_dir_content(src_fd, dst_fd);
- break;
- case S_IFREG:
- ret = copy_file_content(src_fd, dst_fd);
- break;
- default:
- fprintf(stderr, "copy_at: unsupported file inode type\n");
- }
- goto cleanup2;
-
-cleanup2:
- close(dst_fd);
-cleanup1:
- close(src_fd);
-cleanup0:
- return ret;
-}
-
-void cpdir(const char *src_path, const char *dst_path) {
- copy_at(AT_FDCWD, src_path, AT_FDCWD, dst_path);
-}
-
-static int _remove_callback(const char *fpath, const struct stat *sb,
- int typeflag, struct FTW *ftwbuf) {
- (void)sb;
- (void)typeflag;
- (void)ftwbuf;
-
- int res = remove(fpath);
- if (res) {
- log_warn("could not remove: %s", fpath);
- }
- return 0;
-}
-
-int remove_tree(const char *path) {
- return nftw(path, _remove_callback, 256, FTW_DEPTH | FTW_PHYS);
-}
diff --git a/src/globs.c b/src/globs.c
deleted file mode 100644
index cdb9df8..0000000
--- a/src/globs.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#include <stdlib.h>
-
-#include "globs.h"
-
-#include "files.h"
-#include "log.h"
-#include "pkgit_lua.h"
-#include "str.h"
-
-install_dirs_t inst_dirs = {0};
-cli_flags_t flags = {0};
-config_t cfg = {0};
-
-void init_vars(void) {
- if (file_exists("/etc/pkgit/repos.lua")) {
- cfg.dir = mstr("/etc/pkgit");
- cfg.name = mstr("/etc/pkgit/repos.lua");
- } else {
- const char *tmp = getenv("XDG_CONFIG_HOME");
- if (tmp) {
- cfg.dir = str_format("%s/pkgit", tmp);
- } else {
- cfg.dir = str_format("%s/.config/pkgit", getenv("HOME"));
- }
- cfg.name = str_concat_cstr(&cfg.dir, "/init.lua");
- }
- // TODO: read into cfg.content
-
- cfg.repos = str_format("%.*s/repos.lua", str_fmt(&cfg.dir));
- init_install_directories();
- init_prefix_directory();
-}
-
-void free_vars(void) {
- str_free(&cfg.dir);
- str_free(&cfg.name);
- str_free(&cfg.content);
- str_free(&cfg.repos);
- str_free(&inst_dirs.prefix);
- str_free(&inst_dirs.bin);
- str_free(&inst_dirs.lib);
- str_free(&inst_dirs.include);
- str_free(&inst_dirs.src);
-}
diff --git a/src/help.c b/src/help.c
index a568e7c..e35cbcc 100644
--- a/src/help.c
+++ b/src/help.c
@@ -20,10 +20,11 @@
#include <stdio.h>
+#include "common.h"
#include "help.h"
-#include "globs.h"
+#include "state.h"
-void help(void) {
+void print_help(void) {
typedef struct {
const char *short_flag;
const char *long_flag;
@@ -50,7 +51,7 @@ void help(void) {
{"-f", "--force", "", "build a package"},
};
- if (flags.verbose) {
+ if (!flags.quiet) {
printf(BOLD_MAGENTA " , \n");
printf(BOLD_MAGENTA " / \\ \n");
printf(BOLD_MAGENTA "_.--' '--._ \n");
diff --git a/src/is_updated.c b/src/is_updated.c
deleted file mode 100644
index fef759e..0000000
--- a/src/is_updated.c
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#include <string.h>
-#include <stdbool.h>
-#include <unistd.h>
-
-#include "is_updated.h"
-
-#include "files.h"
-#include "str.h"
-#include "pkgit_lua.h"
-
-bool is_updated(str *src) {
- bool result = false;
- if (str_is_valid(src) && src->len > 0 && chdir(src->data) != 0) return result;
- str bldit_pkgver = bldit_pkg_getver();
- str git_tag = cmd_out("git tag | tail -n 1");
- result = (strstr(git_tag.data, bldit_pkgver.data) != NULL && bldit_pkgver.len != 0);
- if (result) {
- str_free(&bldit_pkgver);
- str_free(&git_tag);
- return result;
- }
- str git_pull = cmd_out("git pull");
- result = (strstr(git_pull.data, "Already up to date.") != NULL);
- str_free(&git_tag);
- str_free(&git_pull);
- str_free(&bldit_pkgver);
- return result;
-}
diff --git a/src/log.c b/src/log.c
index 078afde..469187e 100644
--- a/src/log.c
+++ b/src/log.c
@@ -2,7 +2,7 @@
#include <stdarg.h>
#include <stdio.h>
-#include "globs.h"
+#include "common.h"
#include "log.h"
#define X(n, N) \
diff --git a/src/lua_globs.c b/src/lua_globs.c
deleted file mode 100644
index 3f8ac2a..0000000
--- a/src/lua_globs.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "lua.h"
-#include "pkgit_lua.h"
-
-#include "files.h"
-#include "globs.h"
-#include "log.h"
-#include "str.h"
-
-lua_State *L = NULL;
-lua_State *B = NULL;
-bool config_loaded = false;
-bool bldit_loaded = false;
-
-void push_lua_path(lua_State *L, const char *new_path) {
- lua_getglobal(L, "package");
- lua_getfield(L, -1, "path");
- const char *current_path = lua_tostring(L, -1);
- if (!current_path)
- current_path = "";
- lua_pop(L, 1);
- lua_pushfstring(L, "%s;%s", current_path, new_path);
- lua_setfield(L, -2, "path");
- lua_pop(L, 1);
-}
-
-void init_lua_state(void) {
- if (L != NULL)
- return;
- L = luaL_newstate();
- luaL_openlibs(L);
- str lua_path = str_format("%.*s/?.lua", str_fmt(&cfg.dir));
- push_lua_path(L, lua_path.data);
- if (luaL_loadfile(L, cfg.name.data) || lua_pcall(L, 0, 0, 0)) {
- log_error("cannot run configuration script: %s", lua_tostring(L, -1));
- log_pkgit("to generate a configuration file, head into the");
- log_pkgit(
- "root directory of the pkgit source and run `make defconfig`");
- exit(EXIT_FAILURE);
- }
- if (file_exists(cfg.repos.data)) {
- if (luaL_loadfile(L, cfg.repos.data) || lua_pcall(L, 0, 0, 0)) {
- if (flags.verbose)
- log_warn("cannot load repository file: %s",
- lua_tostring(L, -1));
- lua_pop(L, 1);
- }
- }
- str_free(&lua_path);
- config_loaded = true;
-}
-
-void init_bldit_state(void) {
- if (B != NULL)
- return;
- B = luaL_newstate();
- luaL_openlibs(B);
- if (luaL_loadfile(B, "bldit.lua") || lua_pcall(B, 0, 0, 0)) {
- if (flags.verbose)
- log_warn("cannot run bldit: %s", lua_tostring(B, -1));
- return;
- }
- lua_pushfstring(B, "%s", inst_dirs.prefix.data);
- lua_setglobal(B, "prefix");
- bldit_loaded = true;
-}
-
-void free_lua_state(void) {
- if (L != NULL) {
- lua_close(L);
- L = NULL;
- }
- config_loaded = false;
-}
-
-void free_bldit_state(void) {
- if (B != NULL) {
- lua_close(B);
- B = NULL;
- }
- bldit_loaded = false;
-}
-
-lua_State *get_lua_state(void) {
- return L;
-}
-
-lua_State *get_bldit_state(void) {
- return B;
-}
-
-void lua_isnt_type(char *variable, char *type) {
- if (flags.verbose)
- log_error("init.lua: '%s' is not a %s.", variable, type);
-}
-
-void bldit_isnt_type(char *variable, char *type) {
- if (flags.verbose)
- log_error("bldit.lua: '%s' is not a %s.", variable, type);
-}
-
-bool lua_try_function(lua_State *L, char *lua_file, char *fname) {
- lua_getfield(L, -1, fname);
- if (!lua_isfunction(L, -1)) {
- if (!strcmp(lua_file, "bldit.lua"))
- bldit_isnt_type(fname, "function");
- else
- lua_isnt_type(fname, "function");
- lua_pop(L, 1);
- return false;
- } else if (lua_pcall(L, 0, 1, 0) != LUA_OK) {
- if (flags.verbose)
- log_warn("%s: '%s' function borked: %s", lua_file, fname,
- lua_tostring(L, -1));
- lua_pop(L, 1);
- return false;
- }
- if (!lua_isnumber(L, -1) || lua_tonumber(L, -1) != 0) {
- if (flags.verbose)
- log_warn("%s: '%s' failed: %s", lua_file, fname,
- lua_tostring(L, -1));
- lua_pop(L, 1);
- return false;
- }
- lua_pop(L, 1);
- return true;
-}
-
-bool lua_try_table(lua_State *L, char *lua_file, char *tname) {
- lua_getfield(L, -1, tname);
- if (!lua_istable(L, -1)) {
- if (!strcmp(lua_file, "bldit.lua"))
- bldit_isnt_type(tname, "table");
- else
- lua_isnt_type(tname, "table");
- return false;
- }
- return true;
-}
-
-str bldit_getver(void) {
- init_bldit_state();
- if (!bldit_loaded) {
- return mstr("");
- }
- lua_getglobal(B, "bldit_version");
- if (!lua_isstring(B, -1)) {
- if (flags.verbose)
- log_warn("bldit.lua: 'bldit_version' is not a string.");
- lua_pop(B, 1);
- return mstr("");
- }
- str bldit_version = mstr(lua_tostring(B, -1));
- lua_pop(B, 1);
- return bldit_version;
-}
-
-str bldit_pkg_getver(void) {
- init_bldit_state();
- lua_getglobal(B, "package_version");
- if (!lua_isstring(B, -1)) {
- if (flags.verbose)
- log_warn("bldit.lua: 'package_version' is not a string.");
- lua_pop(B, 1);
- return mstr("");
- }
- str package_version = mstr(lua_tostring(B, -1));
- lua_pop(B, 1);
- return package_version;
-}
-
-bool is_bldit_usable(void) {
- str bldit_version = bldit_getver();
- if (bldit_version.len && str_equal_cstr(&bldit_version, VERSION))
- goto done;
- bool prev_pass = false;
- for (size_t i = 0; i < bldit_version.len; i++) {
- if (bldit_version.data[i] == '.')
- continue;
- if ((bldit_version.data[i] - '0') <= (VERSION[i] - '0')) {
- prev_pass = ((bldit_version.data[i] - '0') != (VERSION[i] - '0'));
- continue;
- } else {
- str_free(&bldit_version);
- return prev_pass;
- }
- }
-done:
- str_free(&bldit_version);
- return true;
-}
diff --git a/src/lua_state.c b/src/lua_state.c
new file mode 100644
index 0000000..7e9dd18
--- /dev/null
+++ b/src/lua_state.c
@@ -0,0 +1,146 @@
+#include <lua.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "lua_state.h"
+#include "state.h"
+#include "str.h"
+
+lua_State *L = NULL;
+bool L_is_loaded = false;
+
+static void lua_path_push(const char *new_path) {
+ lua_getglobal(L, "package");
+ lua_getfield(L, -1, "path");
+
+ const char *current_path = lua_tostring(L, -1);
+ if (!current_path)
+ current_path = "";
+ lua_pop(L, 1);
+
+ lua_pushfstring(L, "%s;%s", current_path, new_path);
+ lua_setfield(L, -2, "path");
+
+ lua_pop(L, 1);
+}
+
+bool init_lua_state(void) {
+ if (L_is_loaded)
+ return true;
+
+ L = luaL_newstate();
+ luaL_openlibs(L);
+
+ str lua_path = str_format("%.*s/?.lua", str_fmt(&config.dir));
+ lua_path_push(lua_path.data);
+
+ if (luaL_loadfile(L, config.init_path.data) || lua_pcall(L, 0, 1, 0)) {
+ log_error("cannot run configuration script: %s", lua_tostring(L, -1));
+ log_pkgit("to generate a configuration file, head into the");
+ log_pkgit(
+ "root directory of the pkgit source and run `make defconfig`");
+ lua_close(L);
+ return false;
+ }
+
+ if (!lua_istable(L, -1)) {
+ log_error("top-level configuration is not a table");
+ lua_pop(L, 1);
+ lua_close(L);
+ return false;
+ }
+
+ str_free(&lua_path);
+ L_is_loaded = true;
+ return true;
+}
+
+static void setup_base_dirs(void) {
+ int uid = getuid(), euid = geteuid();
+
+ config.is_root = false;
+
+ if (uid < 0 || uid != euid) {
+ config.is_root = true;
+ config.dir = mstr("/etc/pkgit");
+ } else {
+ char *tmp = getenv("XDG_CONFIG_HOME");
+ if (tmp) {
+ config.dir = str_format("%s/pkgit", tmp);
+ } else {
+ config.dir = str_format("%s/.config/pkgit", getenv("HOME"));
+ }
+ }
+
+ config.init_path = str_concat_cstr(&config.dir, "/init.lua");
+ config.autogenerated_path =
+ str_concat_cstr(&config.dir, "/autogenerated.lua");
+}
+
+#define HANDLE_LUA_ERROR \
+ do { \
+ log_error("lua: %s", lua_tostring(L, -1)); \
+ lua_pop(L, 1); \
+ return false; \
+ } while (0)
+
+#define lua_get_field_type(name, type, file) \
+ do { \
+ lua_getfield(L, -1, name); \
+ if (!lua_is##type(L, -1)) { \
+ log_error("lua: expected type " #type " for " name " in %.*s", \
+ str_fmt(file)); \
+ lua_pop(L, 1); \
+ return false; \
+ } \
+ } while (0)
+
+static bool load_init_lua(void) {
+ if (luaL_loadfile(L, config.init_path.data))
+ HANDLE_LUA_ERROR;
+
+ lua_setglobal(L, "__PkgitUserConfig");
+
+ lua_getglobal(L, "__PkgitUserConfig");
+ lua_get_field_type("dirs", table, &config.init_path);
+
+#define X(field) \
+ do { \
+ lua_get_field_type(#field, string, &config.init_path); \
+ config.inst_dirs.field = str_adopt((char *)lua_tostring(L, -1)); \
+ lua_pop(L, -1); \
+ } while (0)
+
+ X(prefix);
+ X(bin);
+ X(include);
+ X(lib);
+ X(src);
+
+#undef X
+
+ lua_getfield(L, -1, "prefix");
+
+ return true;
+}
+
+bool init_lua_config(void) {
+ if (!init_lua_state())
+ return false;
+
+ setup_base_dirs();
+
+ if (!load_init_lua())
+ return false;
+
+ return true;
+}
+
+void free_lua_state(void) {
+ if (L != NULL) {
+ lua_close(L);
+ L = NULL;
+ }
+ L_is_loaded = false;
+}
diff --git a/src/lua_vars.c b/src/lua_vars.c
deleted file mode 100644
index 7420ae0..0000000
--- a/src/lua_vars.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#include "pkgit_lua.h"
-
-#include "globs.h"
-
-void init_install_directories(void) {
- init_lua_state();
- if (!lua_istable(L, -1)) {
- lua_getglobal(L, "install_directories");
- }
- if (!lua_istable(L, -1)) {
- lua_isnt_type("install_directories", "table");
- return;
- }
-
-#define SETUP_INST_DIRS \
- X(bin) \
- X(lib) \
- X(include) \
- X(src)
-
-#define X(dir) \
- do { \
- lua_getfield(L, -1, #dir); \
- if (!lua_isstring(L, -1)) { \
- lua_isnt_type("install_directories." #dir, "string"); \
- } else { \
- str_copy_cstr_into(&inst_dirs.dir, lua_tostring(L, -1)); \
- } \
- lua_pop(L, 1); \
- } while (0);
-
- SETUP_INST_DIRS
-#undef X
-
- lua_pop(L, 1);
-}
-
-void init_prefix_directory(void) {
- lua_getglobal(L, "prefix");
- if (!lua_isstring(L, -1)) {
- lua_isnt_type("prefix", "string.");
- return;
- }
- inst_dirs.prefix = mstr(lua_tostring(L, -1));
- lua_pop(L, 1);
-}
diff --git a/src/main.c b/src/main.c
index 341b7ad..0cba246 100644
--- a/src/main.c
+++ b/src/main.c
@@ -18,12 +18,28 @@
*/
-#include "parse_args.h"
-#include "globs.h"
+#include "cli.h"
+#include "lua_state.h"
int main(int argc, char **argv) {
- init_vars();
- parse_args(argc, argv);
- free_vars();
- return 0;
+ // if (!init_vars())
+ // return 1;
+
+ int retval = 0;
+ cli_result_t result = cli(argc, argv);
+ if (!result) {
+ retval = 1;
+ goto done;
+ }
+
+ if (result != CLI_HELP_CALLED) {
+ init_lua_state();
+ // if (!init_vars())
+ // return 1;
+ }
+
+done:
+ free_lua_state();
+ // free_vars();
+ return retval;
}
diff --git a/src/parse_args.c b/src/parse_args.c
deleted file mode 100644
index ff72334..0000000
--- a/src/parse_args.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#include <stdio.h>
-#include <string.h>
-
-#include "parse_args.h"
-
-#include "build.h"
-#include "globs.h"
-#include "log.h"
-#include "str.h"
-#include "check.h"
-#include "help.h"
-#include "pkg.h"
-#include "pkgit_lua.h"
-#include "add_repo.h"
-#include "search.h"
-
-#define COMMAND(large, small) \
- (!strcmp(argv[i], large) || !strcmp(argv[i], small))
-
-#define NOT_ENOUGH_ARGS(arg, next) \
- log_error("Not enough arguments! Try: pkgit %s [%s]", (arg), (next))
-
-void cmd_add(char **argv, int i) {
- if (argv[i + 1]) {
- str arg = mstr(argv[i + 1]);
- package_t pkg = pkg_create(&arg);
- add_repo(&pkg);
- pkg_free(&pkg);
- if (str_is_valid(&arg)) str_free(&arg);
- } else {
- NOT_ENOUGH_ARGS(argv[i], "url");
- }
-}
-
-void cmd_build(int argc, char **argv, int i) {
- if (argv[i + 1]) {
- for (int j = i + 1; j < argc; j++) {
- if (argv[j][0] == '-')
- continue;
- str arg = mstr(argv[j]);
- package_t pkg = pkg_create(&arg);
- build(&pkg);
- pkg_free(&pkg);
- if (str_is_valid(&arg)) str_free(&arg);
- }
- } else {
- str arg = mstr(".");
- package_t pkg = pkg_create(&arg);
- build(&pkg);
- pkg_free(&pkg);
- if (str_is_valid(&arg)) str_free(&arg);
- }
-}
-
-void cmd_install(int argc, char **argv, int i) {
- if (argv[i + 1]) {
- for (int j = i + 1; j < argc; j++) {
- if (argv[j][0] == '-') continue;
- str arg = mstr(argv[j]);
- package_t pkg = pkg_create(&arg);
- if (flags.force) pkg_remove(&pkg);
- pkg_install(&pkg);
- pkg_free(&pkg);
- if (str_is_valid(&arg)) str_free(&arg);
- }
- } else {
- NOT_ENOUGH_ARGS(argv[i], "url/pkg");
- }
-}
-
-void cmd_update(int argc, char **argv, int i) {
- if (argv[i + 1]) {
- for (int j = i + 1; j < argc; j++) {
- if (argv[j][0] == '-') continue;
- str arg = mstr(argv[j]);
- package_t pkg = pkg_create(&arg);
- pkg_update(&pkg);
- pkg_free(&pkg);
- if (str_is_valid(&arg)) str_free(&arg);
- }
- } else {
- all_update();
- }
-}
-
-void cmd_remove(int argc, char **argv, int i) {
- if (argv[i + 1]) {
- for (int j = i + 1; j < argc; j++) {
- if (argv[j][0] == '-') continue;
- str arg = mstr(argv[j]);
- package_t pkg = pkg_create(&arg);
- pkg_remove(&pkg);
- pkg_free(&pkg);
- if (str_is_valid(&arg)) str_free(&arg);
- }
- } else {
- NOT_ENOUGH_ARGS(argv[i], "url/pkg");
- }
-}
-
-void cmd_search(int argc, char **argv, int i) {
- if (argv[i + 1]) {
- for (int j = i + 1; j < argc; j++) {
- if (argv[j][0] == '-') continue;
- search(argv[j]);
- }
- } else {
- search("");
- }
-}
-
-void flags_mod(char **argv, int i) {
- for (size_t j = 1; j < strlen(argv[i]); j++) {
- switch (argv[i][j]) {
- case 'q':
- flags.verbose = false;
- break;
- case 'f':
- flags.force = true;
- break;
- default:
- break;
- }
- }
-}
-
-void flags_cmd(int argc, char **argv, int i) {
- for (size_t j = 1; j < strlen(argv[i]); j++) {
- switch (argv[i][j]) {
- case 'a': cmd_add(argv, i); break;
- case 'b': cmd_build(argc, argv, i); break;
- case 'c': check(); break;
- case 'd': declare(); break;
- case 'i': cmd_install(argc, argv, i); break;
- case 'r': cmd_remove(argc, argv, i); break;
- case 'u': cmd_update(argc, argv, i); break;
- case 'l': list_installed(); break;
- case 's': cmd_search(argc, argv, i); break;
- case 'v': printf("%s\n", VERSION); break;
- case 'h': help(); break;
- default: break;
- }
- }
-}
-
-void parse_flags(int argc, char **argv) {
- for (int i = 1; i < argc; i++) {
- if (argv[i][0] != '-') continue;
- if (argv[i][1] == '-') {
- if (COMMAND("--quiet", "-q")) flags.verbose = false;
- if (COMMAND("--force", "-f")) flags.force = true;
- } else {
- flags_mod(argv, i);
- flags_cmd(argc, argv, i);
- }
- }
- return;
-}
-
-void parse_cmds(int argc, char **argv) {
- for (int i = 1; i < argc; i++) {
- if (COMMAND("--add", "a")) { cmd_add(argv, i); }
- if (COMMAND("--build", "b")) { cmd_build(argc, argv, i); }
- if (COMMAND("--install", "i")) { cmd_install(argc, argv, i); }
- if (COMMAND("--remove", "r")) { cmd_remove(argc, argv, i); }
- if (COMMAND("--update", "u")) { cmd_update(argc, argv, i); }
- if (COMMAND("--declare", "d")) { declare(); }
- if (COMMAND("--list", "l")) { list_installed(); }
- if (COMMAND("--search", "s")) { cmd_search(argc, argv, i); }
- if (COMMAND("--version", "v")) { printf(VERSION "\n"); }
- if (COMMAND("--help", "h")) { help(); }
- if (COMMAND("--check", "c")) { check(); }
- }
-}
-
-void parse_args(int argc, char **argv) {
- flags.force = false;
- flags.verbose = true;
- if (argc == 1) {
- help();
- return;
- }
- parse_flags(argc, argv);
- parse_cmds(argc, argv);
- return;
-}
diff --git a/src/pkg_create.c b/src/pkg_create.c
deleted file mode 100644
index 5afd1b0..0000000
--- a/src/pkg_create.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
-
- pkgit - package_t it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>. */
-
-#include <string.h>
-#include <unistd.h>
-
-#include "pkg.h"
-
-#include "files.h"
-#include "globs.h"
-#include "log.h"
-#include "pkgit_lua.h"
-#include "str.h"
-
-static str get_destdir(str *cwd, str *arg) {
- str result = {0};
- if (str_first(arg) == '.' && arg->len == 1) {
- result = str_format("%.*s/%.*s", str_fmt(&inst_dirs.src), str_fmt(cwd));
- } else {
- str name = {0};
- if (str_find_char(arg, '/') != 0) name = str_from_after_delim(arg, '/');
- else str_copy_into(&name, arg);
- result = str_format("%.*s/%.*s", str_fmt(&inst_dirs.src), str_fmt(&name));
- str_free(&name);
- }
- return result;
-}
-
-static str get_pkgsrc(package_t pkg) {
- if (pkg.is_local == 1)
- return str_format("%.*s/%.*s", str_fmt(&inst_dirs.src), str_fmt(&pkg.name));
- else
- return str_format("%.*s/%.*s/%.*s", str_fmt(&inst_dirs.src),
- str_fmt(&pkg.name), str_fmt(&pkg.version));
-}
-
-static void assign_pkg_version(package_t *pkg, str *new_arg_str) {
- if (str_find_char(new_arg_str, '@')) {
- str tmp_arg = str_from_after_delim(new_arg_str, '@');
- if (str_find_char(new_arg_str, ',')) {
- str tmp_trg = str_from_after_delim(&tmp_arg, ',');
- pkg->version = str_from_after_delim(new_arg_str, '@');
- pkg->version.len -= tmp_trg.len;
- str_free(&tmp_trg);
- } else {
- pkg->version = str_from_after_delim(new_arg_str, '@');
- }
- if (str_find_char(&pkg->version, ',')) pkg->version.len--;
- str_free(&tmp_arg);
- pkg->name.len -= pkg->version.len + 1;
- } else str_copy_cstr_into(&pkg->version, "HEAD");;
-}
-
-static void assign_pkg_target(package_t *pkg, str *new_arg_str) {
- if (str_find_char(new_arg_str, ',')) {
- str tmp_arg = str_from_after_delim(new_arg_str, ',');
- if (str_find_char(new_arg_str, '@')) {
- str tmp_ver = str_from_after_delim(&tmp_arg, '@');
- pkg->target = str_from_after_delim(new_arg_str, ',');
- pkg->target.len -= tmp_ver.len;
- str_free(&tmp_ver);
- } else {
- pkg->target = str_from_after_delim(new_arg_str, ',');
- }
- if (str_find_char(&pkg->target, '@')) pkg->target.len--;
- str_free(&tmp_arg);
- pkg->name.len -= pkg->target.len + 1;
- } else if (!flags.verbose) {
- str_copy_cstr_into(&pkg->target, "quiet");
- } else str_copy_cstr_into(&pkg->target, "quiet");
-}
-
-package_t pkg_create(str *arg) {
- package_t pkg = { .is_local = false, };
-
- assign_pkg_version(&pkg, arg);
- assign_pkg_target(&pkg, arg);
- if (str_find_char(arg, '@')) {
- str tmp = str_from_after_delim(arg, '@');
- arg->len -= tmp.len + 1;
- str_free(&tmp);
- } if (str_find_char(arg, ',')) {
- str tmp = str_from_after_delim(arg, ',');
- arg->len -= tmp.len + 1;
- str_free(&tmp);
- }
-
- char cwd[MAX_PATH_LEN];
- getcwd(cwd, MAX_PATH_LEN);
- str cwd_str = mstr(cwd);
- str dest_dir = get_destdir(&cwd_str, arg);
- bool is_installed_locally = is_directory(dest_dir.data);
-
- if (strncmp(arg->data, "http", 4) == 0 || strncmp(arg->data, "ssh", 3) == 0) {
- str_copy_into(&pkg.url, arg);
- pkg.name = str_from_after_delim(arg, '/');
- } else if (str_equal_cstr(arg, ".")) {
- str_copy_cstr_into(&pkg.url, "");
- pkg.name = str_from_after_delim(&cwd_str, '/');
- pkg.is_local = true;
- } else if (pkg_exists(arg)) {
- str_copy_into(&pkg.name, arg);
- pkg.url = pkg_get_url(&pkg.name);
- } else if (is_installed_locally) {
- str_copy_cstr_into(&pkg.url, "");
- pkg.name = str_from_after_delim(&dest_dir, '/');
- pkg.is_local = true;
- } else {
- log_error("'%.*s' is not a valid package", str_fmt(arg));
- str_free(&cwd_str);
- str_free(&dest_dir);
- str_free(arg);
- free_vars();
- exit(EXIT_FAILURE);
- }
-
- if (str_find_char(arg, ',')
- && str_find_char(arg, '@')
- ) pkg.name.len--;
- pkg.src = get_pkgsrc(pkg);
-
- str_free(&cwd_str);
- str_free(&dest_dir);
- if (pkg.is_local) str_free(arg);
- return pkg;
-}
diff --git a/src/pkg_exists.c b/src/pkg_exists.c
deleted file mode 100644
index 3ff761a..0000000
--- a/src/pkg_exists.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
-
- pkgit - package it!
-Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#include "pkgit_lua.h"
-#include "str.h"
-
-bool pkg_exists(str *name) {
- lua_getglobal(L, "repositories");
- if (!config_loaded || !lua_istable(L, -1)) {
- lua_pop(L, 1);
- return false;
- }
- str_slc new_name = {name->data, name->len};
- char* name_cstr = (char*)new_name.data;
- name_cstr[new_name.len] = '\0';
- if (!lua_try_table(L, "init.lua", name_cstr)) {
- lua_pop(L, 2);
- return false;
- }
- lua_pop(L, 2);
- return true;
-}
-
-str pkg_get_url(str *name) {
- lua_getglobal(L, "repositories");
- if (!config_loaded || !lua_istable(L, -1)) {
- lua_pop(L, 1);
- return mstr("");
- }
- if (!lua_try_table(L, "init.lua", name->data)) {
- lua_pop(L, 2);
- return mstr("");
- }
- lua_getfield(L, -1, "url");
- if (lua_isstring(L, -1)) {
- return mstr(lua_tostring(L, -1));
- }
- lua_pop(L, 3);
- return mstr("");
-}
diff --git a/src/pkg_free.c b/src/pkg_free.c
deleted file mode 100644
index 5944c4d..0000000
--- a/src/pkg_free.c
+++ /dev/null
@@ -1,10 +0,0 @@
-#include "pkg.h"
-#include "globs.h"
-
-void pkg_free(package_t *pkg) {
- str_free(&pkg->name);
- str_free(&pkg->url);
- str_free(&pkg->version);
- str_free(&pkg->target);
- str_free(&pkg->src);
-}
diff --git a/src/pkg_install.c b/src/pkg_install.c
deleted file mode 100644
index 0a1f427..0000000
--- a/src/pkg_install.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include "lua.h"
-#include "pkgit_lua.h"
-
-#include "add_repo.h"
-#include "build.h"
-#include "fetch.h"
-#include "files.h"
-#include "globs.h"
-#include "log.h"
-#include "pkg.h"
-
-void install_dependencies(lua_State *L) {
- while (lua_next(L, -2) != 0) {
- const char *depname = lua_tostring(L, -2);
- if (depname && lua_istable(L, -1)) {
- lua_getfield(L, -1, "url");
- str dep_url = mstr(lua_tostring(L, -1));
- lua_pop(L, 1);
- package_t pkg = pkg_create(&dep_url);
- str_free(&dep_url);
- lua_getfield(L, -1, "version");
- str_free(&pkg.version);
- const char *ver = lua_tostring(L, -1);
- pkg.version = ver ? mstr(ver) : mstr("HEAD");
- lua_pop(L, 1);
- const int top = lua_gettop(L);
- char cwd[MAX_PATH_LEN];
- if (getcwd(cwd, sizeof(cwd)) != NULL) {
- pkg_install(&pkg);
- chdir(cwd);
- }
- lua_settop(L, top);
- pkg_free(&pkg);
- }
- lua_pop(L, 1);
- }
-}
-
-bool target_install(lua_State *L, char *lua_file, str *target) {
- if (!lua_try_table(L, lua_file, target->data)) {
- lua_pop(L, 1);
- return false;
- }
- if (!lua_try_function(L, lua_file, "install")) {
- lua_pop(L, 1);
- return false;
- }
- lua_pop(L, 1);
- return true;
-}
-
-bool repo_install(package_t *pkg) {
- lua_getglobal(L, "repositories");
- if (!config_loaded || !lua_istable(L, -1)) {
- lua_isnt_type("repositories", "table");
- lua_pop(L, 1);
- return false;
- }
- if (!lua_try_table(L, "init.lua", pkg->name.data)) {
- lua_isnt_type(pkg->name.data, "table");
- lua_pop(L, 2);
- return false;
- }
- if (!lua_try_table(L, "init.lua", "targets"))
- return false;
- bool target_success = target_install(L, "init.lua", &pkg->target);
- lua_pop(L, 3);
- return target_success;
-}
-
-bool bldit_install(package_t *pkg) {
- init_bldit_state();
- if (!bldit_loaded) {
- return false;
- }
- if (!is_bldit_usable()) {
- log_error("bldit version is newer than the installed pkgit version");
- log_error("consider updating pkgit");
- if (!flags.force)
- return false;
- }
- lua_pushfstring(B, "%s", inst_dirs.prefix.data);
- lua_setglobal(B, "prefix");
- lua_getglobal(B, "targets");
- if (!lua_istable(B, -1)) {
- log_error("targets is not a table in bldit.lua!");
- return false;
- }
- bool target_success = target_install(B, "bldit.lua", &pkg->target);
- lua_pop(B, 1);
- return target_success;
-}
-
-bool config_install(package_t *pkg) {
- lua_getglobal(L, "build_systems");
- lua_pushnil(L);
- bool target_success = false;
- while (lua_next(L, -2) != 0) {
- const char *key = lua_tostring(L, -2);
- if (!lua_istable(L, -1)) {
- lua_pop(L, 1);
- continue;
- }
- str file_path = str_format("%.*s/%s", str_fmt(&pkg->src), key);
- if (access(file_path.data, F_OK) != 0) {
- lua_pop(L, 1);
- str_free(&file_path);
- continue;
- }
- str_free(&file_path);
- if (!lua_try_table(L, "init.lua", "targets")) {
- lua_pop(L, 2);
- continue;
- }
- target_success = target_install(L, "init.lua", &pkg->target);
- lua_pop(L, 2);
- if (target_success)
- break;
- }
- lua_pop(L, 1);
- return target_success;
-}
-
-void pkg_install(package_t *pkg) {
- if (is_directory(pkg->src.data)) {
- if (!flags.force) {
- log_info("%.*s is already installed.", str_fmt(&pkg->name));
- return;
- } else {
- if (flags.verbose)
- log_warn("%.*s is already installed. reinstalling...",
- str_fmt(&pkg->name));
- }
- } else {
- mkdir(pkg->src.data, 0755);
- chdir(pkg->src.data);
- }
- char cwd[MAX_PATH_LEN];
- getcwd(cwd, MAX_PATH_LEN);
- if (str_equal_cstr(&pkg->src, cwd))
- chdir(pkg->src.data);
-
- if (pkg->is_local) {
- cpdir(cwd, pkg->src.data);
- } else {
- log_pkgit("fetching " GREEN "%.*s" COLOR_RESET, str_fmt(&pkg->name));
- if (!fetch(pkg))
- return;
- if (flags.verbose)
- log_pkgit("fetched " GREEN "%.*s" COLOR_RESET, str_fmt(&pkg->name));
- }
-
- log_pkgit("building " GREEN "%.*s" COLOR_RESET, str_fmt(&pkg->name));
- if (!build(pkg))
- return;
- if (flags.verbose)
- log_pkgit("built " GREEN "%.*s" COLOR_RESET, str_fmt(&pkg->name));
-
- bool install_success = false;
- if (flags.verbose)
- log_info("attempting init.lua: 'repositories.%.*s.install'",
- str_fmt(&pkg->name));
- if (!install_success && repo_install(pkg))
- install_success = true;
- if (flags.verbose)
- log_info("attempting bldit.lua: 'repositories.%.*s.install'",
- str_fmt(&pkg->name));
- if (!install_success && bldit_install(pkg))
- install_success = true;
- if (flags.verbose)
- log_info("attempting init.lua: 'build_systems'", str_fmt(&pkg->name));
- if (!install_success && config_install(pkg))
- install_success = true;
- if (!install_success) {
- log_error("no install function availible for package: %.*s",
- str_fmt(&pkg->name));
- return;
- }
- log_success("installed " GREEN "%.*s" COLOR_RESET, str_fmt(&pkg->name));
-
- if (!pkg_exists(&pkg->name)) {
- log_pkgit("adding " GREEN "%.*s" COLOR_RESET, str_fmt(&pkg->name));
- if (pkg->url.len > 0) {
- add_repo(pkg);
- log_pkgit("added " GREEN "%.*s" COLOR_RESET, str_fmt(&pkg->name));
- }
- } else {
- log_info("repo already exists, done");
- }
-}
diff --git a/src/pkg_remove.c b/src/pkg_remove.c
deleted file mode 100644
index 20105c2..0000000
--- a/src/pkg_remove.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-#define _XOPEN_SOURCE 700
-
-#include <ftw.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include "files.h"
-#include "globs.h"
-#include "log.h"
-#include "pkgit_lua.h"
-
-bool target_uninstall(lua_State *L, char *lua_file, str *target) {
- if (!lua_try_table(L, lua_file, target->data)) {
- lua_pop(L, 1);
- return false;
- }
- if (!lua_try_function(L, lua_file, "uninstall")) {
- lua_pop(L, 1);
- return false;
- }
- lua_pop(L, 1);
- return true;
-}
-
-bool repo_uninstall(package_t *pkg) {
- if (!config_loaded || !lua_istable(L, -1)) {
- lua_isnt_type("repositories", "table");
- lua_pop(L, 1);
- return false;
- }
- if (!lua_try_table(L, "init.lua", pkg->name.data)) {
- lua_isnt_type(pkg->name.data, "table");
- lua_pop(L, 2);
- return false;
- }
- if (!lua_try_table(L, "init.lua", "targets"))
- return false;
- bool target_success = target_uninstall(L, "init.lua", &pkg->target);
- lua_pop(L, 3);
- return target_success;
-}
-
-bool bldit_uninstall(package_t *pkg) {
- init_bldit_state();
- if (!bldit_loaded) {
- lua_close(B);
- return false;
- }
- if (!is_bldit_usable()) {
- log_error("bldit version is newer than the installed pkgit version");
- log_error("consider updating pkgit");
- if (!flags.force)
- return false;
- }
- lua_pushfstring(B, "%s", inst_dirs.prefix.data);
- lua_setglobal(B, "prefix");
- if (!lua_try_table(B, "bldit.lua", "targets"))
- return false;
- bool target_success = target_uninstall(B, "bldit.lua", &pkg->target);
- lua_pop(B, 1);
- return target_success;
-}
-
-bool config_uninstall(package_t *pkg) {
- lua_getglobal(L, "build_systems");
- lua_pushnil(L);
- bool target_success = false;
- while (lua_next(L, -2) != 0) {
- const char *key = lua_tostring(L, -2);
- if (!lua_istable(L, -1)) {
- lua_pop(L, 1);
- continue;
- }
- str file_path = str_format("%.*s/%s", str_fmt(&pkg->src), key);
- if (access(file_path.data, F_OK) != 0) {
- lua_pop(L, 1);
- str_free(&file_path);
- continue;
- }
- str_free(&file_path);
- if (!lua_try_table(L, "init.lua", "targets")) {
- lua_pop(L, 2);
- continue;
- }
- target_success = target_uninstall(L, "init.lua", &pkg->target);
- lua_pop(L, 2);
- if (target_success)
- break;
- }
- lua_pop(L, 1);
- return target_success;
-}
-
-static int remove_installed(const char *src_path, const struct stat *sb,
- int typeflag, struct FTW *ftwbuf) {
- (void)sb;
- (void)ftwbuf;
-
- if (typeflag == FTW_F) {
- const char *filename = src_path + ftwbuf->base;
- const char *ext = strrchr(filename, '.');
- if (!ext)
- ext = "";
-
- if (strncmp(ext, ".so", 3) == 0) {
- char dest[MAX_PATH_LEN];
- snprintf(dest, sizeof(dest), "%s/%s", inst_dirs.lib.data, filename);
- if (file_exists(dest))
- remove(dest);
- } else if (access(src_path, X_OK) == 0) {
- if (strcmp(ext, ".sample") != 0 && strcmp(filename, "bldit") != 0 &&
- strcmp(filename, "build.sh") != 0 &&
- strcmp(filename, "compile.sh") != 0) {
- char dest[MAX_PATH_LEN];
- snprintf(dest, sizeof(dest), "%s/%s", inst_dirs.bin.data,
- filename);
- if (file_exists(dest))
- remove(dest);
- }
- } else if (strcmp(ext, ".h") == 0) {
- char dest[MAX_PATH_LEN];
- snprintf(dest, sizeof(dest), "%s/%s", inst_dirs.include.data,
- filename);
- if (file_exists(dest))
- remove(dest);
- }
- }
- return 0;
-}
-
-void pkg_remove(package_t *pkg) {
- if (!is_directory(pkg->src.data)) {
- log_error("%.*s is not installed!", str_fmt(&pkg->name));
- return;
- }
- chdir(pkg->src.data);
-
- bool uninstall_available = false;
- if (!uninstall_available)
- if (repo_uninstall(pkg))
- uninstall_available = true;
- if (!uninstall_available)
- if (bldit_uninstall(pkg))
- uninstall_available = true;
- if (!uninstall_available)
- if (config_uninstall(pkg))
- uninstall_available = true;
-
- if (!uninstall_available) {
- log_error("no uninstall function availible for package: %.*s",
- str_fmt(&pkg->name));
- return;
- }
-
- nftw(pkg->src.data, remove_installed, 64, FTW_PHYS);
- const char *last_slash = strrchr(pkg->src.data, '/');
- char target[MAX_PATH_LEN];
- if (last_slash && last_slash != pkg->src.data) {
- size_t parent_len = last_slash - pkg->src.data;
- snprintf(target, sizeof(target), "%.*s", (int)parent_len,
- pkg->src.data);
- } else {
- snprintf(target, sizeof(target), "%s", pkg->src.data);
- }
- remove_tree(pkg->src.data);
- log_success("removed %.*s", str_fmt(&pkg->name));
-}
diff --git a/src/pkg_update.c b/src/pkg_update.c
deleted file mode 100644
index 3441db0..0000000
--- a/src/pkg_update.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
-pkgit - package it!
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#include <dirent.h>
-#include <string.h>
-
-#include "files.h"
-#include "globs.h"
-#include "is_updated.h"
-#include "log.h"
-#include "pkgit_lua.h"
-
-lua_State *U = NULL;
-
-void init_update_state(void) {
- if (U != NULL)
- return;
- U = luaL_newstate();
- luaL_openlibs(U);
- str lua_path = str_format("%.*s/?.lua", str_fmt(&cfg.dir));
- push_lua_path(U, lua_path.data);
- if (luaL_loadfile(U, cfg.name.data) || lua_pcall(U, 0, 0, 0)) {
- log_error("cannot run configuration script: %s", lua_tostring(U, -1));
- log_pkgit("to generate a configuration file, head into the");
- log_pkgit(
- "root directory of the pkgit source and run `make defconfig`");
- exit(EXIT_FAILURE);
- }
- if (file_exists(cfg.repos.data)) {
- if (luaL_loadfile(U, cfg.repos.data) || lua_pcall(U, 0, 0, 0)) {
- if (flags.verbose) log_warn("cannot load repository file: %s", lua_tostring(U, -1));
- lua_pop(U, 1);
- }
- }
- str_free(&lua_path);
- config_loaded = true;
-}
-
-bool on_pkg_update(lua_State *L, package_t *pkg) {
- lua_getglobal(L, "repositories");
- if (!lua_istable(L, -1)) {
- lua_pop(L, 1);
- return false;
- }
- if (!lua_try_table(L, "init.lua", pkg->name.data)) {
- lua_pop(L, 2);
- return false;
- }
- if (!lua_try_table(L, "init.lua", "targets")) {
- lua_pop(L, 3);
- return false;
- }
- if (!lua_try_table(L, "init.lua", pkg->target.data)) {
- lua_pop(L, 4);
- return false;
- }
- if (!lua_try_function(L, "init.lua", "on_update")) {
- lua_pop(L, 4);
- return false;
- }
- lua_pop(L, 4);
- return true;
-}
-
-void pkg_update(package_t *pkg) {
- if (is_updated(&pkg->src)) {
- if (flags.verbose) log_info(
- "%.*s is already up to date.",
- str_fmt(&pkg->name)
- );
- return;
- }
- flags.force = true;
- str_copy_cstr_into(&pkg->version, "HEAD");
- pkg_install(pkg);
- if (!on_pkg_update(L, pkg)) log_warn(
- "init.lua: 'repositories.%.*s.%.*s.on_update' function failed",
- str_fmt(&pkg->name), str_fmt(&pkg->target)
- );
- pkg_free(pkg);
-}
-
-bool on_all_update(lua_State *L) {
- char* fname = "on_update";
- char* lua_file = "init.lua";
- lua_getglobal(L, fname);
- if (!lua_isfunction(L, -1)) {
- if (flags.verbose) lua_isnt_type(fname, "function");
- lua_pop(L, 1);
- } else if (lua_pcall(L, 0, 1, 0) != LUA_OK) {
- if (flags.verbose) log_warn(
- "%s: '%s' function borked: %s",
- lua_file, fname, lua_tostring(L, -1)
- );
- lua_pop(L, 1);
- return false;
- }
- if (!lua_isnumber(L, -1) || lua_tonumber(L, -1) != 0) {
- if (flags.verbose) log_warn(
- "%s: '%s' failed: %s",
- lua_file, fname, lua_tostring(L, -1)
- );
- lua_pop(L, 1);
- return false;
- }
- lua_pop(L, 1);
- return true;
-}
-
-void all_update(void) {
- struct dirent* dirent_ptr;
- DIR* dir_ptr;
- if ((dir_ptr = opendir(inst_dirs.src.data)) == NULL) {
- log_error("could not open %s", str_fmt(&inst_dirs.src));
- }
- while ((dirent_ptr = readdir(dir_ptr)) != NULL) {
- if (
- strcmp(dirent_ptr->d_name, "..") == 0 ||
- strcmp(dirent_ptr->d_name, ".") == 0
- ) continue;
- str pkg_name = mstr(dirent_ptr->d_name);
- if (!pkg_exists(&pkg_name)) {
- str_free(&pkg_name);
- continue;
- }
- package_t pkg = pkg_create(&pkg_name);
- pkg_update(&pkg);
- pkg_free(&pkg);
- if (str_is_valid(&pkg_name)) str_free(&pkg_name);
- }
- closedir(dir_ptr);
- if (!on_all_update(L))
- log_warn("init.lua: 'on_update' function failed");
-}
-
-void declare(void) {
- init_update_state();
- lua_getglobal(U, "repositories");
- if (!lua_istable(U, -1)) {
- lua_isnt_type("repositories", "table");
- lua_pop(U, 1);
- return;
- }
- lua_pushnil(U);
- while (lua_next(U, -2) != 0) {
- str key = mstr(lua_tostring(U, -2));
- if (!lua_istable(U, -1)) {
- lua_pop(U, 1);
- str_free(&key);
- continue;
- }
- package_t pkg = pkg_create(&key);
- pkg_install(&pkg);
- pkg_free(&pkg);
- if (str_is_valid(&key)) str_free(&key);
- lua_pop(U, 1);
- }
- if (!on_all_update(L))
- log_warn("init.lua: 'on_update' function failed");
- lua_pop(U, 1);
-}
diff --git a/src/search.c b/src/search.c
deleted file mode 100644
index 20d3e8c..0000000
--- a/src/search.c
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
-
- pkgit - package it!
-
- Copyright (C) 2026 dacctal
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-*/
-
-#include <dirent.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#include "search.h"
-
-#include "globs.h"
-#include "log.h"
-#include "pkgit_lua.h"
-
-void search(char *arg) {
- lua_getglobal(L, "repositories");
- if (!lua_istable(L, -1)) {
- lua_isnt_type("repositories", "table");
- lua_pop(L, 1);
- return;
- }
- lua_pushnil(L);
- while (lua_next(L, -2) != 0) {
- str key = mstr(lua_tostring(L, -2));
- if (!lua_istable(L, -1)) {
- lua_pop(L, 1);
- str_free(&key);
- continue;
- }
- if (key.len <= 1) str_println(&key);
- else if (strstr(key.data, arg)) str_println(&key);
- if (str_is_valid(&key)) str_free(&key);
- lua_pop(L, 1);
- }
-}
-
-void list_installed(void) {
- struct dirent* dirent_ptr;
- DIR* dir_ptr = opendir(inst_dirs.src.data);
-
- if (dir_ptr == NULL) {
- log_error("could not open %.*s", str_fmt(&inst_dirs.src));
- return;
- }
-
- while ((dirent_ptr = readdir(dir_ptr)) != NULL) {
- if (
- strcmp(dirent_ptr->d_name, "..") == 0 ||
- strcmp(dirent_ptr->d_name, ".") == 0
- ) continue;
- printf("%s\n", dirent_ptr->d_name);
- }
-}
diff --git a/include/check.h b/src/state.c
index c43385b..ec05250 100644
--- a/include/check.h
+++ b/src/state.c
@@ -1,24 +1,27 @@
/*
-
+
pkgit - package it!
-
+
Copyright (C) 2026 dacctal
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
-
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-#ifndef PKGIT_CHECK_H
-#define PKGIT_CHECK_H
-void check(void);
-#endif
+#include <lua.h>
+
+#include "state.h"
+
+cli_flags_t flags = {0};
+user_config_t config = {0};
+install_dirs_t inst_dirs = {0};
diff --git a/src/str.c b/src/str.c
index e12719c..1a9e677 100644
--- a/src/str.c
+++ b/src/str.c
@@ -27,7 +27,7 @@ along with this program.If not, see <https://www.gnu.org/licenses/>.
#include <stdlib.h>
#include <string.h>
-#include "globs.h"
+#include "common.h"
#include "str.h"
// NOTE: only use this assertion in functions where you don't depend
@@ -76,6 +76,15 @@ str mstr(const char *s) {
return str_from_str_slc(_cstrslc(s));
}
+str str_adopt(char *s) {
+ str adopted = {
+ .data = s,
+ .len = strlen(s),
+ };
+ adopted.cap = adopted.len;
+ return adopted;
+}
+
str str_from_str_slc(const str_slc s) {
str res = str_new_with_capacity(s.len + 1);
str_copy_str_slc_into(&res, s);
@@ -207,13 +216,32 @@ void str_append_str_slc(str *src, const str_slc new) {
size_t len_after_append = src->len + new.len;
if (len_after_append + 1 > src->cap) {
src->cap = len_after_append + 1;
- str_reserve(src, src->cap);
+ str_reserve_exact(src, src->cap);
}
memcpy(src->data + src->len, new.data, new.len + 1);
src->len = len_after_append;
src->data[len_after_append] = 0;
}
+void str_append_format(str *src, const char *format, ...) {
+ assert_str_is_valid(dest);
+
+ va_list args, args_copy;
+ va_start(args, format);
+ va_copy(args_copy, args);
+
+ size_t len = vsnprintf(NULL, 0, format, args_copy);
+ if (src->len + len + 1 > src->cap) {
+ src->cap = 1 + src->len + len;
+ str_reserve_exact(src, src->cap);
+ }
+
+ vsnprintf(src->data + src->len, len + 1, format, args);
+ src->len += len;
+
+ va_end(args);
+}
+
char str_pop_last(str *src) {
char c = str_last(src);
src->len--;
@@ -502,7 +530,7 @@ str_slc str_slc_from_cstr(const char *s) {
return _cstrslc(s);
}
-str_slc mstrslc(const char *s) {
+str_slc mslc(const char *s) {
return (str_slc){s, strlen(s)};
}