/* MuSE - Multiple Streaming Engine * profile (config file) class * * this file contains algorithms written by Flavio de Ayra Mender and * an object oriented approach to them (and bugfix) by Denis "jaromil" Rojo * * Copyright (C) 2001 - 2002 Flavio de Ayra Mendes * Copyright (C) 2002 - 2003 Denis Rojo * * This source code is free software; you can redistribute it and/or * modify it under the terms of the GNU Public License as published * by the Free Software Foundation; either version 2 of the License, * or (at your option) any later version. * * This source code 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. * Please refer to the GNU Public License for more details. * * You should have received a copy of the GNU Public License along with * this source code; if not, write to: * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * "$Id: profile.cpp,v 1.1.1.1 2003/12/08 12:20:33 jaromil Exp $" * */ #include #include #include #include #include #include #include #include #include #include #include char *Profile::is_a_section(char *str) { char section_tmp[MAX_SECTION_SIZE]; // fprintf(stderr,"CFG::is_a_section(%s)\n",str); if(str[0]!='[') return(NULL); if (!sscanf(str, "[%[^]]]", section_tmp)) return(NULL); // fprintf(stderr,"CFG::YES it is! %s\n",section_tmp); /* return it */ return(strdup(section_tmp)); } char **Profile::parse_option(char *str) { char option_tmp[MAX_OPTION_SIZE]; char value_tmp[MAX_VALUE_SIZE]; memset(option_tmp, '\0', MAX_OPTION_SIZE); memset(value_tmp, '\0', MAX_VALUE_SIZE); /* if its not an option return 0 */ if (sscanf(str, "%[^= ] = %[^=\n]", option_tmp, value_tmp) != 2) return NULL; /* if dest_opt were passed copy the option name there */ res[0] = strdup(option_tmp); /* if dest_val were passed copy the value there */ res[1] = strdup(value_tmp); // func("parse_option: opt[%s] val[%s]",res[0],res[1]); /* return true */ return res; } /* returns: -1 EOF or offset of section */ off_t Profile::find_section(FILE *fp, const char *section) { char tmp[MAX_LINE_SIZE], *sect; off_t offset = -1; off_t bck, pos; // fprintf(stderr,"CFG::find_section: [%s]\n",section); /* save initial offset */ bck = ftell(fp); rewind(fp); while (!feof(fp)) { pos = ftell(fp); if(!fgets(tmp, MAX_LINE_SIZE,fp)) break; chomp(tmp); // fprintf(stderr,"find section : \"%s\"\n",tmp); /* ignore comments */ if (tmp[0] == '#' || tmp[0] == '\r' || tmp[0] == '\n' || tmp[0]=='\0') continue; /* is a section line */ sect = is_a_section(tmp); if(sect) { /* yes, this is our section. return his offset */ if (!strncmp(sect,section,MAX_SECTION_SIZE)) { offset = pos; free(sect); break; } else free(sect); } } /* get back to initial offset */ fseek(fp,bck,SEEK_SET); return offset; } /* returns: -1 EOF 0 no option in current section OR offset of the option */ off_t Profile::find_option(FILE *fp, const char *option, char *value, off_t section_offset) { char tmp[MAX_LINE_SIZE]; char **res = NULL; off_t offset = -1; off_t bck, pos; // func("find_option(%s)",option); /* backup file pointer */ bck = ftell(fp); /* seek to section */ fseek(fp, section_offset, SEEK_SET); fgets(tmp, MAX_LINE_SIZE, fp); /* goes to the first option line */ while (!feof(fp)) { pos = ftell(fp); if(!fgets(tmp, MAX_LINE_SIZE, fp)) break; chomp(tmp); // fprintf(stderr,"find option : \"%s\"\n",tmp); if (tmp[0] == '#' || tmp[0] == '\r' || tmp[0] == '\n' || tmp[0] == '\0') continue; if (tmp[0] == '[') break;/* END OF SECTION - nothing found */ res = parse_option(tmp); if(res) { if (!strncmp(res[0], option, MAX_OPTION_SIZE)) { /* SUCCESS */ if(value) strncpy(value,res[1],MAX_VALUE_SIZE); offset = pos; // func("find_option() : FOUND at offset %lu\n",offset); break; } // free(res[0]); free(res[1]); } } /* restore backup */ fseek(fp,bck,SEEK_SET); if(res) { free(res[0]); free(res[1]); } return offset; } int Profile::cfg_check(const char *file, int output) { unsigned int line = 0; /* line number */ unsigned int section = 0; unsigned int err = 0; /* error checking */ char buff[MAX_LINE_SIZE]; /* line buffer */ char *sect = NULL; char **res = NULL; FILE *fd; fd = fopen(file,"r"); if(!fd) { error("cfg_check(): can't open %s (%s)",file,strerror(errno)); return(false); } /* let's work */ while (fgets(buff, MAX_LINE_SIZE, fd)) { /* line counting */ line++; chomp(buff); /* ignore comments and blank lines */ if (*buff == '#' || *buff == '\r' || *buff == '\n' || *buff == '\0') continue; if ((sect = is_a_section(buff))) { /* ok, its a section */ section = 1; free(sect); } else if ((res = parse_option(buff))) { if (!section) { /* option without a section */ err = 1; if (output) error("CFG::check_config : %s:%d: option '%s' whithout a section.\n", file, line, res[0]); } free(res[0]); free(res[1]); } else { err = 1; if (output) error("%s:%d: syntax error: '%s'\n", file, line, buff); } } free(res[0]); free(res[1]); fclose(fd); /* return 0 if failed or 1 if ok */ return (err ? 0: 1); } bool Profile::cfg_read(const char *file, const char *section, const char *option, char *value) { off_t option_offset; off_t section_offset; FILE *fd; fd = fopen(file,"r"); if(!fd) { error("cfg_read : can't open %s",file); error("%s",strerror(errno)); return(false); } /* return if section not found */ if ((section_offset = find_section(fd, section)) < 0) { func("cfg_read(): can't find section '%s'", section); return false; } option_offset = find_option(fd, option, value, section_offset); /* return if option not found */ if(option_offset<1) { func("cfg_read(): can't find option[%s] section[%s]", option, section); return false; } fclose(fd); return(true); } bool Profile::cfg_write(const char *file, const char *section, const char *option, const char *value) { off_t section_offset; off_t option_offset; char val[MAX_VALUE_SIZE]; char tmp[MAX_LINE_SIZE]; FILE *fd, *tmp_fp; struct stat st; // func("cfg_write(%s,%s,%s,%s)",file,section,option,value); if (stat(file, &st) == -1) { /* file is NEW */ fd = fopen(file,"w"); if(!fd) { error("cfg_write() : can't open %s",file); error("%s",strerror(errno)); return(false); } fprintf(fd, "[%s]\n%s = %s\n", section,option,value); fflush(fd); fclose(fd); return(true); } else fd = fopen(file,"r+"); if(!fd) { error("cfg_write : can't open %s (%s)",file, strerror(errno)); return(false); } section_offset = find_section(fd, section); if(section_offset<0) { /* there is no section */ func("cfg_write() : create new section [%s]",section); /* write at the end if section not found */ fseek(fd, 0, SEEK_END); fprintf(fd, "\n[%s]\n%s = %s\n\n", section,option,value); // fflush(fd); fclose(fd); return(true); } option_offset = find_option(fd, option, val, section_offset); if(option_offset>0) /* option found */ if(!strncmp(val,value,MAX_VALUE_SIZE)) { /* value is the same */ fclose(fd); return(true); } /* start a tmp file */ tmp_fp = tmpfile(); if (tmp_fp == NULL) { error("cfg_write(): can't create temp file (%s)",strerror(errno)); fclose(fd); return(false); } if(option_offset>0) { /* option exists, change value */ fseek(fd, option_offset, SEEK_SET); fgets(tmp, MAX_LINE_SIZE, fd); // func("substituting: %s\n",tmp); while(fgets(tmp, MAX_LINE_SIZE, fd)) fputs(tmp, tmp_fp); ftruncate(fileno(fd), option_offset); fprintf(fd,"%s = %s\n", option, value); rewind(tmp_fp); while(fgets(tmp, MAX_LINE_SIZE, tmp_fp)) fputs(tmp, fd); } else { /* option not found, add to section (on top) */ fseek(fd, section_offset, SEEK_SET); fgets(tmp, MAX_LINE_SIZE, fd); option_offset = ftell(fd); while(fgets(tmp, MAX_LINE_SIZE, fd)) fputs(tmp, tmp_fp); ftruncate(fileno(fd), option_offset); fseek(fd,0,SEEK_END); fprintf(fd,"%s = %s\n", option, value); rewind(tmp_fp); while(fgets(tmp, MAX_LINE_SIZE, tmp_fp)) fputs(tmp, fd); } /* close tmp file (gets deleted automatically) */ fclose(tmp_fp); fclose(fd); return(true); } bool Profile::cfg_erase(const char *file, const char *section, const char *option) { off_t section_offset; off_t option_offset; FILE *fd, *tmp_fp; char tmp[MAX_LINE_SIZE]; struct stat st; if (stat(file, &st) == -1) { error("cfg_erase(): %s does'nt exist",file); return(false); } else fd = fopen(file,"r+"); if(!fd) { error("cfg_erase(): can't open %s (%s)",file, strerror(errno)); return(false); } section_offset = find_section(fd, section); if(section_offset < 0) return(0); /* section not found */ option_offset = find_option(fd, option, NULL, section_offset); if(option_offset < 0) return(0); /* option not found */ /* start a tmp file */ tmp_fp = tmpfile(); if (tmp_fp == NULL) { error("cfg_erase(): can't create temp file (%s)",strerror(errno)); fclose(fd); return(false); } /* go one line under the one to be deleted */ fseek(fd,option_offset,SEEK_SET); fgets(tmp,MAX_LINE_SIZE,fd); /* copy everything after truncate point to tmp file */ while (!fgets(tmp, MAX_LINE_SIZE, fd)) fputs(tmp, tmp_fp); /* truncate config file */ ftruncate(fileno(fd), option_offset); /* restore the backup after truncation from tmp file */ rewind(tmp_fp); while (!fgets(tmp, MAX_LINE_SIZE, tmp_fp)) fputs(tmp,fd); fclose(tmp_fp); fclose(fd); return(true); } int Profile::cfg_get_sections(const char *file, char *dest) { char tmp[MAX_LINE_SIZE], *sect; unsigned int num = 0; unsigned int spac = 0; FILE *fd; fd = fopen(file,"r"); if(!fd) { error("cfg_get_sections(): can't open %s (%s)",file,strerror(errno)); return(false); } while (fgets(tmp,MAX_LINE_SIZE,fd)) { chomp(tmp); /* ignore comments */ if (tmp[0] == '#' || tmp[0] == '\r' || tmp[0] == '\n' || tmp[0]=='\0') continue; sect = is_a_section(tmp); /* copy the section name there */ if(sect) { sprintf(&dest[spac],"%s:",sect); num++; spac+=strlen(sect)+1; free(sect); } } return num; } Profile::Profile(char *name) { char *home = getenv("HOME"); /* TODO: FIXME */ if(!home) { error("i'm very sorry: you got no $HOME!"); cfg = NULL; return; } snprintf(profile_path,MAX_PATH_SIZE,"%s/.muse/%s.muserc",home,name); cfg = NULL; } Profile::~Profile() { } /* returns false if a option was missing */ bool Profile::load_profile(const char *section) { int ires, *tvar; float fres, *fvar; bool tres, res = true; struct stat st; if(!cfg) return(false); /* no setup() called */ if(stat(profile_path, &st) == -1) return(false); /* file not there */ for(int i=0;cfg[i].name;i++) { switch(cfg[i].type) { case cfgINT: tvar = (int*)cfg[i].var; ires = read_int(section,cfg[i].name); *tvar = (ires<0) ? atoi(cfg[i].defval) : ires; func("load_profile(%s) parsed %s value %i", section, cfg[i].name, *(int*)cfg[i].var); res &= (ires>=0); break; case cfgSTR: tres = read_str(section,cfg[i].name,(char*)cfg[i].var); if(!tres) { snprintf((char*)cfg[i].var,MAX_VALUE_SIZE,"%s",cfg[i].defval); res = false; } func("load_profile(%s) parsed %s value %s", section, cfg[i].name,(char*)cfg[i].var); break; case cfgFLOAT: fvar = (float*)cfg[i].var; fres = read_float(section,cfg[i].name); if(fres==-1.0f) { sscanf(cfg[i].defval,"%f",fvar); res = false; } else *fvar = fres; func("load_profile(%s) parsed %s value %.4f", section, cfg[i].name, *(float*)cfg[i].var); break; case cfgNULL: break; } } return(res); } bool Profile::write_profile(const char *section) { bool res = true; bool err = true; if(!cfg) return(false); for(int i=0;cfg[i].name;i++) { switch(cfg[i].type) { case cfgINT: res = write_int(section,cfg[i].name,*(int*)cfg[i].var ); if(!res) { error("write_profile(%s) : write_int error",section); write_str(section,cfg[i].name,cfg[i].defval); } break; case cfgSTR: res = write_str(section,cfg[i].name,(const char*)cfg[i].var); if(!res) { error("write_profile(%s) : write_str error",section); write_str(section,cfg[i].name,cfg[i].defval); } break; case cfgFLOAT: res = write_float(section, cfg[i].name,*(float*)cfg[i].var ); if(!res) { error("write_profile(%s) : write_float error",section); write_str(section,cfg[i].name,cfg[i].defval); } break; case cfgNULL: break; } } return(err); } bool Profile::create_default_profile() { int i; if(!cfg) return(false); for(i=0;cfg[i].name;i++) { switch(cfg[i].type) { case cfgINT: sscanf(cfg[i].defval,"%i",(int*)cfg[i].var); break; case cfgSTR: snprintf((char*)cfg[i].var,MAX_VALUE_SIZE,"%s",cfg[i].defval); break; case cfgFLOAT: sscanf(cfg[i].defval,"%f",(float*)cfg[i].var); break; case cfgNULL: break; } } return write_profile("default"); } bool Profile::default_profile() { /* loads the default */ if(!load_profile("default")) { if(!create_default_profile()) { error("can't create default profile %s", profile_path); return false; } } return apply_profile(); } int Profile::read_int(const char *section, const char *option) { char temp[MAX_VALUE_SIZE]; int res = -1; if(!cfg_read(profile_path,section,option,temp)) { warning("Profile::read_int(%s,%s) : %s", option,section,profile_path); return(-1); } res = strtol(temp,NULL,10); if(errno==ERANGE) { error("Profile::read_int(%s,%s) value %s : ", section, option, temp, strerror(errno)); return(-1); } else return(res); } bool Profile::write_int(const char *section, const char *option, const int value) { // func("Profile::write_int(%s,%s,%i)",section,option,value); char temp[MAX_VALUE_SIZE]; snprintf(temp,MAX_VALUE_SIZE,"%i",value); if(!cfg_write(profile_path, section, option, temp)) { warning("Profile::write_int(%s,%s,%s) : %s", section,option,value,profile_path); return(false); } return(true); } float Profile::read_float(const char *section, const char *option) { char temp[MAX_VALUE_SIZE]; float val; if(!cfg_read(profile_path, section, option, temp)) { warning("Profile::read_float(%s,%s) : %s", option,section,profile_path); return(-1.0f); } sscanf(temp,"%f",&val); return val; } bool Profile::write_float(const char *section, const char *option, const float value) { char temp[MAX_VALUE_SIZE]; snprintf(temp,MAX_VALUE_SIZE,"%.1f",value); if(!cfg_write(profile_path, section, option, temp)) { warning("Profile::write_float(%s,%s,%s) : %s", section,option,value,profile_path); return false; } return true; } bool Profile::read_str(const char *section, const char *option, char *store) { if(!cfg_read(profile_path,section,option,store)) { warning("Profile::read_str(%s,%s) : %s", option,section,profile_path); return(false); } return(true); } bool Profile::write_str(const char *section, const char *option, const char *value) { // func("Profile::write_int(%s,%s,%s)",section,option,value); if(!cfg_write(profile_path, section, option, value)) { warning("Profile::write_str(%s,%s,%s) : %s", section,option,value,profile_path); return(false); } return(true); }