1813 lines
68 KiB
C
1813 lines
68 KiB
C
/*
|
|
* This file is part of pgn-extract: a Portable Game Notation (PGN) extractor.
|
|
* Copyright (C) 1994-2022 David J. Barnes
|
|
*
|
|
* pgn-extract 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* pgn-extract 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 pgn-extract. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* David J. Barnes may be contacted as d.j.barnes@kent.ac.uk
|
|
* https://www.cs.kent.ac.uk/people/staff/djb/
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include "bool.h"
|
|
#include "defs.h"
|
|
#include "typedef.h"
|
|
#include "lines.h"
|
|
#include "taglist.h"
|
|
#include "tokens.h"
|
|
#include "lex.h"
|
|
#include "taglines.h"
|
|
#include "moves.h"
|
|
#include "end.h"
|
|
#include "eco.h"
|
|
#include "argsfile.h"
|
|
#include "apply.h"
|
|
#include "output.h"
|
|
#include "lists.h"
|
|
#include "mymalloc.h"
|
|
#include "fenmatcher.h"
|
|
|
|
#define CURRENT_VERSION "v22-11"
|
|
#define URL "https://www.cs.kent.ac.uk/people/staff/djb/pgn-extract/"
|
|
|
|
/* The prefix of the arguments allowed in an argsfile.
|
|
* The full format is:
|
|
* :-?
|
|
* where ? is an argument letter.
|
|
*
|
|
* A line of the form:
|
|
* :filename
|
|
* means use filename as a NORMALFILE source of games.
|
|
*
|
|
* A line with no leading colon character is taken to apply to the
|
|
* move-reason argument line. Currently, this only applies to the
|
|
* -t -v -x -z
|
|
* arguments.
|
|
*/
|
|
static const char argument_prefix[] = ":-";
|
|
static const int argument_prefix_len = sizeof (argument_prefix) - 1;
|
|
|
|
static ArgType classify_arg(const char *line);
|
|
static game_number *extract_game_number_list(const char *number_list);
|
|
static void read_args_file(const char *infile);
|
|
static Boolean set_move_bounds(char bounds_or_ply, char limit, unsigned number);
|
|
|
|
#if defined(__unix__) || defined(__linux__) || defined(__APPLE__)
|
|
int strcasecmp(const char *, const char *);
|
|
#else
|
|
int _stricmp(const char *s1, const char *s2);
|
|
#endif
|
|
|
|
/* Select the correct function according to operating system. */
|
|
static int
|
|
stringcompare(const char *s1, const char *s2)
|
|
{
|
|
#if defined(__unix__) || defined(__linux__) || defined(__APPLE__)
|
|
return strcasecmp(s1, s2);
|
|
#else
|
|
return _stricmp(s1, s2);
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
|
|
/* Return TRUE if str contains prefix as a prefix, FALSE otherwise. */
|
|
static Boolean
|
|
prefixMatch(const char *prefix, const char *str)
|
|
{
|
|
size_t prefixLen = strlen(prefix);
|
|
if (strlen(str) >= prefixLen) {
|
|
#if defined(__unix__) || defined(__linux__)
|
|
return strncasecmp(prefix, str, prefixLen) == 0;
|
|
#else
|
|
return _strnicmp(prefix, str, prefixLen) == 0;
|
|
#endif
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Skip over leading spaces from the string. */
|
|
static const char *skip_leading_spaces(const char *str)
|
|
{
|
|
while (*str == ' ') {
|
|
str++;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
/* Print a usage message, and exit. */
|
|
static void
|
|
usage_and_exit(void)
|
|
{
|
|
const char *help_data[] = {
|
|
"-7 -- output only the seven tag roster for each game. Other tags (apart",
|
|
" from FEN and possibly ECO) are discarded (See -e).",
|
|
"-#num[,num] -- output num games per file, to files named 1.pgn, 2.pgn, etc.",
|
|
|
|
"",
|
|
|
|
"-aoutputfile -- append extracted games to outputfile. (See -o).",
|
|
"-Aargsfile -- read the program's arguments from argsfile.",
|
|
"-b[elu]num -- restricted bounds on the number of moves in a game.",
|
|
" lnum set a lower bound of 'num' moves,",
|
|
" unum set an upper bound of 'num' moves,",
|
|
" otherwise num (or enum) means equal-to 'num' moves.",
|
|
"-cfile[.pgn] -- Use file.pgn as a check-file for duplicates or",
|
|
" contents of file (no pgn suffix) as a list of check-file names.",
|
|
"-C -- don't include comments in the output. Ordinarily these are retained.",
|
|
"-dduplicates -- write duplicate games to the file duplicates.",
|
|
"-D -- don't output duplicate games.",
|
|
"-eECO_file -- perform ECO classification of games. The optional",
|
|
" ECO_file should contain a PGN format list of ECO lines",
|
|
" Default is to use eco.pgn from the current directory.",
|
|
"-E[123 etc.] -- split output into separate files according to ECO.",
|
|
" E1 : Produce files from ECO letter, A.pgn, B.pgn, ...",
|
|
" E2 : Produce files from ECO letter and first digit, A0.pgn, ...",
|
|
" E3 : Produce files from full ECO code, A00.pgn, A01.pgn, ...",
|
|
" Further digits may be used to produce non-standard further",
|
|
" refined division of games.",
|
|
" All files are opened in append mode.",
|
|
"-F[text] -- output a FEN string comment after the final (or other selected) move.",
|
|
"-ffile_list -- file_list contains the list of PGN source files, one per line.",
|
|
"-Hhash -- match games in which the given Zobrist polyglot hash value occurs",
|
|
"-h -- print details of the arguments.",
|
|
"-llogfile -- Save the diagnostics in logfile rather than using stderr.",
|
|
"-Llogfile -- Append all diagnostics to logfile, rather than overwriting.",
|
|
"-M -- Match only games which end in checkmate.",
|
|
"-noutputfile -- Write all valid games not otherwise output to outputfile.",
|
|
"-N -- don't include NAGs in the output. Ordinarily these are retained.",
|
|
"-ooutputfile -- write extracted games to outputfile (existing contents lost).",
|
|
"-p[elu]num -- restricted bounds on the number of ply in a game.",
|
|
" lnum set a lower bound of 'num' ply,",
|
|
" unum set an upper bound of 'num' ply,",
|
|
" otherwise num (or enum) means equal-to 'num' ply.",
|
|
"-P -- don't match permutations of the textual variations (-v).",
|
|
"-Rtagorder -- Use the tag ordering specified in the file tagorder.",
|
|
"-r -- report any errors but don't extract.",
|
|
"-S -- Use a simple soundex algorithm for some tag matches. If used",
|
|
" this option must precede the -t or -T options.",
|
|
"-s -- silent mode: don't report each game as it is extracted.",
|
|
"-ttagfile -- file of player, date, result or FEN extraction criteria.",
|
|
"-Tcriterion -- player, date, eco code, hashcode, FEN position, annotator or result, extraction criteria.",
|
|
"-U -- don't output games that only occur once. (See -d).",
|
|
"-vvariations -- the file variations contains the textual lines of interest.",
|
|
"-V -- don't include variations in the output. Ordinarily these are retained.",
|
|
"-wwidth -- set width as an approximate line width for output.",
|
|
"-W[cm|epd|halg|lalg|elalg|xlalg|xolalg|san] -- specify the output format to use.",
|
|
" Default is SAN.",
|
|
" -W means use the input format.",
|
|
" -Wcm is (a possibly obsolete) ChessMaster format.",
|
|
" -Wepd is EPD format.",
|
|
" -Wfen is FEN format.",
|
|
" -Wsan[PNBRQK] for language specific output.",
|
|
" -Whalg is hyphenated long algebraic.",
|
|
" -Wlalg is long algebraic.",
|
|
" -Welalg is enhanced long algebraic.",
|
|
" -Wxlalg is enhanced long algebraic with x for captures and - for non capture moves.",
|
|
" -Wxolalg is -Wxlalg but with O-O and O-O-O for castling.",
|
|
" -Wuci is output compatible with the UCI protocol.",
|
|
"-xvariations -- the file variations contains the lines resulting in",
|
|
" positions of interest.",
|
|
"-yfile -- file contains a material balance of interest.",
|
|
"-zfile -- file contains a material balance of interest.",
|
|
"-Z -- use the file virtual.tmp as an external hash table for duplicates.",
|
|
" Use when MallocOrDie messages occur with big datasets.",
|
|
|
|
"",
|
|
|
|
"--50 - only output games that include fifty moves with no capture or pawn move.",
|
|
"--75 - only output games that include seventy-five moves with no capture or pawn move.",
|
|
"--addfencastling - add potentially missing castling rights to FEN tags",
|
|
"--addhashcode - output a HashCode tag",
|
|
"--addlabeltag - output a MatchLabel tag with FENPattern",
|
|
"--addmatchtag - output a MaterialMatch tag with -z",
|
|
"--allownullmoves - allow NULL moves in the main line",
|
|
"--append - see -a",
|
|
"--btm - match position only if Black is to move (see -t)",
|
|
"--checkfile - see -c",
|
|
"--checkmate - see -M",
|
|
"--commentlines - output each comment on a separate line",
|
|
"--deletesamesetup - suppress games with the same initial position as one already processed",
|
|
"--detag tag - don't include tag in the output",
|
|
"--dropbefore - drop opening ply before a matching comment string",
|
|
"--dropply - drop the given number of ply from the beginning of the game",
|
|
"--duplicates - see -d",
|
|
"--evaluation - include a position evaluation after each move",
|
|
"--fencomments - include a FEN string after each move",
|
|
"--fenpattern pattern - match games reaching a position matching the given FEN pattern",
|
|
"--fenpatterni pattern - match games reaching a position matching the given FEN pattern for either side",
|
|
"--fifty - only output games that include fifty moves with no capture or pawn move.",
|
|
"--firstgame N - start matching from game number N (default 1).",
|
|
"--fixresulttags - correct Result tags that conflict with the game outcome or terminating result.",
|
|
"--fixtagstrings - attempt to correct tag strings that are not properly terminated.",
|
|
"--fuzzydepth plies - positional duplicates match",
|
|
"--gamelimit N - only process up to and including game number N.",
|
|
"--hashcomments - include a hashcode string after each move",
|
|
"--help - see -h",
|
|
"--json - output the game in JSON format",
|
|
"--keepbroken - retain games with errors",
|
|
"--lichesscommentfix - move comments at the start of a variation to after the first move of the variation.",
|
|
"--linelength - see -w",
|
|
"--linenumbers marker - include a comment with the source line numbers of each game { marker:start:end }",
|
|
"--matchplylimit - maximum ply depth to search for positional matches",
|
|
"--markmatches - mark positional and material matches with a comment; see -t, -v, and -z",
|
|
"--materialy material - material is a string describing a material balance; see -y",
|
|
"--materialz material - material is a string describing a material balance; see -z",
|
|
"--minmoves N - only output games with at least N moves.",
|
|
"--minply N - only output games with at least N ply.",
|
|
"--maxmoves N - only output games with at N or fewer moves.",
|
|
"--maxply N - only output games with at N or fewer ply.",
|
|
"--nestedcomments - allow nested comments.",
|
|
"--nobadresults - reject games with inconsistent result indications.",
|
|
"--nochecks - don't output + and # after moves.",
|
|
"--nocomments - see -C",
|
|
"--noduplicates - see -D",
|
|
"--nofauxep - don't output ep squares in FEN when the capture is not possible",
|
|
"--nomovenumbers - don't output move numbers.",
|
|
"--nonags - see -N",
|
|
"--noresults - don't output results.",
|
|
"--nosetuptags - don't match games with a SetUp tag.",
|
|
"--notags - don't output any tags.",
|
|
"--nounique - see -U",
|
|
"--novars - see -V",
|
|
"--onlysetuptags - only match games with a SetUp tag.",
|
|
"--output - see -o",
|
|
"--plycount - include a PlyCount tag.",
|
|
"--plylimit - limit the number of plies output.",
|
|
"--quiescent N - position quiescence length (default 0)",
|
|
"--quiet - No status processing output (see, also, -s).",
|
|
"--repetition - only output games that include 3-fold repetition.",
|
|
"--repetition5 - only output games that include 5-fold repetition.",
|
|
"--selectonly range[,range ...] - only output the selected matched game(s)",
|
|
"--seven - see -7",
|
|
"--seventyfive - only output games that include seventy-five moves with no capture or pawn move.",
|
|
"--skipmatching range[,range ...] - don't output the selected matched game(s)",
|
|
"--splitvariants [depth] - output each variation (to the given depth) as a separate game.",
|
|
"--stalemate - only output games that end in stalemate.",
|
|
"--startply N - only start matching after N ply (N >= 1).",
|
|
"--stopafter N - stop after matching N games (N > 0)",
|
|
"--tagsubstr - match in any part of a tag (see -T and -t).",
|
|
"--totalplycount - include a tag with the total number of plies in a game.",
|
|
"--underpromotion - match only games that contain an underpromotion.",
|
|
"--version - print the current version number and exit.",
|
|
"--wtm - match position only if White is to move (see -t)",
|
|
"--xroster - don't output tags not included with the -R option (see -R).",
|
|
|
|
/* Must be NULL terminated. */
|
|
(char *) NULL,
|
|
};
|
|
|
|
const char **data = help_data;
|
|
|
|
fprintf(GlobalState.logfile,
|
|
"pgn-extract %s (%s): a Portable Game Notation (PGN) manipulator.\n",
|
|
CURRENT_VERSION, __DATE__);
|
|
fprintf(GlobalState.logfile,
|
|
"Copyright (C) 1994-2022 David J. Barnes (d.j.barnes@kent.ac.uk)\n");
|
|
fprintf(GlobalState.logfile, "%s\n\n", URL);
|
|
fprintf(GlobalState.logfile, "Usage: pgn-extract [arguments] [file.pgn ...]\n");
|
|
|
|
for (; *data != NULL; data++) {
|
|
fprintf(GlobalState.logfile, "%s\n", *data);
|
|
}
|
|
exit(1);
|
|
}
|
|
|
|
static void
|
|
read_args_file(const char *infile)
|
|
{
|
|
char *line;
|
|
FILE *fp = fopen(infile, "r");
|
|
|
|
if (fp == NULL) {
|
|
fprintf(GlobalState.logfile, "Cannot open %s for reading.\n", infile);
|
|
exit(1);
|
|
}
|
|
else {
|
|
ArgType linetype = NO_ARGUMENT_MATCH;
|
|
ArgType nexttype;
|
|
while ((line = read_line(fp)) != NULL) {
|
|
if (blank_line(line)) {
|
|
(void) free((void *) line);
|
|
continue;
|
|
}
|
|
nexttype = classify_arg(line);
|
|
if (nexttype == NO_ARGUMENT_MATCH) {
|
|
if (*line == argument_prefix[0]) {
|
|
/* Treat the line as a source file name. */
|
|
add_filename_to_source_list(&line[1], NORMALFILE);
|
|
}
|
|
else if (linetype != NO_ARGUMENT_MATCH) {
|
|
/* Handle the line. */
|
|
switch (linetype) {
|
|
case MOVES_ARGUMENT:
|
|
add_textual_variation_from_line(line);
|
|
break;
|
|
case POSITIONS_ARGUMENT:
|
|
add_positional_variation_from_line(line);
|
|
break;
|
|
case TAGS_ARGUMENT:
|
|
process_tag_line(infile, line);
|
|
break;
|
|
case TAG_ROSTER_ARGUMENT:
|
|
process_roster_line(line);
|
|
break;
|
|
case ENDINGS_ARGUMENT:
|
|
case ENDINGS_COLOURED_ARGUMENT:
|
|
process_material_description(line, linetype == ENDINGS_ARGUMENT, FALSE);
|
|
(void) free((void *) line);
|
|
break;
|
|
default:
|
|
fprintf(GlobalState.logfile,
|
|
"Internal error: unknown linetype %d in read_args_file\n",
|
|
linetype);
|
|
(void) free((void *) line);
|
|
exit(-1);
|
|
}
|
|
}
|
|
else {
|
|
/* It should have been a line applying to the
|
|
* current linetype.
|
|
*/
|
|
fprintf(GlobalState.logfile,
|
|
"Missing argument type for line %s in the argument file.\n",
|
|
line);
|
|
exit(1);
|
|
}
|
|
}
|
|
else {
|
|
switch (nexttype) {
|
|
/* Arguments with a possible additional
|
|
* argument value.
|
|
* All of these apply only to the current
|
|
* line in the argument file.
|
|
*/
|
|
case WRITE_TO_OUTPUT_FILE_ARGUMENT:
|
|
case APPEND_TO_OUTPUT_FILE_ARGUMENT:
|
|
case WRITE_TO_LOG_FILE_ARGUMENT:
|
|
case APPEND_TO_LOG_FILE_ARGUMENT:
|
|
case DUPLICATES_FILE_ARGUMENT:
|
|
case USE_ECO_FILE_ARGUMENT:
|
|
case CHECK_FILE_ARGUMENT:
|
|
case FILE_OF_FILES_ARGUMENT:
|
|
case MOVE_BOUNDS_ARGUMENT:
|
|
case PLY_BOUNDS_ARGUMENT:
|
|
case GAMES_PER_FILE_ARGUMENT:
|
|
case ECO_OUTPUT_LEVEL_ARGUMENT:
|
|
case FILE_OF_ARGUMENTS_ARGUMENT:
|
|
case NON_MATCHING_GAMES_ARGUMENT:
|
|
case TAG_EXTRACTION_ARGUMENT:
|
|
case LINE_WIDTH_ARGUMENT:
|
|
case OUTPUT_FORMAT_ARGUMENT:
|
|
process_argument(line[argument_prefix_len],
|
|
&line[argument_prefix_len + 1]);
|
|
linetype = NO_ARGUMENT_MATCH;
|
|
break;
|
|
case LONG_FORM_ARGUMENT:
|
|
{
|
|
char *arg = &line[argument_prefix_len + 1];
|
|
char *space = strchr(arg, ' ');
|
|
if (space != NULL) {
|
|
/* We need to drop an associated value from arg. */
|
|
int arglen = space - arg;
|
|
char *just_arg = (char *) malloc_or_die(arglen + 1);
|
|
strncpy(just_arg, arg, arglen);
|
|
just_arg[arglen] = '\0';
|
|
process_long_form_argument(just_arg,
|
|
skip_leading_spaces(space));
|
|
(void) free((void *) just_arg);
|
|
}
|
|
else {
|
|
process_long_form_argument(arg, "");
|
|
linetype = NO_ARGUMENT_MATCH;
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* Arguments with no additional
|
|
* argument value.
|
|
* All of these apply only to the current
|
|
* line in the argument file.
|
|
*/
|
|
case SEVEN_TAG_ROSTER_ARGUMENT:
|
|
case HELP_ARGUMENT:
|
|
case ALTERNATIVE_HELP_ARGUMENT:
|
|
case DONT_KEEP_COMMENTS_ARGUMENT:
|
|
case DONT_KEEP_DUPLICATES_ARGUMENT:
|
|
case DONT_MATCH_PERMUTATIONS_ARGUMENT:
|
|
case DONT_KEEP_NAGS_ARGUMENT:
|
|
case CHECK_ONLY_ARGUMENT:
|
|
case KEEP_SILENT_ARGUMENT:
|
|
case USE_SOUNDEX_ARGUMENT:
|
|
case MATCH_CHECKMATE_ARGUMENT:
|
|
case SUPPRESS_ORIGINALS_ARGUMENT:
|
|
case DONT_KEEP_VARIATIONS_ARGUMENT:
|
|
case USE_VIRTUAL_HASH_TABLE_ARGUMENT:
|
|
process_argument(line[argument_prefix_len], "");
|
|
linetype = NO_ARGUMENT_MATCH;
|
|
break;
|
|
|
|
/* Arguments whose values persist beyond
|
|
* the current line.
|
|
*/
|
|
case ENDINGS_ARGUMENT:
|
|
case ENDINGS_COLOURED_ARGUMENT:
|
|
case HASHCODE_MATCH_ARGUMENT:
|
|
case MOVES_ARGUMENT:
|
|
case OUTPUT_FEN_STRING_ARGUMENT:
|
|
case POSITIONS_ARGUMENT:
|
|
case TAG_ROSTER_ARGUMENT:
|
|
process_argument(line[argument_prefix_len],
|
|
&line[argument_prefix_len + 1]);
|
|
case TAGS_ARGUMENT:
|
|
/* Apply this type to subsequent lines. */
|
|
linetype = nexttype;
|
|
break;
|
|
default:
|
|
linetype = nexttype;
|
|
break;
|
|
}
|
|
(void) free((void *) line);
|
|
}
|
|
}
|
|
(void) fclose(fp);
|
|
}
|
|
}
|
|
|
|
/* Determine which (if any) type of argument is
|
|
* indicated by the contents of the current line.
|
|
* Arguments are assumed to start with the prefix ":-"
|
|
*/
|
|
static ArgType
|
|
classify_arg(const char *line)
|
|
{
|
|
/* Valid arguments must have at least one character beyond
|
|
* the prefix.
|
|
*/
|
|
static const size_t min_argument_length = 1 + sizeof (argument_prefix) - 1;
|
|
size_t line_length = strlen(line);
|
|
|
|
/* Check for a line of the form:
|
|
* :-argument
|
|
*/
|
|
if ((strncmp(line, argument_prefix, argument_prefix_len) == 0) &&
|
|
(line_length >= min_argument_length)) {
|
|
char argument_letter = line[argument_prefix_len];
|
|
switch (argument_letter) {
|
|
case TAGS_ARGUMENT:
|
|
case MOVES_ARGUMENT:
|
|
case POSITIONS_ARGUMENT:
|
|
case ENDINGS_ARGUMENT:
|
|
case ENDINGS_COLOURED_ARGUMENT:
|
|
case TAG_EXTRACTION_ARGUMENT:
|
|
case LINE_WIDTH_ARGUMENT:
|
|
case OUTPUT_FORMAT_ARGUMENT:
|
|
case SEVEN_TAG_ROSTER_ARGUMENT:
|
|
case FILE_OF_ARGUMENTS_ARGUMENT:
|
|
case NON_MATCHING_GAMES_ARGUMENT:
|
|
case DONT_KEEP_COMMENTS_ARGUMENT:
|
|
case DONT_KEEP_DUPLICATES_ARGUMENT:
|
|
case DONT_KEEP_NAGS_ARGUMENT:
|
|
case DONT_MATCH_PERMUTATIONS_ARGUMENT:
|
|
case OUTPUT_FEN_STRING_ARGUMENT:
|
|
case CHECK_ONLY_ARGUMENT:
|
|
case KEEP_SILENT_ARGUMENT:
|
|
case USE_SOUNDEX_ARGUMENT:
|
|
case MATCH_CHECKMATE_ARGUMENT:
|
|
case SUPPRESS_ORIGINALS_ARGUMENT:
|
|
case DONT_KEEP_VARIATIONS_ARGUMENT:
|
|
case WRITE_TO_OUTPUT_FILE_ARGUMENT:
|
|
case WRITE_TO_LOG_FILE_ARGUMENT:
|
|
case APPEND_TO_LOG_FILE_ARGUMENT:
|
|
case APPEND_TO_OUTPUT_FILE_ARGUMENT:
|
|
case DUPLICATES_FILE_ARGUMENT:
|
|
case USE_ECO_FILE_ARGUMENT:
|
|
case CHECK_FILE_ARGUMENT:
|
|
case FILE_OF_FILES_ARGUMENT:
|
|
case MOVE_BOUNDS_ARGUMENT:
|
|
case PLY_BOUNDS_ARGUMENT:
|
|
case GAMES_PER_FILE_ARGUMENT:
|
|
case ECO_OUTPUT_LEVEL_ARGUMENT:
|
|
case HELP_ARGUMENT:
|
|
case ALTERNATIVE_HELP_ARGUMENT:
|
|
case TAG_ROSTER_ARGUMENT:
|
|
case LONG_FORM_ARGUMENT:
|
|
case HASHCODE_MATCH_ARGUMENT:
|
|
return (ArgType) argument_letter;
|
|
default:
|
|
fprintf(GlobalState.logfile,
|
|
"Unrecognized argument: %s in the argument file.\n",
|
|
line);
|
|
exit(1);
|
|
return NO_ARGUMENT_MATCH;
|
|
}
|
|
}
|
|
else {
|
|
return NO_ARGUMENT_MATCH;
|
|
}
|
|
}
|
|
|
|
/* Process the argument character and its associated value.
|
|
* This function processes arguments from the command line and
|
|
* from an argument file associated with the -A argument.
|
|
*
|
|
* An argument -ofile.pgn would be passed in as:
|
|
* 'o' and "file.pgn".
|
|
* A zero-length string for associated_value is not necessarily
|
|
* an error, e.g. -e has an optional following filename.
|
|
* NB: If the associated_value is to be used beyond this function,
|
|
* it must be copied.
|
|
*/
|
|
void
|
|
process_argument(char arg_letter, const char *associated_value)
|
|
{
|
|
/* Provide an alias for associated_value because it will
|
|
* often represent a file name.
|
|
*/
|
|
const char *filename = skip_leading_spaces(associated_value);
|
|
|
|
switch (arg_letter) {
|
|
case WRITE_TO_OUTPUT_FILE_ARGUMENT:
|
|
case APPEND_TO_OUTPUT_FILE_ARGUMENT:
|
|
if (GlobalState.ECO_level > 0) {
|
|
fprintf(GlobalState.logfile, "-%c conflicts with -E\n",
|
|
arg_letter);
|
|
}
|
|
else if (GlobalState.games_per_file > 0) {
|
|
fprintf(GlobalState.logfile, "-%c conflicts with -#\n",
|
|
arg_letter);
|
|
}
|
|
else if (GlobalState.output_filename != NULL) {
|
|
fprintf(GlobalState.logfile,
|
|
"-%c: File %s has already been selected for output.\n",
|
|
arg_letter, GlobalState.output_filename);
|
|
exit(1);
|
|
}
|
|
else if (*filename == '\0') {
|
|
fprintf(GlobalState.logfile, "Usage: -%cfilename.\n", arg_letter);
|
|
exit(1);
|
|
}
|
|
else {
|
|
if (GlobalState.outputfile != NULL) {
|
|
(void) fclose(GlobalState.outputfile);
|
|
}
|
|
if (arg_letter == WRITE_TO_OUTPUT_FILE_ARGUMENT) {
|
|
GlobalState.outputfile = must_open_file(filename, "w");
|
|
}
|
|
else {
|
|
GlobalState.outputfile = must_open_file(filename, "a");
|
|
}
|
|
GlobalState.output_filename = filename;
|
|
}
|
|
break;
|
|
case WRITE_TO_LOG_FILE_ARGUMENT:
|
|
case APPEND_TO_LOG_FILE_ARGUMENT:
|
|
/* Take precautions against multiple log files. */
|
|
if ((GlobalState.logfile != stderr) && (GlobalState.logfile != NULL)) {
|
|
(void) fclose(GlobalState.logfile);
|
|
}
|
|
if (arg_letter == WRITE_TO_LOG_FILE_ARGUMENT) {
|
|
GlobalState.logfile = fopen(filename, "w");
|
|
}
|
|
else {
|
|
GlobalState.logfile = fopen(filename, "a");
|
|
}
|
|
if (GlobalState.logfile == NULL) {
|
|
fprintf(stderr, "Unable to open %s for writing.\n", filename);
|
|
GlobalState.logfile = stderr;
|
|
}
|
|
break;
|
|
case DUPLICATES_FILE_ARGUMENT:
|
|
if (*filename == '\0') {
|
|
fprintf(GlobalState.logfile, "Usage: -%cfilename.\n", arg_letter);
|
|
exit(1);
|
|
}
|
|
else if (GlobalState.suppress_duplicates) {
|
|
fprintf(GlobalState.logfile,
|
|
"-%c clashes with the -%c flag.\n", arg_letter,
|
|
DONT_KEEP_DUPLICATES_ARGUMENT);
|
|
exit(1);
|
|
}
|
|
else {
|
|
GlobalState.duplicate_file = must_open_file(filename, "w");
|
|
}
|
|
break;
|
|
case USE_ECO_FILE_ARGUMENT:
|
|
GlobalState.add_ECO = TRUE;
|
|
if (*filename != '\0') {
|
|
GlobalState.eco_file = copy_string(filename);
|
|
}
|
|
else if ((filename = getenv("ECO_FILE")) != NULL) {
|
|
GlobalState.eco_file = filename;
|
|
}
|
|
else {
|
|
/* Use the default which is already set up. */
|
|
}
|
|
initEcoTable();
|
|
break;
|
|
case ECO_OUTPUT_LEVEL_ARGUMENT:
|
|
{
|
|
unsigned level;
|
|
|
|
if (GlobalState.output_filename != NULL) {
|
|
fprintf(GlobalState.logfile,
|
|
"-%c: File %s has already been selected for output.\n",
|
|
arg_letter,
|
|
GlobalState.output_filename);
|
|
exit(1);
|
|
}
|
|
else if (GlobalState.games_per_file > 0) {
|
|
fprintf(GlobalState.logfile,
|
|
"-%c conflicts with -#.\n",
|
|
arg_letter);
|
|
exit(1);
|
|
}
|
|
else if (sscanf(associated_value, "%u", &level) != 1) {
|
|
fprintf(GlobalState.logfile,
|
|
"-%c requires a number attached, e.g., -%c1.\n",
|
|
arg_letter, arg_letter);
|
|
exit(1);
|
|
}
|
|
else if ((level < MIN_ECO_LEVEL) || (level > MAX_ECO_LEVEL)) {
|
|
fprintf(GlobalState.logfile,
|
|
"-%c level should be between %u and %u.\n",
|
|
MIN_ECO_LEVEL, MAX_ECO_LEVEL, arg_letter);
|
|
exit(1);
|
|
}
|
|
else {
|
|
GlobalState.ECO_level = level;
|
|
}
|
|
}
|
|
break;
|
|
case CHECK_FILE_ARGUMENT:
|
|
if (*filename != '\0') {
|
|
/* See if it is a single PGN file, or a list
|
|
* of files.
|
|
*/
|
|
size_t len = strlen(filename);
|
|
/* Check for a .PGN suffix. */
|
|
const char *suffix = output_file_suffix(SAN);
|
|
|
|
if ((len > strlen(suffix)) &&
|
|
(stringcompare(&filename[len - strlen(suffix)],
|
|
suffix) == 0)) {
|
|
add_filename_to_source_list(filename, CHECKFILE);
|
|
}
|
|
else {
|
|
FILE *fp = must_open_file(filename, "r");
|
|
add_filename_list_from_file(fp, CHECKFILE);
|
|
(void) fclose(fp);
|
|
}
|
|
}
|
|
break;
|
|
case FILE_OF_FILES_ARGUMENT:
|
|
if (*filename != '\0') {
|
|
FILE *fp = must_open_file(filename, "r");
|
|
add_filename_list_from_file(fp, NORMALFILE);
|
|
(void) fclose(fp);
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile, "Filename expected with -%c\n",
|
|
arg_letter);
|
|
}
|
|
break;
|
|
case MOVE_BOUNDS_ARGUMENT:
|
|
case PLY_BOUNDS_ARGUMENT:
|
|
{
|
|
/* Bounds on the number of moves to be found.
|
|
* "l#" means less-than-or-equal-to.
|
|
* "g#" means greater-than-or-equal-to.
|
|
* Otherwise "#" (or "e#") means that number.
|
|
*/
|
|
/* Equal by default. */
|
|
char which = 'e';
|
|
unsigned number;
|
|
Boolean Ok = TRUE;
|
|
const char *bound = associated_value;
|
|
|
|
switch (*bound) {
|
|
case 'l':
|
|
case 'u':
|
|
case 'e':
|
|
which = *bound;
|
|
bound++;
|
|
break;
|
|
default:
|
|
if (!isdigit((int) *bound)) {
|
|
fprintf(GlobalState.logfile,
|
|
"-%c must be followed by e, l, or u.\n",
|
|
arg_letter);
|
|
Ok = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
if (Ok && (sscanf(bound, "%u", &number) == 1)) {
|
|
Ok = set_move_bounds(arg_letter, which, number);
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"-%c should be in the form -%c[elu]number.\n",
|
|
arg_letter, arg_letter);
|
|
Ok = FALSE;
|
|
}
|
|
if (!Ok) {
|
|
exit(1);
|
|
}
|
|
}
|
|
break;
|
|
case GAMES_PER_FILE_ARGUMENT:
|
|
if (GlobalState.ECO_level > 0) {
|
|
fprintf(GlobalState.logfile,
|
|
"-%c conflicts with -E.\n", arg_letter);
|
|
exit(1);
|
|
}
|
|
else if (GlobalState.output_filename != NULL) {
|
|
fprintf(GlobalState.logfile,
|
|
"-%c: File %s has already been selected for output.\n",
|
|
arg_letter,
|
|
GlobalState.output_filename);
|
|
exit(1);
|
|
}
|
|
else {
|
|
if(strchr(associated_value, ',') != NULL) {
|
|
unsigned games, file_number;
|
|
if(sscanf(associated_value, "%u,%u", &games, &file_number) == 2) {
|
|
GlobalState.games_per_file = games;
|
|
GlobalState.next_file_number = file_number;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"-%c should be followed by either one or two unsigned integers.\n",
|
|
arg_letter);
|
|
exit(1);
|
|
}
|
|
}
|
|
else if (sscanf(associated_value, "%u",
|
|
&GlobalState.games_per_file) != 1) {
|
|
fprintf(GlobalState.logfile,
|
|
"-%c should be followed by an unsigned integer.\n",
|
|
arg_letter);
|
|
exit(1);
|
|
}
|
|
else {
|
|
/* Value set. */
|
|
}
|
|
}
|
|
break;
|
|
case FILE_OF_ARGUMENTS_ARGUMENT:
|
|
if (*filename != '\0') {
|
|
/* @@@ Potentially recursive call. Is this safe? */
|
|
read_args_file(filename);
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile, "Usage: -%cfilename.\n",
|
|
arg_letter);
|
|
}
|
|
break;
|
|
case NON_MATCHING_GAMES_ARGUMENT:
|
|
if (*filename != '\0') {
|
|
if (GlobalState.non_matching_file != NULL) {
|
|
(void) fclose(GlobalState.non_matching_file);
|
|
}
|
|
GlobalState.non_matching_file = must_open_file(filename, "w");
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile, "Usage: -%cfilename.\n", arg_letter);
|
|
exit(1);
|
|
}
|
|
break;
|
|
case TAG_EXTRACTION_ARGUMENT:
|
|
/* A single tag extraction criterion. */
|
|
extract_tag_argument(associated_value);
|
|
break;
|
|
case LINE_WIDTH_ARGUMENT:
|
|
{ /* Specify an output line width. */
|
|
unsigned length;
|
|
|
|
if (sscanf(associated_value, "%u", &length) > 0) {
|
|
set_output_line_length(length);
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"-%c should be followed by an unsigned integer.\n",
|
|
arg_letter);
|
|
exit(1);
|
|
}
|
|
}
|
|
break;
|
|
case HELP_ARGUMENT:
|
|
usage_and_exit();
|
|
break;
|
|
case OUTPUT_FORMAT_ARGUMENT:
|
|
/* Whether to use the source form of moves or
|
|
* rewrite them into another format.
|
|
*/
|
|
{
|
|
OutputFormat format = which_output_format(associated_value);
|
|
if (format == UCI) {
|
|
/* Rewrite the game in a format suitable for input to
|
|
* a UCI-compatible engine.
|
|
* This is actually LALG but involves adjusting a lot of
|
|
* the other statuses, too.
|
|
*/
|
|
GlobalState.keep_NAGs = FALSE;
|
|
GlobalState.keep_comments = FALSE;
|
|
GlobalState.keep_move_numbers = FALSE;
|
|
GlobalState.keep_checks = FALSE;
|
|
GlobalState.keep_variations = FALSE;
|
|
/* @@@ Warning: arbitrary value. */
|
|
set_output_line_length(5000);
|
|
}
|
|
GlobalState.output_format = format;
|
|
}
|
|
break;
|
|
case SEVEN_TAG_ROSTER_ARGUMENT:
|
|
if ((GlobalState.tag_output_format == ALL_TAGS ||
|
|
GlobalState.tag_output_format == SEVEN_TAG_ROSTER) &&
|
|
!GlobalState.only_output_wanted_tags) {
|
|
GlobalState.tag_output_format = SEVEN_TAG_ROSTER;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"-%c clashes with another roster-related argument.\n",
|
|
SEVEN_TAG_ROSTER_ARGUMENT);
|
|
exit(1);
|
|
}
|
|
break;
|
|
case DONT_KEEP_COMMENTS_ARGUMENT:
|
|
GlobalState.keep_comments = FALSE;
|
|
break;
|
|
case DONT_KEEP_DUPLICATES_ARGUMENT:
|
|
/* Make sure that this doesn't clash with -d. */
|
|
if (GlobalState.duplicate_file == NULL) {
|
|
GlobalState.suppress_duplicates = TRUE;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"-%c clashes with -%c flag.\n",
|
|
DONT_KEEP_DUPLICATES_ARGUMENT,
|
|
DUPLICATES_FILE_ARGUMENT);
|
|
exit(1);
|
|
}
|
|
break;
|
|
case DONT_MATCH_PERMUTATIONS_ARGUMENT:
|
|
GlobalState.match_permutations = FALSE;
|
|
break;
|
|
case DONT_KEEP_NAGS_ARGUMENT:
|
|
GlobalState.keep_NAGs = FALSE;
|
|
break;
|
|
case OUTPUT_FEN_STRING_ARGUMENT:
|
|
/* Output a FEN string at one or more positions.
|
|
* Default is at the end of the game.
|
|
* The FEN string is displayed in a comment.
|
|
*/
|
|
if(*associated_value != '\0') {
|
|
if(!GlobalState.add_FEN_comments) {
|
|
GlobalState.FEN_comment_pattern = copy_string(associated_value);
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile, "-%c%s conflicts with --%s\n",
|
|
OUTPUT_FEN_STRING_ARGUMENT, associated_value,
|
|
"fencomments");
|
|
}
|
|
}
|
|
if (GlobalState.add_FEN_comments) {
|
|
/* Already implied. */
|
|
GlobalState.output_FEN_string = FALSE;
|
|
}
|
|
else {
|
|
GlobalState.output_FEN_string = TRUE;
|
|
}
|
|
break;
|
|
case CHECK_ONLY_ARGUMENT:
|
|
/* Report errors, but don't convert. */
|
|
GlobalState.check_only = TRUE;
|
|
break;
|
|
case KEEP_SILENT_ARGUMENT:
|
|
/* Turn off progress reporting
|
|
* and only report the number of games processed.
|
|
*/
|
|
GlobalState.verbosity = 1;
|
|
break;
|
|
case USE_SOUNDEX_ARGUMENT:
|
|
/* Use soundex matches for player tags. */
|
|
GlobalState.use_soundex = TRUE;
|
|
break;
|
|
case MATCH_CHECKMATE_ARGUMENT:
|
|
/* Match only games that end in checkmate. */
|
|
GlobalState.match_only_checkmate = TRUE;
|
|
break;
|
|
case SUPPRESS_ORIGINALS_ARGUMENT:
|
|
GlobalState.suppress_originals = TRUE;
|
|
break;
|
|
case DONT_KEEP_VARIATIONS_ARGUMENT:
|
|
if(!GlobalState.split_variants) {
|
|
GlobalState.keep_variations = FALSE;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"-%c clashes with the --splitvariants flag.\n", arg_letter);
|
|
exit(1);
|
|
}
|
|
break;
|
|
case USE_VIRTUAL_HASH_TABLE_ARGUMENT:
|
|
GlobalState.use_virtual_hash_table = TRUE;
|
|
break;
|
|
|
|
case TAGS_ARGUMENT:
|
|
if (*filename != '\0') {
|
|
read_tag_file(filename);
|
|
}
|
|
break;
|
|
case TAG_ROSTER_ARGUMENT:
|
|
if (*filename != '\0') {
|
|
read_tag_roster_file(filename);
|
|
}
|
|
break;
|
|
case MOVES_ARGUMENT:
|
|
if (*filename != '\0') {
|
|
/* Where the list of variations of interest are kept. */
|
|
FILE *variation_file = must_open_file(filename, "r");
|
|
/* We wish to search for particular variations. */
|
|
add_textual_variations_from_file(variation_file);
|
|
fclose(variation_file);
|
|
}
|
|
break;
|
|
case POSITIONS_ARGUMENT:
|
|
if (*filename != '\0') {
|
|
FILE *variation_file = must_open_file(filename, "r");
|
|
/* We wish to search for positional variations. */
|
|
add_positional_variations_from_file(variation_file);
|
|
fclose(variation_file);
|
|
}
|
|
break;
|
|
case ENDINGS_ARGUMENT:
|
|
case ENDINGS_COLOURED_ARGUMENT:
|
|
if (*filename != '\0') {
|
|
if (!build_endings(filename,
|
|
arg_letter == ENDINGS_ARGUMENT)) {
|
|
exit(1);
|
|
}
|
|
}
|
|
break;
|
|
case HASHCODE_MATCH_ARGUMENT:
|
|
if(save_polyglot_hashcode(associated_value)) {
|
|
GlobalState.positional_variations = TRUE;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"-%c must be followed by a hexadecimal hash value rather than %s.\n",
|
|
arg_letter, associated_value);
|
|
exit(1);
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(GlobalState.logfile,
|
|
"Unrecognized argument -%c\n", arg_letter);
|
|
}
|
|
}
|
|
|
|
/* The argument has been expressed in a long-form, i.e. prefixed
|
|
* by --
|
|
* Decode and act on the argument.
|
|
* The associated_value will only be required by some arguments.
|
|
* Return whether one or both were required.
|
|
*/
|
|
int
|
|
process_long_form_argument(const char *argument, const char *associated_value)
|
|
{
|
|
if (stringcompare(argument, "addfencastling") == 0) {
|
|
GlobalState.add_fen_castling = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "addhashcode") == 0) {
|
|
GlobalState.add_hashcode_tag = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "addlabeltag") == 0) {
|
|
GlobalState.add_matchlabel_tag = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "addmatchtag") == 0) {
|
|
GlobalState.add_match_tag = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "allownullmoves") == 0) {
|
|
GlobalState.allow_null_moves = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "append") == 0) {
|
|
process_argument(APPEND_TO_OUTPUT_FILE_ARGUMENT, associated_value);
|
|
return 2;
|
|
}
|
|
else if(stringcompare(argument, "btm") == 0) {
|
|
if(GlobalState.whose_move == EITHER_TO_MOVE) {
|
|
GlobalState.whose_move = BLACK_TO_MOVE;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile, "%s conflicts with previous setting of white to move.\n", argument);
|
|
}
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "checkfile") == 0) {
|
|
process_argument(CHECK_FILE_ARGUMENT, associated_value);
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "checkmate") == 0) {
|
|
process_argument(MATCH_CHECKMATE_ARGUMENT, "");
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "commentlines") == 0) {
|
|
GlobalState.separate_comment_lines = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "deletesamesetup") == 0) {
|
|
GlobalState.delete_same_setup = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "detag") == 0) {
|
|
/* Save the tag to be dropped. */
|
|
if (associated_value != NULL) {
|
|
suppress_tag(associated_value);
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a tag name following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "dropbefore") == 0) {
|
|
/* Save the comment string to be matched. */
|
|
if (associated_value != NULL) {
|
|
GlobalState.drop_comment_pattern = copy_string(associated_value);
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a string following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "dropply") == 0) {
|
|
/* Extract the number. */
|
|
int number = 0;
|
|
|
|
if (sscanf(associated_value, "%d", &number) == 1) {
|
|
GlobalState.drop_ply_number = number;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a number following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "duplicates") == 0) {
|
|
process_argument(DUPLICATES_FILE_ARGUMENT, associated_value);
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "evaluation") == 0) {
|
|
/* Output an evaluation is required with each move. */
|
|
GlobalState.output_evaluation = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "fencomments") == 0) {
|
|
if(GlobalState.FEN_comment_pattern == NULL) {
|
|
/* Output a FEN comment after each move. */
|
|
GlobalState.add_FEN_comments = TRUE;
|
|
/* Turn off any separate setting of output_FEN_comment. */
|
|
GlobalState.output_FEN_string = FALSE;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile, "--%s conflicts with -%cpattern",
|
|
argument, OUTPUT_FEN_STRING_ARGUMENT);
|
|
}
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "fenpattern") == 0) {
|
|
if(*associated_value != '\0') {
|
|
add_fen_pattern(associated_value, FALSE, "");
|
|
GlobalState.positional_variations = TRUE;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a pattern following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "fenpatterni") == 0) {
|
|
if(*associated_value != '\0') {
|
|
add_fen_pattern(associated_value, TRUE, "");
|
|
GlobalState.positional_variations = TRUE;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a pattern following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "fifty") == 0 || stringcompare(argument, "50") == 0) {
|
|
if(GlobalState.check_for_N_move_rule == 0) {
|
|
GlobalState.check_for_N_move_rule = 50;
|
|
return 1;
|
|
}
|
|
else if(GlobalState.check_for_N_move_rule == 50) {
|
|
return 1;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile, "--%s conflicts with a previous setting of %u.\n",
|
|
argument, GlobalState.check_for_N_move_rule);
|
|
exit(1);
|
|
}
|
|
}
|
|
else if (stringcompare(argument, "firstgame") == 0) {
|
|
/* Extract the number. */
|
|
unsigned long number;
|
|
|
|
if (sscanf(associated_value, "%lu", &number) == 1) {
|
|
if(number >= 1) {
|
|
if(number <= GlobalState.game_limit) {
|
|
GlobalState.first_game_number = number;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s %lu is incompatible with --gamelimit %lu.\n",
|
|
argument, number, GlobalState.game_limit);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a number following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "fixresulttags") == 0) {
|
|
GlobalState.fix_result_tags = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "fixtagstrings") == 0) {
|
|
GlobalState.fix_tag_strings = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "fuzzydepth") == 0) {
|
|
/* Extract the depth. */
|
|
unsigned depth = 0;
|
|
|
|
if (sscanf(associated_value, "%u", &depth) == 1) {
|
|
GlobalState.fuzzy_match_duplicates = TRUE;
|
|
GlobalState.fuzzy_match_depth = depth;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a positive number following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "hashcomments") == 0) {
|
|
/* Output a hashcode comment after each move. */
|
|
GlobalState.add_hashcode_comments = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "help") == 0) {
|
|
process_argument(HELP_ARGUMENT, "");
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "json") == 0) {
|
|
GlobalState.json_format = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "keepbroken") == 0) {
|
|
GlobalState.keep_broken_games = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "lichesscommentfix") == 0) {
|
|
GlobalState.lichess_comment_fix = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "linelength") == 0) {
|
|
process_argument(LINE_WIDTH_ARGUMENT,
|
|
associated_value);
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "linenumbers") == 0) {
|
|
/* Save the marker string to be output. */
|
|
if (associated_value != NULL) {
|
|
GlobalState.line_number_marker = copy_string(associated_value);
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a string following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "markmatches") == 0) {
|
|
if (*associated_value != '\0') {
|
|
GlobalState.add_position_match_comments = TRUE;
|
|
GlobalState.position_match_comment = copy_string(associated_value);
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a comment string following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "matchplylimit") == 0) {
|
|
unsigned limit = 0;
|
|
|
|
/* Extract the limit. */
|
|
if (sscanf(associated_value, "%u", &limit) == 1) {
|
|
if (limit > 0) {
|
|
if(limit >= GlobalState.depth_of_positional_search) {
|
|
GlobalState.depth_of_positional_search = limit;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s of %u conflicts with existing higher limit of %u\n",
|
|
argument,
|
|
limit,
|
|
GlobalState.depth_of_positional_search);
|
|
exit(1);
|
|
}
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a number greater than or equal to zero.\n", argument);
|
|
exit(1);
|
|
}
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a number following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "materialy") == 0) {
|
|
if (*associated_value != '\0') {
|
|
(void) process_material_description(associated_value, FALSE, FALSE);
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a string of material following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "materialz") == 0) {
|
|
if (*associated_value != '\0') {
|
|
(void) process_material_description(associated_value, TRUE, FALSE);
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a string of material following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "minmoves") == 0) {
|
|
/* Extract the number. */
|
|
unsigned number = 0;
|
|
|
|
if (sscanf(associated_value, "%u", &number) == 1) {
|
|
set_move_bounds(MOVE_BOUNDS_ARGUMENT, 'l', number);
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a number following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "gamelimit") == 0) {
|
|
/* Extract the number. */
|
|
unsigned long number = 0;
|
|
|
|
if (sscanf(associated_value, "%lu", &number) == 1) {
|
|
if(number >= GlobalState.first_game_number) {
|
|
GlobalState.game_limit = number;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s %lu is incompatible with --firstgame %lu.\n",
|
|
argument, number, GlobalState.first_game_number);
|
|
exit(1);
|
|
}
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a number following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "maxmoves") == 0) {
|
|
/* Extract the number. */
|
|
unsigned number = 0;
|
|
|
|
if (sscanf(associated_value, "%u", &number) == 1) {
|
|
set_move_bounds(MOVE_BOUNDS_ARGUMENT, 'u', number);
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a number following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "minply") == 0) {
|
|
/* Extract the number. */
|
|
unsigned number = 0;
|
|
|
|
if (sscanf(associated_value, "%u", &number) == 1) {
|
|
set_move_bounds(PLY_BOUNDS_ARGUMENT, 'l', number);
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a number following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "maxply") == 0) {
|
|
/* Extract the number. */
|
|
unsigned number = 0;
|
|
|
|
if (sscanf(associated_value, "%u", &number) == 1) {
|
|
set_move_bounds(PLY_BOUNDS_ARGUMENT, 'u', number);
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a number following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "nestedcomments") == 0) {
|
|
GlobalState.allow_nested_comments = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "nobadresults") == 0) {
|
|
GlobalState.reject_inconsistent_results = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "nochecks") == 0) {
|
|
GlobalState.keep_checks = FALSE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "nocomments") == 0) {
|
|
process_argument(DONT_KEEP_COMMENTS_ARGUMENT, "");
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "noduplicates") == 0) {
|
|
process_argument(DONT_KEEP_DUPLICATES_ARGUMENT, "");
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "nofauxep") == 0) {
|
|
GlobalState.suppress_redundant_ep_info = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "nomovenumbers") == 0) {
|
|
GlobalState.keep_move_numbers = FALSE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "nonags") == 0) {
|
|
process_argument(DONT_KEEP_NAGS_ARGUMENT, "");
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "nosetuptags") == 0) {
|
|
if (GlobalState.setup_status != SETUP_TAG_OK) {
|
|
fprintf(GlobalState.logfile, "--%s conflicts with --onlysetuptagso\n", argument);
|
|
exit(1);
|
|
}
|
|
GlobalState.setup_status = NO_SETUP_TAG;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "noresults") == 0) {
|
|
GlobalState.keep_results = FALSE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "notags") == 0) {
|
|
if (GlobalState.tag_output_format == ALL_TAGS ||
|
|
GlobalState.tag_output_format == NO_TAGS) {
|
|
GlobalState.tag_output_format = NO_TAGS;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--notags clashes with another roster-related argument.\n");
|
|
exit(1);
|
|
}
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "nounique") == 0) {
|
|
process_argument(SUPPRESS_ORIGINALS_ARGUMENT, "");
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "novars") == 0) {
|
|
process_argument(DONT_KEEP_VARIATIONS_ARGUMENT, "");
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "onlysetuptags") == 0) {
|
|
if (GlobalState.setup_status != SETUP_TAG_OK) {
|
|
fprintf(GlobalState.logfile, "--%s conflicts with --nosetuptags\n", argument);
|
|
exit(1);
|
|
}
|
|
GlobalState.setup_status = SETUP_TAG_ONLY;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "output") == 0) {
|
|
process_argument(WRITE_TO_OUTPUT_FILE_ARGUMENT, associated_value);
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "plycount") == 0) {
|
|
GlobalState.output_plycount = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "plylimit") == 0) {
|
|
int limit = 0;
|
|
|
|
/* Extract the limit. */
|
|
if (sscanf(associated_value, "%d", &limit) == 1) {
|
|
if (limit >= 0) {
|
|
GlobalState.output_ply_limit = limit;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a number greater than or equal to zero.\n", argument);
|
|
exit(1);
|
|
}
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a number following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "quiescent") == 0) {
|
|
int threshold = 0;
|
|
|
|
/* Extract the threshold. */
|
|
if (sscanf(associated_value, "%d", &threshold) == 1) {
|
|
if (threshold >= 0) {
|
|
GlobalState.quiescence_threshold = threshold;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a number greater than or equal to zero.\n", argument);
|
|
exit(1);
|
|
}
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a number following it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "quiet") == 0) {
|
|
/* No progress output at all. */
|
|
GlobalState.verbosity = 0;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "repetition") == 0) {
|
|
if(GlobalState.check_for_repetition == 0) {
|
|
GlobalState.check_for_repetition = 3;
|
|
return 1;
|
|
}
|
|
else if(GlobalState.check_for_repetition == 3) {
|
|
/* Duplicate. */
|
|
return 1;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile, "--%s conflicts with a previous setting.\n",
|
|
argument);
|
|
exit(1);
|
|
}
|
|
}
|
|
else if (stringcompare(argument, "repetition5") == 0) {
|
|
if(GlobalState.check_for_repetition == 0) {
|
|
GlobalState.check_for_repetition = 5;
|
|
return 1;
|
|
}
|
|
else if(GlobalState.check_for_repetition == 5) {
|
|
/* Duplicate. */
|
|
return 1;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile, "--%s clashes with a different setting.\n",
|
|
argument);
|
|
exit(1);
|
|
}
|
|
}
|
|
else if (stringcompare(argument, "selectonly") == 0) {
|
|
/* Extract the selected match numbers from a list. */
|
|
game_number *number_list = extract_game_number_list(associated_value);
|
|
if (number_list != NULL) {
|
|
GlobalState.matching_game_numbers = number_list;
|
|
GlobalState.next_game_number_to_output = number_list;
|
|
}
|
|
else {
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "seven") == 0) {
|
|
process_argument(SEVEN_TAG_ROSTER_ARGUMENT, "");
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "seventyfive") == 0 || stringcompare(argument, "75") == 0) {
|
|
if(GlobalState.check_for_N_move_rule == 0) {
|
|
GlobalState.check_for_N_move_rule = 75;
|
|
return 1;
|
|
}
|
|
else if(GlobalState.check_for_N_move_rule == 75) {
|
|
return 1;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile, "--%s conflicts with a previous setting of %u.\n",
|
|
argument, GlobalState.check_for_N_move_rule);
|
|
exit(1);
|
|
}
|
|
}
|
|
else if (stringcompare(argument, "skipmatching") == 0) {
|
|
/* Extract the selected match numbers from a list. */
|
|
game_number *number_list = extract_game_number_list(associated_value);
|
|
if (number_list != NULL) {
|
|
GlobalState.skip_game_numbers = number_list;
|
|
GlobalState.next_game_number_to_skip = number_list;
|
|
}
|
|
else {
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "splitvariants") == 0) {
|
|
if(GlobalState.keep_variations) {
|
|
GlobalState.split_variants = TRUE;
|
|
if(associated_value != NULL) {
|
|
unsigned limit;
|
|
if(sscanf(associated_value, "%u", &limit) == 1) {
|
|
GlobalState.split_depth_limit = limit;
|
|
return 2;
|
|
}
|
|
else {
|
|
return 1;
|
|
}
|
|
}
|
|
else {
|
|
return 1;
|
|
}
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s clashes with the -%c flag.\n", argument,
|
|
DONT_KEEP_VARIATIONS_ARGUMENT);
|
|
exit(1);
|
|
return 1;
|
|
}
|
|
}
|
|
else if (stringcompare(argument, "stalemate") == 0) {
|
|
GlobalState.match_only_stalemate = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "startply") == 0) {
|
|
if(associated_value != NULL) {
|
|
int limit;
|
|
if(sscanf(associated_value, "%d", &limit) == 1) {
|
|
if(limit >= 1) {
|
|
GlobalState.startply = (unsigned) limit;
|
|
return 2;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s must be greater than or equal to 1.\n", argument);
|
|
exit(1);
|
|
}
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a number greater than or equal to 1.\n", argument);
|
|
exit(1);
|
|
}
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a number greater than or equal to 1.\n", argument);
|
|
exit(1);
|
|
}
|
|
}
|
|
else if (stringcompare(argument, "stopafter") == 0) {
|
|
int limit = 0;
|
|
|
|
/* Extract the limit. */
|
|
if (sscanf(associated_value, "%d", &limit) == 1) {
|
|
if (limit > 0) {
|
|
GlobalState.maximum_matches = limit;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a number greater than zero.\n", argument);
|
|
exit(1);
|
|
}
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s requires a number greater than zero to follow it.\n", argument);
|
|
exit(1);
|
|
}
|
|
return 2;
|
|
}
|
|
else if (stringcompare(argument, "tagsubstr") == 0) {
|
|
GlobalState.tag_match_anywhere = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "totalplycount") == 0) {
|
|
GlobalState.output_total_plycount = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "underpromotion") == 0) {
|
|
GlobalState.match_underpromotion = TRUE;
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "version") == 0) {
|
|
fprintf(GlobalState.logfile, "pgn-extract %s\n", CURRENT_VERSION);
|
|
exit(0);
|
|
return 1;
|
|
}
|
|
else if(stringcompare(argument, "wtm") == 0) {
|
|
if(GlobalState.whose_move == EITHER_TO_MOVE) {
|
|
GlobalState.whose_move = WHITE_TO_MOVE;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile, "%s conflicts with previous setting of black to move.\n", argument);
|
|
}
|
|
return 1;
|
|
}
|
|
else if (stringcompare(argument, "xroster") == 0) {
|
|
if(GlobalState.tag_output_format == SEVEN_TAG_ROSTER) {
|
|
fprintf(GlobalState.logfile,
|
|
"--%s clashes with -%c.\n",
|
|
argument, SEVEN_TAG_ROSTER_ARGUMENT);
|
|
exit(1);
|
|
}
|
|
GlobalState.only_output_wanted_tags = TRUE;
|
|
return 1;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"Unrecognised long-form argument: --%s\n",
|
|
argument);
|
|
exit(1);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Extract a list of game numbers of the form: range[,range ...].
|
|
* Where range is either N or N1:N2.
|
|
* The numbers must be in ascending order and > 0.
|
|
*/
|
|
static game_number *
|
|
extract_game_number_list(const char *number_list)
|
|
{
|
|
char *csv = copy_string(number_list);
|
|
Boolean ok = TRUE;
|
|
game_number *head = NULL, *tail = NULL;
|
|
const char *token = strtok(csv, ",");
|
|
unsigned long last_number = 0;
|
|
while(token != NULL && ok) {
|
|
unsigned long min, max;
|
|
if(strchr(token, ':') != NULL) {
|
|
if(sscanf(token, "%lu:%lu", &min, &max) == 2) {
|
|
if(min > last_number && min <= max) {
|
|
last_number = max;
|
|
}
|
|
else {
|
|
ok = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
ok = FALSE;
|
|
}
|
|
}
|
|
else if(sscanf(token, "%lu", &min) == 1) {
|
|
if(min > last_number) {
|
|
max = min;
|
|
last_number = max;
|
|
}
|
|
else {
|
|
ok = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
ok = FALSE;
|
|
}
|
|
if(ok) {
|
|
game_number *list_item = (game_number *) malloc_or_die(sizeof(*list_item));
|
|
list_item->min = min;
|
|
list_item->max = max;
|
|
list_item->next = NULL;
|
|
if(tail != NULL) {
|
|
tail->next = list_item;
|
|
tail = list_item;
|
|
}
|
|
else {
|
|
head = tail = list_item;
|
|
}
|
|
token = strtok(NULL, ",");
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"Numbers in %s must be in the format N or N:N and in ascending order.",
|
|
number_list);
|
|
}
|
|
}
|
|
(void) free((void *) csv);
|
|
if(ok) {
|
|
return head;
|
|
}
|
|
else {
|
|
while(head != NULL) {
|
|
game_number *next = head->next;
|
|
(void) free((void *) head);
|
|
head = next;
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Set the lower and/or upper bounds limits.
|
|
* which must be one of l/e/u
|
|
*/
|
|
static Boolean set_move_bounds(char bounds_or_ply, char limit, unsigned number)
|
|
{
|
|
Boolean Ok;
|
|
GlobalState.check_move_bounds = TRUE;
|
|
switch (limit) {
|
|
case 'e':
|
|
GlobalState.lower_move_bound =
|
|
bounds_or_ply == MOVE_BOUNDS_ARGUMENT ? 2 * (number - 1) + 1 : number;
|
|
GlobalState.upper_move_bound =
|
|
bounds_or_ply == MOVE_BOUNDS_ARGUMENT ? 2 * number : number;
|
|
Ok = TRUE;
|
|
break;
|
|
case 'l':
|
|
if (number <= GlobalState.upper_move_bound) {
|
|
GlobalState.lower_move_bound =
|
|
bounds_or_ply == MOVE_BOUNDS_ARGUMENT ? 2 * (number - 1) + 1 : number;
|
|
Ok = TRUE;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"Lower bound of ply limit is greater than the upper bound: bound ignored.\n");
|
|
Ok = FALSE;
|
|
}
|
|
break;
|
|
case 'u':
|
|
if (number >= GlobalState.lower_move_bound) {
|
|
GlobalState.upper_move_bound =
|
|
bounds_or_ply == MOVE_BOUNDS_ARGUMENT ? 2 * number : number;
|
|
Ok = TRUE;
|
|
}
|
|
else {
|
|
fprintf(GlobalState.logfile,
|
|
"Upper bound of ply limit is smaller than the lower bound: bound ignored.\n");
|
|
Ok = FALSE;
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(GlobalState.logfile,
|
|
"Internal error: %c must be one of e/l/u.\n",
|
|
limit);
|
|
Ok = FALSE;
|
|
break;
|
|
}
|
|
return Ok;
|
|
}
|