#include #include #include #include #include #include #include "mfile.h" #include "cfg.h" /* -------- start of defines, global variables, etc... */ #define read_double_quoted_string(mem_file) read_delimited_string(mem_file, '"', '"') #define read_quoted_string(mem_file) read_delimited_string(mem_file, '\'', '\'') #define read_section_name(mem_file) read_delimited_string(mem_file, '[', ']') #define read_env_variable_name(mem_file) read_delimited_string(mem_file, '(', ')') char current_character, next_character; int current_nesting, section_ended, local_simple, nested_section; unsigned int line_number, last_line; /* When adding sections/entries helper_section contains the section set by the user with 'set_section()'. While reading the .cfg file(s) it contains the last nested section */ CFG_NODE *last_node, *helper_section, *last_section; /* -------- end of defines, global variables, etc... */ /* -------- start of functions */ void set_concat(CFG_FILE *cfg) { set_cfg_option(cfg, concat); } void unset_concat(CFG_FILE *cfg) { unset_cfg_option(cfg, concat); } void set_simple(CFG_FILE *cfg) { set_cfg_option(cfg, simple); } void unset_simple(CFG_FILE *cfg) { unset_cfg_option(cfg, simple); } // sets the separator char for accessing the file. /***set_section_sep_char: @type function @desc If this function is not called the default char @desc used to separate section names is a '.'. @arg config_file The cfg file to manipulate. @arg character the char to use as the separator. ***/ inline void set_section_sep_char(CFG_FILE *config_file, char *character) { strcpy(config_file->section_sep_character,character); } CFG_NODE *link_node(CFG_NODE *current_node, CFG_NODE *new_node) { if(current_node) current_node->next_node=new_node; new_node->previous_node=current_node; new_node->next_node=NULL; return new_node; } CFG_NODE *add_node(CFG_NODE *current_node, CFG_NODE *new_node) { CFG_NODE *backup_node=NULL; // if a section ended before variable if(section_ended==TRUE) { // get the node before the last section node backup_node=last_section; // now we need the section that came before backup_node=backup_node->parent_node; // now we need the last entry of this section while (backup_node->next_node) backup_node=backup_node->next_node; // the next node of this node is the new node backup_node->next_node=new_node; // the new nodes previous node is the node before the last section new_node->previous_node=backup_node; // problably should be left alone instead section_ended=2; } // if entry is somewhere else else { if(current_node->next_node) current_node->next_node=link_node(current_node->next_node,new_node); else { current_node->next_node=new_node; // At the start of a (sub)section or a file there's no previous entry if(current_node->typeofnodeprevious_node=current_node; } } // give the entry its id number new_node->number=last_node->number+1; return new_node; } CFG_NODE *add_section(CFG_NODE *new_section) { // if the current section is not the main section if(last_section!=NULL) { new_section->parent_node=last_section; last_section->child_node=new_section; } else return NULL; new_section->typeofnode=node_section; if(helper_section) // it's a subsection, if the section is nested if(helper_section->nested==TRUE) new_section->typeofnode=node_subsection; if(section_ended==FALSE) new_section->typeofnode=node_subsection; // give the section its id number new_section->number=last_node->number+1; last_section=new_section; return new_section; } CFG_NODE *create_node(NODE_TYPE type, char *name, void *data) { CFG_NODE *new_node; new_node=calloc(1,sizeof(CFG_NODE)); if(!new_node) return NULL; new_node->typeofnode=type; new_node->node_id=0; if(name) { new_node->name=calloc(strlen(name)+1,sizeof(char)); if(!new_node->name) return NULL; strcpy(new_node->name,name); } else new_node->name=NULL; new_node->nested=0; if(data) { new_node->data=calloc(strlen(data)+1,sizeof(char)); if(!new_node->data) return NULL; strcpy(new_node->data,data); } else new_node->data=data; new_node->child_node=NULL; new_node->next_node=NULL; new_node->parent_node=NULL; new_node->previous_node=NULL; return new_node; } CFG_NODE *node_exists(CFG_NODE *parent, NODE_TYPE type, char *node_name) { CFG_NODE *temp_node=NULL; CFG_NODE *temp_node2=NULL; CFG_FILE *included_file; if(!parent) return NULL; temp_node=parent; if((type==node_section) || (type==node_subsection)) { // while(temp_node->parent_node) temp_node=temp_node->parent_node; while(temp_node) { if(temp_node->typeofnode == type) { if(strcmp(temp_node->name,node_name)==0) return temp_node; } if(temp_node->typeofnode == node_command) { included_file=(CFG_FILE *)temp_node->data; temp_node2=node_exists(included_file->list,type,node_name); if(temp_node2!=NULL) return temp_node2; } temp_node=temp_node->child_node; } } else { while(temp_node->previous_node) temp_node=temp_node->previous_node; while(temp_node) { if(temp_node->typeofnode==type) { if(strcmp(temp_node->name,node_name)==0) return temp_node; } temp_node=temp_node->next_node; } } return NULL; } // search for a variable of the same name in the current section CFG_NODE *id_exists(CFG_NODE *parent, char *node_name) { CFG_NODE *temp_node=NULL; if(!parent) return NULL; temp_node=parent; while(temp_node->previous_node) temp_node=temp_node->previous_node; while(temp_node) { if(temp_node->typeofnode==node_var) { if(strcmp(temp_node->name,node_name)==0) return temp_node; } temp_node=temp_node->next_node; } return NULL; } void free_data(CFG_NODE *node) { if(!node) return; if(node->name) { free(node->name); node->name=NULL; } if(node->search_name) { free(node->search_name); node->search_name=NULL; } // Only entries have memory allocated for the data pointer if((node->data) && (node->node_id>0)) { free(node->data); node->data=NULL; } } // finally without memory leaks but slower :-( void free_children(CFG_NODE *current_node) { CFG_NODE *temp_node=NULL; if(!current_node) return; if(current_node->child_node) free_children(current_node->child_node); temp_node=current_node->next_node; while(temp_node) { free_data(temp_node); if(temp_node->previous_node!=NULL) { free(temp_node->previous_node); temp_node->previous_node=NULL; } if (temp_node->next_node) temp_node=temp_node->next_node; else { free(temp_node); temp_node=NULL; } } if(strcmp(current_node->name,"[?main?]")!=0) { free_data(current_node); free(current_node); } } void get_next_char(mfile *mem_file) { if(!next_character) { current_character=mem_getc(mem_file); } else current_character=next_character; if(next_character!=EOF) next_character=mem_getc(mem_file); if((current_character=='\n') && (next_character!=EOF)) line_number++; } // Where are we in the file ?? int cfg_file_pos(mfile *mem_file) { return mem_ftell(mem_file)-2; } // type may be: 1 for a variable or // 2 for a comment // reads what it can after [=;#]'s... char *read_line(mfile *mem_file, int type) { int start_pos=0, end_pos=0; char *string=NULL; start_pos=cfg_file_pos(mem_file); while((next_character!='\n') && (current_character!=EOF) && (((next_character!=';') && (next_character!='#')) || (type!=1))) get_next_char(mem_file); // no more cfg_file_rewind() :-) if(current_character==EOF) end_pos=cfg_file_pos(mem_file)+2; else end_pos=cfg_file_pos(mem_file)+1; string=calloc(end_pos-start_pos+1,sizeof(char)); if(!string) return NULL; memcpy(string, mem_file->data+start_pos, end_pos-start_pos); return string; } char *read_delimited_string(mfile *mem_file, char left_delimiter, char right_delimiter) { int start_pos=0, end_pos=0, escaped_character=0; char *string=NULL; if(current_character!=left_delimiter) return NULL; get_next_char(mem_file); start_pos=cfg_file_pos(mem_file); while(current_character!=right_delimiter || escaped_character) { if(current_character=='\\') escaped_character=1; else if(escaped_character) escaped_character=0; if(current_character==EOF) return NULL; get_next_char(mem_file); } if(next_character==EOF) end_pos=cfg_file_pos(mem_file)+1; else end_pos=cfg_file_pos(mem_file); string=calloc(end_pos-start_pos+1,sizeof(char)); if(!string) return NULL; memcpy(string, mem_file->data+start_pos, end_pos-start_pos); if(current_character!=right_delimiter) { printf("wrong right delimiting character read on line %u.\n",line_number); return NULL; } return string; } /***unescape_string: @type function @desc Will try to unescape a string. @desc it returns a new string containing @desc the unescaped form of the original. @arg string the string to unescape. @desc the returned string must be freed manually. @desc returns NULL on error. ***/ char *unescape_string(char *string) { int num=0, temp_num=0; char *escaped_string=NULL; escaped_string=calloc(strlen(string)+1, sizeof(char)); if(!escaped_string) return NULL; while(1) { if(escaped_string[num] == '\0') break; if(escaped_string[num] == '\\') { num++; switch(escaped_string[num]) { \ case 'r': escaped_string[temp_num]='\r'; break; case 'n': escaped_string[temp_num]='\n'; break; case 't': escaped_string[temp_num]='\t'; break; case 'a': escaped_string[temp_num]='\a'; break; case '0': escaped_string[temp_num]='\0'; break; case '\n': num++; escaped_string[temp_num]=escaped_string[num]; break; default: /* every other character is just put back with out the slash */ break; } } temp_num++; num++; } escaped_string=realloc(escaped_string, strlen(escaped_string)+1); if(!escaped_string) return NULL; return escaped_string; } /**escape_string: @type function @desc Will try to escape certain nonprintable chars. @desc It returns a new string containing the escaped @desc form of the original. @desc the returned string must be freed manually. @desc returns NULL on error. @arg string the string to escape. ***/ char *escape_string(char *string) { int num=0, temp_num=0; char *escaped_string=NULL; escaped_string=calloc(strlen(string)*2+1, sizeof(char)); if(!escaped_string) return NULL; while(1) { if(string[num] == '\0') break; switch(string[num]) { case '\r': escaped_string[temp_num++]='\\'; escaped_string[temp_num++]='r'; break; case '\n': escaped_string[temp_num++]='\\'; escaped_string[temp_num++]='n'; break; case '\t': escaped_string[temp_num++]='\\'; escaped_string[temp_num++]='t'; break; case '\a': escaped_string[temp_num++]='\\'; escaped_string[temp_num++]='a'; break; case '\0': escaped_string[temp_num++]='\\'; escaped_string[temp_num++]='0'; break; default: escaped_string[temp_num++]=string[num]; /* every other character is just put back */ break; } num++; } return escaped_string; } // a word consists of the alphabet, digits and '_'. // words can also start with digits. char *get_next_word(mfile *mem_file) { int start_pos=0, end_pos=0; char *word=NULL; start_pos=cfg_file_pos(mem_file); while(isalnum(current_character) || current_character=='_') get_next_char(mem_file); if(next_character==EOF) end_pos=cfg_file_pos(mem_file)+1; else end_pos=cfg_file_pos(mem_file); word=calloc(1, end_pos-start_pos+1); if(!word) return NULL; memcpy(word, mem_file->data+start_pos, end_pos-start_pos); return word; } /***new_cfg: @type function @desc Creates and returns a blank CFG_FILE. @desc returns NULL on error. ***/ CFG_FILE *new_cfg(void) { CFG_FILE *new_cfg_file=calloc(1, sizeof(CFG_FILE)); if(!new_cfg_file) return NULL; new_cfg_file->section_sep_character=calloc(1,sizeof(char)); if(!new_cfg_file->section_sep_character) return NULL; set_section_sep_char(new_cfg_file, "."); new_cfg_file->list=calloc(1,sizeof(CFG_NODE)); if(!new_cfg_file->list) { mem_fclose(new_cfg_file->mem_file); free(new_cfg_file->section_sep_character); free(new_cfg_file->name); free(new_cfg_file); return NULL; } // setup global variables current_character=0; next_character=0; current_nesting=0; section_ended=FALSE; local_simple=-1; nested_section=FALSE; line_number=1; last_line=1; // setup main section new_cfg_file->list->typeofnode=node_section; new_cfg_file->list->number=0; new_cfg_file->list->nested=1; new_cfg_file->list->name="[?main?]"; new_cfg_file->list->search_name=NULL; new_cfg_file->list->data="This section is the default section, where every thing else is put. " "It doesn't physically show up in the file"; new_cfg_file->section_name=NULL; new_cfg_file->list->child_node=NULL; new_cfg_file->list->next_node=NULL; new_cfg_file->list->parent_node=NULL; new_cfg_file->list->previous_node=NULL; helper_section=new_cfg_file->list; last_section=new_cfg_file->list; last_node=new_cfg_file->list; return new_cfg_file; } mfile *_read_mfile(char *name) { mfile *new_mem_file=NULL; FILE *config_file=NULL; int length; char *text=NULL; if(!name) return NULL; config_file=fopen(name,"r"); if(!config_file) { free(new_mem_file); return NULL; } fseek(config_file,0,SEEK_END); length=ftell(config_file); fseek(config_file,0,SEEK_SET); text=calloc(length+1,sizeof(char)); if(!text) { fclose(config_file); free(new_mem_file); return NULL; } fread(text,sizeof(char),length,config_file); fclose(config_file); new_mem_file=mem_fopen(length,text); return new_mem_file; } // skip spaces, tabs, 'returns', and newlines. void _skip_all_white(mfile *mem_file) { if(!mem_file) return; while((current_character==' ') || (current_character=='\t') || (current_character=='\r') || (current_character=='\n')) { get_next_char(mem_file); } } // skip spaces, tabs, and 'returns'. void _skip_white(mfile *mem_file) { if(!mem_file) return; while((current_character==' ') || (current_character=='\t') || (current_character=='\r')) { get_next_char(mem_file); } } CFG_NODE *_parse_cfg_var(CFG_FILE *config_file, CFG_NODE *current_node) { char *variable_name=NULL; char *variable_data=NULL; int start_pos=0, end_pos=0, temp_concat=0; int length, temp_length; ID_TYPE var_id=0; CFG_NODE *new_node=NULL; // save pos in mfile start_pos=cfg_file_pos(config_file->mem_file); // FF through wanted chars while(isalnum(current_character) || current_character=='_') { if(current_character==EOF) { printf("end of file while reading variable name on line %u\n", line_number); return NULL; } get_next_char(config_file->mem_file); } // save end pos of string end_pos=cfg_file_pos(config_file->mem_file); // alloc memory for wanted chars variable_name=calloc(end_pos-start_pos+1,sizeof(char)); if(!variable_name) { printf("memory for variable name on line %u was not allocated\n", line_number); return NULL; } // copy chars from mfile to variable_name memcpy(variable_name,config_file->mem_file->data+start_pos,end_pos-start_pos); // pass over any spaces or tabs. while(current_character==' ' || current_character=='\t') { if(current_character==EOF) { printf("end of file inside variable name on line %u\n", line_number); free(variable_name); return NULL; } get_next_char(config_file->mem_file); } // is it a concat command ?? if(current_character=='.') { get_next_char(config_file->mem_file); if(current_character!='=') { printf("error: no '=' encountered while trying to read command '.=' on line %u\n", line_number); free(variable_name); return NULL; } temp_concat=1; } // is variable valueless ?? if(current_character!='=') { var_id=id_bare_str; variable_data=NULL; } // else read variable's value else get_next_char(config_file->mem_file); // pass over any spaces or tabs. while(current_character==' ' || current_character=='\t') { if(current_character==EOF) { printf("end of file while reading value on line %u\n", line_number); free(variable_name); return NULL; } get_next_char(config_file->mem_file); } // is it a environment variable ? if (current_character=='$') { get_next_char(config_file->mem_file); if(current_character=='(') { variable_data=read_env_variable_name(config_file->mem_file); var_id=id_env_var; } else { printf("error reading environment(?) variable '%s' on line %u\n",variable_name, line_number); free(variable_name); return NULL; } } else // is it a "string" ? if (current_character=='"') { var_id=id_dq_str; variable_data=read_double_quoted_string(config_file->mem_file); } else // is it a 'string' ? if (current_character=='\'') { var_id=id_sq_str; variable_data=read_quoted_string(config_file->mem_file); } else // is it a number ? if(isdigit(current_character)) { start_pos=cfg_file_pos(config_file->mem_file); get_next_char(config_file->mem_file); while(isxdigit(current_character) || current_character=='.') { get_next_char(config_file->mem_file); } end_pos=cfg_file_pos(config_file->mem_file); // if line just happens to start with number(s) if((current_character>=33 && current_character<=126)) { var_id=id_bare_str; // set position back to first character mem_fseek(config_file->mem_file, start_pos+2, SEEK_SET); // and read the string variable_data=read_line(config_file->mem_file,1); } // else alloc memory for wanted chars else { variable_data=calloc(end_pos-start_pos+1,sizeof(char)); if(!variable_data) { printf("memory for value was not allocated for variable value on line %u\n", line_number); free(variable_name); return NULL; } // copy wanted chars from mfile to variable_name memcpy(variable_data,config_file->mem_file->data+start_pos,end_pos-start_pos); var_id=id_num; } } else // is it a string ? if((current_character>=33 && current_character<=126)) { var_id=id_bare_str; variable_data=read_line(config_file->mem_file,1); } new_node=id_exists(last_node, variable_name); // if the node already exists in this section if(new_node) { // and concatenating is allowed if((config_file->options.concat) || (temp_concat==1)) { // do so if the variable had a value assigned if(variable_data) { length=strlen(new_node->data); temp_length=strlen(variable_data); new_node->data=realloc(new_node->data, length+temp_length+2); if(!new_node->data) { printf("node for entry on line %u was not created\n", line_number); free(variable_name); if(variable_data) free(variable_data); return NULL; } memset(new_node->data+length, 0, temp_length+2); new_node->node_id=id_multiple_str; // the ZAngband way of placing several values on the same line: '|' new_node->data[length]='|'; strcat(new_node->data, variable_data); } // else there's an error else { if(new_node->data) free(new_node->data); new_node->data=NULL; } } // if not we overwrite the last value else { if(new_node->data) free(new_node->data); if(variable_data) { new_node->data=calloc(strlen(variable_data)+1, sizeof(char)); strcpy(new_node->data, variable_data); } else new_node->data=NULL; } } // if it's indeed a new node, create it else { new_node=create_node(node_var, variable_name, (variable_data) ? variable_data : NULL); if(!new_node) { printf("node for entry on line %u was not created\n", line_number); free(variable_name); if(variable_data) free(variable_data); return NULL; } new_node->node_id=var_id; add_node(current_node,new_node); } free(variable_name); variable_name=NULL; if(variable_data) free(variable_data); variable_data=NULL; last_node=new_node; last_line=line_number; return new_node; } CFG_NODE *_parse_cfg_section(CFG_FILE *config_file, CFG_NODE *current_node) { char *section_name=NULL; CFG_NODE *new_section=NULL; section_name=read_section_name(config_file->mem_file); if(!section_name) return NULL; get_next_char(config_file->mem_file); _skip_white(config_file->mem_file); /* if we read an open curly bracket after reading the section name of a simple section, we give up */ if(((config_file->options.simple) || (local_simple==TRUE)) && (current_character=='{')) { if(section_name) free(section_name); return NULL; } new_section=create_node(node_section, section_name, NULL); // create the name used for searching new_section->search_name=calloc(strlen(section_name)+1,sizeof(char)); if(!new_section->search_name) { if(section_name) free(section_name); return NULL; } strcpy(new_section->search_name,section_name); /* modify the name used for searching accordingly if it is a simple section inside a nested section */ if ((local_simple==TRUE) && (helper_section!=NULL)) { new_section->search_name=realloc(new_section->search_name, strlen(helper_section->search_name)+strlen(section_name)+2); if(!new_section->search_name) { if(section_name) free(section_name); return NULL; } sprintf(new_section->search_name, "%s%s%s", helper_section->search_name, config_file->section_sep_character,section_name); } // if this is a nested section if(current_character=='{') { if (((config_file->options.simple==TRUE) && (local_simple==FALSE)) || ((config_file->options.simple==FALSE) && (local_simplenested=TRUE; new_section=add_section(new_section); // modify the name used for searching accordingly if it is a subsection if (helper_section!=NULL) { new_section->search_name=realloc(new_section->search_name, strlen(helper_section->search_name)+strlen(section_name)+2); if(!new_section->search_name) return NULL; sprintf(new_section->search_name, "%s%s%s", helper_section->search_name, config_file->section_sep_character,section_name); } helper_section=new_section; } } // it's a normal section else new_section=add_section(new_section); free(section_name); section_name=NULL; last_node=new_section; last_line=line_number; return new_section; } CFG_NODE *_parse_cfg_command(CFG_FILE *config_file, CFG_NODE *current_node) { char current_character_backup,next_character_backup; int current_nesting_backup,section_ended_backup,local_simple_backup,nested_section_backup; unsigned int line_number_backup,last_line_backup; char *command_name=NULL; char *command_argument=NULL; CFG_NODE *new_node=NULL; CFG_NODE *last_node_backup=NULL; CFG_NODE *helper_section_backup=NULL; CFG_NODE *last_section_backup=NULL; CFG_FILE *new_file=NULL; get_next_char(config_file->mem_file); command_name=get_next_word(config_file->mem_file); if(!command_name) { printf("error: command could not be read on line %u.\n", line_number); return NULL; } if((strcmp(command_name,"nested")==0) || (strcmp(command_name,"simple")==0)) { new_node=create_node(node_command, command_name, NULL); if(!new_node) { printf("node for command on line %u was not created\n", line_number); free(command_name); command_name=NULL; return NULL; } add_node(current_node,new_node); if(strcmp(command_name,"nested")==0) local_simple=FALSE; if(strcmp(command_name,"simple")==0) local_simple=TRUE; } else if(strcmp(command_name,"include")==0) { _skip_white(config_file->mem_file); command_argument=read_double_quoted_string(config_file->mem_file); if(command_argument==NULL) { printf("error: no file name given for include command on line %u\n", line_number); free(command_name); command_name=NULL; free(command_argument); command_argument=NULL; return NULL; } else { // save all the global variables current_character_backup=current_character; next_character_backup=next_character; current_nesting_backup=current_nesting; section_ended_backup=section_ended; local_simple_backup=local_simple; nested_section_backup=nested_section; line_number_backup=line_number; last_line_backup=last_line; last_node_backup=last_node; helper_section_backup=helper_section; last_section_backup=last_section; // create a new cfg file new_file=new_cfg(); if(!new_file) { printf("error: couldn't create CFG_FILE structure for included file '%s' on line %u\n",command_argument, line_number); free(command_name); command_name=NULL; free(command_argument); command_argument=NULL; return NULL; } // parse the file if(!read_cfg(new_file,command_argument)) { printf("error: failed to read included file '%s' on line %u\n",command_argument, line_number); free(command_name); command_name=NULL; free(command_argument); command_argument=NULL; free(new_file->list); free(new_file); return NULL; } // restore all the global variables current_character=current_character_backup; next_character=next_character_backup; current_nesting=current_nesting_backup; section_ended=section_ended_backup; local_simple=local_simple_backup; nested_section=nested_section_backup; line_number=line_number_backup; last_line=last_line_backup; last_node=last_node_backup; helper_section=helper_section_backup; last_section=last_section_backup; new_node=create_node(node_command, command_name, NULL); if(!new_node) { printf("node for command on line %u was not created\n", line_number); free(command_name); command_name=NULL; free(command_argument); command_argument=NULL; return NULL; } new_file->list->parent_node=new_node; add_section(new_node); // need to set the type again because it got overwritten new_node->typeofnode=node_command; // node->data=CFG_file pointer (CFG_FILE *)new_node->data=new_file; } } else { printf("error: unknown command '%s' on line %u\n",command_name, line_number); free(command_name); command_name=NULL; free(command_argument); command_argument=NULL; return NULL; } if(command_name) free(command_name); command_name=NULL; if(command_argument) free(command_argument); command_argument=NULL; last_node=new_node; last_line=line_number; return new_node; } CFG_NODE *_parse_cfg_comment(CFG_FILE *config_file, CFG_NODE *current_node) { char *comment_text=NULL; ID_TYPE var_id=0; CFG_NODE *new_node=NULL; if(current_character==';') var_id=id_semi_comment; else var_id=id_hash_comment; get_next_char(config_file->mem_file); // are we on a new line ?? if(last_line!=line_number) { comment_text=read_line(config_file->mem_file,2); new_node=create_node(node_comment, NULL, comment_text); } // still on the same line ?? else { comment_text=read_line(config_file->mem_file,2); new_node=create_node(node_comment, last_node->name, comment_text); } new_node->node_id=var_id; add_node(current_node,new_node); last_node=new_node; free(comment_text); comment_text=NULL; return new_node; } CFG_NODE *_parse_cfg_file(CFG_FILE *config_file) { CFG_NODE *current_section=NULL; CFG_NODE *tmp_node=NULL; int entries=0; if(!config_file) return NULL; helper_section=NULL; tmp_node=config_file->list; while(current_character!=EOF) { get_next_char(config_file->mem_file); _skip_all_white(config_file->mem_file); // variable if(isalnum(current_character)) { tmp_node=_parse_cfg_var(config_file,tmp_node); if(tmp_node==NULL) return tmp_node; if (tmp_node->node_id!=id_multiple_str) entries++; } // comment else if(current_character==';' || current_character=='#') { tmp_node=_parse_cfg_comment(config_file,tmp_node); if(tmp_node==NULL) return tmp_node; entries++; } // section else if(current_character=='[') { tmp_node=_parse_cfg_section(config_file,tmp_node); if(tmp_node==NULL) return tmp_node; // if the section should be written in simple mode the last section, // if it isn't a nested function, gets its number of entries if ((config_file->options.simple==TRUE) || (local_simple==TRUE)) { if ((current_section!=NULL) && (current_section->nested!=TRUE)) if ((unsigned int)current_section->data==0) (unsigned int)current_section->data=entries-current_section->number; } current_section=tmp_node; if(tmp_node->nested) current_nesting++; section_ended=FALSE; entries++; } // command else if(current_character==':') { tmp_node=_parse_cfg_command(config_file,tmp_node); if(tmp_node==NULL) return tmp_node; entries++; } // end of nested section else if((current_character=='}') && ((config_file->options.simple!=TRUE) || (local_simple>=FALSE))) { current_nesting--; local_simple=-1; if(current_nesting<0) { printf("error: too many '}' found before line %u\n", line_number); return NULL; } section_ended=TRUE; // if the last section is a simple section inside a nested section it needs it's // number of entries if(current_section->nested!=TRUE) (unsigned int)current_section->data=entries-current_section->number; // Go back until a nested section is found; in case that we have nested // section while (current_section) { if (current_section->nested!=TRUE) current_section=current_section->parent_node; else break; } (unsigned int)current_section->data=entries-current_section->number; if(current_section->typeofnode==node_subsection) { current_section=current_section->parent_node; helper_section=current_section; // is it the main section, then we don't want to // add to the search_name if (helper_section->search_name==NULL) helper_section=NULL; } else helper_section=NULL; nested_section=FALSE; } } if(current_nesting>0) { printf("error: too many '{'\n"); return NULL; } tmp_node=config_file->list; // the number of entries in the file is the - unused - ID number of the files // main section tmp_node->number=entries; section_ended=TRUE; // in case the user creates sections helper_section must no be NULL helper_section=last_section; return tmp_node; } /***read_cfg: @type function @desc Loads a file into an already created CFG_FILE. @desc returns 0 on error, and 1 on success. @arg config_file the structure to load into. @arg name the file to load. ***/ int read_cfg(CFG_FILE *config_file, char *name) { if(!name) return 0; config_file->mem_file=_read_mfile(name); if(!config_file->mem_file) { //printf("config file doesn't exist\n"); return 0; } config_file->name=calloc(strlen(name)+1,sizeof(char)); if(!config_file->name) { mem_fclose(config_file->mem_file); free(config_file->mem_file); free(config_file->section_sep_character); printf("config file not read\n"); return 0; } strcpy(config_file->name,name); if(_parse_cfg_file(config_file)==NULL) { mem_fclose(config_file->mem_file); free_children(config_file->list); free(config_file->mem_file); free(config_file->section_sep_character); printf("error while reading config file\n"); return 0; } mem_fclose(config_file->mem_file); return 1; } /***load_cfg: @type function @desc loads a cfg file. @desc retruns the newly created CFG_FILE. @desc returns NULL on error. @arg name file to open. ***/ CFG_FILE *load_cfg(char *name) { CFG_FILE *new_config_file=NULL; new_config_file=new_cfg(); if(!new_config_file) return NULL; if(!read_cfg(new_config_file, name)) { free(new_config_file->list); free(new_config_file); return NULL; } return new_config_file; } // helper to write a variable to a cfg file void write_id(FILE *config_file, CFG_NODE *current_entry) { int i, multi_line=0; char tabs[25]; char *temp_str; for(i=0; i!=current_nesting; i++) tabs[i]='\t'; tabs[i]=0; if (current_entry->number!=1) fprintf(config_file,"\n"); switch (current_entry->node_id) { case id_num : fprintf(config_file,"%s%s=",tabs,current_entry->name); fprintf(config_file, "%s", (char *)current_entry->data); break; case id_env_var : fprintf(config_file,"%s%s=",tabs,current_entry->name); fprintf(config_file, "$(%s)", (char *)current_entry->data); break; case id_sq_str : fprintf(config_file,"%s%s=",tabs,current_entry->name); fprintf(config_file, "'%s'", (char *)current_entry->data); break; case id_dq_str : fprintf(config_file,"%s%s=",tabs,current_entry->name); fprintf(config_file, "\"%s\"", (char *)current_entry->data); break; case id_bare_str : fprintf(config_file,"%s%s",tabs,current_entry->name); if(current_entry->data) fprintf(config_file, "=%s", (char *)current_entry->data); break; case id_multiple_str : for(temp_str=strtok((char *)current_entry->data, "|"); temp_str; temp_str=strtok(0, "|")) { if(multi_line==0) { fprintf(config_file,"%s%s=",tabs,current_entry->name); multi_line=1; } else fprintf(config_file,"\n%s%s.=",tabs,current_entry->name); fprintf(config_file, "%s",temp_str); } break; } } // helper to write a command int write_command(FILE *config_file, CFG_NODE *current_entry) { int i; char tabs[25]; CFG_FILE *included_file=NULL; for(i=0; i!=current_nesting; i++) tabs[i]='\t'; tabs[i]=0; if (current_entry->number!=1) fprintf(config_file,"\n"); if(strcmp(current_entry->name,"nested")==0) { fprintf(config_file,"%s:%s",tabs, current_entry->name); return 0; } if (strcmp(current_entry->name,"simple")==0) { fprintf(config_file,"%s:%s",tabs, current_entry->name); return 1; } else { included_file=(CFG_FILE *)current_entry->data; fprintf(config_file,"%s:include \"%s\"",tabs,included_file->name); return 2; } return -1; } // helper to write a comment void write_comment(FILE *config_file, CFG_NODE *current_entry) { int i; char tabs[25]; for(i=0; i!=current_nesting; i++) tabs[i]='\t'; tabs[i]=0; // was comment on the same line as an entry/section if(current_entry->name) { if(current_entry->node_id == id_semi_comment) fprintf(config_file," ;%s",(current_entry->data) ? (char *)current_entry->data : " "); else fprintf(config_file," #%s",(current_entry->data) ? (char *)current_entry->data : " "); } // print it on the next line if not else { if (current_entry->number!=1) fprintf(config_file,"\n"); if(current_entry->node_id == id_semi_comment) fprintf(config_file,"%s;%s",tabs,(current_entry->data) ? (char *)current_entry->data : " "); else fprintf(config_file,"%s#%s",tabs,(current_entry->data) ? (char *)current_entry->data : " "); } } // helper to write a section including any entries in it. CFG_NODE *write_section(FILE *config_file, CFG_NODE *section_node, int simple) { unsigned int entry, last_entry; int i, temp_cmd=-1; char tabs[25]; CFG_NODE *entry_node=NULL; CFG_NODE *temp_node=NULL; CFG_FILE *included_file=NULL; for(i=0; i!=current_nesting; i++) tabs[i]='\t'; tabs[i]=0; entry=section_node->number; if (entry!=1) fprintf(config_file,"\n"); if(simple==0) { fprintf(config_file,"%s[%s] {",current_nesting ? tabs: "", section_node->name); current_nesting++; } else { fprintf(config_file,"%s[%s]",current_nesting ? tabs: "",section_node->name); } entry_node=section_node->next_node; entry++; // write a number of items given by the data pointer or // write until the last entry is reached if ((unsigned int)section_node->data>0) last_entry=entry+(unsigned int)section_node->data; else last_entry=last_node->number; while(entrynumber==entry)) { switch (entry_node->typeofnode) { case node_var: write_id(config_file,entry_node); break; case node_comment: write_comment(config_file,entry_node); break; case node_command: temp_cmd=write_command(config_file,entry_node); break; } entry++; temp_node=entry_node; entry_node=entry_node->next_node; } else { if(section_node->child_node!=NULL) { section_node=section_node->child_node; if(section_node->number==entry) { // if command == include file, save it as well if (section_node->typeofnode==node_command) { temp_cmd=write_command(config_file,section_node); included_file=(CFG_FILE*)section_node->data; close_cfg(included_file); temp_cmd=-1; temp_node=section_node; entry++; } else // it's a subsection then...save it if(section_node->typeofnode==node_subsection) { if((temp_cmd==0) || (temp_cmd==1)) { temp_node=write_section(config_file,section_node,temp_cmd); entry=temp_node->number+1; } else { temp_node=write_section(config_file,section_node,simple); entry=temp_node->number+1; } } } } } } if(simple==0) { if (section_node->nested!=1) fprintf(config_file,"%s}",current_nesting ? tabs : ""); else fprintf(config_file,"\n%s}",current_nesting ? tabs : ""); current_nesting--; } else fprintf(config_file,"\n"); return temp_node; } /***flush_cfg: @type function @desc forces the saving of a CFG_FILE. @arg config_file the CFG_FILE to save. ***/ int flush_cfg(CFG_FILE *config_file) { unsigned int entry=1, last_entry; int temp_cmd=-1; FILE *new_config_file=NULL; CFG_NODE *temp_node=NULL; CFG_NODE *section_node=NULL; CFG_NODE *entry_node=NULL; CFG_FILE *included_file=NULL; current_nesting=0; if(!config_file) return 0; // we don't need it anymore if(config_file->section_name) free(config_file->section_name); section_node=config_file->list; last_entry=section_node->number; // now we can start to save the file new_config_file=fopen(config_file->name,"w"); if(!new_config_file) return 0; // don't write node of main section entry_node=section_node->next_node; while(entry<=last_entry) { if((entry_node!=NULL) && (entry_node->number==entry)) { switch (entry_node->typeofnode) { case node_var: write_id(new_config_file,entry_node); break; case node_comment: write_comment(new_config_file,entry_node); break; case node_command: temp_cmd=write_command(new_config_file,entry_node); break; } entry++; entry_node=entry_node->next_node; } else if(section_node->child_node!=NULL) { section_node=section_node->child_node; if(section_node->number==entry) { if (section_node->typeofnode==node_command) { temp_cmd=write_command(new_config_file,section_node); // if command == include file, save it as well included_file=(CFG_FILE*)section_node->data; // and of course close it close_cfg(included_file); temp_cmd=-1; entry++; } else { if((temp_cmd==0) || (temp_cmd==1)) { temp_node=write_section(new_config_file,section_node,temp_cmd); temp_cmd=-1; entry=temp_node->number+1; } else { temp_node=write_section(new_config_file,section_node,config_file->options.simple); entry=temp_node->number+1; } } } } } fclose(new_config_file); config_file->dirty=0; return 1; } /***write_cfg: @type function @desc Will save a CFG_FILE if it has been changed. @arg config_file CFG_FILE to save. ***/ int write_cfg(CFG_FILE *config_file) { if(config_file && config_file->dirty) return flush_cfg(config_file); return config_file ? 1 : 0; } /***save_cfg: @type function @desc Saves a CFG_FILE. @desc If the CFG_FILE already has a name and 'name' is NULL @desc the file will be saved with its current name, if 'name' @desc is a string it is changed to the given one before saving. @arg config_file the CFG_FILE to save. @arg name the file name to save it as. ***/ int save_cfg(CFG_FILE *config_file, char *name) { int value=0; if(!config_file) return 0; if(name) { config_file->name=realloc(config_file->name, strlen(name)+1); if(!config_file->name) return 0; strcpy(config_file->name, name); } value=flush_cfg(config_file); config_file->dirty=0; return value; } /***split: @type function @desc splits a string into an array of char * @desc Frees the char* array if the user didn't. @desc Even if called with a new string to work on. @arg char* delimiter -> which sign is used in the source_string @arg char* source_string -> the string to split apart @arg int* number -> the number of sub strings ***/ char **split(char *delimiter,char *source_string,int *number) { int i=0; char *node_name=NULL; char *temp_string=NULL; static char **char_array=NULL; if(char_array) { while(char_array[i]) { free(char_array[i]); char_array[i]=NULL; i++; } free(char_array); char_array=NULL; // in case split was called with a new string but the // old char* array wasn't freed i=0; } if((!delimiter) || (!source_string)) return NULL; // make copy of source_string because strtok would mangle source_string, // which is not what we want temp_string=calloc(strlen(source_string)+1,sizeof(char)); if(!temp_string) return NULL; strcpy(temp_string,source_string); // how many substrings are there for(node_name=strtok(temp_string,delimiter); node_name; node_name=strtok(0,delimiter)) i++; // temp_string got mangled by strtok, a refresh is needed strcpy(temp_string,source_string); // create the array with one entry to mark the last. char_array=calloc(i+1,sizeof(char*)); if(!char_array) return NULL; i=0; // go through all substrings one by one for(node_name=strtok(temp_string,delimiter); node_name; node_name=strtok(0,delimiter)) { if(!node_name) return NULL; // reserve memory for the substring char_array[i]=calloc(strlen(node_name)+1,sizeof(char)); if(!char_array[i]) return NULL; // fill the memory with data strcpy(char_array[i],node_name); i++; } // this is needed or the loop freeing the array at the beginning will not stop char_array[i]=calloc(1,sizeof(char)); if(char_array[i]) free(char_array[i]); char_array[i]=NULL; // node_name is already freed & NULL // but temp_string still contains the first substring if(temp_string) free(temp_string); *number=i; return char_array; } /***close_cfg: @type function @desc Saves and frees all data from a CFG_FILE. @arg config_file the CFG_FILE to close. ***/ void close_cfg(CFG_FILE *config_file) { if(!config_file) return; write_cfg(config_file); // free the char* array used by split if it wasn't freed by the user split(NULL,NULL,0); free_children(config_file->list); if(config_file->name) free(config_file->name); free(config_file->section_sep_character); if(config_file->list) free(config_file->list); free(config_file); } // find a section or return NULL if it doesn't exists CFG_NODE *get_section(CFG_FILE *config_file, char *section_name) { int exit=FALSE; CFG_NODE *current_node=NULL; CFG_NODE *temp_node=NULL; CFG_FILE *included_file=NULL; if(!config_file) return NULL; current_node=config_file->list; if (section_name==NULL) return current_node; // the main section is already checked, lets go to the next... current_node=current_node->child_node; // and onward while(exit!=TRUE) { if(!current_node) return NULL; while(current_node) { if(current_node->typeofnode == node_command) { included_file=(CFG_FILE *)current_node->data; temp_node=get_section(included_file,section_name); if(temp_node!=NULL) { current_node=temp_node; exit=TRUE; break; } } else if(strcmp(current_node->search_name,section_name)==0){ exit=TRUE; break; } current_node=current_node->child_node; } } return current_node; } // create section+subsections if they don't exist // But might be slower than the last version, not sure int put_section(CFG_FILE *config_file, char *section_name) { int i=0; char *node_name=NULL; char *complete_name=NULL; CFG_NODE *new_section=NULL; CFG_NODE *temp_node=NULL; if(!config_file) return 0; if (section_name==NULL) return 0; complete_name=calloc(strlen(section_name)+1,sizeof(char)); if(!complete_name) return 0; strcpy(complete_name,section_name); // split name apart for(node_name=strtok(section_name,config_file->section_sep_character); node_name; node_name=strtok(0,config_file->section_sep_character)) { // after the first run stitch all previous parts and the current together if(i>0) { complete_name=realloc(complete_name,strlen(helper_section->search_name)+strlen(node_name)+2); if(!complete_name) return 0; sprintf(complete_name, "%s%s%s", helper_section->search_name, config_file->section_sep_character,node_name); } temp_node=get_section(config_file,complete_name); // does node exist??? if(temp_node==NULL) { // no? then create it if((i>0) && (config_file->options.simple==FALSE)) new_section=create_node(node_subsection, node_name, NULL); else new_section=create_node(node_section, node_name, NULL); if(!new_section) return 0; // set the search name to the complete_name after the first run if(i>0) { new_section->search_name=calloc(strlen(complete_name)+1,sizeof(char)); if(!new_section->search_name) return 0; strcpy(new_section->search_name,complete_name); (unsigned int)helper_section->data+=1; } // set the search name to the node_name on the first loop only else { new_section->search_name=calloc(strlen(node_name)+1,sizeof(char)); if(!new_section->search_name) return 0; strcpy(new_section->search_name,node_name); } // is the section nested ?? if (((config_file->options.simple==TRUE) && (local_simple==FALSE)) || ((config_file->options.simple==FALSE) && (local_simplenested=TRUE; } new_section=add_section(new_section); config_file->list->number+=1; last_node=helper_section=new_section; } // yes? make it the current one and then continue else helper_section=temp_node; i++; } config_file->dirty=1; last_node=helper_section; last_section=helper_section; if(complete_name) free(complete_name); return 1; } /***get_cfg_string: @type function @desc Retrives a string from a CFG_FILE. @desc returns NULL on error. @arg config_file The CFG_FILE to access. @arg section The section to get the string's variable from. @arg name The variable's name. @arg comment This is returned if the requested variable wasn't found. ***/ char *get_cfg_string(CFG_FILE *config_file, char *section, char *name, char *comment) { CFG_NODE *temp_node=NULL; char *section_name=NULL; if(!config_file || !name) return NULL; if(config_file->section_name) { section_name=calloc(1, strlen(config_file->section_name)+(section ? strlen(section)+1 : 1)); strcpy(section_name,config_file->section_name); if(section) strcat(section_name,section); } // if a section was given if(section) { // and there was no section set as current if(section_name==NULL) temp_node=get_section(config_file, section); // and there was a section set as current else temp_node=get_section(config_file, section_name); } // if no section name was given else { // and there was no section set as current, it's the main if(section_name==NULL) temp_node=config_file->list; // otherwise its the current section else temp_node=helper_section; } if(!temp_node) return comment; while(temp_node) { if(temp_node->typeofnode==node_var && strcmp(name,temp_node->name)==0) { if(section_name) free(section_name); if(temp_node->node_id == id_env_var) { if(getenv(temp_node->data)!=NULL) return getenv(temp_node->data); else return comment; } else if(temp_node->data!=NULL) { return temp_node->data; } } temp_node=temp_node->next_node; } if(section_name) free(section_name); return comment; } /***get_cfg_argv: @type function @desc Gets an argv style list from a CFG_FILE; i.e something like -d -e -f -k @desc The array returned here is not stable. @desc It must be saved/copied before it is used. @desc returns NULL on error. @desc if the switches aren't separated by a single space it will fail. @arg config_file the CFG_FILE to access. @arg section The section to get the argv list's variable from. @arg name The argv's variable name. @arg argc A pointer to a variable that is assigned to the number of items in the argv list. ***/ char **get_cfg_argv(CFG_FILE *config_file, char *section, char *name, int *argc) { char *string=NULL; string=get_cfg_string(config_file,section,name,NULL); if(string) return split(" ",string,argc); *argc=0; return NULL; } /***get_cfg_int: @type function @desc Gets an integer from a CFG_FILE. @arg config_file the CFG_FILE to access. @arg section The section to get the integer's variable from. @arg name The integer's variable name. @arg value The value of this arg is returned if the requested variable isn't found. ***/ int get_cfg_int(CFG_FILE *config_file, char *section, char *name, int value) { char *string=NULL; string=get_cfg_string(config_file,section,name,NULL); if(string) return strtol(string, NULL, 0); return value; } /***get_cfg_truth: @type function @desc returns 1 if the requested variable exists, @desc and 0 otherwise. @arg config_file CFG_FILE to access. @arg section section to get the variable from. @arg name the variable's name. ***/ int get_cfg_truth(CFG_FILE *config_file, char *section, char *name) { char *string=NULL; string=get_cfg_string(config_file,section,name,NULL); if(string) return 1; return 0; } /***get_cfg_hex: @type function @desc Returns an integer containing the value of a hexadecimal string. @arg config_file The CFG_FILE to access. @arg section The section to get the variable from. @arg name The variable's name. @arg value The value of this arg is returned if the requested variable isn't found. ***/ int get_cfg_hex(CFG_FILE *config_file, char *section, char *name, int value) { int i; char *string; string=get_cfg_string(config_file,section,name,NULL); if(string) { i=strtol(string, NULL, 16); if((i == 0x7FFFFFFF) && (stricmp(string, "0x7FFFFFFF") != 0)) i=-1; free(string); return i; } return value; } /***get_cfg_float: @type function @desc Returns an integer containing the value of a floating point string. @arg config_file The CFG_FILE to access. @arg section The section to get the variable from. @arg name The variable's name. @arg value The value of this arg is returned if the requested variable isn't found. ***/ float get_cfg_float(CFG_FILE *config_file, char *section, char *name, float value) { char *string; string=get_cfg_string(config_file, section, name, NULL); if(string) return atof(string); return value; } void _set_cfg_string(CFG_FILE *config_file, char *section, char *var_name, char *var_value, ID_TYPE var_id) { char *var_data=NULL; char *section_name=NULL; int length, temp_length; CFG_NODE *temp_node=NULL; CFG_NODE *new_node=NULL; if(!config_file || !var_name) return; if(config_file->section_name) { section_name=calloc(1, strlen(config_file->section_name)+(section?strlen(section)+1:1)); strcpy(section_name, config_file->section_name); if(section) strcat(section_name, section); } // if a section name was given if(section) { // and there was no section set as current, create it if(section_name==NULL) put_section(config_file, section); // and there was a section set as current, create it else put_section(config_file, section_name); } // if no section name was given else { // and there was no section set as current, it's the main if(section_name==NULL) put_section(config_file, NULL); } if(!helper_section) { if(section_name) free(section_name); return; } if(var_value) { var_data=calloc(strlen(var_value)+1, sizeof(char)); if(!var_data) { if(section_name) free(section_name); return; } strcpy(var_data, var_value); } new_node=node_exists(helper_section, node_var, var_name); if(new_node) { if(strcmp(var_data,new_node->data)==0) { if(section_name) free(section_name); free(var_data); return; } if(config_file->options.concat) { if(var_data) { length=strlen(new_node->data); temp_length=strlen(var_data); new_node->data=realloc(new_node->data, length+temp_length+2); if(!new_node->data) { printf("node was not created for %s",var_name); if(section_name) free(section_name); free(var_data); return; } memset(new_node->data+length, 0, temp_length+2); new_node->data[length]='|'; strcat(new_node->data, var_data); config_file->dirty=1; last_node=new_node; } else { if(new_node->data) free(new_node->data); new_node->data=NULL; } } else { if(new_node->data) free(new_node->data); if(var_data) { new_node->data=calloc(strlen(var_data)+1, sizeof(char)); strcpy(new_node->data, var_data); config_file->dirty=1; last_node=new_node; } else new_node->data=NULL; } } else { new_node=create_node(node_var, var_name, (var_data) ? var_data : NULL); if(!new_node) { printf("node was not created for %s",var_name); if(section_name) free(section_name); free(var_data); return; } new_node->node_id=var_id; // if section has entries if(helper_section->next_node) { temp_node=helper_section; // go to the last while(temp_node->next_node) temp_node=temp_node->next_node; // and add this entry to it temp_node->next_node=link_node(temp_node,new_node); new_node->number=temp_node->number+1; } // else it is the first entry else { helper_section->next_node=new_node; new_node->number=helper_section->number+1; } // increase the number of entries of the section by 1 (unsigned int)helper_section->data+=1; // increase the number of entries in the file by 1 config_file->list->number+=1; last_node=new_node; config_file->dirty=1; } if(section_name) free(section_name); free(var_data); } /***set_cfg_string: @type function @desc Creates or changes the value of a variable. @desc value is always saved as a double quoted string. @arg config_file The CFG_FILE to access. @arg section The section to create the variable in. @arg name The variable's name. @arg value The variable's value. ***/ void set_cfg_string(CFG_FILE *config_file, char *section, char *name, char *value) { _set_cfg_string(config_file, section, name, value, id_dq_str); } /***set_cfg_int: @type function @desc Creates or changes the value of a variable in integer format (12345). @desc the value parameter is limited to 31 digits. @arg config_file The CFG_FILE to access. @arg section The section to find the variable. @arg name The variable's name. @arg value The variable's value. ***/ void set_cfg_int(CFG_FILE *config_file, char *section, char *name, int value) { char text[32]; if(!config_file || !name) return; sprintf(text, "%i", value); _set_cfg_string(config_file,section,name,text,id_num); } /***set_cfg_hex: @type function @desc Creates or changes the value of a variable in hexadecimal format (0x13fe3). @desc the value parameter is limited to 29 digits pluss the '0x'. @arg config_file The CFG_FILE to access. @arg section The section to find the variable. @arg name The variable's name. @arg value The variable's value. ***/ void set_cfg_hex(CFG_FILE *config_file, char *section, char *name, int value) { char text[32]; if(!config_file || !name) return; if(value>=0) { sprintf(text, "%#X", value); strlwr(text); _set_cfg_string(config_file,section,name,text, id_num); } else _set_cfg_string(config_file,section,name,"-1", id_num); } /***set_cfg_float: @type function @desc Creates or changes the value of a variable in floating piont format (1.2334). @desc the value parameter is limited to 30 digits pluss the point. @arg config_file The CFG_FILE to access. @arg section The section to find the variable. @arg name The variable's name. @arg value The variable's value. ***/ void set_cfg_float(CFG_FILE *config_file, char *section, char *name, float value) { char text[32]; if(!config_file || !name) return; if(value>=0) { sprintf(text, "%f", value); _set_cfg_string(config_file,section,name,text, id_num); } else _set_cfg_string(config_file,section,name,"-1", id_num); } /***set_cfg_var_type: @type function @desc Sets a variable's type. (ie: double quoted string, env var, etc...) @desc WARNING: changing a variable's type to a 'comment' will discard the variable's name. @desc This function doesn't automagically create sections/variables like the other set_cfg_*() functions. @arg config_file The CFG_FILE to access. @arg section The section to find the variable. @arg name The variable's name. @arg type Can be any one of: id_num, id_env_var, id_sq_str, id_dq_str, id_bare_str, id_multiple_str, id_semi_comment or id_hash_comment. ***/ void set_cfg_var_type(CFG_FILE *config_file, char *section, char *name, ID_TYPE type) { CFG_NODE *temp_node=NULL; CFG_NODE *temp_node2=NULL; temp_node=get_section(config_file, section); if(!temp_node) return; temp_node2=node_exists(temp_node, node_var, name); if(!temp_node2) return; temp_node2->node_id=type; } /***set_section: @type function @desc Sets the current working 'section'. ***/ void set_section(CFG_FILE *config_file, char *section_name) { CFG_NODE *temp_node=NULL; if(!config_file) return; if(!section_name) { if(config_file->section_name) free(config_file->section_name); config_file->section_name=NULL; helper_section=get_section(config_file,NULL); } else { config_file->section_name=realloc(config_file->section_name, strlen(section_name)+1); if(!config_file->section_name) return; sprintf(config_file->section_name, "%s", section_name); temp_node=get_section(config_file,config_file->section_name); if(temp_node==NULL) put_section(config_file,config_file->section_name); else helper_section=temp_node; } } /* -------- end of functions */