commit 26f2dd931f94f37e2c389a2680ca21a3542edfe6
parent e87b61d4c850001ddee1e2420e576c676887aa57
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date: Mon, 16 Mar 2026 16:27:41 +0100
Full bootstrap achieved
Diffstat:
7 files changed, 254 insertions(+), 94 deletions(-)
diff --git a/project.janet b/project.janet
@@ -3,6 +3,7 @@
(declare-binscript
:main "instowl"
+ :hardcode-syspath true
:is-janet true)
(declare-native
diff --git a/src/file.janet b/src/file.janet
@@ -10,9 +10,18 @@
(defn mkdirp [path]
(if (not (dir-exists? path)) (do (mkdirp (libc/dirname path)) (os/mkdir path))))
+(defn copy-file [src dst]
+ (def src_fd (libc/ctry (nftw/open src :r)))
+ (if (file-exists? dst) (os/rm dst))
+ (def dst_fd (libc/ctry (nftw/open dst :wxc (nftw/fstat src_fd :int-permissions))))
+ (libc/sendfile dst_fd src_fd)
+ (nftw/close src_fd)
+ (nftw/close dst_fd))
+
(defn move-file [src dst]
(mkdirp (libc/dirname dst))
- (os/rename src dst))
+ (try (os/rename src dst)
+ ([a b] (copy-file src dst))))
(defn rmrf [path]
(nftw/nftw path (fn [file stat ftype info]
diff --git a/src/libc.janet b/src/libc.janet
@@ -1,4 +1,5 @@
(import ./utils)
+(import ./native/nftw)
(defmacro bind [name & types]
(with-syms [$fn $sig]
@@ -18,6 +19,13 @@
(defmacro defbind/str [name]
~(def ,name (bind/str ,(string/join [name]))))
+(defmacro ctry [call]
+ (with-syms [$ret]
+ ~(let [,$ret ,call]
+ (if (= ,$ret -1)
+ (error (string/join ["Function failed with:" (nftw/strerror)] " "))
+ ,$ret))))
+
(def c/glob :private (bind "glob" :int :string :int :ptr :ptr))
(def c/globfree :private (bind "globfree" :void :ptr))
(def c/glob_t :private (ffi/struct :size :ptr :size))
@@ -42,6 +50,28 @@
(c/ioctl fd (args 0) arg)
(ffi/read (args 1) arg))))
+(def c/sendfile :private (bind "sendfile" :ssize :int :int :ptr :size))
+
+(def c/lseek :private (bind "lseek" :int :int :int :int))
+
+(defn lseek [fd &opt whence offset]
+ (def whence_
+ (case whence
+ :set 0
+ :cur 1
+ :end 2
+ nil 1))
+ (def offset_ (if (nil? offset) 0 offset))
+ (c/lseek fd offset_ whence_))
+
+(defn sendfile [out_fd in_fd &opt offset count]
+ (def offset_ (if (nil? offset) (lseek in_fd) offset))
+ (var count_ (if (nil? count) (- (ctry (nftw/fstat in_fd :size)) offset_) count))
+ (def offset/ptr @"")
+ (buffer/push-uint64 offset/ptr :native offset_)
+ #(while (> (set count_ (- count_ (ctry (c/sendfile out_fd in_fd offset/ptr count_)))) 0)))
+ (ctry (c/sendfile out_fd in_fd offset/ptr count_)))
+
(defbind get_nprocs :int)
(defbind/str dirname)
(defbind/str basename)
diff --git a/src/main.janet b/src/main.janet
@@ -78,6 +78,8 @@
(def pkgdir (path/join stowdir pkg))
(def destdir (libc/mkdtemp "/tmp/instowl.XXXXXX"))
+ (def adopt (= (get args 1) "--adopt"))
+
(def env (os/environ))
(merge-into env {:err :pipe
:out :pipe
@@ -231,7 +233,7 @@
(file/close logfile))
:stow
- (checkrun :done :stow "-v" "-d" stowdir "-t" target "--override=.*" pkg)
+ (checkrun :done :stow "-v" "-d" stowdir "-t" target ;(if adopt ["--adopt"] []) pkg)
:error
(do
diff --git a/src/native/nftw.c b/src/native/nftw.c
@@ -1,14 +1,18 @@
#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64
+#include <errno.h>
+#include <fcntl.h>
#include <ftw.h>
#include <janet.h>
+#include <stdbool.h>
+#include <unistd.h>
#include "os.h"
static JanetFunction *janet_callback = NULL;
-static const char *flagname(int flag) {
+static const char *FTW_(int flag) {
switch (flag) {
case FTW_F:
return "f";
@@ -31,14 +35,6 @@ static const char *flagname(int flag) {
int callback(const char *path, const struct stat *st, int flag,
struct FTW *info) {
- JanetTable *stattab = janet_table(0);
-
- if (st) {
- for (const struct OsStatGetter *sg = os_stat_getters; sg->name != NULL;
- sg++) {
- janet_table_put(stattab, janet_ckeywordv(sg->name), sg->fn(st));
- }
- }
JanetTable *ftwtab = janet_table(0);
janet_table_put(ftwtab, janet_ckeywordv("base"),
@@ -48,10 +44,8 @@ int callback(const char *path, const struct stat *st, int flag,
return janet_unwrap_integer(janet_call(
janet_callback, 4,
- (const Janet[]){janet_cstringv(path),
- st ? janet_wrap_table(stattab) : janet_wrap_nil(),
- janet_ckeywordv(flagname(flag)),
- janet_wrap_table(ftwtab)}));
+ (const Janet[]){janet_cstringv(path), stat2table(st, NULL),
+ janet_ckeywordv(FTW_(flag)), janet_wrap_table(ftwtab)}));
}
struct {
@@ -86,8 +80,90 @@ static Janet c_nftw(int32_t argc, Janet *argv) {
flags));
}
+static Janet c_fileno(int32_t argc, Janet *argv) {
+ janet_fixarity(argc, 1);
+ return janet_wrap_integer(
+ fileno(((JanetFile *)janet_unwrap_abstract(argv[0]))->file));
+}
+
+static Janet c_fstat(int32_t argc, Janet *argv) {
+ janet_arity(argc, 1, 2);
+
+ struct stat st;
+ if (fstat(janet_unwrap_integer(argv[0]), &st) == -1)
+ return janet_wrap_nil();
+
+ const char *key = argc == 2 ? janet_unwrap_string(argv[1]) : NULL;
+
+ return stat2table(&st, key);
+}
+
+static int O_(char name) {
+ switch (name) {
+ case '+':
+ return O_APPEND;
+ case 'c':
+ return O_CREAT;
+ case 'x':
+ return O_EXCL;
+ case '-':
+ return O_TRUNC;
+ default:
+ return 0;
+ }
+}
+
+static int parseO(const char *opts) {
+ int flags = 0;
+ bool read = false;
+ bool write = false;
+ for (int i = 0; opts[i]; i++) {
+ if (opts[i] == 'w')
+ write = true;
+ else if (opts[i] == 'r')
+ read = true;
+ else
+ flags |= O_(opts[i]);
+ }
+
+ if (read && write)
+ flags |= O_RDWR;
+ else if (write)
+ flags |= O_WRONLY;
+ else if (read)
+ flags |= O_RDONLY;
+
+ return flags;
+}
+
+static Janet c_open(int argc, Janet *argv) {
+ janet_arity(argc, 1, 3);
+
+ int flags = argc > 1 ? parseO(janet_unwrap_string(argv[1])) : 0;
+ jmode_t mode = argc > 2 ? os_getmode(argv, 2) : 644;
+
+ return janet_wrap_integer(open(janet_unwrap_string(argv[0]), flags, mode));
+}
+
+static Janet c_close(int argc, Janet *argv) {
+ janet_fixarity(argc, 1);
+
+ return janet_wrap_integer(close(janet_unwrap_integer(argv[0])));
+}
+
+static Janet c_strerror(int argc, Janet *argv) {
+ janet_fixarity(argc, 0);
+
+ return janet_cstringv(strerror(errno));
+}
+
static JanetReg cfuns[] = {
{"nftw", c_nftw, "(nftw path callback fd_limit & flags)\n\nruns nftw"},
+ {"fileno", c_fileno, "(fileno file)\n\nreturns fd"},
+ {"fstat", c_fstat, "(fstat fd &opt key)\n\nreturns stats from fd"},
+ {"open", c_open, "(open path &opt flags mode)\n\nreturns fd"},
+ {"close", c_close, "(close fd)\n\ncloses fd"},
+ {"strerror", c_strerror, "(strerror)\n\nprint errno error string"},
{NULL, NULL, NULL}};
JANET_MODULE_ENTRY(JanetTable *env) { janet_cfuns(env, "nftw", cfuns); }
diff --git a/src/native/os.c b/src/native/os.c
@@ -1,128 +1,149 @@
#include "os.h"
-static int32_t janet_perm_to_unix(mode_t m) {
- return (int32_t) m;
-}
+static int32_t janet_perm_to_unix(mode_t m) { return (int32_t)m; }
-static mode_t janet_perm_from_unix(int32_t x) {
- return (mode_t) x;
-}
+static mode_t janet_perm_from_unix(int32_t x) { return (mode_t)x; }
static const uint8_t *janet_decode_mode(mode_t m) {
- const char *str = "other";
- if (S_ISREG(m)) str = "file";
- else if (S_ISDIR(m)) str = "directory";
- else if (S_ISFIFO(m)) str = "fifo";
- else if (S_ISBLK(m)) str = "block";
- else if (S_ISSOCK(m)) str = "socket";
- else if (S_ISLNK(m)) str = "link";
- else if (S_ISCHR(m)) str = "character";
- return janet_ckeyword(str);
+ const char *str = "other";
+ if (S_ISREG(m))
+ str = "file";
+ else if (S_ISDIR(m))
+ str = "directory";
+ else if (S_ISFIFO(m))
+ str = "fifo";
+ else if (S_ISBLK(m))
+ str = "block";
+ else if (S_ISSOCK(m))
+ str = "socket";
+ else if (S_ISLNK(m))
+ str = "link";
+ else if (S_ISCHR(m))
+ str = "character";
+ return janet_ckeyword(str);
}
static int32_t janet_decode_permissions(jmode_t mode) {
- return (int32_t)(mode & 0777);
+ return (int32_t)(mode & 0777);
}
static int32_t os_parse_permstring(const uint8_t *perm) {
- int32_t m = 0;
- if (perm[0] == 'r') m |= 0400;
- if (perm[1] == 'w') m |= 0200;
- if (perm[2] == 'x') m |= 0100;
- if (perm[3] == 'r') m |= 0040;
- if (perm[4] == 'w') m |= 0020;
- if (perm[5] == 'x') m |= 0010;
- if (perm[6] == 'r') m |= 0004;
- if (perm[7] == 'w') m |= 0002;
- if (perm[8] == 'x') m |= 0001;
- return m;
+ int32_t m = 0;
+ if (perm[0] == 'r')
+ m |= 0400;
+ if (perm[1] == 'w')
+ m |= 0200;
+ if (perm[2] == 'x')
+ m |= 0100;
+ if (perm[3] == 'r')
+ m |= 0040;
+ if (perm[4] == 'w')
+ m |= 0020;
+ if (perm[5] == 'x')
+ m |= 0010;
+ if (perm[6] == 'r')
+ m |= 0004;
+ if (perm[7] == 'w')
+ m |= 0002;
+ if (perm[8] == 'x')
+ m |= 0001;
+ return m;
}
static Janet os_make_permstring(int32_t permissions) {
- uint8_t bytes[9] = {0};
- bytes[0] = (permissions & 0400) ? 'r' : '-';
- bytes[1] = (permissions & 0200) ? 'w' : '-';
- bytes[2] = (permissions & 0100) ? 'x' : '-';
- bytes[3] = (permissions & 0040) ? 'r' : '-';
- bytes[4] = (permissions & 0020) ? 'w' : '-';
- bytes[5] = (permissions & 0010) ? 'x' : '-';
- bytes[6] = (permissions & 0004) ? 'r' : '-';
- bytes[7] = (permissions & 0002) ? 'w' : '-';
- bytes[8] = (permissions & 0001) ? 'x' : '-';
- return janet_stringv(bytes, sizeof(bytes));
+ uint8_t bytes[9] = {0};
+ bytes[0] = (permissions & 0400) ? 'r' : '-';
+ bytes[1] = (permissions & 0200) ? 'w' : '-';
+ bytes[2] = (permissions & 0100) ? 'x' : '-';
+ bytes[3] = (permissions & 0040) ? 'r' : '-';
+ bytes[4] = (permissions & 0020) ? 'w' : '-';
+ bytes[5] = (permissions & 0010) ? 'x' : '-';
+ bytes[6] = (permissions & 0004) ? 'r' : '-';
+ bytes[7] = (permissions & 0002) ? 'w' : '-';
+ bytes[8] = (permissions & 0001) ? 'x' : '-';
+ return janet_stringv(bytes, sizeof(bytes));
}
static int32_t os_get_unix_mode(const Janet *argv, int32_t n) {
- int32_t unix_mode;
- if (janet_checkint(argv[n])) {
- /* Integer mode */
- int32_t x = janet_unwrap_integer(argv[n]);
- if (x < 0 || x > 0777) {
- janet_panicf("bad slot #%d, expected integer in range [0, 8r777], got %v", n, argv[n]);
- }
- unix_mode = x;
- } else {
- /* Bytes mode */
- JanetByteView bytes = janet_getbytes(argv, n);
- if (bytes.len != 9) {
- janet_panicf("bad slot #%d: expected byte sequence of length 9, got %v", n, argv[n]);
- }
- unix_mode = os_parse_permstring(bytes.bytes);
+ int32_t unix_mode;
+ if (janet_checkint(argv[n])) {
+ /* Integer mode */
+ int32_t x = janet_unwrap_integer(argv[n]);
+ if (x < 0 || x > 0777) {
+ janet_panicf("bad slot #%d, expected integer in range [0, 8r777], got %v",
+ n, argv[n]);
}
- return unix_mode;
+ unix_mode = x;
+ } else {
+ /* Bytes mode */
+ JanetByteView bytes = janet_getbytes(argv, n);
+ if (bytes.len != 9) {
+ janet_panicf("bad slot #%d: expected byte sequence of length 9, got %v",
+ n, argv[n]);
+ }
+ unix_mode = os_parse_permstring(bytes.bytes);
+ }
+ return unix_mode;
}
-static jmode_t os_getmode(const Janet *argv, int32_t n) {
- return janet_perm_from_unix(os_get_unix_mode(argv, n));
+jmode_t os_getmode(const Janet *argv, int32_t n) {
+ return janet_perm_from_unix(os_get_unix_mode(argv, n));
}
/* Getters */
static Janet os_stat_dev(const jstat_t *st) {
- return janet_wrap_number(st->st_dev);
+ return janet_wrap_number(st->st_dev);
}
static Janet os_stat_inode(const jstat_t *st) {
- return janet_wrap_number(st->st_ino);
+ return janet_wrap_number(st->st_ino);
}
static Janet os_stat_mode(const jstat_t *st) {
- return janet_wrap_keyword(janet_decode_mode(st->st_mode));
+ return janet_wrap_keyword(janet_decode_mode(st->st_mode));
}
static Janet os_stat_int_permissions(const jstat_t *st) {
- return janet_wrap_integer(janet_perm_to_unix(janet_decode_permissions(st->st_mode)));
+ return janet_wrap_integer(
+ janet_perm_to_unix(janet_decode_permissions(st->st_mode)));
}
static Janet os_stat_permissions(const jstat_t *st) {
- return os_make_permstring(janet_perm_to_unix(janet_decode_permissions(st->st_mode)));
+ return os_make_permstring(
+ janet_perm_to_unix(janet_decode_permissions(st->st_mode)));
}
static Janet os_stat_uid(const jstat_t *st) {
- return janet_wrap_number(st->st_uid);
+ return janet_wrap_number(st->st_uid);
}
static Janet os_stat_gid(const jstat_t *st) {
- return janet_wrap_number(st->st_gid);
+ return janet_wrap_number(st->st_gid);
}
static Janet os_stat_nlink(const jstat_t *st) {
- return janet_wrap_number(st->st_nlink);
+ return janet_wrap_number(st->st_nlink);
}
static Janet os_stat_rdev(const jstat_t *st) {
- return janet_wrap_number(st->st_rdev);
+ return janet_wrap_number(st->st_rdev);
}
static Janet os_stat_size(const jstat_t *st) {
- return janet_wrap_number(st->st_size);
+ return janet_wrap_number(st->st_size);
}
static Janet os_stat_accessed(const jstat_t *st) {
- return janet_wrap_number((double) st->st_atime);
+ return janet_wrap_number((double)st->st_atime);
}
static Janet os_stat_modified(const jstat_t *st) {
- return janet_wrap_number((double) st->st_mtime);
+ return janet_wrap_number((double)st->st_mtime);
}
static Janet os_stat_changed(const jstat_t *st) {
- return janet_wrap_number((double) st->st_ctime);
+ return janet_wrap_number((double)st->st_ctime);
}
static Janet os_stat_blocks(const jstat_t *st) {
- return janet_wrap_number(st->st_blocks);
+ return janet_wrap_number(st->st_blocks);
}
static Janet os_stat_blocksize(const jstat_t *st) {
- return janet_wrap_number(st->st_blksize);
+ return janet_wrap_number(st->st_blksize);
}
+struct OsStatGetter {
+ const char *name;
+ Janet (*fn)(const jstat_t *st);
+};
+
const struct OsStatGetter os_stat_getters[] = {
{"dev", os_stat_dev},
{"inode", os_stat_inode},
@@ -139,5 +160,30 @@ const struct OsStatGetter os_stat_getters[] = {
{"accessed", os_stat_accessed},
{"modified", os_stat_modified},
{"changed", os_stat_changed},
- {NULL, NULL}
-};
+ {NULL, NULL}};
+
+Janet stat2table(const jstat_t *st, const uint8_t *key) {
+
+ if (NULL == st)
+ return janet_wrap_nil();
+
+ JanetTable *tab = janet_table(0);
+
+ if (NULL == key) {
+ /* Put results in table */
+ for (const struct OsStatGetter *sg = os_stat_getters; sg->name != NULL;
+ sg++) {
+ janet_table_put(tab, janet_ckeywordv(sg->name), sg->fn(st));
+ }
+ return janet_wrap_table(tab);
+ } else {
+ /* Get one result */
+ for (const struct OsStatGetter *sg = os_stat_getters; sg->name != NULL;
+ sg++) {
+ if (janet_cstrcmp(key, sg->name))
+ continue;
+ return sg->fn(st);
+ }
+ janet_panicf("unexpected keyword %v", janet_wrap_keyword(key));
+ }
+}
diff --git a/src/native/os.h b/src/native/os.h
@@ -12,9 +12,5 @@
typedef struct stat jstat_t;
typedef mode_t jmode_t;
-struct OsStatGetter {
- const char *name;
- Janet(*fn)(const jstat_t *st);
-};
-
-extern const struct OsStatGetter os_stat_getters[];
+Janet stat2table(const jstat_t *st, const uint8_t *key);
+jmode_t os_getmode(const Janet *argv, int32_t n);