--- a/src/main.c Sun Mar 29 13:26:00 2026 +0200 +++ b/src/main.c Sun Mar 29 15:17:28 2026 +0200 @@ -37,14 +37,16 @@ #include <time.h> #include <getopt.h> #include <locale.h> +#include <sys/stat.h> int get_settings(int argc, char **argv, Settings *settings) { char *valid; unsigned long int time, port; uint8_t timeunit = 60; size_t len; + bool port_set = false; - for (int opt ; (opt = getopt(argc, argv, "a:bc:Fhp:rsS:t:Uv")) != -1 ;) { + for (int opt ; (opt = getopt(argc, argv, "a:bc:Fhp:rsS:t:uUv")) != -1 ;) { switch (opt) { case 'c': settings->continuepgn = optarg; @@ -61,6 +63,14 @@ case 'F': settings->disableflip = true; break; + case 'u': + if (port_set) { + fprintf(stderr, "Cannot use Unix domain sockets " + "when a TCP port was specified.\n"); + return 1; + } + settings->usedomainsocket = true; + break; case 'U': settings->unicode = false; break; @@ -87,6 +97,15 @@ } break; case 'p': + if (port_set) { + fprintf(stderr, "Cannot use -p twice.\n"); + return 1; + } + if (settings->usedomainsocket) { + fprintf(stderr, "Cannot specify TCP port " + "when using Unix domain sockets.\n"); + return 1; + } port = strtol(optarg, &valid, 10); if (port < 1025 || port > 65535 || *valid != '\0') { fprintf(stderr, @@ -96,6 +115,7 @@ return 1; } else { settings->port = (short) port; + port_set = true; } break; case 'v': @@ -111,6 +131,7 @@ " -c <PGN file> Continue the specified game\n" " -h This help page\n" " -p TCP port to use (default: 27015)\n" +" -u Use Unix domain socket instead of TCP\n" " -U Disables unicode pieces\n" " -v Print version information and exits\n" "\nServer options\n" @@ -119,13 +140,16 @@ " -r Distribute color randomly\n" " -t <time> Specifies time limit (default: no limit)\n" "\nHot seat\n" -" -s Play a hot seat game (no client/server)\n" +" -s Play a hot seat game (network options are ignored)\n" " -F Do not automatically flip the board in hot seat games\n" "\nNotes\n" "The time unit for -a is seconds and for -t minutes by default. To " "specify\nseconds for the -t option, use the s suffix.\n" "Example: -t 150s\n\n" -"Use '-' for PGN files to read PGN data from standard input\n" +"Use '-' for PGN files to read PGN data from standard input\n\n" +"When playing over Unix domain socket, the HOST denotes the socket path.\n" +"When the path doest not exist, a game is created. Otherwise, the program\n" +"joins the existing game. When HOST is omitted, /tmp/chess.sock is used.\n" ); exit(0); } @@ -145,22 +169,50 @@ return 1; } } + + if (settings->usedomainsocket) { + if (!settings->serverhost) { + settings->serverhost = "/tmp/chess.sock"; + } + struct stat st; + if (stat(settings->serverhost, &st) == 0) { + if (S_ISSOCK(st.st_mode)) { + settings->ishost = false; + } else { + fprintf(stderr, "%s is not a Unix domain socket.\n", + settings->serverhost); + return 1; + } + } else { + settings->ishost = true; + } + } else { + settings->ishost = !settings->serverhost; + } return 0; } -static Settings default_settings() { - Settings settings = {0}; +static Settings settings; + +static void init_settings(void) { + memset(&settings, 0, sizeof(settings)); settings.gameinfo.servercolor = WHITE; settings.port = 27015; settings.unicode = !!setlocale(LC_CTYPE, "C.UTF-8"); - return settings; +} + +static void cleanup() { + endwin(); + if (settings.usedomainsocket && settings.ishost) { + remove(settings.serverhost); + } } int main(int argc, char **argv) { srand(time(NULL)); - - Settings settings = default_settings(); + + init_settings(); if (get_settings(argc, argv, &settings)) { return 1; } @@ -177,14 +229,14 @@ endwin(); return EXIT_FAILURE; } - atexit((void(*)())endwin); + atexit(cleanup); int exitcode; if (settings.singlemachine) { game_play_singlemachine(&settings); exitcode = EXIT_SUCCESS; } else { - exitcode = is_server(&settings) ? + exitcode = settings.ishost ? server_run(&settings) : client_run(&settings); }