/* $Id: cfg.c,v 1.62.2.3 2005/11/13 05:54:11 stas_degteff Exp $
******************************************************************************
* FIDOCONFIG --- library for fidonet configs
******************************************************************************
* Copyright (C) 2000-2002
*
* Max Levenkov
* Husky development team
* http://husky.sourceforge.net/team.html
*
* This file is part of FIDOCONFIG.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; see file COPYING. If not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* See also http://www.gnu.org
*****************************************************************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <smapi/compiler.h>
#if defined (__OS2__)
# define INCL_DOSFILEMGR
# include <os2.h>
#endif
#include "typesize.h"
#include "fidoconf.h"
#include "common.h"
#include "xstr.h"
#include "log.h"
#define setcond for (i=0, condition=1; i<=iflevel; condition=ifstack[i++].state && condition);
static char *curconfname=NULL;
static long curconfpos=0;
static FILE *hcfg=NULL;
static short condition;
static int iflevel, nvars, sp;
static int maxnvars, maxsp, maxif;
static struct { short state, inelse, wastrue;
} *ifstack=NULL;
static struct { char *var, *value;
} *set=NULL;
static struct {
FILE *farr;
int curline;
char *confname;
} *incstack=NULL;
static unsigned int cfgNamesCount;
static char **cfgNames=NULL;
int init_conf(const char *conf_name)
{
if( conf_name==NULL || conf_name[0]==0 )
{
w_log(LL_ERR, __FILE__ "::init_conf(): config name %s", conf_name?"has null length":"is NULL pointer");
return -1;
}
iflevel=-1;
condition=1;
sp=0;
cfgNamesCount=0;
hcfg=fopen(conf_name, "rb");
if (hcfg==NULL)
{
fprintf(stderr, "Can't open config file %s: %s!\n",
conf_name, strerror(errno));
wasError = 1;
return -1;
}
curconfname=sstrdup(conf_name);
actualLineNr=0;
#if defined(__UNIX__)
setvar("OS", "UNIX");
#elif defined(__OS2__)
setvar("OS", "OS/2");
#elif defined(__NT__)
setvar("OS", "WIN");
#elif defined(__DOS__)
setvar("OS", "MSDOS");
#endif
setvar("[", "[");
setvar("`", "`");
return 0;
}
char *getvar(char *name)
{ int i;
for (i=0; i<nvars; i++)
if (sstricmp(name, set[i].var)==0)
{ if (set[i].value[0]==0)
return NULL;
return set[i].value;
}
return getenv(name);
}
void setvar(char *name, char *value)
{ int i, j;
/* find var */
for (i=0; i<nvars; i++)
if (sstricmp(set[i].var, name)==0)
break;
if (i<nvars)
{ /* remove var */
nfree(set[i].var);
for (j=i; j<nvars-1; j++)
{ set[j].var=set[j+1].var;
set[j].value=set[j+1].value;
}
nvars--;
}
if (value==NULL) value="";
if (value[0]==0)
if (getvar(value)==NULL)
return;
if (nvars==maxnvars)
set = srealloc(set, (maxnvars+=10)*sizeof(*set));
set[nvars].var=smalloc(sstrlen(name)+sstrlen(value)+2);
sstrcpy(set[nvars].var, name);
set[nvars].value=set[nvars].var+sstrlen(name)+1;
sstrcpy(set[nvars].value, value);
nvars++;
return;
}
void close_conf(void)
{
int i;
char *module;
module = getvar("module");
if (module) module = sstrdup(module);
for(i=0; i<nvars; i++)
nfree(set[i].var);
maxnvars=nvars=0;
nfree(set);
if (module)
{ setvar("module", module);
nfree(module);
}
nfree(ifstack);
maxif=0;
if (hcfg) fclose(hcfg);
hcfg=NULL;
for (i=0; i<sp; i++) {
fclose(incstack[i].farr);
nfree(incstack[i].confname);
}
nfree(curconfname);
nfree(incstack);
sp=maxsp=0;
for (i=0; i<(int)cfgNamesCount; i++) nfree(cfgNames[i]);
nfree(cfgNames);
cfgNamesCount=0;
}
static char *_configline(void)
{
char *line;
curconfpos = ftell(hcfg);
line = readLine(hcfg);
if (line == NULL)
return NULL;
actualLineNr++;
return line;
}
char *vars_expand(char *line)
{
int curlen;
char *parsed, *src, *dest, *p, *p1, *newparsed;
#if defined(__UNIX__) || (defined(__OS2__) && defined(__EMX__))
FILE *f;
int i;
#endif
#if defined(__UNIX__) || (defined(__OS2__) && defined(__EMX__))
if (strpbrk(line, "[`")==NULL)
#else
if (strchr(line, '[')==NULL)
#endif
return line;
curlen = sstrlen(line)+1;
parsed = dest = smalloc(curlen);
for (src = line; *src; src++)
{
if (dest-parsed >= curlen-2)
{
size_t offset = (size_t) (dest - parsed);
/* we need this to fake around boundary checking */
newparsed = srealloc(parsed, curlen+=80);
dest = newparsed + offset;
parsed = newparsed;
}
switch (*src)
{
#if defined(__UNIX__) || (defined(__OS2__) && defined(__EMX__))
case '`':
p = strchr(src+1, '`');
if (p == NULL)
{
*dest++ = *src;
continue;
}
*p = '\0';
src++;
f = popen(src, "r");
*p = '`';
src = p;
while ((i = fgetc(f)) != EOF)
{
if (dest-parsed >= curlen-2)
{
newparsed = srealloc(parsed, curlen+=80);
dest = newparsed+(unsigned)(dest-parsed);
parsed = newparsed;
}
if (i!='\n') *dest++ = (char)i;
}
pclose(f);
continue;
#endif
case '[':
p = strchr(src, ']');
if (p)
{
src++;
*p = '\0';
if ((p1 = getvar(src)) == NULL)
p1 = src;
if (sstrlen(p1) > sstrlen(src)+2)
{
newparsed = srealloc(parsed, curlen += sstrlen(p1)-sstrlen(src)-2);
dest = newparsed+(unsigned)(dest-parsed);
parsed = newparsed;
}
sstrcpy(dest, p1);
dest += sstrlen(p1);
*p = ']';
src = p;
continue;
}
default:
*dest++ = *src;
continue;
}
}
*dest++ = '\0';
if (curlen != dest-parsed)
parsed = srealloc(parsed, (unsigned)(dest-parsed));
nfree(line);
return parsed;
}
static short boolexpr(char *str)
{ char *p, *p1, *p2;
short ret, inquote, relax;
ret=1;
for (p=str; isspace(*p); p++);
if (strncasecmp(p, "not ", 4)==0)
{ ret=0;
for (p+=4; isspace(*p); p++);
}
inquote=0;
for (p1=p; *p1; p1++)
{
if (p1[0]=='\\' && (p1[1]=='\\' || p1[1]=='\"'))
{ p1++;
continue;
}
if (*p1=='\"')
{ inquote = !inquote;
continue;
}
if (!inquote)
if ((p1[0] == '=' || p1[0] == '!') && (p1[1] == '=' || p1[1] == '~'))
break;
}
if (*p1==0)
{ fprintf(stderr, "Bad if expression in config %s, line %d: '%s'\n",
curconfname, actualLineNr, str);
wasError = 1;
return ret;
}
if (p1[0]=='!') ret=!ret;
relax=(p1[1]=='~');
*p1=0;
for (p2=p1-1; isspace(*p2); *p2--=0);
for (p1+=2; isspace(*p1); p1++);
for (p2=p1+sstrlen(p1)-1; isspace(*p2); *p2--=0);
if (relax ? patimat(p, p1) : sstricmp(p, p1))
ret=!ret;
return ret;
}
char *configline(void)
{ int i;
char *p, *p1, *p2, *str, *line=NULL;
for (;;) {
nfree(line);
line=str=_configline();
if (str==NULL) {
/* save parsed config name */
cfgNames = srealloc(cfgNames, sizeof(char*)*(cfgNamesCount+1));
cfgNames[cfgNamesCount] = NULL;
xstrcat(&cfgNames[cfgNamesCount], curconfname);
cfgNamesCount++;
if (sp) {
fclose(hcfg);
nfree(curconfname);
hcfg=incstack[--sp].farr;
actualLineNr=incstack[sp].curline;
curconfname=incstack[sp].confname;
continue;
}
return NULL;
}
while (*str && isspace(*str)) str++;
if (strncasecmp(str, "if ", 3)==0)
{
p=vars_expand(line); str+=(p-line); line=p;
iflevel++;
if (iflevel==maxif)
ifstack=srealloc(ifstack, (maxif+=10)*sizeof(*ifstack));
ifstack[iflevel].inelse=0;
ifstack[iflevel].state=ifstack[iflevel].wastrue=boolexpr(str+3);
condition = condition && ifstack[iflevel].state;
continue;
}
if ((strncasecmp(str, "ifdef ", 6)==0) ||
(strncasecmp(str, "ifndef ", 7)==0))
{
p=vars_expand(line); str+=(p-line); line=p;
for (p1=str+sstrlen(str)-1; isspace(*p1); *p1--='\0');
for (p=str+6; isspace(*p); p++);
if (*p=='\0')
{ fprintf(stderr, "Bad %s in config %s line %d!\n",
str, curconfname, actualLineNr);
wasError = 1;
continue;
}
iflevel++;
if (iflevel==maxif)
ifstack=srealloc(ifstack, (maxif+=10)*sizeof(*ifstack));
ifstack[iflevel].inelse=0;
ifstack[iflevel].state=(getvar(p)!=NULL);
if (tolower(str[2])=='n') /* ifndef */
ifstack[iflevel].state=!ifstack[iflevel].state;
ifstack[iflevel].wastrue=ifstack[iflevel].state;
condition = condition && ifstack[iflevel].state;
continue;
}
if (strncasecmp(str, "elseif ", 7)==0 || strncasecmp(str, "elif ", 5) == 0)
{
if ((iflevel==-1) || ifstack[iflevel].inelse)
{ fprintf(stderr, "Misplaces elseif in config %s line %d ignored!\n",
curconfname, actualLineNr);
wasError = 1;
continue;
}
p=vars_expand(line); str+=(p-line); line=p;
if (ifstack[iflevel].wastrue)
ifstack[iflevel].state=0;
else
ifstack[iflevel].state=ifstack[iflevel].wastrue=boolexpr(strchr(str, ' '));
setcond;
continue;
}
if (strncasecmp(str, "else", 4)==0)
{
if ((iflevel==-1) || ifstack[iflevel].inelse)
{ fprintf(stderr, "Misplaces else in config %s line %d ignored!\n",
curconfname, actualLineNr);
wasError = 1;
continue;
}
ifstack[iflevel].inelse=1;
ifstack[iflevel].state=!ifstack[iflevel].wastrue;
setcond;
continue;
}
if (strncasecmp(str, "endif", 5)==0)
{
if (iflevel==-1)
{ fprintf(stderr, "Misplaced endif in config %s line %d ignored!\n",
curconfname, actualLineNr);
wasError = 1;
continue;
}
iflevel--;
setcond;
continue;
}
if (!condition)
continue;
if (strncasecmp(str, "set ", 4)==0)
{
p=vars_expand(line); str+=(p-line); line=p;
p=strchr(str, '\n');
if (p) *p=0;
p1=strchr(str+4, '=');
if (p1==NULL)
{ fprintf(stderr, "Incorrect set in config %s line %d!\n",
curconfname, actualLineNr);
wasError = 1;
continue;
}
*p1=0;
for (p=p1-1; isspace(*p); *p--='\0');
for (p=str+4; isspace(*p); p++);
/* now p - name of var */
for (p1++; isspace(*p1); p1++);
if (*p1=='\"')
{ /* remove quote chars */
for (p2=p1; (p2=strchr(p2+1, '\"'))!=NULL;)
if (*(p2-1)!='\\')
*p2--='\0';
p1++;
}
setvar(p, p1);
continue;
}
if (strncasecmp(str, "include", 7)==0)
{
p=vars_expand(line); str+=(p-line); line=p;
for (p=str+7; (*p==' ') || (*p=='\t'); p++);
for (p1=p+sstrlen(p)-1; isspace(*p1); *p1--=0);
for (i=0; i<sp; i++)
if (sstrcmp(incstack[i].confname, p) == 0)
{ fprintf(stderr, "Line %d: WARNING: recursive include of file %s detected and fixed!\n", actualLineNr, p);
continue;
}
if (sp==maxsp)
incstack=srealloc(incstack, (maxsp+=10)*sizeof(*incstack));
incstack[sp].farr=hcfg;
hcfg=fopen(p, "rb");
if (hcfg==NULL)
{ fprintf(stderr, "Can't open include file %s: %s!\n", p, strerror(errno));
hcfg=incstack[sp].farr;
wasError = 1;
continue;
}
incstack[sp].confname=curconfname;
incstack[sp].curline=actualLineNr;
sp++;
curconfname=sstrdup(p);
actualLineNr=0;
continue;
}
if ((strncasecmp(str, "commentchar", 11) == 0) && isspace(str[11]))
{
for (p=str+11; isspace(*p); p++);
if (!*p)
{ printf("\"%s\", line %d: There is a comment character missing after CommentChar!\n", curconfname, actualLineNr);
continue;
}
if (!strchr(TRUE_COMMENT, *p))
{ printf("\"%s\", line %d: CommentChar - '%c' is not valid comment characters!\n", curconfname, actualLineNr, *p);
} else
{ CommentChar = *p;
}
continue;
}
return line;
}
}
/* Retrieve full file name (path+file)
Borland - char* _fullpath
MSVC - char* _fullpath
Watcom - char* _fullpath
MinGw32 - char* _fullpath
gcc/emx-os2 - int _fullpath
gcc/linux - char* realpath (not in all distributions)
*/
#if defined (__UNIX__)
int cmpfnames(char *file1, char *file2)
{
struct stat st1, st2;
if (stat(file1, &st1) || stat(file2, &st2))
return 1;
if (st1.st_dev!=st2.st_dev || st1.st_ino!=st2.st_ino)
return 1;
return 0;
}
#elif defined(__NT__)
int cmpfnames(char *file1, char *file2)
{
char buf[256], path1[256], path2[256], *p;
if (sstricmp(file1, file2) == 0) return 0;
if (!GetShortPathName(file1, buf, sizeof(buf)))
sstrncpy(buf, file1, sizeof(buf));
if (!GetFullPathName(buf, sizeof(path1), path1, &p)) return 1;
if (!GetShortPathName(file2, buf, sizeof(buf)))
sstrncpy(buf, file2, sizeof(buf));
if (!GetFullPathName(buf, sizeof(path2), path2, &p)) return 1;
return sstricmp(path1, path2);
}
#elif defined (__OS2__)
int cmpfnames(char *file1, char *file2)
{
char path1[256], path2[256];
/* DosQueryPathInfo declaration in os2emx.h:
ULONG DosQueryPathInfo (PCSZ pszPathName, ULONG ulInfoLevel, PVOID pInfoBuffer, ULONG ulInfoLength);
(PCSZ defined as const char* or unsigned const char* for different conditions)
*/
if( DosQueryPathInfo( (PCSZ)file1, FIL_QUERYFULLNAME, path1, sizeof(path1) )
|| DosQueryPathInfo( (PCSZ)file2, FIL_QUERYFULLNAME, path2, sizeof(path2) )
)
return 1;
return sstricmp(path1, path2);
}
#elif defined (__DJGPP__)
int cmpfnames(char *file1, char *file2)
{
char *path1 = NULL, *path2 = NULL;
int result;
/* _truename() call DOS FN 0x60 (undocumented: return real file name)
and store string into 2nd parameter (if NULL - malloc).
Return value is pointer to this string or NULL.
See c:\djgpp\src\libc\dos\dos\truename.c for details */
result = sstricmp(file1, file2); /* sstricmp() compare NULL strings also */
if( result==0 || file1==NULL || file2==NULL )
return result;
path1 = _truename( file1, NULL );
path2 = _truename( file2, NULL );
if( path1 && path2 ) /* if _truename() error: use result of compare original filenames */
result = sstricmp(path1, path2); /* else compare pathnames */
nfree(path1);
nfree(path2);
return result;
}
#elif defined(__DOS__) && !defined(__FLAT__)
#include <dos.h>
int cmpfnames(char *file1, char *file2)
{
struct REGPACK r;
char path1[128], path2[128];
r.r_ds = FP_SEG(file1);
r.r_si = FP_OFF(file1);
r.r_es = FP_SEG(path1);
r.r_di = FP_OFF(path1);
r.r_ax = 0x6000;
intr(0x21, &r);
r.r_ds = FP_SEG(file2);
r.r_si = FP_OFF(file2);
r.r_es = FP_SEG(path2);
r.r_di = FP_OFF(path2);
r.r_ax = 0x6000;
intr(0x21, &r);
return sstricmp(path1, path2);
}
#elif defined(__WATCOMC__) && defined(__DOS__) && defined(__FLAT__)
#include <i86.h>
/*
struct REGPACKX {
unsigned int eax, ebx, ecx, edx, ebp, esi, edi;
unsigned short ds, es, fs, gs;
unsigned int flags;
};
union REGPACK {
struct REGPACKB h;
struct REGPACKW w;
#if defined(__386__) && !defined(__WINDOWS_386__)
struct REGPACKX x;
#else
struct REGPACKW x;
#endif
};
*/
int cmpfnames(char *file1, char *file2)
{
union REGPACK r;
char path1[128], path2[128];
r.x.ds = (unsigned short)FP_SEG(file1);
r.x.esi = (unsigned int) FP_OFF(file1);
r.x.es = (unsigned short)FP_SEG(path1);
r.x.edi = (unsigned int) FP_OFF(path1);
r.x.eax = 0x6000;
intr(0x21, &r);
r.x.ds = (unsigned short)FP_SEG(file2);
r.x.esi = (unsigned int) FP_OFF(file2);
r.x.es = (unsigned short)FP_SEG(path2);
r.x.edi = (unsigned int) FP_OFF(path2);
r.x.eax = 0x6000;
intr(0x21, &r);
return sstricmp(path1, path2);
}
#else /* Unknown OS */
int cmpfnames(char *file1, char *file2)
{
return sstricmp(file1, file2);
}
#endif
void checkIncludeLogic(ps_fidoconfig config)
{
UINT i, j;
for (j=0; j<config->linkCount; j++) {
if (config->links[j].autoAreaCreateFile==NULL) continue;
for (i=0; i<cfgNamesCount; i++) {
if (cmpfnames(cfgNames[i],config->links[j].autoAreaCreateFile)==0)
break;
}
/* if not found include file - return error */
if (i==cfgNamesCount) {
printf("AutoAreaCreateFile %s has never been included in config!\n",
config->links[j].autoAreaCreateFile);
exit(EX_CONFIG);
}
}
for (j=0; j<config->linkCount; j++) {
if (config->links[j].autoFileCreateFile==NULL) continue;
for (i=0; i<cfgNamesCount; i++) {
if (cmpfnames(cfgNames[i],config->links[j].autoFileCreateFile)==0) break;
}
/* if not found include file - return error */
if (i==cfgNamesCount) {
printf("AutoFileCreateFile %s has never been included in config!\n",
config->links[j].autoFileCreateFile);
exit(EX_CONFIG);
}
}
/* check for duplicate includes */
for( i = 0; i < cfgNamesCount - 1; i++ )
for ( j = i+1; j < cfgNamesCount; j++ )
if (cmpfnames(cfgNames[i],cfgNames[j])==0)
{
printf("File %s is included in config more then one time!\n",cfgNames[i]);
exit(EX_CONFIG);
}
}
const char* getCurConfName()
{
return curconfname;
}
long getCurConfPos()
{
return curconfpos;
}
long get_hcfgPos()
{
return ftell(hcfg);
}
FILE *get_hcfg()
{
return hcfg;
}
syntax highlighted by Code2HTML, v. 0.9.1