-/*
- This modules is based on the params.c module from Samba, written by Karl Auer
- and much modifed by Christopher Hertel.
+/* This modules is based on the params.c module from Samba, written by Karl Auer
+ and much modified 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
+ * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* 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.
- *
- * -------------------------------------------------------------------------- **
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+/* -------------------------------------------------------------------------- **
*
* Module name: params
*
* 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
+ * to single spaces. Leading and trailing whitespace is stripped from
* both names and values.
*
* Only the first equals sign in a parameter line is significant.
*/
#include "rsync.h"
+#include "ifuncs.h"
+#include "itypes.h"
/* -------------------------------------------------------------------------- **
* Constants...
static char *bufr = NULL;
static int bSize = 0;
+static BOOL (*the_sfunc)(char *);
+static BOOL (*the_pfunc)(char *, char *);
/* -------------------------------------------------------------------------- **
* Functions...
static int Continuation( char *line, int pos )
/* ------------------------------------------------------------------------ **
- * Scan backards within a string to discover if the last non-whitespace
+ * Scan backwards 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
*/
{
pos--;
- while( (pos >= 0) && isspace(((unsigned char *)line)[pos]) )
+ while( pos >= 0 && isSpace(line + pos) )
pos--;
return( ((pos >= 0) && ('\\' == line[pos])) ? pos : -1 );
{
bSize += BUFR_INC;
bufr = realloc_array( bufr, char, bSize );
- if( NULL == bufr )
- {
- rprintf(FERROR, "%s Memory re-allocation failure.", func);
- return( False );
- }
}
/* Handle a single character. */
bufr[end] = '\0';
if( 0 == end ) /* Don't allow an empty name. */
{
- rprintf(FERROR, "%s Empty section name in configuration file.\n", func );
+ rprintf(FLOG, "%s Empty section name in config file.\n", func );
return( False );
}
if( !sfunc( bufr ) ) /* Got a valid name. Deal with it. */
if( i < 0 )
{
bufr[end] = '\0';
- rprintf(FERROR, "%s Badly formed line in configuration file: %s\n",
+ rprintf(FLOG, "%s Badly formed line in config file: %s\n",
func, bufr );
return( False );
}
}
/* 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 );
+ rprintf(FLOG, "%s Unexpected EOF in the config file: %s\n", func, bufr );
return( False );
} /* Section */
{
bSize += BUFR_INC;
bufr = realloc_array( bufr, char, 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 );
+ rprintf(FLOG, "%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. */
+ i = vstart = end; /* New string starts here. */
+ c = EatWhitespace(InFile);
break;
case '\n': /* Find continuation char, else error. */
if( i < 0 )
{
bufr[end] = '\0';
- rprintf(FERROR, "%s Ignoring badly formed line in configuration file: %s\n",
+ rprintf(FLOG, "%s Ignoring badly formed line in config file: %s\n",
func, bufr );
return( True );
}
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 );
+ rprintf(FLOG, "%s Unexpected end-of-file at: %s\n", func, bufr );
return( True );
+ case ' ':
+ case '\t':
+ /* A directive divides at the first space or tab. */
+ if (*bufr == '&') {
+ bufr[end++] = '\0';
+ i = vstart = end;
+ c = EatWhitespace(InFile);
+ if (c == '=')
+ c = EatWhitespace(InFile);
+ break;
+ }
+ /* FALL THROUGH */
+
default:
if( isspace( c ) ) /* One ' ' per whitespace region. */
{
}
/* Now parse the value. */
- c = EatWhitespace( InFile ); /* Again, trim leading whitespace. */
while( (EOF !=c) && (c > 0) )
{
{
bSize += BUFR_INC;
bufr = realloc_array( bufr, char, bSize );
- if( NULL == bufr )
- {
- rprintf(FERROR, "%s Memory re-allocation failure.", func) ;
- return( False );
- }
}
switch( c )
c = 0;
else
{
- for( end = i; (end >= 0) && isspace(((unsigned char *) bufr)[end]); end-- )
+ for( end = i; end >= 0 && isSpace(bufr + end); end-- )
;
c = getc( InFile );
}
return( pfunc( bufr, &bufr[vstart] ) ); /* Pass name & value to pfunc(). */
} /* Parameter */
-static BOOL Parse( FILE *InFile,
+static int name_cmp(const void *n1, const void *n2)
+{
+ return strcmp(*(char * const *)n1, *(char * const *)n2);
+}
+
+static int include_config(char *include, int manage_globals)
+{
+ STRUCT_STAT sb;
+ char *match = manage_globals ? "*.conf" : "*.inc";
+ int ret;
+
+ if (do_stat(include, &sb) < 0) {
+ rsyserr(FLOG, errno, "unable to stat config file \"%s\"", include);
+ return 0;
+ }
+
+ if (S_ISREG(sb.st_mode)) {
+ if (manage_globals && the_sfunc)
+ the_sfunc("]push");
+ ret = pm_process(include, the_sfunc, the_pfunc);
+ if (manage_globals && the_sfunc)
+ the_sfunc("]pop");
+ } else if (S_ISDIR(sb.st_mode)) {
+ char buf[MAXPATHLEN], **bpp;
+ item_list conf_list;
+ struct dirent *di;
+ size_t j;
+ DIR *d;
+
+ if (!(d = opendir(include))) {
+ rsyserr(FLOG, errno, "unable to open config dir \"%s\"", include);
+ return 0;
+ }
+
+ memset(&conf_list, 0, sizeof conf_list);
+
+ while ((di = readdir(d)) != NULL) {
+ char *dname = d_name(di);
+ if (!wildmatch(match, dname))
+ continue;
+ bpp = EXPAND_ITEM_LIST(&conf_list, char *, 32);
+ pathjoin(buf, sizeof buf, include, dname);
+ *bpp = strdup(buf);
+ }
+ closedir(d);
+
+ if (!(bpp = conf_list.items))
+ return 1;
+
+ if (conf_list.count > 1)
+ qsort(bpp, conf_list.count, sizeof (char *), name_cmp);
+
+ for (j = 0, ret = 1; j < conf_list.count; j++) {
+ if (manage_globals && the_sfunc)
+ the_sfunc(j == 0 ? "]push" : "]reset");
+ if ((ret = pm_process(bpp[j], the_sfunc, the_pfunc)) != 1)
+ break;
+ }
+
+ if (manage_globals && the_sfunc)
+ the_sfunc("]pop");
+
+ for (j = 0; j < conf_list.count; j++)
+ free(bpp[j]);
+ free(bpp);
+ } else
+ ret = 0;
+
+ return ret;
+}
+
+static int parse_directives(char *name, char *val)
+{
+ if (strcasecmp(name, "&include") == 0)
+ return include_config(val, 1);
+ if (strcasecmp(name, "&merge") == 0)
+ return include_config(val, 0);
+ rprintf(FLOG, "Unknown directive: %s.\n", name);
+ return 0;
+}
+
+static int Parse( FILE *InFile,
BOOL (*sfunc)(char *),
BOOL (*pfunc)(char *, char *) )
/* ------------------------------------------------------------------------ **
* pfunc - Function to be called when a parameter is scanned.
* See Parameter().
*
- * Output: True if the file was successfully scanned, else False.
+ * Output: 1 if the file was successfully scanned, 2 if the file was
+ * scanned until a section header with no section function, else 0.
*
* Notes: The input can be viewed in terms of 'lines'. There are four
* types of lines:
* The remainder of the line is ignored.
* Section - First non-whitespace character is a '['.
* Parameter - The default case.
- *
+ *
* ------------------------------------------------------------------------ **
*/
{
break;
case '[': /* Section Header. */
- if (!sfunc) return True;
- if( !Section( InFile, sfunc ) )
- return( False );
- c = EatWhitespace( InFile );
- break;
+ if (!sfunc)
+ return 2;
+ if( !Section( InFile, sfunc ) )
+ return 0;
+ c = EatWhitespace( InFile );
+ break;
case '\\': /* Bogus backslash. */
c = EatWhitespace( InFile );
break;
+ case '&': /* Handle directives */
+ the_sfunc = sfunc;
+ the_pfunc = pfunc;
+ c = Parameter( InFile, parse_directives, c );
+ if (c != 1)
+ return c;
+ c = EatWhitespace( InFile );
+ break;
+
default: /* Parameter line. */
if( !Parameter( InFile, pfunc, c ) )
- return( False );
+ return 0;
c = EatWhitespace( InFile );
break;
}
}
- return( True );
+ return 1;
} /* Parse */
static FILE *OpenConfFile( char *FileName )
/* ------------------------------------------------------------------------ **
- * Open a configuration file.
+ * Open a config file.
*
* Input: FileName - The pathname of the config file to be opened.
*
if( NULL == FileName || 0 == *FileName )
{
- rprintf(FERROR,"%s No configuration filename specified.\n", func);
+ rprintf(FLOG, "%s No config filename specified.\n", func);
return( NULL );
}
OpenedFile = fopen( FileName, "r" );
if( NULL == OpenedFile )
{
- rprintf(FERROR,"rsync: unable to open configuration file \"%s\": %s\n",
- FileName, strerror(errno));
+ rsyserr(FLOG, errno, "unable to open config file \"%s\"",
+ FileName);
}
return( OpenedFile );
} /* OpenConfFile */
-BOOL pm_process( char *FileName,
+int pm_process( char *FileName,
BOOL (*sfunc)(char *),
BOOL (*pfunc)(char *, char *) )
/* ------------------------------------------------------------------------ **
* 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.
+ * Output: 1 if the file was successfully parsed, 2 if parsing ended at a
+ * section header w/o a section function, else 0.
*
* ------------------------------------------------------------------------ **
*/
{ /* allocate one, then parse, */
bSize = BUFR_INC; /* then free. */
bufr = new_array( char, 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;
if( !result ) /* Generic failure. */
{
- rprintf(FERROR,"%s Failed. Error returned from params.c:parse().\n", func);
- return( False );
+ rprintf(FLOG, "%s Failed. Error returned from params.c:parse().\n", func);
+ return 0;
}
- return( True ); /* Generic success. */
+ return result;
} /* pm_process */
/* -------------------------------------------------------------------------- */