1 /* $Id$
2  ******************************************************************************
3  * FIDOCONFIG --- library for fidonet configs
4  ******************************************************************************
5  * Copyright (C) 2000-2002
6  *
7  * Max Levenkov
8  * Husky development team
9  * http://husky.sourceforge.net/team.html
10  *
11  * This file is part of FIDOCONFIG.
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Library General Public
15  * License as published by the Free Software Foundation; either
16  * version 2 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Library General Public License for more details.
22  *
23  * You should have received a copy of the GNU Library General Public
24  * License along with this library; see file COPYING. If not, write to the Free
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  *
27  * See also http://www.gnu.org
28  *****************************************************************************
29  */
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <ctype.h>
40 
41 #include <huskylib/huskylib.h>
42 
43 #ifdef HAS_STRINGS_H
44 #   include <strings.h>
45 #endif /* HAS_STRINGS_H */
46 
47 #if defined (__OS2__)
48 # define INCL_DOSFILEMGR
49 # include <os2.h>
50 #endif
51 
52 /* export functions from DLL */
53 #define DLLEXPORT
54 #include <huskylib/huskyext.h>
55 
56 #include "fidoconf.h"
57 #include "common.h"
58 
59 #define setcond for (i=0, condition=1; i<=iflevel; condition=ifstack[i++].state && condition);
60 
61 #define DEFAULT_MODULE " " /* Default value if variable [module] is not defined. This is necessary to prevent warning about undefined [module] */
62 
63 static char *curconfname=NULL;
64 static long curconfpos=0;
65 static FILE *hcfg=NULL;
66 static short condition;
67 static int  iflevel, nvars, sp;
68 static int  maxnvars, maxsp, maxif;
69 static struct { short state, inelse, wastrue;
70               } *ifstack=NULL;
71 static struct { char *var, *value;
72               } *set=NULL;
73 static struct {
74         FILE *farr;
75         int  curline;
76         char *confname;
77       } *incstack=NULL;
78 
79 static unsigned int cfgNamesCount;
80 static char **cfgNames=NULL;
81 
init_conf(const char * conf_name)82 int init_conf(const char *conf_name)
83 {
84   if( conf_name==NULL || conf_name[0]==0 )
85   {
86     w_log(LL_ERR, __FILE__ "::init_conf(): config name %s", conf_name?"has null length":"is NULL pointer");
87     return -1;
88   }
89   iflevel=-1;
90   condition=1;
91   sp=0;
92   cfgNamesCount=0;
93   hcfg=fopen(conf_name, "rb");
94   if (hcfg==NULL)
95   {
96     fprintf(stderr, "Can't open config file %s: %s!\n",
97             conf_name, strerror(errno));
98     wasError = 1;
99     return -1;
100   }
101   curconfname=sstrdup(conf_name);
102   actualLineNr=0;
103 #if defined(__UNIX__)
104   setvar("OS", "UNIX");
105 #elif defined(__OS2__)
106   setvar("OS", "OS/2");
107 #elif defined(__NT__)
108   setvar("OS", "WIN");
109 #elif defined(__DOS__)
110   setvar("OS", "MSDOS");
111 #endif
112   /* Variables for special symbols escaping */
113   setvar("[", "[");
114   setvar("`", "`");
115   setvar("\"", "\"");
116   setvar("'", "'");
117   setvar("#", "#");
118   { /* default value for the [module] */
119     char *module = getvar("module");
120     if (!module)
121       setvar("module", DEFAULT_MODULE);
122   }
123   /* Reinit CommentChar to the default value */
124   CommentChar='#';
125   return 0;
126 }
127 
getvar(char * name)128 char *getvar(char *name)
129 { int i;
130 
131   for (i=0; i<nvars; i++)
132     if (sstricmp(name, set[i].var)==0)
133     { if (set[i].value[0]==0)
134         return NULL;
135       return set[i].value;
136     }
137   return getenv(name);
138 }
139 
setvar(char * name,char * value)140 void setvar(char *name, char *value)
141 { int i, j;
142 
143   /* find var */
144   for (i=0; i<nvars; i++)
145     if (sstricmp(set[i].var, name)==0)
146       break;
147   if (i<nvars)
148   { /* remove var */
149     nfree(set[i].var);
150     for (j=i; j<nvars-1; j++)
151     { set[j].var=set[j+1].var;
152       set[j].value=set[j+1].value;
153     }
154     nvars--;
155   }
156   if (value==NULL) value="";
157   if (value[0]==0)
158     if (getvar(value)==NULL)
159       return;
160   if (nvars==maxnvars)
161     set = srealloc(set, (maxnvars+=10)*sizeof(*set));
162   set[nvars].var=smalloc(sstrlen(name)+sstrlen(value)+2);
163   sstrcpy(set[nvars].var, name);
164   set[nvars].value=set[nvars].var+sstrlen(name)+1;
165   sstrcpy(set[nvars].value, value);
166   nvars++;
167   return;
168 }
169 
free_vars(void)170 void free_vars(void)
171 {
172   int i;
173   for(i=0; i<nvars; i++)
174     nfree(set[i].var);
175   maxnvars=nvars=0;
176   nfree(set);
177 }
close_conf(void)178 void close_conf(void)
179 {
180   int i;
181   char *module;
182 
183   module = getvar("module");
184   if (module) module = sstrdup(module);
185   free_vars();
186   if (module)
187   { setvar("module", module);
188     nfree(module);
189   }
190   else
191     setvar("module", DEFAULT_MODULE);
192   nfree(ifstack);
193   maxif=0;
194   if (hcfg) fclose(hcfg);
195   hcfg=NULL;
196   for (i=0; i<sp; i++) {
197     fclose(incstack[i].farr);
198     nfree(incstack[i].confname);
199   }
200   nfree(curconfname);
201   nfree(incstack);
202   sp=maxsp=0;
203   for (i=0; i<(int)cfgNamesCount; i++) nfree(cfgNames[i]);
204   nfree(cfgNames);
205   cfgNamesCount=0;
206 }
207 
_configline(void)208 static char *_configline(void)
209 {
210   char *line;
211 
212   curconfpos = ftell(hcfg);
213   line = readLine(hcfg);
214   if (line == NULL)
215     return NULL;
216   actualLineNr++;
217   return line;
218 }
219 
vars_expand(char * line)220 char *vars_expand(char *line)
221 {
222   int  curlen;
223   char *parsed, *src, *dest, *p, *p1, *newparsed;
224 #if defined(__UNIX__) || (defined(__OS2__) && defined(__EMX__))
225   FILE *f;
226   int  i;
227 #endif
228 
229 #if defined(__UNIX__) || (defined(__OS2__) && defined(__EMX__))
230   if (strpbrk(line, "[`")==NULL)
231 #else
232   if (strchr(line, '[')==NULL)
233 #endif
234      return line;
235   curlen = sstrlen(line)+1;
236   parsed = dest = smalloc(curlen);
237   for (src = line; *src; src++)
238   {
239     if (dest-parsed >= curlen-2)
240     {
241       size_t offset = (size_t) (dest - parsed);
242              /* we need this to fake around boundary checking */
243 
244       newparsed = srealloc(parsed, curlen+=80);
245       dest = newparsed + offset;
246       parsed = newparsed;
247     }
248     switch (*src)
249     {
250 #if defined(__UNIX__) || (defined(__OS2__) && defined(__EMX__))
251       case '`':
252         p = strchr(src+1, '`');
253         if (p == NULL)
254         {
255           *dest++ = *src;
256           continue;
257         }
258         *p = '\0';
259         src++;
260         f = popen(src, "r");
261         if (f)
262         {
263           w_log (LL_EXEC, "Run command `%s` (specified in config file %s)", src, curconfname);
264           *p = '`';
265           src = p;
266           while ((i = fgetc(f)) != EOF)
267           {
268             if (dest-parsed >= curlen-2)
269             {
270               newparsed = srealloc(parsed, curlen+=80);
271               dest = newparsed+(unsigned)(dest-parsed);
272               parsed = newparsed;
273             }
274             if (i!='\n') *dest++ = (char)i;
275           }
276           pclose(f);
277         }
278         else
279           w_log (LL_ERR, "Can't run command `%s` (specified in config file %s)", src, curconfname);
280         continue;
281 #endif
282       case '[':
283         p = strchr(src, ']');
284         if (p)
285         {
286           src++;
287           *p = '\0';
288           if ((p1 = getvar(src)))
289           {
290             if (sstrlen(p1) > sstrlen(src)+2)
291             {
292               newparsed = srealloc(parsed, curlen += sstrlen(p1)-sstrlen(src)-2);
293               dest = newparsed+(unsigned)(dest-parsed);
294               parsed = newparsed;
295             }
296             sstrcpy(dest, p1);
297             dest += sstrlen(p1);
298           }
299           else
300           {
301             w_log(LL_WARN, "Enviroment variable [%s] (used in config file %s) is not defined and is replaced with empty string", src, curconfname);
302           }
303           *p = ']';
304           src = p;
305           continue;
306         }
307       default:
308         *dest++ = *src;
309         continue;
310     }
311   }
312   *dest++ = '\0';
313   if (curlen != dest-parsed)
314     parsed = srealloc(parsed, (unsigned)(dest-parsed));
315   nfree(line);
316   return parsed;
317 }
318 
boolexpr(char * str)319 static short boolexpr(char *str)
320 { char *p, *p1, *p2;
321   short ret, inquote, relax;
322 
323   ret=1;
324   for (p=str; isspace(*p); p++);
325   if (strncasecmp(p, "not ", 4)==0)
326   { ret=0;
327     for (p+=4; isspace(*p); p++);
328   }
329   inquote=0;
330   for (p1=p; *p1; p1++)
331   {
332     if (p1[0]=='\\' && (p1[1]=='\\' || p1[1]=='\"'))
333     { p1++;
334       continue;
335     }
336     if (*p1=='\"')
337     { inquote = !inquote;
338       continue;
339     }
340     if (!inquote)
341       if ((p1[0] == '=' || p1[0] == '!') && (p1[1] == '=' || p1[1] == '~'))
342         break;
343   }
344   if (*p1==0)
345   { fprintf(stderr, "Bad 'if' expression in config %s, line %d: '%s'\n",
346             curconfname, actualLineNr, str);
347     wasError = 1;
348     return ret;
349   }
350   if (p1[0]=='!') ret=!ret;
351   relax=(p1[1]=='~');
352   *p1=0;
353   for (p2=p1-1; isspace(*p2); *p2--=0);
354   for (p1+=2; isspace(*p1); p1++);
355   for (p2=p1+sstrlen(p1)-1; isspace(*p2); *p2--=0);
356   if (relax ? patimat(p, p1) : sstricmp(p, p1))
357     ret=!ret;
358   return ret;
359 }
360 
configline(void)361 char *configline(void)
362 { int  i;
363   char *p, *p1, *p2, *str, *line=NULL;
364 
365   for (;;) {
366     nfree(line);
367     line=str=_configline();
368     if (str==NULL) {
369        /*  save parsed config name */
370        cfgNames = srealloc(cfgNames, sizeof(char*)*(cfgNamesCount+1));
371        cfgNames[cfgNamesCount] = NULL;
372        xstrcat(&cfgNames[cfgNamesCount], curconfname);
373        cfgNamesCount++;
374        if (sp) {
375           fclose(hcfg);
376           nfree(curconfname);
377           hcfg=incstack[--sp].farr;
378           actualLineNr=incstack[sp].curline;
379           curconfname=incstack[sp].confname;
380           continue;
381        }
382        return NULL;
383     }
384     while (*str && isspace(*str)) str++;
385     stripComment(str);
386     if (strncasecmp(str, "if ", 3)==0)
387     {
388       p=vars_expand(line); str+=(p-line); line=p;
389       iflevel++;
390       if (iflevel==maxif)
391         ifstack=srealloc(ifstack, (maxif+=10)*sizeof(*ifstack));
392       ifstack[iflevel].inelse=0;
393       ifstack[iflevel].state=ifstack[iflevel].wastrue=boolexpr(str+3);
394       condition = condition && ifstack[iflevel].state;
395       continue;
396     }
397     if ((strncasecmp(str, "ifdef ",  6)==0) ||
398         (strncasecmp(str, "ifndef ", 7)==0))
399     {
400       p=vars_expand(line); str+=(p-line); line=p;
401       for (p1=str+sstrlen(str)-1; isspace(*p1); *p1--='\0');
402       for (p=str+6; isspace(*p); p++);
403       if (*p=='\0')
404       { fprintf(stderr, "Bad %s in config %s line %d!\n",
405                 str, curconfname, actualLineNr);
406         wasError = 1;
407         continue;
408       }
409       iflevel++;
410       if (iflevel==maxif)
411         ifstack=srealloc(ifstack, (maxif+=10)*sizeof(*ifstack));
412       ifstack[iflevel].inelse=0;
413       ifstack[iflevel].state=(getvar(p)!=NULL);
414       if (tolower(str[2])=='n') /* ifndef */
415         ifstack[iflevel].state=!ifstack[iflevel].state;
416       ifstack[iflevel].wastrue=ifstack[iflevel].state;
417       condition = condition && ifstack[iflevel].state;
418       continue;
419     }
420     if (strncasecmp(str, "elseif ", 7)==0 || strncasecmp(str, "elif ", 5) == 0)
421     {
422       if ((iflevel==-1) || ifstack[iflevel].inelse)
423       { fprintf(stderr, "Misplaced elseif in config %s line %d ignored!\n",
424                 curconfname, actualLineNr);
425         wasError = 1;
426         continue;
427       }
428       p=vars_expand(line); str+=(p-line); line=p;
429       if (ifstack[iflevel].wastrue)
430         ifstack[iflevel].state=0;
431       else
432         ifstack[iflevel].state=ifstack[iflevel].wastrue=boolexpr(strchr(str, ' '));
433       setcond;
434       continue;
435     }
436     if (strncasecmp(str, "else", 4)==0)
437     {
438       if ((iflevel==-1) || ifstack[iflevel].inelse)
439       { fprintf(stderr, "Misplaced else in config %s line %d ignored!\n",
440                 curconfname, actualLineNr);
441         wasError = 1;
442         continue;
443       }
444       ifstack[iflevel].inelse=1;
445       ifstack[iflevel].state=!ifstack[iflevel].wastrue;
446       setcond;
447       continue;
448     }
449     if (strncasecmp(str, "endif", 5)==0)
450     {
451       if (iflevel==-1)
452       { fprintf(stderr, "Misplaced endif in config %s line %d ignored!\n",
453                 curconfname, actualLineNr);
454         wasError = 1;
455         continue;
456       }
457       iflevel--;
458       setcond;
459       continue;
460     }
461     if (!condition)
462       continue;
463     if (strncasecmp(str, "set ", 4)==0)
464     {
465       p=vars_expand(line); str+=(p-line); line=p;
466       p=strchr(str, '\n');
467       if (p) *p=0;
468       p1=strchr(str+4, '=');
469       if (p1==NULL)
470       { fprintf(stderr, "Incorrect set in config %s line %d!\n",
471                 curconfname, actualLineNr);
472         wasError = 1;
473         continue;
474       }
475       *p1=0;
476       for (p=p1-1; isspace(*p); *p--='\0');
477       for (p=str+4; isspace(*p); p++);
478       /* now p - name of var */
479       for (p1++; isspace(*p1); p1++);
480       if (*p1=='\"')
481       { /* remove quote chars */
482         for (p2=p1; (p2=strchr(p2+1, '\"'))!=NULL;)
483           if (*(p2-1)!='\\')
484             *p2--='\0';
485         p1++;
486       }
487       setvar(p, p1);
488       continue;
489     }
490     if (strncasecmp(str, "include", 7)==0)
491     {
492       p=vars_expand(line); str+=(p-line); line=p;
493       for (p=str+7; (*p==' ') || (*p=='\t'); p++);
494       for (p1=p+sstrlen(p)-1; isspace(*p1); *p1--=0);
495       for (i=0; i<sp; i++)
496         if (sstrcmp(incstack[i].confname, p) == 0)
497         { fprintf(stderr, "Line %d: WARNING: recursive include of file %s detected and fixed!\n", actualLineNr, p);
498           continue;
499         }
500       if (sp==maxsp)
501         incstack=srealloc(incstack, (maxsp+=10)*sizeof(*incstack));
502       incstack[sp].farr=hcfg;
503       hcfg=fopen(p, "rb");
504       if (hcfg==NULL)
505       { fprintf(stderr, "Can't open include file %s: %s!\n", p, strerror(errno));
506         hcfg=incstack[sp].farr;
507         wasError = 1;
508         continue;
509       }
510       incstack[sp].confname=curconfname;
511       incstack[sp].curline=actualLineNr;
512       sp++;
513       curconfname=sstrdup(p);
514       actualLineNr=0;
515       continue;
516     }
517     if ((strncasecmp(str, "commentchar", 11) == 0) && isspace(str[11]))
518     {
519       for (p=str+11; isspace(*p); p++);
520       if (!*p)
521       { printf("\"%s\", line %d: a comment character after CommentChar is missing!\n", curconfname, actualLineNr);
522         continue;
523       }
524       if (!strchr(TRUE_COMMENT, *p))
525       { printf("\"%s\", line %d: CommentChar - '%c' is not a valid comment character!\n", curconfname, actualLineNr, *p);
526       } else
527       { char buf2[2]="\0";
528         buf2[0]=CommentChar;
529         setvar(buf2,"");
530         CommentChar = *p;
531         buf2[0]=CommentChar;
532         setvar(buf2,buf2);
533       }
534       continue;
535     }
536     return line;
537   }
538 }
539 
checkIncludeLogic(ps_fidoconfig config)540 void checkIncludeLogic(ps_fidoconfig config)
541 {
542     UINT i, j;
543 
544     for (j=0; j<config->linkCount; j++) {
545         if (config->links[j]->areafix.autoCreateFile==NULL) continue;
546         for (i=0; i<cfgNamesCount; i++) {
547             if (cmpfnames(cfgNames[i],config->links[j]->areafix.autoCreateFile)==0)
548                 break;
549         }
550         /*  if not found include file - return error */
551         if (i==cfgNamesCount) {
552             printf("areafixAutoCreateFile %s has never been included in config!\n",
553                 config->links[j]->areafix.autoCreateFile);
554             exit(EX_CONFIG);
555         }
556     }
557 
558     for (j=0; j<config->linkCount; j++) {
559         if (config->links[j]->filefix.autoCreateFile==NULL) continue;
560         for (i=0; i<cfgNamesCount; i++) {
561             if (cmpfnames(cfgNames[i],config->links[j]->filefix.autoCreateFile)==0) break;
562         }
563         /*  if not found include file - return error */
564         if (i==cfgNamesCount) {
565             printf("filefixAutoCreateFile %s has never been included in config!\n",
566                 config->links[j]->filefix.autoCreateFile);
567             exit(EX_CONFIG);
568         }
569     }
570     /* check for duplicate includes */
571     for( i = 0; i < cfgNamesCount - 1; i++ )
572         for ( j = i+1; j < cfgNamesCount;  j++ )
573             if (cmpfnames(cfgNames[i],cfgNames[j])==0)
574             {
575                 printf("File %s is included in config more than once!\n",cfgNames[i]);
576                 exit(EX_CONFIG);
577             }
578 
579 }
580 
getCurConfName()581 const char* getCurConfName()
582 {
583     return curconfname;
584 }
585 
getCurConfPos()586 long getCurConfPos()
587 {
588     return curconfpos;
589 }
590 
get_hcfgPos()591 long get_hcfgPos()
592 {
593     return ftell(hcfg);
594 }
595 
get_hcfg()596 FILE *get_hcfg()
597 {
598     return hcfg;
599 }
600