summaryrefslogtreecommitdiff
path: root/ape
diff options
context:
space:
mode:
authorZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-25 18:24:05 +0000
committerZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-25 18:24:05 +0000
commite3ab29bb4d7174cae65de2275f19105eb3d93d91 (patch)
tree89970111c1be4c3ddd291b720d4fdf496f10b2d5 /ape
parent489336b2101bf16edeec7bfc4379408eb19b936e (diff)
APE shall never kill APE
Diffstat (limited to 'ape')
-rw-r--r--ape/boot/.args23
-rw-r--r--ape/boot/Makefile14
-rw-r--r--ape/boot/boot.zc118
-rw-r--r--ape/boot/hello.zc4
-rw-r--r--ape/boot/instructions.txt6
-rw-r--r--ape/zc_entry.c257
6 files changed, 422 insertions, 0 deletions
diff --git a/ape/boot/.args b/ape/boot/.args
new file mode 100644
index 0000000..797e7cd
--- /dev/null
+++ b/ape/boot/.args
@@ -0,0 +1,23 @@
+...
+:: system -c 'mkdir -p usr/bin'
+:: system -c 'echo " Downloading into usr/bin"'
+:: system -c 'out=usr/bin/unzip; [ -e "$out" ] || curl -o "$out" https://cosmo.zip/pub/cosmos/bin/unzip'
+:: system -c 'out=usr/bin/unzip; [ -e "$out" ] && echo " [+] $out: exists"'
+:: system -c 'out=usr/bin/zip; [ -e "$out" ] || curl -o "$out" https://cosmo.zip/pub/cosmos/bin/zip'
+:: system -c 'out=usr/bin/zip; [ -e "$out" ] && echo " [+] $out: exists"'
+:: system -c 'chmod 755 usr/bin/zip'
+:: system -c 'chmod 755 usr/bin/unzip'
+:: system -c 'out=usr/cosmocc.zip; [ -e "$out" ] || curl -o "$out" https://cosmo.zip/pub/cosmocc/cosmocc.zip'
+:: system -c 'out=usr/cosmocc.zip; [ -e "$out" ] && echo " [+] $out: exists"'
+:: system -c 'out=usr; [ -e "$out/bin/cosmocc" ] || usr/bin/unzip -d "$out" usr/cosmocc.zip'
+:: system -c 'out=usr; [ -e "$out/bin/cosmocc" ] || unzip -d "$out" usr/cosmocc.zip'
+:: system -c 'out=usr; [ -e "$out/bin/cosmocc" ] && echo " [+] $out/bin/cosmocc: exists"'
+:: system -c 'out=usr/bin/zc.com; [ -e "$out" ] || curl -o "$out" -L https://github.com/OEvgeny/zc-ape/releases/latest/download/zc.com'
+:: system -c 'out=usr/bin/zc.com; [ -e "$out" ] && echo " [+] $out: exists"'
+:: system -c 'chmod 755 usr/bin/zc.com'
+:: system -c 'out=Makefile; [ -e "$out" ] || curl -o "$out" https://raw.githubusercontent.com/OEvgeny/zc-ape/main/zc-boot/Makefile'
+:: system -c 'out=Makefile; [ -e "$out" ] && echo " [+] $out: exists"'
+:: system -c 'out=hello.zc; [ -e "$out" ] || curl -o "$out" https://raw.githubusercontent.com/OEvgeny/zc-ape/main/zc-boot/hello.zc'
+:: system -c 'out=hello.zc; [ -e "$out" ] && echo " [+] $out: exists"'
+:: system -c 'echo " Done."'
+:: system -c 'cat /zip/instructions.txt' \ No newline at end of file
diff --git a/ape/boot/Makefile b/ape/boot/Makefile
new file mode 100644
index 0000000..05f7a36
--- /dev/null
+++ b/ape/boot/Makefile
@@ -0,0 +1,14 @@
+export PATH := $(realpath usr/bin):$(PATH)
+CC=cosmocc
+ZC=zc.com
+
+all: out/hello.com
+
+out/hello.com: hello.zc
+ @mkdir -p out
+ $(ZC) build --cc $(CC) -o $@ $<
+
+clean:
+ rm -rf out
+
+.PHONY: all clean \ No newline at end of file
diff --git a/ape/boot/boot.zc b/ape/boot/boot.zc
new file mode 100644
index 0000000..176f668
--- /dev/null
+++ b/ape/boot/boot.zc
@@ -0,0 +1,118 @@
+#define _COSMO_SOURCE
+#include <libc/cosmo.h>
+#include <string.h>
+#include <stdlib.h>
+
+raw {
+ extern char **environ;
+}
+
+fn normalize_wait(ws: int) -> int {
+ if ws == -1 { return -1 }
+ if WIFEXITED(ws) { return WEXITSTATUS(ws) }
+ if WIFSIGNALED(ws) { return 128 + WTERMSIG(ws) }
+ return -1;
+}
+
+fn run_exec(argv: char**) -> int {
+ fflush(NULL);
+ let ws: int = systemvpe(argv[0], argv, environ);
+ return normalize_wait(ws);
+}
+
+fn needs_quote(s: char*) -> bool {
+ for (let p = s; *p; ++p) {
+ if (*p == ' ' || *p == '\t' || *p == '\n' || *p == '"' || *p == '\'' )
+ return true;
+ }
+ return false;
+}
+
+fn print_quoted(s: char*) {
+ if !needs_quote(s) {
+ print f"{s}";
+ return;
+ }
+
+ print f"'";
+ for (let p = s; *p; ++p) {
+ if (*p == '\'') {
+ print f"'\"'\"'";
+ } else {
+ print f"{*p:c}";
+ }
+ }
+ print f"'";
+}
+
+fn log_cmd(argv: char**) {
+ print f"$ ";
+ let i = 0;
+ while argv[i] != NULL {
+ if i { print f" " }
+ print_quoted(argv[i]);
+ i++;
+ }
+ print f"\n";
+}
+
+fn main(argc: int, argv: string*) {
+ def delim = "::";
+
+ let newargc: int = cosmo_args("/zip/.args", &argv);
+ if newargc != -1 { argc = newargc }
+
+ let i = 1;
+ while i < argc && strcmp(argv[i], delim) { i++ }
+
+ while i < argc {
+ if strcmp(argv[i], delim) {
+ f"Expected '{delim}' before each command, got: {argv[i]}";
+ return 2;
+ }
+ i++;
+
+ let start = i;
+ while i < argc && strcmp(argv[i], delim) { i++ }
+ let cmd_argc = i - start;
+
+ if cmd_argc <= 0 {
+ f"Empty command after '{delim}'";
+ return 2;
+ }
+
+ let rc = 0;
+
+ if !strcmp(argv[start], "system") {
+ if cmd_argc != 3 {
+ !"error: system expects 2 arguments, got {cmd_argc}";
+ return -4;
+ }
+
+ if strcmp(argv[start + 1], "-c") {
+ !"error: system got '{argv[start + 1]}' instead of -c";
+ return -5;
+ }
+
+ "$ {argv[start + 2]}";
+ rc = normalize_wait(system(argv[start + 2]));
+
+ if rc { return rc }
+
+ continue;
+ }
+
+ autofree let cmdv = (char**)malloc((cmd_argc + 1) * sizeof(char*));
+ if !cmdv { return 1 }
+ for (let k = 0; k < cmd_argc; k++) { cmdv[k] = argv[start + k] }
+ cmdv[cmd_argc] = NULL;
+
+ log_cmd(cmdv);
+
+ rc = run_exec(cmdv);
+
+ if rc { return rc }
+ }
+
+ return 0;
+}
diff --git a/ape/boot/hello.zc b/ape/boot/hello.zc
new file mode 100644
index 0000000..8d14292
--- /dev/null
+++ b/ape/boot/hello.zc
@@ -0,0 +1,4 @@
+
+fn main() {
+ "Hello from Zen-C!";
+}
diff --git a/ape/boot/instructions.txt b/ape/boot/instructions.txt
new file mode 100644
index 0000000..edbe367
--- /dev/null
+++ b/ape/boot/instructions.txt
@@ -0,0 +1,6 @@
+Run:
+
+$ usr/bin/make
+$ out/hello.com
+
+To build and run hello world example.
diff --git a/ape/zc_entry.c b/ape/zc_entry.c
new file mode 100644
index 0000000..5594edf
--- /dev/null
+++ b/ape/zc_entry.c
@@ -0,0 +1,257 @@
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <libc/cosmo.h>
+
+int __real_main(int argc, char **argv);
+
+static bool streq(const char *a, const char *b)
+{
+ return a && b && !strcmp(a, b);
+}
+
+static bool has_cc_flag(int argc, char **argv)
+{
+ for (int i = 1; i < argc; ++i)
+ {
+ const char *a = argv[i];
+ if (a && streq(a, "--cc"))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+static const char *get_cc_value(int argc, char **argv)
+{
+ for (int i = 1; i < argc; ++i)
+ {
+ const char *a = argv[i];
+
+ if (a && streq(a, "--cc"))
+ {
+ if (i + 1 < argc)
+ {
+ return argv[i + 1];
+ }
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+static bool cc_is_cosmocc(int argc, char **argv)
+{
+ const char *cc = get_cc_value(argc, argv);
+ if (!cc)
+ {
+ return false;
+ }
+ const char *p = strstr(cc, "cosmocc");
+ return p != NULL;
+}
+
+static bool is_cmd(int argc, char **argv, const char *cmd)
+{
+ // usage: zc [command] [options] <file.zc>
+ return argc > 1 && streq(argv[1], cmd);
+}
+
+static bool has_o_flag(int argc, char **argv)
+{
+ for (int i = 1; i < argc; ++i)
+ {
+ const char *a = argv[i];
+ if (!a)
+ {
+ continue;
+ }
+ if (streq(a, "-o"))
+ {
+ return true;
+ }
+ if (!strncmp(a, "-o", 2) && a[2])
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+static const char *get_out_value(int argc, char **argv)
+{
+ for (int i = 1; i < argc; ++i)
+ {
+ const char *a = argv[i];
+ if (!a)
+ {
+ continue;
+ }
+
+ if (streq(a, "-o"))
+ {
+ if (i + 1 < argc)
+ {
+ return argv[i + 1];
+ }
+ return NULL;
+ }
+ if (!strncmp(a, "-o", 2) && a[2])
+ {
+ return a + 2;
+ }
+ }
+ return NULL;
+}
+
+static void inject_default_out(int *pargc, char ***pargv, const char *out)
+{
+ int argc = *pargc;
+ char **argv = *pargv;
+
+ if (!(is_cmd(argc, argv, "run") || is_cmd(argc, argv, "build")))
+ {
+ return;
+ }
+ if (has_o_flag(argc, argv))
+ {
+ return;
+ }
+
+ char **newv = (char **)malloc((size_t)(argc + 2 + 1) * sizeof(char *));
+ if (!newv)
+ {
+ return;
+ }
+
+ int k = 0;
+ newv[k++] = argv[0];
+ newv[k++] = argv[1];
+ newv[k++] = (char *)"-o";
+ newv[k++] = (char *)out;
+
+ for (int i = 2; i < argc; ++i)
+ {
+ newv[k++] = argv[i];
+ }
+ newv[k] = NULL;
+
+ *pargc = k;
+ *pargv = newv;
+}
+
+static void unlink_if_exists(const char *path)
+{
+ if (!path || !*path)
+ {
+ return;
+ }
+ int old = errno;
+ if (unlink(path) == -1)
+ {
+ if (errno == ENOENT)
+ {
+ errno = old;
+ }
+ }
+ else
+ {
+ errno = old;
+ }
+}
+
+static void cleanup_cosmocc_out(const char *out)
+{
+ char buf[4096];
+
+ if (snprintf(buf, sizeof(buf), "%s.dbg", out) < (int)sizeof(buf))
+ {
+ unlink_if_exists(buf);
+ }
+
+ size_t n = strlen(out);
+ if (n > 4 && !strcmp(out + (n - 4), ".com"))
+ {
+ char base[4096];
+ if (n - 4 < sizeof(base))
+ {
+ memcpy(base, out, n - 4);
+ base[n - 4] = 0;
+
+ if (snprintf(buf, sizeof(buf), "%s.aarch64.elf", base) < (int)sizeof(buf))
+ {
+ unlink_if_exists(buf);
+ }
+ }
+ }
+}
+
+static bool out_exists(const char *out)
+{
+ char buf[4096];
+ if (snprintf(buf, sizeof(buf), "%s.dbg", out) < (int)sizeof(buf))
+ {
+ if (access(buf, F_OK) != -1)
+ {
+ return true;
+ }
+ }
+
+ char base[4096];
+ size_t n = strlen(out);
+ if (n > 4 && !strcmp(out + (n - 4), ".com"))
+ {
+ if (n - 4 < sizeof(base))
+ {
+ memcpy(base, out, n - 4);
+ base[n - 4] = 0;
+ if (snprintf(buf, sizeof(buf), "%s.aarch64.elf", base) < (int)sizeof(buf))
+ {
+ if (access(buf, F_OK) != -1)
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static void ensure_env(const char *key, const char *val)
+{
+ const char *cur = getenv(key);
+ if (cur && *cur)
+ {
+ return;
+ }
+ setenv(key, val, 0);
+}
+
+int __wrap_main(int argc, char **argv)
+{
+ ensure_env("ZC_ROOT", "/zip");
+
+ int newargc = cosmo_args("/zip/.args", &argv);
+ if (newargc != -1)
+ {
+ argc = newargc;
+ }
+
+ inject_default_out(&argc, &argv, "a.out.com");
+
+ const char *out = get_out_value(argc, argv);
+
+ bool do_cleanup = is_cmd(argc, argv, "run") && cc_is_cosmocc(argc, argv) && !out_exists(out);
+
+ int rc = __real_main(argc, argv);
+
+ if (do_cleanup)
+ {
+ cleanup_cosmocc_out(out);
+ }
+
+ return rc;
+}