/* lil-gp Genetic Programming System, version 1.0, 11 July 1995 * Copyright (C) 1995 Michigan State University * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Douglas Zongker (zongker@isl.cps.msu.edu) * Dr. Bill Punch (punch@isl.cps.msu.edu) * * Computer Science Department * A-714 Wells Hall * Michigan State University * East Lansing, Michigan 48824 * USA * */ #include /** array of parameters, along with the size of the array and the number of parameters used. **/ parameter *param; int param_size = 0, param_alloc = 0; /* read_parameter_file() * * reads and parses a parameter file. */ void read_parameter_file ( char *filename ) { FILE *f; char buffer[MAXPARAMLINELENGTH+1]; char *buf2; int buf2size; int line = 0; int errors = 0; int including = 1; int flag; int buflen, buf2len; int cont; /** open it. **/ f = fopen ( filename, "r" ); if ( f == NULL ) error ( E_FATAL_ERROR, "can't open parameter file \"%s\".", filename ); /** lines are read into buffer. they are appended to buf2 until a line which is not continued is hit, then buf2 is parsed and the parameter added. **/ /* initial allocation of buf2. */ buf2 = (char *)MALLOC ( MAXPARAMLINELENGTH * sizeof ( char ) ); buf2size = MAXPARAMLINELENGTH; buf2[0] = 0; buf2len = 0; cont = 0; while ( fgets ( buffer, MAXPARAMLINELENGTH, f ) != NULL ) { ++line; /* remove comments, skip line if it's blank after comment removal. */ if ( delete_comment ( buffer ) ) continue; /** if this is a %ifdef, %ifndef, or %endif directive, then set the including variable accordingly. **/ if ( buffer[0] == '%' ) { flag = 0; if ( strncmp ( buffer+1, "ifdef", 5 ) == 0 ) including = test_directive ( buffer+6 ); else if ( strncmp ( buffer+1, "ifndef", 6 ) == 0 ) including = !test_directive ( buffer+7 ); else if ( strncmp ( buffer+1, "endif", 5 ) == 0 ) including = 1; else flag = 1; if ( !flag ) continue; } /* skip this line unless including is set. */ if ( !including ) continue; /** if this is a %define or %undefine directive, then process it. **/ if ( buffer[0] == '%' ) { if ( strncmp ( buffer+1, "define", 6 ) == 0 ) define_directive ( buffer+7 ); else if ( strncmp ( buffer+1, "undefine", 8 ) == 0 ) undefine_directive ( buffer+9 ); else error ( E_ERROR, "%s line %d: unknown directive.", filename, line ); continue; } /* is this line continued? (is the last nonwhitespace character a backslash?) */ cont = check_continuation ( buffer ); /** make buf2 bigger if necessary. **/ buflen = strlen ( buffer ); while ( buf2len + buflen > buf2size + 10 ) { buf2size += MAXPARAMLINELENGTH; buf2 = (char *)REALLOC ( buf2, buf2size * sizeof ( char ) ); } /** tack the current line onto buf2. **/ strcat ( buf2, buffer ); buf2len += buflen; /** if this line is not continued further, then parse buf2. and add the parameter. **/ if ( !cont ) { if ( parse_one_parameter ( buf2 ) ) { errors = 1; error ( E_ERROR, "%s line %d: syntax error.", filename, line ); } /** reset buf2 as empty. **/ buf2len = 0; buf2[0] = 0; } } /* close file. */ fclose ( f ); /** if the last line was continued, and nothing followed it, that's an error. **/ if ( buf2len != 0 ) { errors = 1; error ( E_ERROR, "unexpected EOF." ); } /** if any errors occurred during parsing, stop now. **/ if ( errors ) error ( E_FATAL_ERROR, "some syntax errors occurred parsing \"%s\".", filename ); FREE ( buf2 ); } /* check_continuation() * * returns true/false depending on whether the given string is continued * (the last nonwhitespace character is a backslash). If so, the backslash * and everything after it is chopped. */ int check_continuation ( char *buffer ) { int i, l; l = strlen ( buffer ); for ( i = l-1; i >= 0; --i ) if ( !isspace(buffer[i]) ) { if ( buffer[i] == '\\' ) { buffer[i] = '\n'; buffer[i+1] = 0; return 1; } else return 0; } /* a blank line was passed. */ return 0; } /* delete_comment() * * this searches the string for a '#' or ';' and chops everything * following, if one is found. returns 1 if the resulting line is blank * (all whitespace), 0 otherwise. */ int delete_comment ( char *buffer ) { int i, l; l = strlen ( buffer ); /* zero-length lines are considered blank. */ if ( l == 0 ) return 1; /* if the last character is a newline, chop it. */ if ( buffer[--l] == '\n' ) buffer[l] = 0; for ( i = 0; i < l; ++i ) if ( buffer[i] == '#' || buffer[i] == ';' ) { /* chop the line at a '#' or ';'. */ buffer[i] = 0; break; } /* look for a nonwhitespace character. */ l = strlen ( buffer ); for ( i = 0; i < l; ++i ) if ( !isspace(buffer[i]) ) /* found one, return 0. */ return 0; /* blank line, return 1. */ return 1; } /* translate_binary() * * this translates all of the valid strings representing binary values * to the corresponding integer. */ int translate_binary ( char *string ) { if ( strcmp ( string, "true" ) == 0 || strcmp ( string, "t" ) == 0 || strcmp ( string, "on" ) == 0 || strcmp ( string, "yes" ) == 0 || strcmp ( string, "y" ) == 0 || strcmp ( string, "1" ) == 0 ) return 1; else if ( strcmp ( string, "false" ) == 0 || strcmp ( string, "f" ) == 0 || strcmp ( string, "off" ) == 0 || strcmp ( string, "no" ) == 0 || strcmp ( string, "n" ) == 0 || strcmp ( string, "0" ) == 0 ) return 0; else return -1; } /* read_parameter_database() * * this reads parameters from a checkpoint file. */ void read_parameter_database ( FILE *f ) { int i, j, k; int count; char *buf1, *buf2; int buf2len, buf2alloc; /* how many parameters are we supposed to find? */ fscanf ( f, "%*s %d\n", &count ); if ( fgetc ( f ) != '#' ) error ( E_FATAL_ERROR, "error in parameter section of checkpoint file." ); buf1 = (char *)MALLOC ( MAXPARAMLINELENGTH ); buf2 = (char *)MALLOC ( MAXPARAMLINELENGTH ); buf2alloc = MAXPARAMLINELENGTH; buf2[0] = 0; buf2len = 0; for ( i = 0; i < count; ) { /* get a line in buf1. */ fgets ( buf1, MAXPARAMLINELENGTH, f ); /** lengthen buf2 if necessary. */ while ( buf2len + strlen ( buf1 ) >= buf2alloc ) { buf2 = (char *)REALLOC ( buf2, buf2alloc + MAXPARAMLINELENGTH ); buf2alloc += MAXPARAMLINELENGTH; } /** tack line onto buf2. **/ strcat ( buf2, buf1 ); buf2len += strlen ( buf1 ); /* get the first character of the next line. */ k = fgetc ( f ); if ( k != '+' ) { /** not a '+', so the line is not continued. **/ /* chop the final newline. */ buf2[buf2len-1] = 0; for ( j = 0; j < buf2len; ++j ) /* look for a " = " substring, and break it into name/value there. */ if ( buf2[j] == ' ' && buf2[j+1] == '=' && buf2[j+2] == ' ' ) { /** add the parameter. **/ buf2[j] = 0; add_parameter ( buf2, buf2+j+3, PARAM_COPY_NAME|PARAM_COPY_VALUE ); #ifdef DEBUG fprintf ( stderr, "name = [%s]\nvalue = [%s]\n", buf2, buf2+j+3 ); #endif } /** reset buf2. **/ buf2[0] = 0; buf2len = 0; /* count of how many we've found. */ ++i; } } /* put the extra character we read back. */ ungetc ( k, f ); FREE ( buf1 ); FREE ( buf2 ); } /* write_parameter_database() * * this writes all the parameters to a checkpoint file, as "name = value\n". * since parameters can have embedded newlines, we begin each line of the * file with a "#" to indicate the start of a new name/value pair or a "+" * to indicate a continuation of the previous line. */ void write_parameter_database ( FILE *f ) { int i, j; /* write the total count. */ fprintf ( f, "parameter-count: %d\n", param_size ); for ( i = 0; i < param_size; ++i ) { /* start the pair with a '#'. */ fputc ( '#', f ); /** write the name, adding '+' after newlines. */ for ( j = 0; j < strlen ( param[i].n ); ++j ) { fputc ( param[i].n[j], f ); if ( param[i].n[j] == '\n' ) fputc ( '+', f ); } /* write " = ". */ fputs ( " = ", f ); /** write the value, adding '+' after newlines. */ for ( j = 0; j < strlen ( param[i].v ); ++j ) { fputc ( param[i].v[j], f ); if ( param[i].v[j] == '\n' ) fputc ( '+', f ); } /* end the pair. */ fputc ( '\n', f ); } } /* initialize_parameters() * * initializes the parameter database. */ void initialize_parameters ( void ) { oputs ( OUT_SYS, 30, " parameter database.\n" ); param = (parameter *)MALLOC ( PARAMETER_MINSIZE * sizeof ( parameter ) ); param_alloc = PARAMETER_MINSIZE; param_size = 0; } /* free_parameters() * * frees all the parameters. */ void free_parameters ( void ) { int i; for ( i = 0; i < param_size; ++i ) { /* if add_parameter made a copy of the name, then free it. */ if ( param[i].copyflags & PARAM_COPY_NAME ) FREE ( param[i].n ); /* if add_parameter make a copy of the value, then free it. */ if ( param[i].copyflags & PARAM_COPY_VALUE ) FREE ( param[i].v ); } FREE ( param ); param = NULL; param_alloc = 0; param_size = 0; } /* add_parameter() * * adds the given name/value pair to the database. the flags indicate * which if any of the strings need to be copied. */ void add_parameter ( char *name, char *value, int copyflags ) { /* erase any existing parameter of the same name. */ delete_parameter ( name ); /** if the database is full, make it bigger. **/ while ( param_alloc < param_size+1 ) { param_alloc += PARAMETER_CHUNKSIZE; param = (parameter *)REALLOC ( param, param_alloc * sizeof ( parameter ) ); } /** add the name. **/ if ( copyflags & PARAM_COPY_NAME ) { /* make a copy of the string if requested. */ param[param_size].n = (char *)MALLOC ( strlen(name)+1 ); strcpy ( param[param_size].n, name ); } else /* just store the pointer passed to us. */ param[param_size].n = name; /** add the value. **/ if ( copyflags & PARAM_COPY_VALUE ) { /* make a copy of the string if requested. */ param[param_size].v = (char *)MALLOC ( strlen(value)+1 ); strcpy ( param[param_size].v, value ); } else /* just store the pointer passed to us. */ param[param_size].v = value; /* record whether our values are copies or not. */ param[param_size].copyflags = copyflags; ++param_size; } /* delete_parameter() * * deletes a parameter from the database. */ int delete_parameter ( char *name ) { int i; for ( i = 0; i < param_size; ++i ) if ( strcmp ( name, param[i].n ) == 0 ) { /** free any copies make by add_parameter. **/ if ( param[i].copyflags & PARAM_COPY_NAME ) FREE ( param[i].n ); if ( param[i].copyflags & PARAM_COPY_VALUE ) FREE ( param[i].v ); /** move the last value in the database to the position of the deleted one. **/ if ( param_size-1 != i ) { param[i].n = param[param_size-1].n; param[i].v = param[param_size-1].v; param[i].copyflags = param[param_size-1].copyflags; } --param_size; return 1; } return 0; } /* get_parameter() * * looks up a parameter in the database. */ char *get_parameter ( const char *name ) { int i; for ( i = 0; i < param_size; ++i ) if ( strcmp ( name, param[i].n ) == 0 ) return param[i].v; return NULL; } /* print_parameters() * * dumps parameter database to stdout. */ void print_parameters ( void ) { int i; for ( i = 0; i < param_size; ++i ) printf ( "name: \"%s\" value: \"%s\" copy: %d\n", param[i].n, param[i].v, param[i].copyflags ); } /* parse_one_parameter() * * breaks a string at the first equals sign into name and value parts. * removes leading and trailing whitespace from both parts. inserts the * resulting pair into the parameter database. */ int parse_one_parameter ( char *buffer ) { char name[MAXPARAMLINELENGTH+1]; char data[MAXPARAMLINELENGTH+1]; int i, j, k, l; int n, d; k = -1; j = 0; l = strlen ( buffer ); /** scan for a equals sign. **/ for ( i = 0; i < l; ++i ) { /* j records whether or not we have found a nonwhitespace character. */ j += (buffer[i] != ' ' && buffer[i] != '\t' && buffer[i] != '\n'); if ( buffer[i] == '=' ) { k = i; /* copy the name part. */ strncpy ( name, buffer, k ); name[k] = 0; /* copy the value part. */ strcpy ( data, buffer+k+1 ); break; } } /* if we found no '=', return an error unless the line was completely blank. */ if ( k == -1 ) return !!j; /* trim leading and trailing whitespace. */ n = trim_string ( name ); d = trim_string ( data ); /** if either section is blank, return an error, otherwise add the pair as a parameter. **/ if ( n == 0 || d == 0 ) return 1; else add_parameter ( name, data, PARAM_COPY_NAME|PARAM_COPY_VALUE ); return 0; } /* trim_string() * * trims leading and trailing whitespace from a string, overwriting the * argument with the result. returns number of characters in result. */ int trim_string ( char *string ) { int i, j, l; j = -1; l = strlen ( string ); for ( i = 0; i < l; ++i ) { if ( j == -1 ) { if ( string[i] != ' ' && string[i] != '\t' && string[i] != '\n' ) { j = i; --i; } } else string[i-j] = string[i]; } if ( j == -1 ) { string[0] = 0; return 0; } string[i-j] = 0; l = i-j; j = -1; for ( i = 0; i < l; ++i ) { if ( string[i] != ' ' && string[i] != '\t' && string[i] != '\n' ) j = i; } string[j+1] = 0; return j+1; } /* define_directive() * * defines a directive "SYMBOL", which is just a parameter called * "__define:SYMBOL". trims leading and trailing whitespace from SYMBOL. */ void define_directive ( char *string ) { char *buffer; int i; for ( i = 0; i < strlen(string) && isspace(string[i]); ++i ); buffer = (char *)MALLOC ( (20 + strlen(string)) * sizeof ( char ) ); strcpy ( buffer, "__define:" ); strcat ( buffer, string+i ); for ( i = strlen(buffer)-1; i >= 0 && isspace(buffer[i]); --i ) buffer[i] = 0; add_parameter ( buffer, "1", PARAM_COPY_NAME ); FREE ( buffer ); } /* undefine_directive() * * undefines a directive "SYMBOL". */ void undefine_directive ( char *string ) { char *buffer; int i; for ( i = 0; i < strlen(string) && isspace(string[i]); ++i ); buffer = (char *)MALLOC ( (20 + strlen(string)) * sizeof ( char ) ); strcpy ( buffer, "__define:" ); strcat ( buffer, string+i ); for ( i = strlen(buffer)-1; i >= 0 && isspace(buffer[i]); --i ) buffer[i] = 0; delete_parameter ( buffer ); FREE ( buffer ); } /* test_directive() * * returns 1 iff a given directive is defined. */ int test_directive ( char *string ) { char *buffer; int ret; int i; for ( i = 0; i < strlen(string) && isspace(string[i]); ++i ); buffer = (char *)MALLOC ( (20 + strlen(string)) * sizeof ( char ) ); strcpy ( buffer, "__define:" ); strcat ( buffer, string+i ); for ( i = strlen(buffer)-1; i >= 0 && isspace(buffer[i]); --i ) buffer[i] = 0; if ( get_parameter ( buffer ) ) ret = 1; else ret = 0; FREE ( buffer ); return ret; } /* binary_parameter() * * checks for the existence of a parameter. if it exists, then it is changed * to the string "0" or "1" using lilgp's list of strings representing * binary values. if the value is not on the list, or the parameter is * not found, then the parameter is set according to the value argument (it * acts as a default). */ void binary_parameter ( char *name, int value ) { char *param = get_parameter ( name ); char string[2]; char *i, *is; int v; if ( param != NULL ) { /* copy the value and lowercase it. */ v = strlen ( param ); i = (char *)MALLOC ( (v+1)*sizeof ( char ) ); strcpy ( i, param ); for ( is = i; *is; ++is ) *is = tolower(*is); /* translate to a binary integer. */ v = translate_binary ( i ); if ( v == -1 ) { /* translation failed, use the value argument. */ error ( E_ERROR, "\"%s\" is not a legal value for \"%s\"; assuming default.", i, name ); v = value; } FREE ( i ); } else /* parameter not found, use the value argument. */ v = value; /** print the value to a string and put it in the parameter database. */ sprintf ( string, "%d", !!v ); add_parameter ( name, string, PARAM_COPY_VALUE|PARAM_COPY_NAME ); }