shithub: moonfish

Download patch

ref: 7330e1b9caab783e434e6336dede10d92a34e2e4
parent: 42d06e8de474fe7b0eccb34700dea696faef9eca
author: zamfofex <zamfofex@twdb.moe>
date: Wed Oct 9 02:06:43 EDT 2024

simplify various things

--- a/.build.yml
+++ b/.build.yml
@@ -2,29 +2,25 @@
 # copyright 2023, 2024 zamfofex
 
 image: alpine/latest
+
 secrets:
   - 72a028fc-f8df-43d3-a315-305d80720e45
+
 packages:
   - build-base
   - libressl-dev
-  - xz
-  - mingw-w64-gcc
   - curl
   - cjson-dev
-  - wasi-sdk
-  - clang
-  - lld
-  - binaryen
+
 tasks:
   - build: |
       cd moonfish
       make LDFLAGS=-static
-      make LDFLAGS=-static CC=x86_64-w64-mingw32-gcc moonfish.exe
-      make LDFLAGS=-static CC='clang --target=wasm32-wasi' CPPFLAGS='-Dmoonfish_no_threads' moonfish.wasm
+  
   - strip: |
       cd moonfish
       strip --strip-all moonfish play lichess analyse chat
-      wasm-opt -O4 -o moonfish.wasm moonfish.wasm
+  
   - deploy: |
       set +x
       
@@ -44,6 +40,4 @@
         -F "$root"/lichess=@lichess \
         -F "$root"/analyse=@analyse \
         -F "$root"/chat=@chat \
-        -F "$root"/moonfish.exe=@moonfish.exe \
-        -F "$root"/moonfish.wasm=@moonfish.wasm \
         https://neocities.org/api/upload
--- a/.gitignore
+++ b/.gitignore
@@ -7,7 +7,6 @@
 !/README.md
 !/makefile
 !/.build.yml
-!/check.sh
 !/moonfish.vcxproj
 !/moonfish.h
 !/chess.c
--- a/check.sh
+++ /dev/null
@@ -1,53 +1,0 @@
-#!/usr/bin/env bash
-
-# moonfish is licensed under the AGPL (v3 or later)
-# copyright 2024 zamfofex
-
-set -e
-
-positions=("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -" "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -" "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1" "r2q1rk1/pP1p2pp/Q4n2/bbp1p3/Np6/1B3NBn/pPPP1PPP/R3K2R b KQ - 0 1" "rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8" "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10")
-
-diff - <(
-	echo 'checking starting position' >&2
-	for n in {1..5}
-	do ./perft -N"$n"
-	done
-	
-	for f in "${positions[@]}"
-	do
-		echo "checking '$f'" >&2
-		for n in {1..4}
-		do ./perft -N"$n" -F"$f"
-		done
-	done
-) <<END
-perft 1: 20
-perft 2: 400
-perft 3: 8902
-perft 4: 197281
-perft 5: 4865609
-perft 1: 48
-perft 2: 2039
-perft 3: 97862
-perft 4: 4085603
-perft 1: 14
-perft 2: 191
-perft 3: 2812
-perft 4: 43238
-perft 1: 6
-perft 2: 264
-perft 3: 9467
-perft 4: 422333
-perft 1: 6
-perft 2: 264
-perft 3: 9467
-perft 4: 422333
-perft 1: 44
-perft 2: 1486
-perft 3: 62379
-perft 4: 2103487
-perft 1: 46
-perft 2: 2079
-perft 3: 89890
-perft 4: 3894594
-END
--- a/makefile
+++ b/makefile
@@ -4,39 +4,26 @@
 CFLAGS ?= -ansi -O3 -Wall -Wextra -Wpedantic
 PREFIX ?= /usr/local
 BINDIR ?= $(PREFIX)/bin
+RM ?= rm -f
 
 cc := $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS)
 
-moonfish_cc := $(cc) -pthread -D_POSIX_C_SOURCE=199309L
-tools_cc := $(cc) -pthread -D_POSIX_C_SOURCE=200809L
-wasm_cc := $(cc) -D_POSIX_C_SOURCE=199309L
+.PHONY: all clean install uninstall
 
-tools_src := moonfish.h tools/tools.h tools/utils.c chess.c
-
-.PHONY: all clean install uninstall check
-
 all: moonfish play lichess analyse chat
 
-moonfish moonfish.exe: moonfish.h chess.c search.c main.c
-	$(moonfish_cc) -o $@ $(filter %.c,$^)
+moonfish: moonfish.h chess.c search.c main.c
+	$(cc) $(filter %.c,$^) -o $@ -pthread -D_POSIX_C_SOURCE=199309L
 
-moonfish.wasm: moonfish.h chess.c search.c main.c
-	$(wasm_cc) -o $@ $(filter %.c,$^)
+%: moonfish.h tools/tools.h tools/utils.c chess.c tools/%.c
+	$(cc) $(filter %.c,$^) -o $@ $(cflags) -D_POSIX_C_SOURCE=200809L
 
-%: $(tools_src) tools/%.c
-	$(tools_cc) -o $@ $(filter %.c,$^)
-
-lichess: $(tools_src) tools/lichess.c tools/https.c
-	$(tools_cc) -o $@ $(filter %.c,$^) -ltls -lssl -lcrypto -lcjson
-
-chat: $(tools_src) tools/chat.c tools/https.c
-	$(tools_cc) -o $@ $(filter %.c,$^) -ltls -lssl -lcrypto
-
-learn: $(tools_src) search.c tools/learn.c
-	$(tools_cc) -Dmoonfish_learn -o $@ $(filter %.c,$^)
-
-check: perft
-	./check.sh
+play analyse: cflags := -pthread
+lichess chat: tools/https.c
+lichess: cflags := -ltls -lssl -lcrypto -lcjson
+chat: cflags := -ltls -lssl -lcrypto
+learn: search.c
+learn: cflags := -Dmoonfish_no_threads -Dmoonfish_learn
 
 clean:
 	git clean -fdx
--- a/tools/analyse.c
+++ b/tools/analyse.c
@@ -319,6 +319,7 @@
 	char *buffer;
 	unsigned int i, length;
 	int changed;
+	int pv;
 	
 	fancy = data;
 	
@@ -353,6 +354,8 @@
 		ply = fancy->plies[fancy->i];
 		ply.depth = 0;
 		
+		pv = 0;
+		
 		for (;;)
 		{
 			arg = strtok_r(NULL, "\r\n\t ", &buffer);
@@ -376,11 +379,25 @@
 				continue;
 			}
 			
+			if (!strcmp(arg, "multipv"))
+			{
+				arg = strtok_r(NULL, "\r\n\t ", &buffer);
+				if (arg == NULL || moonfish_int(arg, &pv) != 0 || pv <= 0)
+				{
+					fprintf(stderr, "%s: malformed 'multipv' in 'info' command\n", fancy->argv0);
+					exit(1);
+				}
+				
+				continue;
+			}
+			
 			if (!strcmp(arg, "pv"))
 			{
 				changed = 1;
 				fancy->idle = 0;
 				
+				if (pv < 1) pv = 1;
+				
 				i = 0;
 				while (i < sizeof fancy->pv - 1)
 				{
@@ -391,7 +408,16 @@
 						fprintf(stderr, "%s: invalid move: %s\n", fancy->argv0, arg);
 						exit(1);
 					}
-					if (i == 0) strcpy(ply.best, arg);
+					if (i == 0 && pv == 1)
+					{
+						strcpy(ply.best, arg);
+					}
+					if (pv > 1)
+					{
+						ply.chess = move.chess;
+						i = 1;
+						continue;
+					}
 					moonfish_to_san(&ply.chess, &move, san);
 					length = strlen(san);
 					if (i + length > sizeof fancy->pv - 2) break;
@@ -635,8 +661,6 @@
 	char **options;
 	int i;
 	
-	moonfish_spawner(argv[0]);
-	
 	/* handle command line arguments */
 	
 	command = moonfish_args(args, format, argc, argv);
@@ -647,18 +671,7 @@
 	for (;;)
 	{
 		value = strchr(*command, '=');
-		if (value == NULL)
-		{
-			if (!strcmp(*command, "--"))
-			{
-				command_count--;
-				command++;
-				
-				if (command_count <= 0) moonfish_usage(args, format, argv[0]);
-			}
-			
-			break;
-		}
+		if (value == NULL) break;
 		
 		if (strchr(*command, '\n') != NULL || strchr(*command, '\r') != NULL) moonfish_usage(args, format, argv[0]);
 		
@@ -668,6 +681,14 @@
 		if (command_count <= 0) moonfish_usage(args, format, argv[0]);
 	}
 	
+	if (!strcmp(*command, "--"))
+	{
+		command_count--;
+		command++;
+		
+		if (command_count <= 0) moonfish_usage(args, format, argv[0]);
+	}
+	
 	/* initialise data structures */
 	
 	fancy = malloc(sizeof *fancy);
@@ -698,6 +719,7 @@
 	fancy->plies[0].checkmate = 0;
 	fancy->plies[0].depth = 0;
 	fancy->plies[0].score = 0;
+	fancy->plies[0].best[0] = 0;
 	
 	moonfish_chess(&fancy->plies[0].chess);
 	if (args[0].value == NULL)
@@ -791,8 +813,15 @@
 	
 	/* main UI loop */
 	
-	for (ch0 = 0 ; ch0 != EOF ; ch0 = getchar())
+	pthread_mutex_lock(fancy->mutex);
+	for (;;)
 	{
+		pthread_mutex_unlock(fancy->mutex);
+		ch0 = getchar();
+		pthread_mutex_lock(fancy->mutex);
+		
+		if (ch0 == EOF) break;
+		
 		if (ch0 != 0x1B) continue;
 		ch0 = getchar();
 		if (ch0 == EOF) break;
@@ -805,11 +834,9 @@
 		if (ch0 == 'A')
 		{
 			if (fancy->i == 0) continue;
-			pthread_mutex_lock(fancy->mutex);
 			fancy->i = 0;
 			moonfish_scroll(fancy);
 			moonfish_go(fancy);
-			pthread_mutex_unlock(fancy->mutex);
 			continue;
 		}
 		
@@ -817,11 +844,9 @@
 		if (ch0 == 'B')
 		{
 			if (fancy->i == fancy->count - 1) continue;
-			pthread_mutex_lock(fancy->mutex);
 			fancy->i = fancy->count - 1;
 			moonfish_scroll(fancy);
 			moonfish_go(fancy);
-			pthread_mutex_unlock(fancy->mutex);
 			continue;
 		}
 		
@@ -829,11 +854,9 @@
 		if (ch0 == 'C')
 		{
 			if (fancy->i == fancy->count - 1) continue;
-			pthread_mutex_lock(fancy->mutex);
 			fancy->i++;
 			moonfish_scroll(fancy);
 			moonfish_go(fancy);
-			pthread_mutex_unlock(fancy->mutex);
 			continue;
 		}
 		
@@ -841,11 +864,9 @@
 		if (ch0 == 'D')
 		{
 			if (fancy->i == 0) continue;
-			pthread_mutex_lock(fancy->mutex);
 			fancy->i--;
 			moonfish_scroll(fancy);
 			moonfish_go(fancy);
-			pthread_mutex_unlock(fancy->mutex);
 			continue;
 		}
 		
@@ -870,13 +891,11 @@
 		/* handle scroll up */
 		if (ch0 == 0x60)
 		{
-			pthread_mutex_lock(fancy->mutex);
 			if (fancy->offset > 0)
 			{
 				fancy->offset--;
 				moonfish_fancy(fancy);
 			}
-			pthread_mutex_unlock(fancy->mutex);
 			continue;
 		}
 		
@@ -883,13 +902,11 @@
 		/* handle scroll down */
 		if (ch0 == 0x61)
 		{
-			pthread_mutex_lock(fancy->mutex);
 			if (fancy->offset < fancy->count / 2 - 6)
 			{
 				fancy->offset++;
 				moonfish_fancy(fancy);
 			}
-			pthread_mutex_unlock(fancy->mutex);
 			continue;
 		}
 		
@@ -896,9 +913,7 @@
 		/* "(+)" button clicked */
 		if (ch0 == 0x20 && y1 == 1 && x1 >= 21 && x1 <= 23)
 		{
-			pthread_mutex_lock(fancy->mutex);
 			moonfish_bump(fancy);
-			pthread_mutex_unlock(fancy->mutex);
 			continue;
 		}
 		
@@ -905,7 +920,6 @@
 		/* move name clicked (on scoresheet) */
 		if (ch0 == 0x20 && y1 >= 2 && y1 <= 7 && x1 >= 21 && x1 <= 40)
 		{
-			pthread_mutex_lock(fancy->mutex);
 			i = (fancy->offset + y1) * 2 - 4;
 			if (fancy->plies[0].chess.white) i++;
 			if (x1 > 30) i++;
@@ -914,7 +928,6 @@
 				fancy->i = i;
 				moonfish_go(fancy);
 			}
-			pthread_mutex_unlock(fancy->mutex);
 			continue;
 		}
 		
@@ -921,7 +934,6 @@
 		/* "best move" button clicked */
 		if (ch0 == 0x20 && y1 == 8 && x1 >= 21 && x1 <= 40)
 		{
-			pthread_mutex_lock(fancy->mutex);
 			if (fancy->plies[fancy->i].best[0] != 0)
 			{
 				if (moonfish_from_uci(&fancy->plies[fancy->i].chess, &move, fancy->plies[fancy->i].best))
@@ -931,7 +943,6 @@
 				}
 				moonfish_play(fancy, &move);
 			}
-			pthread_mutex_unlock(fancy->mutex);
 			continue;
 		}
 		
@@ -939,8 +950,6 @@
 		if (x1 < 2 || x1 > 17 || y1 < 1 || y1 > 8) continue;
 		x1 /= 2;
 		
-		pthread_mutex_lock(fancy->mutex);
-		
 		/* mouse down with no square selected: select the square under the mouse */
 		if (ch0 == 0x20 && fancy->x == 0)
 		{
@@ -947,16 +956,11 @@
 			fancy->x = x1;
 			fancy->y = y1;
 			moonfish_fancy(fancy);
-			pthread_mutex_unlock(fancy->mutex);
 			continue;
 		}
 		
 		/* only handle cases where a square is selected henceforth */
-		if (fancy->x == 0)
-		{
-			pthread_mutex_unlock(fancy->mutex);
-			continue;
-		}
+		if (fancy->x == 0) continue;
 		
 		/* handle mouse down: if the clicked square is the selected square, deselect it */
 		if (ch0 == 0x20 && x1 == fancy->x && y1 == fancy->y)
@@ -963,7 +967,6 @@
 		{
 			fancy->x = 0;
 			moonfish_fancy(fancy);
-			pthread_mutex_unlock(fancy->mutex);
 			continue;
 		}
 		
@@ -972,7 +975,6 @@
 		if (moonfish_move_from(&fancy->plies[fancy->i].chess, &move, fancy->x, fancy->y, x1, y1) == 0)
 		{
 			moonfish_play(fancy, &move);
-			pthread_mutex_unlock(fancy->mutex);
 			continue;
 		}
 		
@@ -982,11 +984,8 @@
 			fancy->x = x1;
 			fancy->y = y1;
 			moonfish_fancy(fancy);
-			pthread_mutex_unlock(fancy->mutex);
 			continue;
 		}
-		
-		pthread_mutex_unlock(fancy->mutex);
 	}
 	
 	return 0;
--- a/tools/chat.c
+++ b/tools/chat.c
@@ -341,7 +341,7 @@
 
 int main(int argc, char **argv)
 {
-	static char *format = "<UCI-options> <cmd> <args>...";
+	static char *format = "<UCI-options> [--] <cmd> <args>...";
 	static struct moonfish_arg args[] =
 	{
 		{"N", "host", "<name>", "irc.libera.chat", "network host name (default: 'irc.libera.chat')"},
@@ -353,9 +353,7 @@
 	
 	char **options, **command;
 	
-	moonfish_spawner(argv[0]);
-	
-	/* todo: validate nickname & channels*/
+	/* todo: validate nickname & channels */
 	options = moonfish_args(args, format, argc, argv);
 	
 	command = options;
@@ -366,7 +364,9 @@
 		command++;
 	}
 	
+	if (!strcmp(*command, "--")) command++;
+	
 	moonfish_chat(argv[0], command, options, args[0].value, args[1].value, args[2].value, args[3].value);
-	fprintf(stderr, "%s: Unreachable\n", argv[0]);
+	fprintf(stderr, "%s: unreachable\n", argv[0]);
 	return 1;
 }
--- a/tools/lichess.c
+++ b/tools/lichess.c
@@ -506,8 +506,6 @@
 	struct tls *tls;
 	char *username;
 	
-	moonfish_spawner(argv[0]);
-	
 	command = moonfish_args(args, format, argc, argv);
 	command_count = argc - (command - argv);
 	if (command_count < 1) moonfish_usage(args, format, argv[0]);
--- a/tools/perft.c
+++ b/tools/perft.c
@@ -33,11 +33,10 @@
 
 int main(int argc, char **argv)
 {
-	static char *format = "";
 	static struct moonfish_arg args[] =
 	{
 		{"F", "fen", "<FEN>", NULL, "starting position for the game"},
-		{"N", "depth", "<ply-count>", "2", "the number of plies to look (default: '2')"},
+		{"N", "depth", "<plies>", "2", "the number of plies to search (default: '2')"},
 		{NULL, NULL, NULL, NULL, NULL},
 	};
 	
@@ -45,17 +44,17 @@
 	long int depth;
 	struct moonfish_chess chess;
 	
-	if (moonfish_args(args, format, argc, argv) - argv != argc)
-		moonfish_usage(args, format, argv[0]);
+	if (moonfish_args(args, NULL, argc, argv) - argv != argc)
+		moonfish_usage(args, NULL, argv[0]);
 	
 	errno = 0;
 	depth = strtol(args[1].value, &end, 10);
 	if (errno != 0 || *end != 0 || depth < 0 || depth >= 24)
-		moonfish_usage(args, format, argv[0]);
+		moonfish_usage(args, NULL, argv[0]);
 	
 	moonfish_chess(&chess);
 	if (args[0].value != NULL && moonfish_from_fen(&chess, args[0].value))
-		moonfish_usage(args, format, argv[0]);
+		moonfish_usage(args, NULL, argv[0]);
 	
 	printf("perft %ld: %ld\n", depth, moonfish_perft(&chess, depth));
 	
--- a/tools/play.c
+++ b/tools/play.c
@@ -3,7 +3,6 @@
 
 #include <unistd.h>
 #include <string.h>
-#include <errno.h>
 #include <stdlib.h>
 #include <time.h>
 #include <pthread.h>
@@ -318,8 +317,6 @@
 	int error;
 	char **command;
 	int command_count;
-	
-	moonfish_spawner(argv[0]);
 	
 	command = moonfish_args(args, format, argc, argv);
 	command_count = argc - (command - argv);
--- a/tools/tools.h
+++ b/tools/tools.h
@@ -15,7 +15,6 @@
 	char *description;
 };
 
-void moonfish_spawner(char *argv0);
 void moonfish_spawn(char **argv, FILE **in, FILE **out, char *directory);
 
 char *moonfish_next(FILE *file);
--- a/tools/utils.c
+++ b/tools/utils.c
@@ -5,464 +5,102 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <fcntl.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <pthread.h>
-#include <sys/un.h>
 #include <limits.h>
 
 #include "tools.h"
 
-static pid_t moonfish_spawner_pid;
-static char *moonfish_spawner_argv0;
-static int moonfish_spawner_fd;
-static char *moonfish_spawner_dir_name;
-static char *moonfish_spawner_socket_name;
-
-static void *moonfish_read_pipe(void *data)
+static void moonfish_fork(char **argv, int *in_fd, int *out_fd, char *directory)
 {
-	char ch;
-	int *fds;
-	fds = data;
-	while (read(fds[0], &ch, 1) > 0) { }
-	if (moonfish_spawner_socket_name != NULL)
-	{
-		if (unlink(moonfish_spawner_socket_name) != 0)
-			perror(moonfish_spawner_argv0);
-	}
-	if (moonfish_spawner_dir_name != NULL)
-	{
-		if (rmdir(moonfish_spawner_dir_name) != 0)
-			perror(moonfish_spawner_argv0);
-	}
-	exit(0);
-}
-
-void moonfish_spawner(char *argv0)
-{
-	int fd;
-	FILE *in, *out;
-	pid_t pid;
-	int i, count, length;
-	char **argv;
-	char *directory;
-	struct sigaction action;
-	pthread_t thread;
-	int fds[2], fds2[2];
-	struct sockaddr_un address = {0};
-	char ch;
-	ssize_t size;
+	int p1[2], p2[2];
+	int pid;
+	long int count, i;
 	
-	moonfish_spawner_argv0 = argv0;
-	
-	moonfish_spawner_fd = socket(AF_UNIX, SOCK_STREAM, 0);
-	if (moonfish_spawner_fd < 0)
+	if (pipe(p1) < 0)
 	{
-		perror(argv0);
+		perror("pipe");
 		exit(1);
 	}
-	
-	if (pipe(fds) != 0)
+	if (pipe(p2) < 0)
 	{
-		perror(argv0);
+		perror("pipe");
 		exit(1);
 	}
 	
-	if (pipe(fds2) != 0)
+	pid = fork();
+	if (pid < 0)
 	{
-		perror(argv0);
+		perror("fork");
 		exit(1);
 	}
 	
-	moonfish_spawner_pid = fork();
-	if (moonfish_spawner_pid < 0)
+	if (pid != 0)
 	{
-		perror(argv0);
-		exit(1);
-	}
-	
-	if (moonfish_spawner_pid != 0)
-	{
-		close(fds2[1]);
-		for (;;)
-		{
-			size = read(fds2[0], &ch, 1);
-			if (size > 0) break;
-			if (size < 0)
-			{
-				perror(argv0);
-				exit(1);
-			}
-		}
-		close(fds2[0]);
+		*in_fd = p1[1];
+		*out_fd = p2[0];
+		close(p1[0]);
+		close(p2[1]);
 		return;
 	}
 	
-	close(fds2[0]);
-	close(fds[1]);
-	
-	pthread_create(&thread, NULL, &moonfish_read_pipe, fds);
-	
-	action.sa_flags = SA_NOCLDWAIT;
-	action.sa_handler = SIG_DFL;
-	if (sigemptyset(&action.sa_mask))
+	if (directory != NULL && chdir(directory) != 0)
 	{
-		perror(argv0);
+		perror("chdir");
 		exit(1);
 	}
-	if (sigaction(SIGCHLD, &action, NULL))
-	{
-		perror(argv0);
-		exit(1);
-	}
 	
-	address.sun_family = AF_UNIX;
-	strcpy(address.sun_path, "/tmp/moon-XXXXXX");
-	
-	if (mkdtemp(address.sun_path) == NULL)
+	if (dup2(p1[0], 0) != 0 || dup2(p2[1], 1) != 1 || dup2(p2[1], 2) != 2)
 	{
-		perror(argv0);
+		perror("dup2");
 		exit(1);
 	}
 	
-	moonfish_spawner_dir_name = strdup(address.sun_path);
-	if (moonfish_spawner_dir_name == NULL)
-	{
-		perror(argv0);
-		exit(1);
-	}
+	count = sysconf(_SC_OPEN_MAX);
+	for (i = 3 ; i < count ; i++) close(i);
 	
-	strcat(address.sun_path, "/socket");
-	
-	moonfish_spawner_socket_name = strdup(address.sun_path);
-	if (moonfish_spawner_socket_name == NULL)
-	{
-		perror(argv0);
-		exit(1);
-	}
-	
-	if (bind(moonfish_spawner_fd, (void *) &address, sizeof address) != 0)
-	{
-		perror(argv0);
-		exit(1);
-	}
-	
-	if (listen(moonfish_spawner_fd, 8) < 0)
-	{
-		perror(argv0);
-		exit(1);
-	}
-	
-	for (;;)
-	{
-		size = write(fds2[1], &ch, 1);
-		if (size > 0) break;
-		if (size < 0)
-		{
-			perror(argv0);
-			exit(1);
-		}
-	}
-	close(fds2[1]);
-	
-	for (;;)
-	{
-		fd = accept(moonfish_spawner_fd, NULL, NULL);
-		if (fd < 0)
-		{
-			perror(argv0);
-			exit(1);
-		}
-		
-		pid = fork();
-		if (pid < 0)
-		{
-			perror(argv0);
-			exit(1);
-		}
-		
-		if (pid == 0) break;
-		close(fd);
-	}
-	
-	close(fds[0]);
-	close(moonfish_spawner_fd);
-	
-	in = fdopen(fd, "r");
-	if (in == NULL)
-	{
-		perror(argv0);
-		exit(1);
-	}
-	
-	out = fdopen(fd, "w");
-	if (out == NULL)
-	{
-		perror(argv0);
-		exit(1);
-	}
-	
-	if (setvbuf(in, NULL, _IONBF, 0) != 0)
-	{
-		perror(argv0);
-		exit(1);
-	}
-	
-	if (setvbuf(out, NULL, _IONBF, 0) != 0)
-	{
-		perror(argv0);
-		exit(1);
-	}
-	
-	pid = getpid();
-	if (fwrite(&pid, sizeof pid, 1, out) != 1)
-	{
-		perror(argv0);
-		exit(1);
-	}
-	
-	if (fread(&length, sizeof length, 1, in) != 1)
-	{
-		perror(argv0);
-		exit(1);
-	}
-	
-	if (length < 0)
-	{
-		fprintf(stderr, "%s: invalid length\n", argv0);
-		exit(1);
-	}
-	
-	if (length > 0)
-	{
-		directory = malloc(length + 1);
-		if (directory == NULL)
-		{
-			perror(argv0);
-			exit(1);
-		}
-		
-		directory[length] = 0;
-		
-		if (fread(directory, length, 1, in) != 1)
-		{
-			perror(argv0);
-			exit(1);
-		}
-		
-		if (chdir(directory) != 0)
-		{
-			perror(argv0);
-			exit(1);
-		}
-	}
-	
-	if (fread(&count, sizeof count, 1, in) != 1)
-	{
-		perror(argv0);
-		exit(1);
-	}
-	
-	if (count < 1)
-	{
-		fprintf(stderr, "%s: too few arguments\n", argv0);
-		exit(1);
-	}
-	
-	argv = malloc(sizeof *argv * (count + 1));
-	if (argv == NULL)
-	{
-		perror(argv0);
-		exit(1);
-	}
-	
-	argv[count] = NULL;
-	
-	for (i = 0 ; i < count ; i++)
-	{
-		if (fread(&length, sizeof length, 1, in) != 1)
-		{
-			perror(argv0);
-			exit(1);
-		}
-		
-		argv[i] = malloc(length + 1);
-		if (argv[i] == NULL)
-		{
-			perror(argv0);
-			exit(1);
-		}
-		argv[i][length] = 0;
-		
-		if (fread(argv[i], length, 1, in) != 1)
-		{
-			perror(argv0);
-			exit(1);
-		}
-	}
-	
-	if (dup2(fd, 0) < 0 || dup2(fd, 1) < 0)
-	{
-		perror(argv0);
-		exit(1);
-	}
-	
-	close(fd);
-	
-	fd = open("/dev/null", O_WRONLY);
-	if (fd < 0)
-	{
-		perror(argv0);
-		exit(1);
-	}
-	
-	if (dup2(fd, 2) < 0)
-	{
-		perror(argv0);
-		exit(1);
-	}
-	
 	execvp(argv[0], argv);
-	fprintf(stderr, "%s: %s: %s\n", argv0, argv[0], strerror(errno));
+	perror("execvp");
 	exit(1);
 }
 
 void moonfish_spawn(char **argv, FILE **in, FILE **out, char *directory)
 {
-	pid_t pid;
-	int fd;
-	int i, count;
-	struct sockaddr_un address;
-	socklen_t length;
+	int in_fd, out_fd;
 	
-	pid = waitpid(moonfish_spawner_pid, NULL, WNOHANG);
-	if (pid < 0)
-	{
-		perror(moonfish_spawner_argv0);
-		exit(1);
-	}
-	if (pid != 0)
-	{
-		fprintf(stderr, "%s: spawner exited\n", moonfish_spawner_argv0);
-		exit(1);
-	}
+	moonfish_fork(argv, &in_fd, &out_fd, directory);
 	
-	length = sizeof address;
-	if (getsockname(moonfish_spawner_fd, (void *) &address, &length) != 0)
-	{
-		perror(moonfish_spawner_argv0);
-		exit(1);
-	}
-	
-	fd = socket(AF_UNIX, SOCK_STREAM, 0);
-	if (fd < 0)
-	{
-		perror(moonfish_spawner_argv0);
-		exit(1);
-	}
-	
-	if (connect(fd, (void *) &address, length) != 0)
-	{
-		perror(moonfish_spawner_argv0);
-		exit(1);
-	}
-	
-	*in = fdopen(dup(fd), "w");
+	*in = fdopen(in_fd, "w");
 	if (*in == NULL)
 	{
-		perror(moonfish_spawner_argv0);
+		perror("fdopen");
 		exit(1);
 	}
 	
-	*out = fdopen(dup(fd), "r");
+	*out = fdopen(out_fd, "r");
 	if (*out == NULL)
 	{
-		perror(moonfish_spawner_argv0);
+		perror("fdopen");
 		exit(1);
 	}
 	
-	close(fd);
-	
-	if (setvbuf(*in, NULL, _IONBF, 0) != 0)
+	errno = 0;
+	if (setvbuf(*in, NULL, _IOLBF, 0))
 	{
-		perror(moonfish_spawner_argv0);
+		if (errno != 0) perror("setvbuf");
 		exit(1);
 	}
 	
-	if (setvbuf(*out, NULL, _IONBF, 0) != 0)
+	errno = 0;
+	if (setvbuf(*out, NULL, _IOLBF, 0))
 	{
-		perror(moonfish_spawner_argv0);
+		if (errno != 0) perror("setvbuf");
 		exit(1);
 	}
-	
-	if (directory == NULL) directory = "";
-	count = strlen(directory);
-	if (fwrite(&count, sizeof count, 1, *in) != 1)
-	{
-		perror(moonfish_spawner_argv0);
-		exit(1);
-	}
-	
-	if (count > 0 && fwrite(directory, count, 1, *in) != 1)
-	{
-		perror(moonfish_spawner_argv0);
-		exit(1);
-	}
-	
-	count = 0;
-	while (argv[count] != NULL)
-		count++;
-	
-	if (fwrite(&count, sizeof count, 1, *in) != 1)
-	{
-		perror(moonfish_spawner_argv0);
-		exit(1);
-	}
-	
-	for (i = 0 ; argv[i] != NULL ; i++)
-	{
-		count = strlen(argv[i]);
-		if (fwrite(&count, sizeof count, 1, *in) != 1)
-		{
-			perror(moonfish_spawner_argv0);
-			exit(1);
-		}
-		
-		if (fwrite(argv[i], count, 1, *in) != 1)
-		{
-			perror(moonfish_spawner_argv0);
-			exit(1);
-		}
-	}
-	
-	if (fread(&pid, sizeof pid, 1, *out) != 1)
-	{
-		perror(moonfish_spawner_argv0);
-		exit(1);
-	}
-	
-	if (setvbuf(*in, NULL, _IOLBF, 0) != 0)
-	{
-		perror(moonfish_spawner_argv0);
-		exit(1);
-	}
-	
-	if (setvbuf(*out, NULL, _IOLBF, 0) != 0)
-	{
-		perror(moonfish_spawner_argv0);
-		exit(1);
-	}
 }
 
 char *moonfish_next(FILE *file)
 {
 	static char line[2048];
-	
-	if (fgets(line, sizeof line, file) == NULL)
-		return NULL;
+	if (fgets(line, sizeof line, file) == NULL) return NULL;
 	return line;
 }
 
@@ -477,8 +115,7 @@
 		
 		arg = strtok_r(line, "\r\n\t ", &buffer);
 		if (arg == NULL) continue;
-		if (!strcmp(line, name))
-			return strtok_r(NULL, "\r\n\t ", &buffer);
+		if (!strcmp(line, name)) return strtok_r(NULL, "\r\n\t ", &buffer);
 	}
 }
 
@@ -486,6 +123,7 @@
 {
 	char *end;
 	long int long_result;
+	
 	errno = 0;
 	long_result = strtol(arg, &end, 10);
 	if (errno != 0 || *end != 0) return 1;
@@ -501,7 +139,6 @@
 	int col1, col2, n;
 	
 	if (argv0 == NULL) argv0 = "<program>";
-	if (rest_format == NULL) rest_format = "<args>...";
 	
 	col1 = 0;
 	col2 = 0;
@@ -525,9 +162,13 @@
 		if (n > col2) col2 = n;
 	}
 	
-	fprintf(out, "usage: %s <options>... [--] %s\n", argv0, rest_format);
-	fprintf(out, "options:\n");
+	if (args[0].letter == NULL && args[0].name == NULL) fprintf(out, "usage: %s", argv0);
+	else fprintf(out, "usage: %s <options>...", argv0);
+	if (rest_format != NULL) fprintf(out, " [--] %s", rest_format);
+	fprintf(out, "\noptions:\n");
 	
+	if (args[0].letter == NULL && args[0].name == NULL) return;
+	
 	for (i = 0 ; args[i].letter != NULL || args[i].name != NULL ; i++)
 	{
 		fprintf(out, "   ");
@@ -596,7 +237,11 @@
 		if (args[i].format == NULL)
 		{
 			arg += length;
-			if (arg[0] == 0) return 0;
+			if (arg[0] == 0)
+			{
+				args[i].value = "";
+				return 0;
+			}
 			continue;
 		}
 		else
@@ -603,8 +248,7 @@
 		{
 			args[i].value = arg + length;
 			
-			if (arg[length] == '=')
-				args[i].value = arg + length + 1;
+			if (arg[length] == '=') args[i].value = arg + length + 1;
 			
 			if (arg[length] == 0)
 			{
@@ -636,8 +280,12 @@
 		
 		if (args[i].format == NULL)
 		{
-			if (arg[length] == 0) return 0;
-			if (arg[length] == '=') return 1;
+			if (arg[length] == 0)
+			{
+				args[i].value = "";
+				return 0;
+			}
+			return 1;
 		}
 		else
 		{
@@ -674,21 +322,27 @@
 		if (argc <= 0) return argv;
 		
 		arg = *argv;
+		
 		if (!strcmp(arg, "-")) return argv;
-		if (arg[0] != '-') return argv;
-		while (arg[0] == '-') arg++;
-		if (arg[0] == 0) return argv + 1;
+		if (!strcmp(arg, "--") && rest_format != NULL) return argv + 1;
 		
-		if (!strcmp(arg, "help") || !strcmp(arg, "h") || !strcmp(arg, "H"))
+		if (!strcmp(arg, "--help") || !strcmp(arg, "-h") || !strcmp(arg, "-H"))
 		{
 			moonfish_usage_to(args, rest_format, argv0, stdout);
 			exit(0);
 		}
 		
-		if (moonfish_letter_arg(args, arg, &argc, &argv) == 0) continue;
-		if (moonfish_name_arg(args, arg, &argc, &argv) == 0) continue;
+		if (arg[0] != '-') return argv;
+		arg++;
 		
-		moonfish_usage(args, rest_format, argv0);
+		if (arg[0] == '-')
+		{
+			arg++;
+			if (moonfish_name_arg(args, arg, &argc, &argv) != 0) moonfish_usage(args, rest_format, argv0);
+			continue;
+		}
+		
+		if (moonfish_letter_arg(args, arg, &argc, &argv) != 0) moonfish_usage(args, rest_format, argv0);
 	}
 }
 
--