diff --git a/loadparm.c b/loadparm.c new file mode 100644 index 00000000..6ac9abdc --- /dev/null +++ b/loadparm.c @@ -0,0 +1,607 @@ +/* This is based on loadparm.c from Samba, written by Andrew Tridgell + and Karl Auer */ + +/* + 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. +*/ + +/* + * Load parameters. + * + * This module provides suitable callback functions for the params + * module. It builds the internal table of service details which is + * then used by the rest of the server. + * + * To add a parameter: + * + * 1) add it to the global or service structure definition + * 2) add it to the parm_table + * 3) add it to the list of available functions (eg: using FN_GLOBAL_STRING()) + * 4) If it's a global then initialise it in init_globals. If a local + * (ie. service) parameter then initialise it in the sDefault structure + * + * + * Notes: + * The configuration file is processed sequentially for speed. It is NOT + * accessed randomly as happens in 'real' Windows. For this reason, there + * is a fair bit of sequence-dependent code here - ie., code which assumes + * that certain things happen before others. In particular, the code which + * happens at the boundary between sections is delicately poised, so be + * careful! + * + */ + +#include "rsync.h" +#define BOOL int +#define False 0 +#define True 1 +#define Realloc realloc +#define PTR_DIFF(p1,p2) ((ptrdiff_t)(((char *)(p1)) - (char *)(p2))) +#define strequal(a,b) (strcasecmp(a,b)==0) +#define BOOLSTR(b) ((b) ? "Yes" : "No") +typedef char pstring[1024]; +#define pstrcpy(a,b) strcpy(a,b) + +/* the following are used by loadparm for option lists */ +typedef enum +{ + P_BOOL,P_BOOLREV,P_CHAR,P_INTEGER,P_OCTAL, + P_STRING,P_GSTRING,P_ENUM,P_SEP +} parm_type; + +typedef enum +{ + P_LOCAL,P_GLOBAL,P_SEPARATOR,P_NONE +} parm_class; + +struct enum_list { + int value; + char *name; +}; + +struct parm_struct +{ + char *label; + parm_type type; + parm_class class; + void *ptr; + struct enum_list *enum_list; + unsigned flags; +}; + +static BOOL bLoaded = False; + +#ifndef GLOBAL_NAME +#define GLOBAL_NAME "global" +#endif + +/* some helpful bits */ +#define pSERVICE(i) ServicePtrs[i] +#define iSERVICE(i) (*pSERVICE(i)) +#define LP_SNUM_OK(iService) (((iService) >= 0) && ((iService) < iNumServices)) + +/* + * This structure describes global (ie., server-wide) parameters. + */ +typedef struct +{ + char *motd_file; +} global; + +static global Globals; + + + +/* + * This structure describes a single service. + */ +typedef struct +{ + char *name; + char *path; + char *comment; + BOOL read_only; + BOOL list; + int uid; + int gid; +} service; + + +/* This is a default service used to prime a services structure */ +static service sDefault = +{ + NULL, /* name */ + NULL, /* path */ + NULL, /* comment */ + True, /* read only */ + True, /* list */ + -2, /* uid */ + -2, /* gid */ +}; + + + +/* local variables */ +static service **ServicePtrs = NULL; +static int iNumServices = 0; +static int iServiceIndex = 0; +static BOOL bInGlobalSection = True; + +#define NUMPARAMETERS (sizeof(parm_table) / sizeof(struct parm_struct)) + + +/* note that we do not initialise the defaults union - it is not allowed in ANSI C */ +static struct parm_struct parm_table[] = +{ + {"motd file", P_STRING, P_GLOBAL, &Globals.motd_file, NULL, 0}, + {"name", P_STRING, P_LOCAL, &sDefault.name, NULL, 0}, + {"comment", P_STRING, P_LOCAL, &sDefault.comment, NULL, 0}, + {"path", P_STRING, P_LOCAL, &sDefault.path, NULL, 0}, + {"read only", P_BOOL, P_LOCAL, &sDefault.read_only, NULL, 0}, + {"list", P_BOOL, P_LOCAL, &sDefault.list, NULL, 0}, + {"uid", P_INTEGER, P_LOCAL, &sDefault.uid, NULL, 0}, + {"gid", P_INTEGER, P_LOCAL, &sDefault.gid, NULL, 0}, + {NULL, P_BOOL, P_NONE, NULL, NULL, 0} +}; + + +/*************************************************************************** +Initialise the global parameter structure. +***************************************************************************/ +static void init_globals(void) +{ +} + +/*************************************************************************** +Initialise the sDefault parameter structure. +***************************************************************************/ +static void init_locals(void) +{ +} + + +/* + In this section all the functions that are used to access the + parameters from the rest of the program are defined +*/ + +#define FN_GLOBAL_STRING(fn_name,ptr) \ + char *fn_name(void) {return(*(char **)(ptr) ? *(char **)(ptr) : "");} +#define FN_GLOBAL_BOOL(fn_name,ptr) \ + BOOL fn_name(void) {return(*(BOOL *)(ptr));} +#define FN_GLOBAL_CHAR(fn_name,ptr) \ + char fn_name(void) {return(*(char *)(ptr));} +#define FN_GLOBAL_INTEGER(fn_name,ptr) \ + int fn_name(void) {return(*(int *)(ptr));} + +#define FN_LOCAL_STRING(fn_name,val) \ + char *fn_name(int i) {return((LP_SNUM_OK(i)&&pSERVICE(i)->val)?pSERVICE(i)->val : (sDefault.val?sDefault.val:""));} +#define FN_LOCAL_BOOL(fn_name,val) \ + BOOL fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);} +#define FN_LOCAL_CHAR(fn_name,val) \ + char fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);} +#define FN_LOCAL_INTEGER(fn_name,val) \ + int fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);} + + +FN_GLOBAL_STRING(lp_motd_file, &Globals.motd_file) +FN_LOCAL_STRING(lp_name, name) +FN_LOCAL_STRING(lp_comment, comment) +FN_LOCAL_STRING(lp_path, path) +FN_LOCAL_BOOL(lp_read_only, read_only) +FN_LOCAL_BOOL(lp_list, list) +FN_LOCAL_INTEGER(lp_uid, uid) +FN_LOCAL_INTEGER(lp_gid, gid) + +/* local prototypes */ +static int strwicmp( char *psz1, char *psz2 ); +static int map_parameter( char *parmname); +static BOOL set_boolean( BOOL *pb, char *parmvalue ); +static int getservicebyname(char *name, service *pserviceDest); +static void copy_service( service *pserviceDest, + service *pserviceSource); +static BOOL do_parameter(char *parmname, char *parmvalue); +static BOOL do_section(char *sectionname); + + +/*************************************************************************** +initialise a service to the defaults +***************************************************************************/ +static void init_service(service *pservice) +{ + bzero((char *)pservice,sizeof(service)); + copy_service(pservice,&sDefault); +} + +static void string_set(char **s, char *v) +{ + if (*s) free(*s); + if (!v) { + *s = NULL; + return; + } + *s = strdup(v); + if (!*s) exit_cleanup(1); +} + + +/*************************************************************************** +add a new service to the services array initialising it with the given +service +***************************************************************************/ +static int add_a_service(service *pservice, char *name) +{ + int i; + service tservice; + int num_to_alloc = iNumServices+1; + + tservice = *pservice; + + /* it might already exist */ + if (name) + { + i = getservicebyname(name,NULL); + if (i >= 0) + return(i); + } + + i = iNumServices; + + ServicePtrs = (service **)Realloc(ServicePtrs,sizeof(service *)*num_to_alloc); + if (ServicePtrs) + pSERVICE(iNumServices) = (service *)malloc(sizeof(service)); + + if (!ServicePtrs || !pSERVICE(iNumServices)) + return(-1); + + iNumServices++; + + init_service(pSERVICE(i)); + copy_service(pSERVICE(i),&tservice); + if (name) + string_set(&iSERVICE(i).name,name); + + return(i); +} + +/*************************************************************************** +Do a case-insensitive, whitespace-ignoring string compare. +***************************************************************************/ +static int strwicmp(char *psz1, char *psz2) +{ + /* if BOTH strings are NULL, return TRUE, if ONE is NULL return */ + /* appropriate value. */ + if (psz1 == psz2) + return (0); + else + if (psz1 == NULL) + return (-1); + else + if (psz2 == NULL) + return (1); + + /* sync the strings on first non-whitespace */ + while (1) + { + while (isspace(*psz1)) + psz1++; + while (isspace(*psz2)) + psz2++; + if (toupper(*psz1) != toupper(*psz2) || *psz1 == '\0' || *psz2 == '\0') + break; + psz1++; + psz2++; + } + return (*psz1 - *psz2); +} + +/*************************************************************************** +Map a parameter's string representation to something we can use. +Returns False if the parameter string is not recognised, else TRUE. +***************************************************************************/ +static int map_parameter(char *parmname) +{ + int iIndex; + + if (*parmname == '-') + return(-1); + + for (iIndex = 0; parm_table[iIndex].label; iIndex++) + if (strwicmp(parm_table[iIndex].label, parmname) == 0) + return(iIndex); + + rprintf(FERROR, "Unknown parameter encountered: \"%s\"\n", parmname); + return(-1); +} + + +/*************************************************************************** +Set a boolean variable from the text value stored in the passed string. +Returns True in success, False if the passed string does not correctly +represent a boolean. +***************************************************************************/ +static BOOL set_boolean(BOOL *pb, char *parmvalue) +{ + BOOL bRetval; + + bRetval = True; + if (strwicmp(parmvalue, "yes") == 0 || + strwicmp(parmvalue, "true") == 0 || + strwicmp(parmvalue, "1") == 0) + *pb = True; + else + if (strwicmp(parmvalue, "no") == 0 || + strwicmp(parmvalue, "False") == 0 || + strwicmp(parmvalue, "0") == 0) + *pb = False; + else + { + rprintf(FERROR, "Badly formed boolean in configuration file: \"%s\".\n", + parmvalue); + bRetval = False; + } + return (bRetval); +} + +/*************************************************************************** +Find a service by name. Otherwise works like get_service. +***************************************************************************/ +static int getservicebyname(char *name, service *pserviceDest) +{ + int iService; + + for (iService = iNumServices - 1; iService >= 0; iService--) + if (strwicmp(iSERVICE(iService).name, name) == 0) + { + if (pserviceDest != NULL) + copy_service(pserviceDest, pSERVICE(iService)); + break; + } + + return (iService); +} + + + +/*************************************************************************** +Copy a service structure to another + +***************************************************************************/ +static void copy_service(service *pserviceDest, + service *pserviceSource) +{ + int i; + + for (i=0;parm_table[i].label;i++) + if (parm_table[i].ptr && parm_table[i].class == P_LOCAL) { + void *def_ptr = parm_table[i].ptr; + void *src_ptr = + ((char *)pserviceSource) + PTR_DIFF(def_ptr,&sDefault); + void *dest_ptr = + ((char *)pserviceDest) + PTR_DIFF(def_ptr,&sDefault); + + switch (parm_table[i].type) + { + case P_BOOL: + case P_BOOLREV: + *(BOOL *)dest_ptr = *(BOOL *)src_ptr; + break; + + case P_INTEGER: + case P_ENUM: + case P_OCTAL: + *(int *)dest_ptr = *(int *)src_ptr; + break; + + case P_CHAR: + *(char *)dest_ptr = *(char *)src_ptr; + break; + + case P_STRING: + string_set(dest_ptr,*(char **)src_ptr); + break; + + default: + break; + } + } +} + + +/*************************************************************************** +Process a parameter for a particular service number. If snum < 0 +then assume we are in the globals +***************************************************************************/ +static BOOL lp_do_parameter(int snum, char *parmname, char *parmvalue) +{ + int parmnum, i; + void *parm_ptr=NULL; /* where we are going to store the result */ + void *def_ptr=NULL; + + parmnum = map_parameter(parmname); + + if (parmnum < 0) + { + rprintf(FERROR, "Ignoring unknown parameter \"%s\"\n", parmname); + return(True); + } + + def_ptr = parm_table[parmnum].ptr; + + /* we might point at a service, the default service or a global */ + if (snum < 0) { + parm_ptr = def_ptr; + } else { + if (parm_table[parmnum].class == P_GLOBAL) { + rprintf(FERROR, "Global parameter %s found in service section!\n",parmname); + return(True); + } + parm_ptr = ((char *)pSERVICE(snum)) + PTR_DIFF(def_ptr,&sDefault); + } + + /* now switch on the type of variable it is */ + switch (parm_table[parmnum].type) + { + case P_BOOL: + set_boolean(parm_ptr,parmvalue); + break; + + case P_BOOLREV: + set_boolean(parm_ptr,parmvalue); + *(BOOL *)parm_ptr = ! *(BOOL *)parm_ptr; + break; + + case P_INTEGER: + *(int *)parm_ptr = atoi(parmvalue); + break; + + case P_CHAR: + *(char *)parm_ptr = *parmvalue; + break; + + case P_OCTAL: + sscanf(parmvalue,"%o",(int *)parm_ptr); + break; + + case P_STRING: + string_set(parm_ptr,parmvalue); + break; + + case P_GSTRING: + strcpy((char *)parm_ptr,parmvalue); + break; + + case P_ENUM: + for (i=0;parm_table[parmnum].enum_list[i].name;i++) { + if (strequal(parmvalue, parm_table[parmnum].enum_list[i].name)) { + *(int *)parm_ptr = parm_table[parmnum].enum_list[i].value; + break; + } + } + break; + case P_SEP: + break; + } + + return(True); +} + +/*************************************************************************** +Process a parameter. +***************************************************************************/ +static BOOL do_parameter(char *parmname, char *parmvalue) +{ + return lp_do_parameter(bInGlobalSection?-2:iServiceIndex, parmname, parmvalue); +} + +/*************************************************************************** +Process a new section (service). At this stage all sections are services. +Later we'll have special sections that permit server parameters to be set. +Returns True on success, False on failure. +***************************************************************************/ +static BOOL do_section(char *sectionname) +{ + BOOL bRetval; + BOOL isglobal = (strwicmp(sectionname, GLOBAL_NAME) == 0); + bRetval = False; + + /* if we were in a global section then do the local inits */ + if (bInGlobalSection && !isglobal) + init_locals(); + + /* if we've just struck a global section, note the fact. */ + bInGlobalSection = isglobal; + + /* check for multiple global sections */ + if (bInGlobalSection) + { + return(True); + } + + /* if we have a current service, tidy it up before moving on */ + bRetval = True; + + if (iServiceIndex >= 0) + bRetval = True; + + /* if all is still well, move to the next record in the services array */ + if (bRetval) + { + /* We put this here to avoid an odd message order if messages are */ + /* issued by the post-processing of a previous section. */ + + if ((iServiceIndex=add_a_service(&sDefault,sectionname)) < 0) + { + rprintf(FERROR,"Failed to add a new service\n"); + return(False); + } + } + + return (bRetval); +} + + +/*************************************************************************** +Load the services array from the services file. Return True on success, +False on failure. +***************************************************************************/ +BOOL lp_load(char *pszFname) +{ + pstring n2; + BOOL bRetval; + + bRetval = False; + + bInGlobalSection = True; + + init_globals(); + + pstrcpy(n2,pszFname); + + /* We get sections first, so have to start 'behind' to make up */ + iServiceIndex = -1; + bRetval = pm_process(n2, do_section, do_parameter); + + bLoaded = True; + + return (bRetval); +} + + +/*************************************************************************** +return the max number of services +***************************************************************************/ +int lp_numservices(void) +{ + return(iNumServices); +} + +/*************************************************************************** +Return the number of the service with the given name, or -1 if it doesn't +exist. Note that this is a DIFFERENT ANIMAL from the internal function +getservicebyname()! This works ONLY if all services have been loaded, and +does not copy the found service. +***************************************************************************/ +int lp_number(char *name) +{ + int iService; + + for (iService = iNumServices - 1; iService >= 0; iService--) + if (strequal(lp_name(iService), name)) + break; + + return (iService); +} + diff --git a/log.c b/log.c new file mode 100644 index 00000000..20dc702a --- /dev/null +++ b/log.c @@ -0,0 +1,96 @@ +/* + Copyright (C) Andrew Tridgell 1998 + + 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. +*/ + +/* + logging and utility functions + + tridge, May 1998 + */ +#include "rsync.h" + +/* this is the rsync debugging function. Call it with FINFO or FERROR */ +void rprintf(int fd, const char *format, ...) +{ + va_list ap; + char buf[1024]; + int len; + FILE *f=NULL; + extern int am_daemon; + + if (am_daemon) { + static FILE *logf; + if (!logf) logf = fopen(RSYNCD_LOG, "a"); + f = logf; + if (!f) return; + } + + va_start(ap, format); + +#if HAVE_VSNPRINTF + len = vsnprintf(buf, sizeof(buf)-1, format, ap); +#else + len = vsprintf(buf, format, ap); +#endif + va_end(ap); + + if (len < 0) exit_cleanup(1); + + if (!am_daemon) { + if (fd == FERROR) { + f = stderr; + } + + if (fd == FINFO) { + extern int am_server; + if (am_server) + f = stderr; + else + f = stdout; + } + } + + if (!f) exit_cleanup(1); + + if (fwrite(buf, len, 1, f) != 1) exit_cleanup(1); +} + +void rflush(int fd) +{ + FILE *f = NULL; + extern int am_daemon; + + if (am_daemon) { + return; + } + + if (fd == FERROR) { + f = stderr; + } + + if (fd == FINFO) { + extern int am_server; + if (am_server) + f = stderr; + else + f = stdout; + } + + if (!f) exit_cleanup(1); + fflush(f); +} + diff --git a/params.c b/params.c new file mode 100644 index 00000000..b89d034c --- /dev/null +++ b/params.c @@ -0,0 +1,560 @@ +/* + This modules is based on the params.c module from Samba, written by Karl Auer + and much modifed by Christopher Hertel. + + * 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. + * + * -------------------------------------------------------------------------- ** + * + * Module name: params + * + * -------------------------------------------------------------------------- ** + * + * This module performs lexical analysis and initial parsing of a + * Windows-like parameter file. It recognizes and handles four token + * types: section-name, parameter-name, parameter-value, and + * end-of-file. Comments and line continuation are handled + * internally. + * + * The entry point to the module is function pm_process(). This + * function opens the source file, calls the Parse() function to parse + * the input, and then closes the file when either the EOF is reached + * or a fatal error is encountered. + * + * A sample parameter file might look like this: + * + * [section one] + * parameter one = value string + * parameter two = another value + * [section two] + * new parameter = some value or t'other + * + * The parameter file is divided into sections by section headers: + * section names enclosed in square brackets (eg. [section one]). + * Each section contains parameter lines, each of which consist of a + * parameter name and value delimited by an equal sign. Roughly, the + * syntax is: + * + * :== {
} EOF + * + *
:==
{ } + * + *
:== '[' NAME ']' + * + * :== NAME '=' VALUE '\n' + * + * Blank lines and comment lines are ignored. Comment lines are lines + * beginning with either a semicolon (';') or a pound sign ('#'). + * + * All whitespace in section names and parameter names is compressed + * to single spaces. Leading and trailing whitespace is stipped from + * both names and values. + * + * Only the first equals sign in a parameter line is significant. + * Parameter values may contain equals signs, square brackets and + * semicolons. Internal whitespace is retained in parameter values, + * with the exception of the '\r' character, which is stripped for + * historic reasons. Parameter names may not start with a left square + * bracket, an equal sign, a pound sign, or a semicolon, because these + * are used to identify other tokens. + * + * -------------------------------------------------------------------------- ** + */ + +#include "rsync.h" +#define BOOL int +#define False 0 +#define True 1 +#define Realloc realloc + +/* -------------------------------------------------------------------------- ** + * Constants... + */ + +#define BUFR_INC 1024 + + +/* -------------------------------------------------------------------------- ** + * Variables... + * + * bufr - pointer to a global buffer. This is probably a kludge, + * but it was the nicest kludge I could think of (for now). + * bSize - The size of the global buffer . + */ + +static char *bufr = NULL; +static int bSize = 0; + +/* -------------------------------------------------------------------------- ** + * Functions... + */ + +static int EatWhitespace( FILE *InFile ) + /* ------------------------------------------------------------------------ ** + * Scan past whitespace (see ctype(3C)) and return the first non-whitespace + * character, or newline, or EOF. + * + * Input: InFile - Input source. + * + * Output: The next non-whitespace character in the input stream. + * + * Notes: Because the config files use a line-oriented grammar, we + * explicitly exclude the newline character from the list of + * whitespace characters. + * - Note that both EOF (-1) and the nul character ('\0') are + * considered end-of-file markers. + * + * ------------------------------------------------------------------------ ** + */ + { + int c; + + for( c = getc( InFile ); isspace( c ) && ('\n' != c); c = getc( InFile ) ) + ; + return( c ); + } /* EatWhitespace */ + +static int EatComment( FILE *InFile ) + /* ------------------------------------------------------------------------ ** + * Scan to the end of a comment. + * + * Input: InFile - Input source. + * + * Output: The character that marks the end of the comment. Normally, + * this will be a newline, but it *might* be an EOF. + * + * Notes: Because the config files use a line-oriented grammar, we + * explicitly exclude the newline character from the list of + * whitespace characters. + * - Note that both EOF (-1) and the nul character ('\0') are + * considered end-of-file markers. + * + * ------------------------------------------------------------------------ ** + */ + { + int c; + + for( c = getc( InFile ); ('\n'!=c) && (EOF!=c) && (c>0); c = getc( InFile ) ) + ; + return( c ); + } /* EatComment */ + +static int Continuation( char *line, int pos ) + /* ------------------------------------------------------------------------ ** + * Scan backards within a string to discover if the last non-whitespace + * character is a line-continuation character ('\\'). + * + * Input: line - A pointer to a buffer containing the string to be + * scanned. + * pos - This is taken to be the offset of the end of the + * string. This position is *not* scanned. + * + * Output: The offset of the '\\' character if it was found, or -1 to + * indicate that it was not. + * + * ------------------------------------------------------------------------ ** + */ + { + pos--; + while( (pos >= 0) && isspace(line[pos]) ) + pos--; + + return( ((pos >= 0) && ('\\' == line[pos])) ? pos : -1 ); + } /* Continuation */ + + +static BOOL Section( FILE *InFile, BOOL (*sfunc)(char *) ) + /* ------------------------------------------------------------------------ ** + * Scan a section name, and pass the name to function sfunc(). + * + * Input: InFile - Input source. + * sfunc - Pointer to the function to be called if the section + * name is successfully read. + * + * Output: True if the section name was read and True was returned from + * . False if failed or if a lexical error was + * encountered. + * + * ------------------------------------------------------------------------ ** + */ + { + int c; + int i; + int end; + char *func = "params.c:Section() -"; + + i = 0; /* is the offset of the next free byte in bufr[] and */ + end = 0; /* is the current "end of string" offset. In most */ + /* cases these will be the same, but if the last */ + /* character written to bufr[] is a space, then */ + /* will be one less than . */ + + c = EatWhitespace( InFile ); /* We've already got the '['. Scan */ + /* past initial white space. */ + + while( (EOF != c) && (c > 0) ) + { + + /* Check that the buffer is big enough for the next character. */ + if( i > (bSize - 2) ) + { + bSize += BUFR_INC; + bufr = Realloc( bufr, bSize ); + if( NULL == bufr ) + { + rprintf(FERROR, "%s Memory re-allocation failure.", func); + return( False ); + } + } + + /* Handle a single character. */ + switch( c ) + { + case ']': /* Found the closing bracket. */ + bufr[end] = '\0'; + if( 0 == end ) /* Don't allow an empty name. */ + { + rprintf(FERROR, "%s Empty section name in configuration file.\n", func ); + return( False ); + } + if( !sfunc( bufr ) ) /* Got a valid name. Deal with it. */ + return( False ); + (void)EatComment( InFile ); /* Finish off the line. */ + return( True ); + + case '\n': /* Got newline before closing ']'. */ + i = Continuation( bufr, i ); /* Check for line continuation. */ + if( i < 0 ) + { + bufr[end] = '\0'; + rprintf(FERROR, "%s Badly formed line in configuration file: %s\n", + func, bufr ); + return( False ); + } + end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i); + c = getc( InFile ); /* Continue with next line. */ + break; + + default: /* All else are a valid name chars. */ + if( isspace( c ) ) /* One space per whitespace region. */ + { + bufr[end] = ' '; + i = end + 1; + c = EatWhitespace( InFile ); + } + else /* All others copy verbatim. */ + { + bufr[i++] = c; + end = i; + c = getc( InFile ); + } + } + } + + /* We arrive here if we've met the EOF before the closing bracket. */ + rprintf(FERROR, "%s Unexpected EOF in the configuration file: %s\n", func, bufr ); + return( False ); + } /* Section */ + +static BOOL Parameter( FILE *InFile, BOOL (*pfunc)(char *, char *), int c ) + /* ------------------------------------------------------------------------ ** + * Scan a parameter name and value, and pass these two fields to pfunc(). + * + * Input: InFile - The input source. + * pfunc - A pointer to the function that will be called to + * process the parameter, once it has been scanned. + * c - The first character of the parameter name, which + * would have been read by Parse(). Unlike a comment + * line or a section header, there is no lead-in + * character that can be discarded. + * + * Output: True if the parameter name and value were scanned and processed + * successfully, else False. + * + * Notes: This function is in two parts. The first loop scans the + * parameter name. Internal whitespace is compressed, and an + * equal sign (=) terminates the token. Leading and trailing + * whitespace is discarded. The second loop scans the parameter + * value. When both have been successfully identified, they are + * passed to pfunc() for processing. + * + * ------------------------------------------------------------------------ ** + */ + { + int i = 0; /* Position within bufr. */ + int end = 0; /* bufr[end] is current end-of-string. */ + int vstart = 0; /* Starting position of the parameter value. */ + char *func = "params.c:Parameter() -"; + + /* Read the parameter name. */ + while( 0 == vstart ) /* Loop until we've found the start of the value. */ + { + + if( i > (bSize - 2) ) /* Ensure there's space for next char. */ + { + bSize += BUFR_INC; + bufr = Realloc( bufr, bSize ); + if( NULL == bufr ) + { + rprintf(FERROR, "%s Memory re-allocation failure.", func) ; + return( False ); + } + } + + switch( c ) + { + case '=': /* Equal sign marks end of param name. */ + if( 0 == end ) /* Don't allow an empty name. */ + { + rprintf(FERROR, "%s Invalid parameter name in config. file.\n", func ); + return( False ); + } + bufr[end++] = '\0'; /* Mark end of string & advance. */ + i = end; /* New string starts here. */ + vstart = end; /* New string is parameter value. */ + bufr[i] = '\0'; /* New string is nul, for now. */ + break; + + case '\n': /* Find continuation char, else error. */ + i = Continuation( bufr, i ); + if( i < 0 ) + { + bufr[end] = '\0'; + rprintf(FERROR, "%s Ignoring badly formed line in configuration file: %s\n", + func, bufr ); + return( True ); + } + end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i); + c = getc( InFile ); /* Read past eoln. */ + break; + + case '\0': /* Shouldn't have EOF within param name. */ + case EOF: + bufr[i] = '\0'; + rprintf(FERROR, "%s Unexpected end-of-file at: %s\n", func, bufr ); + return( True ); + + default: + if( isspace( c ) ) /* One ' ' per whitespace region. */ + { + bufr[end] = ' '; + i = end + 1; + c = EatWhitespace( InFile ); + } + else /* All others verbatim. */ + { + bufr[i++] = c; + end = i; + c = getc( InFile ); + } + } + } + + /* Now parse the value. */ + c = EatWhitespace( InFile ); /* Again, trim leading whitespace. */ + while( (EOF !=c) && (c > 0) ) + { + + if( i > (bSize - 2) ) /* Make sure there's enough room. */ + { + bSize += BUFR_INC; + bufr = Realloc( bufr, bSize ); + if( NULL == bufr ) + { + rprintf(FERROR, "%s Memory re-allocation failure.", func) ; + return( False ); + } + } + + switch( c ) + { + case '\r': /* Explicitly remove '\r' because the older */ + c = getc( InFile ); /* version called fgets_slash() which also */ + break; /* removes them. */ + + case '\n': /* Marks end of value unless there's a '\'. */ + i = Continuation( bufr, i ); + if( i < 0 ) + c = 0; + else + { + for( end = i; (end >= 0) && isspace(bufr[end]); end-- ) + ; + c = getc( InFile ); + } + break; + + default: /* All others verbatim. Note that spaces do */ + bufr[i++] = c; /* not advance . This allows trimming */ + if( !isspace( c ) ) /* of whitespace at the end of the line. */ + end = i; + c = getc( InFile ); + break; + } + } + bufr[end] = '\0'; /* End of value. */ + + return( pfunc( bufr, &bufr[vstart] ) ); /* Pass name & value to pfunc(). */ + } /* Parameter */ + +static BOOL Parse( FILE *InFile, + BOOL (*sfunc)(char *), + BOOL (*pfunc)(char *, char *) ) + /* ------------------------------------------------------------------------ ** + * Scan & parse the input. + * + * Input: InFile - Input source. + * sfunc - Function to be called when a section name is scanned. + * See Section(). + * pfunc - Function to be called when a parameter is scanned. + * See Parameter(). + * + * Output: True if the file was successfully scanned, else False. + * + * Notes: The input can be viewed in terms of 'lines'. There are four + * types of lines: + * Blank - May contain whitespace, otherwise empty. + * Comment - First non-whitespace character is a ';' or '#'. + * The remainder of the line is ignored. + * Section - First non-whitespace character is a '['. + * Parameter - The default case. + * + * ------------------------------------------------------------------------ ** + */ + { + int c; + + c = EatWhitespace( InFile ); + while( (EOF != c) && (c > 0) ) + { + switch( c ) + { + case '\n': /* Blank line. */ + c = EatWhitespace( InFile ); + break; + + case ';': /* Comment line. */ + case '#': + c = EatComment( InFile ); + break; + + case '[': /* Section Header. */ + if( !Section( InFile, sfunc ) ) + return( False ); + c = EatWhitespace( InFile ); + break; + + case '\\': /* Bogus backslash. */ + c = EatWhitespace( InFile ); + break; + + default: /* Parameter line. */ + if( !Parameter( InFile, pfunc, c ) ) + return( False ); + c = EatWhitespace( InFile ); + break; + } + } + return( True ); + } /* Parse */ + +static FILE *OpenConfFile( char *FileName ) + /* ------------------------------------------------------------------------ ** + * Open a configuration file. + * + * Input: FileName - The pathname of the config file to be opened. + * + * Output: A pointer of type (FILE *) to the opened file, or NULL if the + * file could not be opened. + * + * ------------------------------------------------------------------------ ** + */ + { + FILE *OpenedFile; + char *func = "params.c:OpenConfFile() -"; + + if( NULL == FileName || 0 == *FileName ) + { + rprintf(FERROR,"%s No configuration filename specified.\n", func); + return( NULL ); + } + + OpenedFile = fopen( FileName, "r" ); + if( NULL == OpenedFile ) + { + rprintf(FERROR,"%s Unable to open configuration file \"%s\":\n\t%s\n", + func, FileName, strerror(errno)); + } + + return( OpenedFile ); + } /* OpenConfFile */ + +BOOL pm_process( char *FileName, + BOOL (*sfunc)(char *), + BOOL (*pfunc)(char *, char *) ) + /* ------------------------------------------------------------------------ ** + * Process the named parameter file. + * + * Input: FileName - The pathname of the parameter file to be opened. + * sfunc - A pointer to a function that will be called when + * a section name is discovered. + * pfunc - A pointer to a function that will be called when + * a parameter name and value are discovered. + * + * Output: TRUE if the file was successfully parsed, else FALSE. + * + * ------------------------------------------------------------------------ ** + */ + { + int result; + FILE *InFile; + char *func = "params.c:pm_process() -"; + + InFile = OpenConfFile( FileName ); /* Open the config file. */ + if( NULL == InFile ) + return( False ); + + if( NULL != bufr ) /* If we already have a buffer */ + result = Parse( InFile, sfunc, pfunc ); /* (recursive call), then just */ + /* use it. */ + + else /* If we don't have a buffer */ + { /* allocate one, then parse, */ + bSize = BUFR_INC; /* then free. */ + bufr = (char *)malloc( bSize ); + if( NULL == bufr ) + { + rprintf(FERROR,"%s memory allocation failure.\n", func); + fclose(InFile); + return( False ); + } + result = Parse( InFile, sfunc, pfunc ); + free( bufr ); + bufr = NULL; + bSize = 0; + } + + fclose(InFile); + + if( !result ) /* Generic failure. */ + { + rprintf(FERROR,"%s Failed. Error returned from params.c:parse().\n", func); + return( False ); + } + + return( True ); /* Generic success. */ + } /* pm_process */ + +/* -------------------------------------------------------------------------- */