diff --git a/commandline.c b/commandline.c new file mode 100644 index 0000000..94d2c4f --- /dev/null +++ b/commandline.c @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AnnaConnect is a silly little text-based connect 4 game c: + * Copyright (C) 2025 Anna Snoeijs. anna.snoeijs@hotmail.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include "macros.h" +#include "types.h" +#include "commandline.h" + +// The LISENCE file +// Not null-terminated so need pointers for both start and end +extern char _binary_LICENSE_start[]; +extern char _binary_LICENSE_end[]; + +int parseArgs( + int argc, + char *argv[], + board_t *board, + wins_t **wins, + FILE **outputfile, + char **filename, + bool *randomMoves +){ + // Parse options + for(;;){ + // Allowed options + static struct option long_options[] = { + { "help", no_argument, NULL, 'h' }, + { "license", no_argument, NULL, 'l' }, + { "version", no_argument, NULL, '\0' }, + { "columns", required_argument, NULL, 'c' }, + { "rows", required_argument, NULL, 'r' }, + { "output", required_argument, NULL, 'o' }, + { "slow-calcWins", no_argument, NULL, '\0' }, + { "random-moves", no_argument, NULL, '\0' }, + { 0 } + }; + // Index of current option + int option_index = 0; + // Get next option + char c = (char)getopt_long( argc, argv, "hlc:r:o:", + long_options, &option_index + ); + if( c == -1 ) break; + // Handle option + switch( c ){ + case '\0': // Any option without shorthand + switch( option_index ){ + case 2: // --version + printf( VERSIONSTRING ); + return 0; + case 6: // --slow-calcWins + free( *wins ); + *wins = NULL; + break; + case 7: // --random-moves + *randomMoves = (bool)(true); + break; + default: + fprintf( + stderr, + "%s: unhandled option \'--%s\' \n", + argv[0], + long_options[ option_index ].name + ); + return -1; + break; + } + break; + case 'h': // --help + printf( "Usage: %s [OPTIONS]\n\n", argv[0] ); + printf( +" -h --help Print this help text\n" +" -l --license Print the LICENSE file\n" +" --version Print the version string\n" +"\n" +" -c n --columns n Set the amount of columns\n" +" -r n --rows n Set the amount of rows\n" +"\n" +" -o file --output file Ouput moves taken in game to file\n" +"\n" +" --slow-calcwins Use the reference implementation of calcWins()\n" +" --random-moves Play random moves instead of asking for input\n" + ); + return 0; + case 'l': // --license + for( + char *p = _binary_LICENSE_start; + p != _binary_LICENSE_end; + p++ + ){ + putchar( *p ); + } + return 0; + case 'c': // --columns + board->columns = (columnsint_t)atoi(optarg); + break; + case 'r': // --rows + board->rows = (rowsint_t)atoi(optarg); + break; + case 'o': + // filename = strdup(optarg); + *filename = calloc( strlen( optarg ) + 1, sizeof(char) ); + if( *filename != NULL ){ + strcpy( *filename, optarg ); + *outputfile = fopen( *filename, "w" ); + }else{ + fprintf( stderr, "ERR: COULD NOT ALLOCATE FILENAME\n" ); + return -1; + } + break; + } + } + // Check for unhandled options + if( optind < argc ) { + fprintf( stderr, "non-option ARGV-elements: " ); + while( optind < argc ) + printf( "%s ", argv[optind++] ); + putchar( '\n' ); + return -1; + } + if( board->rows < 1 ){ + fprintf( stderr, "ERR: AMOUT OF ROWS MUST BE AT LEAST 1\n" ); + return -1; + } +#define ROWOVERFLOW (rowsint_t)( sizeof(column_t) * CHAR_BIT - 1 ) + if( board->rows >= ROWOVERFLOW ){ + fprintf( stderr, + "ERR: AMOUT OF ROWS MUST BE LESS THAN %d\n", ROWOVERFLOW + ); + return -1; + } + return 0; +} diff --git a/commandline.h b/commandline.h new file mode 100644 index 0000000..7865fb8 --- /dev/null +++ b/commandline.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#pragma once + +#include "macros.h" +#include "types.h" +#include "logic.h" + +extern int parseArgs( + int argc, + char *argv[], + board_t *board, + wins_t **wins, + FILE **outputfile, + char **filename, + bool *randomMoves +); diff --git a/connect4.c b/connect4.c index f4ccdcb..fe991e9 100644 --- a/connect4.c +++ b/connect4.c @@ -20,33 +20,13 @@ #include #include #include -#ifndef NO_OPTIONS -#include -#endif /* ! NO_OPTIONS */ #include #include "macros.h" #include "config.h" #include "logic.h" #include "types.h" #include "ui.h" - -#define VERSION 0.3.1 - -#ifndef GITHASH -#define FULLVERSION VERSION -#else -#define FULLVERSION VERSION~git.GITHASH -#endif - -#define VERSIONSTRING \ - "AnnaConnect version "XSTR(FULLVERSION)", Copyright (C) Anna Snoeijs\n" - -#ifndef NO_OPTIONS -// The LISENCE file -// Not null-terminated so need pointers for both start and end -extern char _binary_LICENSE_start[]; -extern char _binary_LICENSE_end[]; -#endif /* ! NO_OPTIONS */ +#include "commandline.h" int main( #ifndef NO_OPTIONS // [[maybe_unused]] is unsupported in C99, so this will do @@ -66,113 +46,21 @@ int main( FILE *outputfile = NULL; char *filename = NULL; bool randomMoves = 0; - // Parse options - for(;;){ - // Allowed options - static struct option long_options[] = { - { "help", no_argument, NULL, 'h' }, - { "license", no_argument, NULL, 'l' }, - { "version", no_argument, NULL, '\0' }, - { "columns", required_argument, NULL, 'c' }, - { "rows", required_argument, NULL, 'r' }, - { "output", required_argument, NULL, 'o' }, - { "slow-calcWins", no_argument, NULL, '\0' }, - { "random-moves", no_argument, NULL, '\0' }, - { 0 } - }; - // Index of current option - int option_index = 0; - // Get next option - char c = (char)getopt_long( argc, argv, "hlc:r:o:", - long_options, &option_index - ); - if( c == -1 ) break; - // Handle option - switch( c ){ - case '\0': // Any option without shorthand - switch( option_index ){ - case 2: // --version - printf( VERSIONSTRING ); - return 0; - case 6: // --slow-calcWins - free( wins ); - wins = NULL; - break; - case 7: // --random-moves - randomMoves = 1; - break; - default: - fprintf( - stderr, - "%s: unhandled option \'--%s\' \n", - argv[0], - long_options[ option_index ].name - ); - break; - } - break; - case 'h': // --help - printf( "Usage: %s [OPTIONS]\n\n", argv[0] ); - printf( -" -h --help Print this help text\n" -" -l --license Print the LICENSE file\n" -" --version Print the version string\n" -"\n" -" -c n --columns n Set the amount of columns\n" -" -r n --rows n Set the amount of rows\n" -"\n" -" -o file --output file Ouput moves taken in game to file\n" -"\n" -" --slow-calcwins Use the reference implementation of calcWins()\n" -" --random-moves Play random moves instead of asking for input\n" - ); - return 0; - case 'l': // --license - for( - char *p = _binary_LICENSE_start; - p != _binary_LICENSE_end; - p++ - ){ - putchar( *p ); - } - return 0; - case 'c': // --columns - playboard.columns = (columnsint_t)atoi(optarg); - break; - case 'r': // --rows - playboard.rows = (rowsint_t)atoi(optarg); - break; - case 'o': - // filename = strdup(optarg); - filename = calloc( strlen( optarg ) + 1, sizeof(char) ); - if( filename != NULL ){ - strcpy( filename, optarg ); - outputfile = fopen( filename, "w" ); - }else{ - fprintf( stderr, "ERR: COULD NOT ALLOCATE FILENAME\n" ); - } - break; - } + switch( + parseArgs( + argc, + argv, + &playboard, + &wins, + &outputfile, + &filename, + &randomMoves + ) + ){ + case 0: break; + case 1: return 0; + default: return -1; } - // Check for unhandled options - if( optind < argc ) { - fprintf( stderr, "non-option ARGV-elements: " ); - while( optind < argc ) - printf( "%s ", argv[optind++] ); - putchar( '\n' ); - } - if( playboard.rows < 1 ){ - fprintf( stderr, "ERR: AMOUT OF ROWS MUST BE AT LEAST 1\n" ); - return -1; - } -#define ROWOVERFLOW (rowsint_t)( sizeof(column_t) * CHAR_BIT - 1 ) - if( playboard.rows >= ROWOVERFLOW ){ - fprintf( stderr, - "ERR: AMOUT OF ROWS MUST BE LESS THAN %d\n", ROWOVERFLOW - ); - return -1; - } -#undef ROWOVERFLOW #endif /* ! NO_OPTIONS */ // Start the actual program printf( diff --git a/macros.h b/macros.h index 20b6cf4..2ecd282 100644 --- a/macros.h +++ b/macros.h @@ -7,3 +7,14 @@ #define XSTR(s) STR(s) #define STR(s) #s + +#define VERSION 0.3.1 + +#ifndef GITHASH +#define FULLVERSION VERSION +#else +#define FULLVERSION VERSION~git.GITHASH +#endif + +#define VERSIONSTRING \ + "AnnaConnect version "XSTR(FULLVERSION)", Copyright (C) Anna Snoeijs\n" diff --git a/makefile b/makefile index c214f96..e6d05ca 100644 --- a/makefile +++ b/makefile @@ -108,19 +108,19 @@ $(TESTDIR)/test.log.slow: $(OUTDIR)/connect4_$(UI_TESTING).elf # Compile specifically the ncurses version # This one's special because it needs -lncursesw -$(OUTDIR)/connect4_ncurses.elf: $(addprefix $(OBJDIR)/,ui_ncurses.o logic.o connect4.o LICENSE.o) +$(OUTDIR)/connect4_ncurses.elf: $(addprefix $(OBJDIR)/,ui_ncurses.o logic.o connect4.o LICENSE.o commandline.o) @echo $(MSG_LINKING) $@ @mkdir -p $(@D) $(CC) $(FLAGS) -lncursesw -o $@ $^ # Compile the final excecutable for a given UI target -$(OUTDIR)/connect4_%.elf: $(addprefix $(OBJDIR)/,ui_%.o logic.o connect4.o LICENSE.o) +$(OUTDIR)/connect4_%.elf: $(addprefix $(OBJDIR)/,ui_%.o logic.o connect4.o LICENSE.o commandline.o) @echo $(MSG_LINKING) $@ @mkdir -p $(@D) $(CC) $(FLAGS) -o $@ $^ # Compile a windows excecutable -$(OUTDIR)/connect4_%.exe: $(addprefix $(OBJDIR)/w64_,ui_%.o logic.o connect4.o LICENSE.o) +$(OUTDIR)/connect4_%.exe: $(addprefix $(OBJDIR)/w64_,ui_%.o logic.o connect4.o LICENSE.o commandline.o) @echo $(MSG_LINKING) $@ @mkdir -p $(@D) $(CC_W64) $(FLAGS) -o $@ $^