diff --git a/.gitignore b/.gitignore index b2be92b..c4162a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ result +.*.~undo-tree~ +*~ diff --git a/README.md b/README.md new file mode 100644 index 0000000..72a85c8 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +## garage + +Welcome to my garage. Feel free to look around and take as you please, +all of it is free under the public domain. diff --git a/oneliners/git-reauthor-branch b/oneliners/git-reauthor-branch new file mode 100644 index 0000000..08f9c73 --- /dev/null +++ b/oneliners/git-reauthor-branch @@ -0,0 +1,4 @@ +# Change the author and committer of every commit on a branch. +# Any mismatching committer dates will be adjusted to match the +# commit's author date. +EDITOR=true git rebase --committer-date-is-author-date -i --root "$(git rev-parse --abbrev-ref HEAD)" -x 'GIT_COMMITTER_DATE="$(git show -s --format=%ci)" git commit --amend --author "John Doe " -CHEAD' diff --git a/programs/carmel/.gitignore b/programs/carmel/.gitignore new file mode 100644 index 0000000..42730d6 --- /dev/null +++ b/programs/carmel/.gitignore @@ -0,0 +1,3 @@ +*.o +*.tar.gz +carmel-build \ No newline at end of file diff --git a/programs/carmel/Makefile b/programs/carmel/Makefile new file mode 100644 index 0000000..0bace63 --- /dev/null +++ b/programs/carmel/Makefile @@ -0,0 +1,20 @@ +CC = cc +CFLAGS = -std=c99 -pedantic -Wall -Wextra \ + $(shell pkg-config --cflags iniparser) +LDFLAGS = -liniparser +OBJS = carmel-build.o util.o + +all: carmel-build + +carmel-build: ${OBJS} + cc ${CFLAGS} -o $@ ${OBJS} ${LDFLAGS} + +.SUFFIXES: .c .o + +.c.o: + ${CC} ${CFLAGS} -c $< + +clean: + rm -f carmel-build ${OBJS} + +.PHONY: all clean diff --git a/programs/carmel/carmel-build.c b/programs/carmel/carmel-build.c new file mode 100644 index 0000000..e6e47d4 --- /dev/null +++ b/programs/carmel/carmel-build.c @@ -0,0 +1,248 @@ +/* + * carmel-build + * Copyright (c) 2024 Jeremy Baxter. + */ + +#define _DEFAULT_SOURCE + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "util.h" + +typedef struct { + char *name; + char *description; + char *version; + char *builder; +} manifest; + +static const char *buildBase = "/tmp/carmel-"; +static const char *defaultOutputFile = "auto"; +static const char *defaultPackageName = "void"; +static const char *defaultPackageVersion = "0"; +static const char *defaultPackageBuilder = "builder.sh"; +static bool oflag, vflag; + +#define WARNV(...) if (vflag) warnx(__VA_ARGS__) + +void +builder(const char *script) +{ + FILE *proc; + ssize_t ret; + int fd; + char ch[1]; + + proc = popen("sh", "w"); + fd = open(script, O_RDONLY); + + if (!proc) + err(1, "cannot execute shell"); + if (fd == -1) + err(1, "cannot open %s", script); + + while ((ret = read(fd, &ch, 1)) > 0) { + fwrite(ch, sizeof(char), 1, proc); + } + + if (ret == -1) + err(1, "cannot read from %s", script); + + pclose(proc); +} + +#define KEY(name, def) strdup(iniparser_getstring(ini, (":" name), (def))) + +manifest +makeManifest(const char *path) +{ + dictionary *ini; + manifest m; + + ini = iniparser_load(path); + m.name = KEY("name", defaultPackageName); + m.description = KEY("description", ""); + m.version = KEY("version", defaultPackageVersion); + m.builder = KEY("builder", defaultPackageBuilder); + iniparser_freedict(ini); + + return m; +} + +#undef KEY + +void +freeManifest(manifest *m) +{ + free(m->name); + free(m->description); + free(m->version); + free(m->builder); +} + +void +makeDirectory(const char *path) +{ + if (mkdir(path, 0710) == -1) + err(1, "cannot create directory '%s'", path); +} + +void +sh(const char *command) +{ + int ret; + + ret = system(command); + if (ret == -1) + err(1, "system"); + if (WEXITSTATUS(ret) != 0) + errx(1, "command failed with exit code %d: %s", ret, command); +} + +char * +workingDirectory(void) +{ + char *buffer; + size_t len; + + len = 64; + do { + buffer = malloc(len * sizeof(char)); + len *= 2; + } while (getcwd(buffer, len) == NULL); + + return buffer; +} + +void +writeTo(const char *path, const char *contents) +{ + int fd, i; + + fd = open(path, O_WRONLY | O_CREAT, 0644); + for (i = 0; contents[i] != 0; i++) + write(fd, (char[]){contents[i]}, 1); +} + +int +main(int argc, char **argv) +{ + manifest m; + char *buildDirectory; + char *outputFile; + char *startDirectory; + size_t buildDirectoryLen, outputFileLen; + char ch; + + outputFileLen = strlen(defaultOutputFile) + 1; + outputFile = malloc(outputFileLen * sizeof(char)); + strbcpy(outputFile, defaultOutputFile, outputFileLen); + + while ((ch = getopt(argc, argv, "C:o:v")) != -1) { + switch (ch) { + case 'C': + changeDirectory(optarg); + break; + case 'o': + oflag = 1; + outputFileLen = strlen(optarg) + 1; + outputFile = malloc(outputFileLen * sizeof(char)); + strbcpy(outputFile, optarg, outputFileLen); + break; + case 'v': + vflag = 1; + break; + } + } + + if (argc - optind != 1) { + fputs("usage: carmel-build [-v] [-C directory] [-o output] manifest\n", stderr); + return 1; + } + + startDirectory = workingDirectory(); + + if (!exists(argv[optind])) + errx(1, "cannot open %s", argv[optind]); + m = makeManifest(argv[optind]); + + if (strcmp(m.name, defaultPackageName) == 0) + warnx("manifest name not provided; using '%s'", m.name); + if (strcmp(m.version, defaultPackageVersion) == 0) + warnx("manifest version not provided; using '%s'", m.version); + if (strlen(m.builder) == 0) + errx(1, "manifest builder not provided; using '%s'", m.builder); + + if (strcmp(outputFile, defaultOutputFile) == 0) { + char *extension = ".tar.gz"; + outputFileLen = strlen(m.name) + 1 + strlen(m.version) + + strlen(extension) + 1; + outputFile = malloc(outputFileLen * sizeof(char)); + snprintf(outputFile, outputFileLen, "%s-%s%s", + m.name, m.version, extension); + } + WARNV("building %s", outputFile); + + /* PIDs up to 7 digits */ + buildDirectoryLen = strlen(buildBase) + 8; + buildDirectory = malloc(buildDirectoryLen * sizeof(char)); + snprintf(buildDirectory, buildDirectoryLen, "%s%d", + buildBase, getpid()); + + WARNV("entering build origin"); + makeDirectory(buildDirectory); + changeDirectory(buildDirectory); + makeDirectory("origin"); + changeDirectory("origin"); + + { + char *buffer; + size_t len; + + len = strlen(startDirectory) + 1 + strlen(m.builder) + 1; + buffer = malloc(len * sizeof(char)); + snprintf(buffer, len, "%s/%s", startDirectory, m.builder); + copyFile(buffer, "builder.sh"); + } + + WARNV("executing builder"); + setenv("out", buildDirectory, 1); + builder("builder.sh"); + + changeDirectory(".."); + sh("rm -fr origin/"); + + WARNV("writing to carmel-meta"); + makeDirectory("carmel-meta"); + changeDirectory("carmel-meta"); + writeTo("name", m.name); + writeTo("description", m.description); + writeTo("version", m.version); + + WARNV("making tarball"); + setenv("out", outputFile, 1); + setenv("start", startDirectory, 1); + setenv("build", buildDirectory, 1); + changeDirectory(buildDirectory); + sh("tar -czf \"$out\" *"); + sh("mv \"$out\" \"$start\"/"); + changeDirectory(startDirectory); + sh("rm -fr \"$build\""); + + freeManifest(&m); + free(outputFile); + free(startDirectory); + + return 0; +} diff --git a/programs/carmel/packages/callisto/builder.sh b/programs/carmel/packages/callisto/builder.sh new file mode 100644 index 0000000..3ca3080 --- /dev/null +++ b/programs/carmel/packages/callisto/builder.sh @@ -0,0 +1,5 @@ +curl -L https://git.sr.ht/~jeremy/callisto/archive/master.tar.gz | tar -xz +cd callisto-master +./configure -wreadline +make +make install PREFIX=/usr/local DESTDIR=$out diff --git a/programs/carmel/packages/callisto/manifest.ini b/programs/carmel/packages/callisto/manifest.ini new file mode 100644 index 0000000..98ecd6e --- /dev/null +++ b/programs/carmel/packages/callisto/manifest.ini @@ -0,0 +1,3 @@ +name = callisto +version = 0.1-master +description = runtime environment for Lua under POSIX \ No newline at end of file diff --git a/programs/carmel/util.c b/programs/carmel/util.c new file mode 100644 index 0000000..d8da3e3 --- /dev/null +++ b/programs/carmel/util.c @@ -0,0 +1,76 @@ +#include +#include +#include +#include + +#include "util.h" + +void +copyFile(const char *source, const char *dest) +{ + char buffer[1024]; + ssize_t ret; + int s, d; + + if ((s = open(source, O_RDONLY)) == -1) + err(1, "cannot open %s", source); + if ((d = open(dest, O_WRONLY | O_CREAT, 0644)) == -1) + err(1, "cannot open %s", dest); + + while ((ret = read(s, &buffer, 1024)) > 0) { + write(d, buffer, ret); + } + + if (ret == -1) + err(1, "cannot read from %s", source); + + close(s); + close(d); +} + +void +changeDirectory(const char *path) +{ + if (chdir(path) == -1) + err(1, "cd '%s'", path); +} + +bool +exists(const char *path) +{ + if (open(path, O_RDONLY) == -1) { + if (errno == ENOENT) + return 0; + } + return 1; +} + +/* + * Copy string src to buffer dst of size dsize. At most dsize-1 + * chars will be copied. Always NUL terminates (unless dsize == 0). + * Returns strlen(src); if retval >= dsize, truncation occurred. + */ +size_t +strbcpy(char *dst, const char *src, size_t dsize) +{ + const char *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; /* NUL-terminate dst */ + while (*src++) + ; + } + + return (src - osrc - 1); /* count does not include NUL */ +} diff --git a/programs/carmel/util.h b/programs/carmel/util.h new file mode 100644 index 0000000..ac1d2a1 --- /dev/null +++ b/programs/carmel/util.h @@ -0,0 +1,14 @@ +#ifndef _UTIL_H_ +#define _UTIL_H_ + +#include + +typedef char byte; +typedef byte bool; + +void copyFile(const char *, const char *); +void changeDirectory(const char *); +bool exists(const char *); +size_t strbcpy(char *, const char *, size_t); + +#endif diff --git a/scripts/make-release.sh b/scripts/make-release.sh new file mode 100755 index 0000000..87d777f --- /dev/null +++ b/scripts/make-release.sh @@ -0,0 +1,24 @@ +#!/bin/sh +# usage: make-release.sh release +# Make a tarball of software excluding git history and leftover files + +[ -z "$1" ] && printf 'usage: make-release.sh version\n' && exit 1 + +name="$(basename "$(pwd)")" +cd ../ +cp -R "$name" "$1" +cd "$1"/ +rm -fr .git .build.yml .builds + +IFS="\n" +if [ -e .gitignore ]; then + for file in $(cat .gitignore); do + rm -fr "$file" + done +fi + +cd ../ + +tar -czf "$1".tar.gz "$1" +rm -r "$1" +cd "$name"/ diff --git a/snippets/dmath/README.md b/snippets/dmath/README.md new file mode 100644 index 0000000..37334eb --- /dev/null +++ b/snippets/dmath/README.md @@ -0,0 +1,48 @@ +D programs that calculate various mathematical values + +## factors.d - calculate the factors of a number + +To work out the factors of 31: + + ldc2 -run factors.d 31 + +## isfactor.d - determine whether a number is a factor of another + +To determine whether 8 is a factor of 32: + + ldc2 -run isfactor.d 8 32 + +## hcf.d - calculate the highest common factor of two numbers + +To work out the HCF of 12 and 28: + + ldc2 -run hcf.d 12 28 + +To keep it simple this program will write out a list of factors +for each number side-by-side. + +## lcm.d - calculate the lowest common multiple of two numbers + +To work out the LCM of 28 and 42: + + ldc2 -run lcm.d 28 42 + +To keep it simple this program will write out a list of multiples +for each number side-by-side. + +## primes.d - generate a list of prime numbers up to a maximum value + +To generate a list of primes up to 100: + + ldc2 -run primes.d + +If an argument is supplied, the program will calculate primes up to that number: + + ldc2 -run primes.d 10 + +(outputs 2, 3, 5, and 7) +Two arguments can be supplied to specify a minimum as well: + + ldc2 -run primes.d 1 20 + +(outputs primes from 1 to 20) diff --git a/snippets/dmath/factors.d b/snippets/dmath/factors.d new file mode 100644 index 0000000..7b87e72 --- /dev/null +++ b/snippets/dmath/factors.d @@ -0,0 +1,33 @@ +import std.conv : to; +import std.stdio : stderr, write, writeln; + +int +main(string[] args) +{ + int n; + + if (args.length == 1) { + stderr.writeln("usage: factors.d number"); + return 1; + } + + n = args[1].to!int(); + foreach (int factor; factorsOf(n)) { + writeln(factor); + } + + return 0; +} + +int[] +factorsOf(int x) +{ + int[] a; + + foreach (int i; 1 .. x + 1) { + if (x % i == 0) + a ~= i; + } + + return a; +} diff --git a/snippets/dmath/hcf.d b/snippets/dmath/hcf.d new file mode 100644 index 0000000..f9946f8 --- /dev/null +++ b/snippets/dmath/hcf.d @@ -0,0 +1,36 @@ +import std.conv : to; +import std.stdio : write, writeln; + +void +main(string[] args) +{ + int i, x, y; + int[] xf, yf; + + x = args[1].to!int(); + y = args[2].to!int(); + + xf = factorsOf(x); + yf = factorsOf(y); + for (i = 0; xf.length > i || yf.length > i; i++) { + if (i < xf.length) + write(xf[i]); + write(" "); + if (i < yf.length) + write(yf[i]); + writeln(); + } +} + +int[] +factorsOf(int x) +{ + int[] a; + + foreach (int i; 1 .. x + 1) { + if (x % i == 0) + a ~= i; + } + + return a; +} diff --git a/snippets/dmath/isfactor.d b/snippets/dmath/isfactor.d new file mode 100644 index 0000000..f95eba8 --- /dev/null +++ b/snippets/dmath/isfactor.d @@ -0,0 +1,29 @@ +import std.conv : to; +import std.stdio : stderr, writeln; + +int +main(string[] args) +{ + int f, i, n; + + if (args.length < 3) { + stderr.writeln("usage: isfactor.d fac num"); + return 1; + } + + f = args[1].to!int(); + n = args[2].to!int(); + + i = f; + while (i < n) { + i += f; + } + + if (i == n) { + writeln(f, " is a factor of ", n); + return 0; + } + + writeln(f, " is NOT a factor of ", n); + return 1; +} diff --git a/snippets/dmath/lcm.d b/snippets/dmath/lcm.d new file mode 100644 index 0000000..7d48ec0 --- /dev/null +++ b/snippets/dmath/lcm.d @@ -0,0 +1,15 @@ +import std.conv : to; +import std.stdio : writeln; + +void +main(string[] args) +{ + int x, y; + + x = args[1].to!int(); + y = args[2].to!int(); + + foreach (int i; 1 .. 12) { + writeln(x * i, " ", y * i); + } +} diff --git a/snippets/dmath/primes.d b/snippets/dmath/primes.d new file mode 100644 index 0000000..5858a22 --- /dev/null +++ b/snippets/dmath/primes.d @@ -0,0 +1,37 @@ +import std.conv : to; +import std.stdio : write, writeln; + +void +main(string[] args) +{ + int min, max; + + min = 1; + max = 100; + + if (args.length == 2) { + max = args[1].to!int(); + } + if (args.length == 3) { + min = args[1].to!int(); + max = args[2].to!int(); + } + + foreach (int i; min .. max + 1) { + if (factorsOf(i).length == 2) + writeln(i); + } +} + +int[] +factorsOf(int x) +{ + int[] a; + + foreach (int i; 1 .. x + 1) { + if (x % i == 0) + a ~= i; + } + + return a; +} diff --git a/snippets/dsafe/pledge.d b/snippets/dsafe/pledge.d new file mode 100644 index 0000000..3e4d832 --- /dev/null +++ b/snippets/dsafe/pledge.d @@ -0,0 +1,20 @@ +/* + * Snippet of code showing how to call OpenBSD's + * pledge() syscall in safe D (or not!) + */ + +int +main(string[] args) @safe +{ + /* if you're not using @safe you can + * remove this ugly lambda thing */ + version (OpenBSD) () @trusted { + import core.sys.openbsd.unistd : pledge; + import std.string : toStringz; + + immutable(char) *promises; + + promises = toStringz("stdio rpath wpath cpath ..."); + pledge(promises, null); + }(); +} diff --git a/snippets/dsafe/stderr.d b/snippets/dsafe/stderr.d new file mode 100644 index 0000000..2a19dfe --- /dev/null +++ b/snippets/dsafe/stderr.d @@ -0,0 +1,11 @@ +/* + * Opening stderr in safe D + */ + +import std.stdio : File; + +File +stderr() @safe +{ + return File("/dev/stderr", "w"); +} diff --git a/snippets/lispbox/guess.lisp b/snippets/lispbox/guess.lisp new file mode 100644 index 0000000..d764a10 --- /dev/null +++ b/snippets/lispbox/guess.lisp @@ -0,0 +1,49 @@ +;; Simple number guessing game in Common Lisp + +(defmacro while (condition &body body) + "While `condition' is not nil, evaluate `body'." + `(loop while ,condition do (progn ,@body))) + +(defun make-random (max) + "Generate a random number up to `max' using a new random state." + (let ((*random-state* (make-random-state t))) + (random max))) + +(defun read-integer () + (parse-integer (read-line *query-io*) :junk-allowed t)) + +(defun attempt-guess (number) + "Run one round of a number guessing game where `number' +is the number the user is trying to guess." + (let ((guess nil) + (incorrect t)) + (loop do + (format *query-io* "Enter a number: ") + (force-output *query-io*) + (setf guess (read-integer)) + (format t + (cond + ((< guess number) "The number is larger than ~D~%") + ((> guess number) "The number is smaller than ~D~%") + (t "Correct! The number was ~D~%")) + guess) + (when (= guess number) + (setf incorrect nil)) + while incorrect)) + number) + +(defun begin-number-guessing-game (&key one-shot (maximum 100)) + "If `one-shot' is t, run one round of a number guessing game. +Otherwise run a chain of games until the user wants to stop. + +In both cases, `:maximum' is the maximum possible number." + (when one-shot + (return-from begin-number-guessing-game + (attempt-guess (make-random maximum)))) + + (let ((playing nil)) + (loop do + (attempt-guess (make-random maximum)) + (setf playing (y-or-n-p "Play again?")) + (format t "~%") + while playing))) diff --git a/nixctor/README.md b/snippets/nixctor/README.md similarity index 100% rename from nixctor/README.md rename to snippets/nixctor/README.md diff --git a/nixctor/ctor.nix b/snippets/nixctor/ctor.nix similarity index 100% rename from nixctor/ctor.nix rename to snippets/nixctor/ctor.nix diff --git a/nixctor/default.nix b/snippets/nixctor/default.nix similarity index 100% rename from nixctor/default.nix rename to snippets/nixctor/default.nix diff --git a/nixctor/lib.c b/snippets/nixctor/lib.c similarity index 100% rename from nixctor/lib.c rename to snippets/nixctor/lib.c diff --git a/nixctor/main.c b/snippets/nixctor/main.c similarity index 100% rename from nixctor/main.c rename to snippets/nixctor/main.c