static FILE * select_output_file(StateInfo *GameState,const char *eco) { if(GameState->games_per_file > 0) { if(GameState->games_per_file == 1 || (GameState->num_games_matched % GameState->games_per_file) == 1) { /* Time to open the next one. */ char filename[50]; if(GameState->outputfile != NULL) { (void) fclose(GameState->outputfile); } sprintf(filename,"%u%s", GameState->next_file_number, output_file_suffix(GameState->output_format)); GameState->outputfile = must_open_file(filename,"w"); GameState->next_file_number++; } } else if(GameState->ECO_level > DONT_DIVIDE) { /* Open a file of the appropriate name. */ if(GameState->outputfile != NULL) { /* @@@ In practice, this might need refinement. * Repeated opening and closing may prove inefficient. */ (void) fclose(GameState->outputfile); GameState->outputfile = open_eco_output_file( GameState->ECO_level, eco); } } else { } return GameState->outputfile; }
/* 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 BOUNDS_ARGUMENT: { /* Bounds on the number of moves are 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 value; 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",&value) == 1)){ GlobalState.check_move_bounds = TRUE; switch(which){ case 'e': GlobalState.lower_move_bound = value; GlobalState.upper_move_bound = value; break; case 'l': if(value <= GlobalState.upper_move_bound){ GlobalState.lower_move_bound = value; } else{ fprintf(GlobalState.logfile, "Lower bound is greater than the upper bound; -%c ignored.\n", arg_letter); Ok = FALSE; } break; case 'u': if(value >= GlobalState.lower_move_bound){ GlobalState.upper_move_bound = value; } else{ fprintf(GlobalState.logfile, "Upper bound is smaller than the lower bound; -%c ignored.\n", arg_letter); Ok = FALSE; } break; } } 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(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; set_output_line_length(5000); format = LALG; } 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.tag_output_format = SEVEN_TAG_ROSTER; } else { fprintf(GlobalState.logfile, "-%c clashes with another 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 of the final position. * This is displayed in a comment. */ 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. */ GlobalState.verbose = FALSE; 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: GlobalState.keep_variations = FALSE; 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: if(*filename != '\0'){ if(!build_endings(filename)){ exit(1); } } break; default: fprintf(GlobalState.logfile, "Unrecognized argument -%c\n", arg_letter); } }