Initial commit of `icmpmonitor-1.2` from http://www.crocodile.org/software.html
[icmpmonitor] / cfg.c
/*
* $Id: cfg.c,v 1.1.1.1 1999/11/21 08:16:12 lord Exp $
*
* Vadim Zaliva <lord@crocodile.org>
* http://www.crocodile.org/
*
* Copyright (C) 1999 Vadim Zaliva
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*/
#include "cfg.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdarg.h>
#define MAXTOKENLEN 1024
static int cfg_entry_cmp(const void *a,const void *b)
{
return(strcmp((*((struct Dict **)a))->name, (*((struct Dict **)b))->name));
}
static int cfg_entry_match(const void *a,const void *b)
{
return(strcmp((const char *)a, (*((struct Dict **)b))->name));
}
char *cfgfind(const char *name,struct Cfg *cfg, int offset)
{
int j;
struct Dict **res;
res=(struct Dict **)bsearch(name,
cfg->dict,
cfg->nelements,
sizeof(struct Dict *),
cfg_entry_match
);
if(!res)
return NULL;
if(offset>=(*res)->nvalues)
return NULL;
return (*res)->value[offset];
}
int writecfg(const char *name,struct Cfg *cfg)
{
FILE *f;
int j;
int i;
if((f=fopen(name,"wb"))==NULL)
return -1;
if(cfg)
{
for(i=0;i<cfg->nelements;i++)
{
if(!cfg->dict[i]->name)
continue;
fprintf(f,"%s\t",cfg->dict[i]->name);
for(j=0;j<cfg->dict[i]->nvalues;j++)
if(cfg->dict[i]->value[j])
fprintf(f," %s",cfg->dict[i]->value[j]); //TODO: quote values with spaces.
fprintf(f,"\n");
}
}
fclose(f);
return 0;
}
void freecfg(struct Cfg *cfg)
{
int i,j;
if(!cfg)
return;
for(i=0;i<cfg->nelements;i++)
{
if(cfg->dict[i]->value)
{
for(j=0;j<cfg->dict[i]->nvalues;j++)
if(cfg->dict[i]->value[j])
free(cfg->dict[i]->value[j]);
free(cfg->dict[i]->value);
}
if(cfg->dict[i]->name)
free(cfg->dict[i]->name);
}
free(cfg->dict);
free(cfg);
}
struct Cfg *readcfg(const char *name)
{
struct Cfg *cfg;
FILE *f;
char tmp[MAXTOKENLEN];
char *s;
int c;
enum
{
START,
NAME,
VALUE,
INQUOTE,
WHITESPACE,
COMMENT
} state=START;
int n,i;
char *pname,*pvalue;
if((f=fopen(name,"rb"))==NULL)
return NULL;
cfg=malloc(sizeof(struct Cfg));
cfg->nelements=0;
s=tmp;
while((c=fgetc(f))!=EOF)
{
/* Order of 'case' statements is important here! */
switch(state)
{
case START:
if(c=='#')
{
state=COMMENT;
break;
}
else
if(!isspace(c))
{
s=tmp;
state=NAME;
} else
{
break;
}
case NAME:
if(isspace(c))
{
struct Dict *tmp1=malloc(sizeof(struct Dict));
*s='\0';
tmp1->nvalues = 0;
tmp1->value = NULL;
tmp1->name = strdup(tmp);
cfg_add_entry(cfg, tmp1);
state=WHITESPACE;
}
else
{
*s++=c;
if(s==(tmp+sizeof(tmp)))
{
/* internal buffer overflow */
freecfg(cfg);
return NULL;
}
}
break;
case WHITESPACE:
if(c=='\n')
{
state=START;
break;
}
else
{
if(!isspace(c))
{
s=tmp;
state=VALUE;
} else
{
break;
}
}
case VALUE:
if(c=='"')
state=INQUOTE;
else
if(isspace(c))
{
struct Dict *last=cfg->dict[cfg->nelements-1];
char **tmp1;
int i;
*s='\0';
tmp1=last->value;
last->value=malloc((last->nvalues+1)*sizeof(char *));
if(tmp1)
{
for(i=0;i<last->nvalues;i++)
last->value[i]=tmp1[i];
free(tmp1);
}
last->value[last->nvalues]=strdup(tmp);
last->nvalues++;
if(c=='\n')
state=START;
else
state=WHITESPACE;
} else
{
*s++=c;
if(s==(tmp+sizeof(tmp)))
{
/* internal buffer overflow */
freecfg(cfg);
return NULL;
}
}
break;
case INQUOTE:
if(c=='"')
{
state=VALUE;
}
else
{
*s++=c;
if(s==(tmp+sizeof(tmp)))
{
/* internal buffer overflow */
freecfg(cfg);
return NULL;
}
}
break;
case COMMENT:
if(c=='\n')
state=START;
break;
}
}
sortcfg(cfg);
return cfg;
}
/**
* Sorts cfg.
* Should be called after each modification
* before attempting to retrieve any data.
*/
void sortcfg (struct Cfg *cfg)
{
qsort((void *) cfg->dict,
cfg->nelements,
sizeof(struct Dict *),
cfg_entry_cmp);
}
/**
* Adds new cfg entry to the end of the dictionary.
* you need to call sortcfg() before it could be
* really used.
*/
void cfg_add_entry (struct Cfg *cfg, struct Dict *d)
{
if(cfg->nelements)
{
struct Dict **last=cfg->dict;
cfg->dict=malloc(sizeof(struct Dict *)*(cfg->nelements+1));
memcpy(cfg->dict,last,sizeof(struct Dict *)*cfg->nelements);
cfg->dict[cfg->nelements]=d;
cfg->nelements++;
free(last);
}
else
{
cfg->dict = malloc(sizeof(struct Dict *));
cfg->dict[0] = d;
cfg->nelements = 1;
}
}
/**
* Adds entry with given name and list of values.
* list should be terminated with NULL and contain
* only const char pointers.
*/
void cfg_new_entry(struct Cfg *cfg, const char *name, ...)
{
int n;
va_list ap;
struct Dict *tmp=malloc(sizeof(struct Dict));
tmp->name = strdup(name);
va_start(ap,name);
n=0;
while(va_arg(ap, const char *)) n++;
va_end(ap);
tmp->nvalues = n;
if(n)
{
int i;
va_start(ap,name);
tmp->value = malloc(n*sizeof(char *));
for(i=0;i<n;i++)
tmp->value[i] = strdup(va_arg(ap, const char *));
va_end(ap);
} else
{
tmp->value = NULL;
}
cfg_add_entry(cfg, tmp);
}
void cfg_new_ulong_entry (struct Cfg *cfg, const char *name, unsigned long v)
{
char tmp[80];
sprintf(tmp,"%lu",v);
cfg_new_entry(cfg, name, tmp, NULL);
}
/**
* add long extended to 'w' chars, with added trailing zeros.
*
* @param v - field value
* @param w - field width
*/
void cfg_new_fmt_ulong_entry (struct Cfg *cfg, const char *name, unsigned long v, int w)
{
char tmp[80];
sprintf(tmp,"%0*lu",w, v);
cfg_new_entry(cfg, name, tmp, NULL);
}
struct Cfg *newcfg ()
{
struct Cfg *res=malloc(sizeof(struct Cfg));
res->nelements = 0;
return res;
}