--- /dev/null
+/* 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);
+}
+
--- /dev/null
+/*
+ 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);
+}
+
--- /dev/null
+/*
+ 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:
+ *
+ * <file> :== { <section> } EOF
+ *
+ * <section> :== <section header> { <parameter line> }
+ *
+ * <section header> :== '[' NAME ']'
+ *
+ * <parameter line> :== 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 <bufr>.
+ */
+
+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
+ * <sfunc>. False if <sfunc> failed or if a lexical error was
+ * encountered.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+ int i;
+ int end;
+ char *func = "params.c:Section() -";
+
+ i = 0; /* <i> is the offset of the next free byte in bufr[] and */
+ end = 0; /* <end> 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 <end> */
+ /* will be one less than <i>. */
+
+ 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 <end>. 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 */
+
+/* -------------------------------------------------------------------------- */