/*********************************************** ** ** cfg.c v0.01 ** ** Generic config file handler ** (c) Gareth Watts 1998 ** gareth@omnipotent.net ** ** Alpha code as of 980601 */ #include #include #include #include #include #include "cfg.h" /************************** * PUBLIC METHODS */ void cfg_free(cfg *self) { struct cfg_node *ptr, *nextptr; int i; for(i=0;icfg_hash[i]) { for(ptr=self->cfg_hash[i];ptr;ptr=nextptr) { nextptr=ptr->next; free(ptr->optname); free(ptr->optvalue); free(ptr); } } } free(self->filename); free(self->lasterror); self->_queuefree(self); free(self); } #define CFG_BUZSIZE 4096 int cfg_openfile(cfg *self,char *fn,int mode) { char buffer[CFG_BUZSIZE]; char *keyname,*keyvalue; int comment; FILE *fh; self->filename=strdup(fn); fh=fopen(fn,"r"); if (!fh) { if (mode==CFG_READONLY) { self->lasterror=strdup(strerror(errno)); return(1); } return(0); /* read/write but file not found */ } // READ AND PARSE FILE HERE while(feof(fh)==0) { fgets(buffer,CFG_BUZSIZE-1,fh); if (strlen(buffer)) { if (buffer[strlen(buffer)-1]=='\n') { buffer[strlen(buffer)-1]='\0'; } if (buffer[0]=='#') { comment=1; } else { comment=0; } } else { comment=1;} if (!comment&&self->_splitline(self,buffer,&keyname,&keyvalue)) { return(1); /* memory error or somesuch */ } if (!comment&&keyname&&keyvalue) { if (!self->_lookup(self,keyname)) { self->_insert(self,keyname,keyvalue,0); } } } return(0); } char *cfg_getstring(cfg *self,char *keyname) { return(self->_lookup(self,keyname)); } int cfg_getint(cfg *self,char *keyname) { int result=0; char *keydata; keydata=self->_lookup(self,keyname); if (keydata) sscanf(keydata,"%d",&result); return(result); } /* This is virtually useless due to rounding errors */ float cfg_getfloat(cfg *self,char *keyname) { float result=0.0; char *keydata; keydata=self->_lookup(self,keyname); if (keydata) sscanf(keydata,"%f",&result); return(result); } int cfg_setstring(cfg *self,char *keyname, char *keyvalue) { return(self->_set(self,keyname,keyvalue)); } int cfg_setint(cfg *self,char *keyname, int keyvalue) { char buffer[200]; sprintf(buffer,"%d",keyvalue); return(self->_set(self,keyname,buffer)); } int cfg_setfloat(cfg *self,char *keyname, float keyvalue) { char buffer[200]; sprintf(buffer,"%f",keyvalue); return(self->_set(self,keyname,buffer)); } int cfg_flush(cfg *self) { if (self->filemode==CFG_READONLY) { return(0); } else { return(self->_flush(self)); } } int cfg_autoflush(cfg *self,int aflush) { self->aflush=(aflush>0); return(0); } /************************** * PRIVATE/INTERNAL METHODS */ int _cfg_init(cfg *self) { memset(self->cfg_hash,0,sizeof(self->cfg_hash)); return(0); } int _cfg_queuepush(cfg *self, struct cfg_node *node) { struct cfg_queuenode *ptr; ptr=(struct cfg_queuenode *)calloc(1,sizeof(struct cfg_queuenode)); if (!ptr) return(1); ptr->ref=node; if (self->cfg_newsttail) { self->cfg_newsttail->next=ptr; self->cfg_newsttail=ptr; } else { self->cfg_newsthead=self->cfg_newsttail=ptr; } return(0); } struct cfg_queuenode *_cfg_queuenext(cfg *self, struct cfg_queuenode *last) { if (last) { return(last->next); } else { return(self->cfg_newsthead); } } int _cfg_queuefree(cfg *self) { struct cfg_queuenode *ptr,*next; for(ptr=self->cfg_newsthead;ptr;ptr=next) { next=ptr->next; free(ptr); } self->cfg_newsthead=self->cfg_newsttail=NULL; return(0); } int _cfg_getkey(cfg *self,char *str) { unsigned int total; int i=1; int j=0; total=(*str)-0x20; str++; while(*str) { total+=i*((*str)-0x20); str++; i+=(33+j++)*j; } return(total % CFG_HASHSIZE); } int _cfg_insert(cfg *self,char *keyname, char *keyval,int dirty) { int key,match=0; struct cfg_node *ptr,*nextptr;; key=self->_getkey(self,keyname); if (!self->cfg_hash[key]) { self->cfg_hash[key]=(struct cfg_node *)calloc(1,sizeof(struct cfg_node)); if (!(self->cfg_hash[key]->optname=strdup(keyname))) { return(1); } if (!(self->cfg_hash[key]->optvalue=strdup(keyval))) { return(1); } if (dirty) self->_queuepush(self,self->cfg_hash[key]); } else { for(nextptr=self->cfg_hash[key];(nextptr)&&(!match);nextptr=ptr->next) { ptr=nextptr; match=(strcasecmp(ptr->optname,keyname)==0); } if (match) { /* node already exists / overwrite */ free(ptr->optvalue); ptr->dirty=dirty; } else { /* new node */ ptr->next=(struct cfg_node *)calloc(1,sizeof(struct cfg_node)); ptr=ptr->next; if (!(ptr->optname=strdup(keyname))) { return(1); } if (dirty) self->_queuepush(self,ptr); } if (!(ptr->optvalue=strdup(keyval))) { return(1); } } return(0); } struct cfg_node *_cfg_getnode(cfg *self, char *keyname) { int key; struct cfg_node *ptr,*next; int match=0; key=self->_getkey(self,keyname); if (!self->cfg_hash[key]) return(NULL); for(next=self->cfg_hash[key];next&&!match;next=ptr->next) { ptr=next; match=(strcasecmp(ptr->optname,keyname)==0); } if (match) return(ptr); return(NULL); } char *_cfg_lookup(cfg *self,char *keyname) { struct cfg_node *ptr; ptr=self->_getnode(self,keyname); if (ptr) return(ptr->optvalue); else return(NULL); } int _cfg_cleardirty(cfg *self,struct cfg_node *ptr) { ptr->dirty=0; return(0); } int _cfg_set(cfg *self,char *keyname,char *keyvalue) { int key; key=self->_getkey(self,keyname); if (self->_insert(self,keyname,keyvalue,1)!=0) { return(1); /* error */ } if (self->aflush) { return(self->flush(self)); } return(0); } int _cfg_splitline(cfg *self,char *src, char **f, char **s) { char *brkpoint; int offset,slen,fpos; static char first[4096],second[4096]; *f=*s=NULL; if ((brkpoint=strchr(src,'='))==NULL) { return(0); } offset=brkpoint-src; slen=strlen(src); if (offset==slen-1) { return(0); } if (offset>4094) {offset=4094;} memset(first,0,4096); memset(second,0,4096); strncpy(first,src,offset); strncpy(second,brkpoint+1,4095); fpos=strlen(first)-1; while((first[fpos]==' ')&&fpos) { first[fpos]='\0'; fpos--; } *f=first; for(*s=second;(**s)&&(**s==' ');(*s)++); return(0); } #define CFGF_MAXLINES 50000 #define CFGF_LINELEN 4096 int _cfg_realflush(cfg *self) { char *outbuf[CFGF_MAXLINES]; char buffer[CFGF_LINELEN]; int linenum=0; int comment,i; char *keyname,*keyvalue; struct cfg_queuenode *qptr; struct cfg_node *ptr; FILE *fh; char *rd; fh=fopen(self->filename,"r"); if (fh) { while((feof(fh)==0)&&(linenum_splitline(self,buffer,&keyname,&keyvalue)) { fclose(fh); return(1); /* memory error or somesuch */ } if ((keyname)&&(ptr=self->_getnode(self,keyname))&&(ptr->dirty)) { sprintf(buffer,"%s = %s",ptr->optname,ptr->optvalue); outbuf[linenum]=strdup(buffer); self->_cleardirty(self,ptr); } else { outbuf[linenum]=strdup(buffer); } } linenum++; } } fclose(fh); } for(qptr=self->_queuenext(self,NULL);qptr;qptr=self->_queuenext(self,qptr)) { sprintf(buffer,"%s=%s",qptr->ref->optname,qptr->ref->optvalue); outbuf[linenum]=strdup(buffer); linenum++; } fh=fopen(self->filename,"w"); if (!fh) { self->lasterror=strdup(strerror(errno)); return(1); } for(i=0;i_queuefree(self); return(0); } /***************** * THE CONSTRUCTOR */ cfg *cfg_new() { cfg *ptr; ptr=(cfg *)calloc(1,sizeof(cfg)); if (!ptr) return(NULL); ptr->getstring=cfg_getstring; ptr->getint=cfg_getint; ptr->getfloat=cfg_getfloat; ptr->openfile=cfg_openfile; ptr->setstring=cfg_setstring; ptr->setint=cfg_setint; ptr->setfloat=cfg_setfloat; ptr->flush=cfg_flush; ptr->autoflush=cfg_autoflush; ptr->free=cfg_free; ptr->_getkey=_cfg_getkey; ptr->_insert=_cfg_insert; ptr->_lookup=_cfg_lookup; ptr->_cleardirty=_cfg_cleardirty; ptr->_set=_cfg_set; ptr->_splitline=_cfg_splitline; ptr->_queuepush=_cfg_queuepush; ptr->_queuenext=_cfg_queuenext; ptr->_queuefree=_cfg_queuefree; ptr->_getnode=_cfg_getnode; ptr->_flush=_cfg_realflush; if (_cfg_init(ptr)) return(NULL); return(ptr); }