From 133d7e7c5bb33a467fd9197a8dc12d2128333334 Mon Sep 17 00:00:00 2001 From: atagen Date: Wed, 19 Nov 2025 17:03:01 +1100 Subject: [PATCH] init --- .envrc | 1 + .gitignore | 1 + flake.lock | 27 ++++++++++++ flake.nix | 47 ++++++++++++++++++++ main.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 201 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 main.c diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..92b2793 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.direnv diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..e40cd8d --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1763464769, + "narHash": "sha256-AJHrsT7VoeQzErpBRlLJM1SODcaayp0joAoEA35yiwM=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "6f374686605df381de8541c072038472a5ea2e2d", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..434840d --- /dev/null +++ b/flake.nix @@ -0,0 +1,47 @@ +{ + inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + + outputs = + { self, nixpkgs }: + let + lib = nixpkgs.lib; + forAllSystems = + func: lib.genAttrs lib.systems.flakeExposed (system: func (import nixpkgs { inherit system; })); + in + { + devShells = forAllSystems (pkgs: { + default = pkgs.mkShell { + packages = with pkgs; [ + clang-tools + gdb + ]; + }; + }); + packages = forAllSystems (pkgs: { + default = self.packages.${pkgs.stdenv.hostPlatform.system}.qstn; + qstn = pkgs.stdenv.mkDerivation (finalAttrs: { + pname = "qstn"; + version = "0.0.1"; + src = ./.; + buildPhase = '' + mkdir -p $out/bin + $CC -s -O3 --std=c17 -Wall -Wpedantic -Werror main.c -o $out/bin/qstn + ''; + meta.mainProgram = "qstn"; + }); + }); + nixosModules = { + default = self.nixosModules.qstn; + qstn = + { pkgs, lib, ... }: + { + boot.binfmt.registrations.qstn = { + wrapInterpreterInShell = false; + recognitionType = "magic"; + magicOrExtension = "#?"; + interpreter = lib.getExe self.packages.${pkgs.stdenv.hostPlatform.system}.qstn; + }; + }; + }; + }; +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..d4bf16e --- /dev/null +++ b/main.c @@ -0,0 +1,125 @@ +#define _DEFAULT_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#define exit_with(f) \ + exit_code = f; \ + goto cleanup; + +#define throw_on(f, e, c) \ + if (f == e) { \ + exit_with(c); \ + } + +#define free_if_used(f) \ + if (f != NULL) { \ + free(f); \ + } + +int main(int argc, char *argv[]) { + + int exit_code = 0; + if (argc < 2) { + exit(EINVAL); + } + + // open file + char *script_path = argv[1]; + FILE *script = fopen(script_path, "r"); + if (script == NULL) { + exit_with(errno); + } + + // init variables + char found_path [PATH_MAX+1]; + int nargc = 0; + char *args [128]; + + // read magic bytes + size_t read = 0; + char *line = NULL; + read = getline(&line, &read, script); + if (read == -1) { + exit_with(errno); + } + + // close file + throw_on(fclose(script), EOF, EIO); + + if (strncmp(line, "#?", 2) != 0) { + exit_with(ENOEXEC); + } + + // read argv[0] + char *token = strtok(&line[2], " "); + throw_on(token, NULL, EINVAL); + + args[nargc] = malloc(sizeof(char) * strlen(token)); + // avoid copying the magic bytes and newline + char* p = mempcpy(args[nargc], token, strlen(token) - 1); + *p = '\0'; + token = strtok(NULL, " "); + ++nargc; + + // collect rest of args + while (token != NULL && nargc < 127) { + args[nargc] = malloc(sizeof(char) * (strlen(token) + 1)); + strcpy(args[nargc++], token); + token = strtok(NULL, " "); + } + args[nargc] = script_path; + + + // search for path env var + char *path_env = getenv("PATH"); + if (path_env == NULL) { + exit(EINVAL); + } + + // split it by colon + token = strtok(path_env, ":"); + // search each member of PATH for executable with name + struct dirent *entity; + DIR *dir; + char rpath [PATH_MAX+1]; + while (token != NULL) { + char* __attribute__((unused)) _ = realpath(token, rpath); + dir = opendir(token); + if (dir != NULL) { + while ((entity = readdir(dir)) != NULL) { + if (entity->d_type == DT_REG && strcmp(args[0], entity->d_name) == 0) { + throw_on(closedir(dir), -1, EIO); + int len = strlen(token) + strlen(args[0]) + 2; + snprintf(found_path, len, "%s/%s", token, args[0]); + goto end_loop; + } + } + throw_on(closedir(dir), -1, EIO); + } + token = strtok(NULL, ":"); + } + +end_loop: + if (strlen(found_path) == 0) { + exit_with(ENOENT); + } + +cleanup: + free_if_used(line); + + if (exit_code == 0) { + if (execv(found_path, args) == -1) { + exit(errno); + } + } + for (int n = 0; n < nargc; ++n) { + free_if_used(args[nargc]); + } + + return exit_code; +}