Annotation of embedaddon/rsync/params.c, revision 1.1.1.3
1.1 misho 1: /* This modules is based on the params.c module from Samba, written by Karl Auer
1.1.1.3 ! misho 2: and much modified by Christopher Hertel. */
1.1 misho 3:
4: /*
5: * This program is free software; you can redistribute it and/or modify
6: * it under the terms of the GNU General Public License as published by
7: * the Free Software Foundation; either version 3 of the License, or
8: * (at your option) any later version.
9: *
10: * This program is distributed in the hope that it will be useful,
11: * but WITHOUT ANY WARRANTY; without even the implied warranty of
12: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: * GNU General Public License for more details.
14: *
15: * You should have received a copy of the GNU General Public License along
16: * with this program; if not, visit the http://fsf.org website.
17: */
18:
19: /* -------------------------------------------------------------------------- **
20: *
21: * Module name: params
22: *
23: * -------------------------------------------------------------------------- **
24: *
25: * This module performs lexical analysis and initial parsing of a
26: * Windows-like parameter file. It recognizes and handles four token
27: * types: section-name, parameter-name, parameter-value, and
28: * end-of-file. Comments and line continuation are handled
29: * internally.
30: *
31: * The entry point to the module is function pm_process(). This
32: * function opens the source file, calls the Parse() function to parse
33: * the input, and then closes the file when either the EOF is reached
34: * or a fatal error is encountered.
35: *
36: * A sample parameter file might look like this:
37: *
38: * [section one]
39: * parameter one = value string
40: * parameter two = another value
41: * [section two]
42: * new parameter = some value or t'other
43: *
44: * The parameter file is divided into sections by section headers:
45: * section names enclosed in square brackets (eg. [section one]).
46: * Each section contains parameter lines, each of which consist of a
47: * parameter name and value delimited by an equal sign. Roughly, the
48: * syntax is:
49: *
50: * <file> :== { <section> } EOF
51: *
52: * <section> :== <section header> { <parameter line> }
53: *
54: * <section header> :== '[' NAME ']'
55: *
56: * <parameter line> :== NAME '=' VALUE '\n'
57: *
58: * Blank lines and comment lines are ignored. Comment lines are lines
59: * beginning with either a semicolon (';') or a pound sign ('#').
60: *
61: * All whitespace in section names and parameter names is compressed
1.1.1.3 ! misho 62: * to single spaces. Leading and trailing whitespace is stripped from
1.1 misho 63: * both names and values.
64: *
65: * Only the first equals sign in a parameter line is significant.
66: * Parameter values may contain equals signs, square brackets and
67: * semicolons. Internal whitespace is retained in parameter values,
68: * with the exception of the '\r' character, which is stripped for
69: * historic reasons. Parameter names may not start with a left square
70: * bracket, an equal sign, a pound sign, or a semicolon, because these
71: * are used to identify other tokens.
72: *
73: * -------------------------------------------------------------------------- **
74: */
75:
76: #include "rsync.h"
77: #include "ifuncs.h"
1.1.1.2 misho 78: #include "itypes.h"
1.1 misho 79:
80: /* -------------------------------------------------------------------------- **
81: * Constants...
82: */
83:
84: #define BUFR_INC 1024
85:
86:
87: /* -------------------------------------------------------------------------- **
88: * Variables...
89: *
90: * bufr - pointer to a global buffer. This is probably a kludge,
91: * but it was the nicest kludge I could think of (for now).
92: * bSize - The size of the global buffer <bufr>.
93: */
94:
95: static char *bufr = NULL;
96: static int bSize = 0;
1.1.1.2 misho 97: static BOOL (*the_sfunc)(char *);
98: static BOOL (*the_pfunc)(char *, char *);
1.1 misho 99:
100: /* -------------------------------------------------------------------------- **
101: * Functions...
102: */
103:
104: static int EatWhitespace( FILE *InFile )
105: /* ------------------------------------------------------------------------ **
106: * Scan past whitespace (see ctype(3C)) and return the first non-whitespace
107: * character, or newline, or EOF.
108: *
109: * Input: InFile - Input source.
110: *
111: * Output: The next non-whitespace character in the input stream.
112: *
113: * Notes: Because the config files use a line-oriented grammar, we
114: * explicitly exclude the newline character from the list of
115: * whitespace characters.
116: * - Note that both EOF (-1) and the nul character ('\0') are
117: * considered end-of-file markers.
118: *
119: * ------------------------------------------------------------------------ **
120: */
121: {
122: int c;
123:
124: for( c = getc( InFile ); isspace( c ) && ('\n' != c); c = getc( InFile ) )
125: ;
126: return( c );
127: } /* EatWhitespace */
128:
129: static int EatComment( FILE *InFile )
130: /* ------------------------------------------------------------------------ **
131: * Scan to the end of a comment.
132: *
133: * Input: InFile - Input source.
134: *
135: * Output: The character that marks the end of the comment. Normally,
136: * this will be a newline, but it *might* be an EOF.
137: *
138: * Notes: Because the config files use a line-oriented grammar, we
139: * explicitly exclude the newline character from the list of
140: * whitespace characters.
141: * - Note that both EOF (-1) and the nul character ('\0') are
142: * considered end-of-file markers.
143: *
144: * ------------------------------------------------------------------------ **
145: */
146: {
147: int c;
148:
149: for( c = getc( InFile ); ('\n'!=c) && (EOF!=c) && (c>0); c = getc( InFile ) )
150: ;
151: return( c );
152: } /* EatComment */
153:
154: static int Continuation( char *line, int pos )
155: /* ------------------------------------------------------------------------ **
1.1.1.3 ! misho 156: * Scan backwards within a string to discover if the last non-whitespace
1.1 misho 157: * character is a line-continuation character ('\\').
158: *
159: * Input: line - A pointer to a buffer containing the string to be
160: * scanned.
161: * pos - This is taken to be the offset of the end of the
162: * string. This position is *not* scanned.
163: *
164: * Output: The offset of the '\\' character if it was found, or -1 to
165: * indicate that it was not.
166: *
167: * ------------------------------------------------------------------------ **
168: */
169: {
170: pos--;
171: while( pos >= 0 && isSpace(line + pos) )
172: pos--;
173:
174: return( ((pos >= 0) && ('\\' == line[pos])) ? pos : -1 );
175: } /* Continuation */
176:
177:
178: static BOOL Section( FILE *InFile, BOOL (*sfunc)(char *) )
179: /* ------------------------------------------------------------------------ **
180: * Scan a section name, and pass the name to function sfunc().
181: *
182: * Input: InFile - Input source.
183: * sfunc - Pointer to the function to be called if the section
184: * name is successfully read.
185: *
186: * Output: True if the section name was read and True was returned from
187: * <sfunc>. False if <sfunc> failed or if a lexical error was
188: * encountered.
189: *
190: * ------------------------------------------------------------------------ **
191: */
192: {
193: int c;
194: int i;
195: int end;
196: char *func = "params.c:Section() -";
197:
198: i = 0; /* <i> is the offset of the next free byte in bufr[] and */
199: end = 0; /* <end> is the current "end of string" offset. In most */
200: /* cases these will be the same, but if the last */
201: /* character written to bufr[] is a space, then <end> */
202: /* will be one less than <i>. */
203:
204: c = EatWhitespace( InFile ); /* We've already got the '['. Scan */
205: /* past initial white space. */
206:
207: while( (EOF != c) && (c > 0) )
208: {
209:
210: /* Check that the buffer is big enough for the next character. */
211: if( i > (bSize - 2) )
212: {
213: bSize += BUFR_INC;
214: bufr = realloc_array( bufr, char, bSize );
215: }
216:
217: /* Handle a single character. */
218: switch( c )
219: {
220: case ']': /* Found the closing bracket. */
221: bufr[end] = '\0';
222: if( 0 == end ) /* Don't allow an empty name. */
223: {
1.1.1.2 misho 224: rprintf(FLOG, "%s Empty section name in config file.\n", func );
1.1 misho 225: return( False );
226: }
227: if( !sfunc( bufr ) ) /* Got a valid name. Deal with it. */
228: return( False );
229: (void)EatComment( InFile ); /* Finish off the line. */
230: return( True );
231:
232: case '\n': /* Got newline before closing ']'. */
233: i = Continuation( bufr, i ); /* Check for line continuation. */
234: if( i < 0 )
235: {
236: bufr[end] = '\0';
1.1.1.2 misho 237: rprintf(FLOG, "%s Badly formed line in config file: %s\n",
1.1 misho 238: func, bufr );
239: return( False );
240: }
241: end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
242: c = getc( InFile ); /* Continue with next line. */
243: break;
244:
245: default: /* All else are a valid name chars. */
246: if( isspace( c ) ) /* One space per whitespace region. */
247: {
248: bufr[end] = ' ';
249: i = end + 1;
250: c = EatWhitespace( InFile );
251: }
252: else /* All others copy verbatim. */
253: {
254: bufr[i++] = c;
255: end = i;
256: c = getc( InFile );
257: }
258: }
259: }
260:
261: /* We arrive here if we've met the EOF before the closing bracket. */
1.1.1.2 misho 262: rprintf(FLOG, "%s Unexpected EOF in the config file: %s\n", func, bufr );
1.1 misho 263: return( False );
264: } /* Section */
265:
266: static BOOL Parameter( FILE *InFile, BOOL (*pfunc)(char *, char *), int c )
267: /* ------------------------------------------------------------------------ **
268: * Scan a parameter name and value, and pass these two fields to pfunc().
269: *
270: * Input: InFile - The input source.
271: * pfunc - A pointer to the function that will be called to
272: * process the parameter, once it has been scanned.
273: * c - The first character of the parameter name, which
274: * would have been read by Parse(). Unlike a comment
275: * line or a section header, there is no lead-in
276: * character that can be discarded.
277: *
278: * Output: True if the parameter name and value were scanned and processed
279: * successfully, else False.
280: *
281: * Notes: This function is in two parts. The first loop scans the
282: * parameter name. Internal whitespace is compressed, and an
283: * equal sign (=) terminates the token. Leading and trailing
284: * whitespace is discarded. The second loop scans the parameter
285: * value. When both have been successfully identified, they are
286: * passed to pfunc() for processing.
287: *
288: * ------------------------------------------------------------------------ **
289: */
290: {
291: int i = 0; /* Position within bufr. */
292: int end = 0; /* bufr[end] is current end-of-string. */
293: int vstart = 0; /* Starting position of the parameter value. */
294: char *func = "params.c:Parameter() -";
295:
296: /* Read the parameter name. */
297: while( 0 == vstart ) /* Loop until we've found the start of the value. */
298: {
299:
300: if( i > (bSize - 2) ) /* Ensure there's space for next char. */
301: {
302: bSize += BUFR_INC;
303: bufr = realloc_array( bufr, char, bSize );
304: }
305:
306: switch( c )
307: {
308: case '=': /* Equal sign marks end of param name. */
309: if( 0 == end ) /* Don't allow an empty name. */
310: {
1.1.1.2 misho 311: rprintf(FLOG, "%s Invalid parameter name in config file.\n", func );
1.1 misho 312: return( False );
313: }
314: bufr[end++] = '\0'; /* Mark end of string & advance. */
1.1.1.2 misho 315: i = vstart = end; /* New string starts here. */
316: c = EatWhitespace(InFile);
1.1 misho 317: break;
318:
319: case '\n': /* Find continuation char, else error. */
320: i = Continuation( bufr, i );
321: if( i < 0 )
322: {
323: bufr[end] = '\0';
1.1.1.2 misho 324: rprintf(FLOG, "%s Ignoring badly formed line in config file: %s\n",
1.1 misho 325: func, bufr );
326: return( True );
327: }
328: end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
329: c = getc( InFile ); /* Read past eoln. */
330: break;
331:
332: case '\0': /* Shouldn't have EOF within param name. */
333: case EOF:
334: bufr[i] = '\0';
335: rprintf(FLOG, "%s Unexpected end-of-file at: %s\n", func, bufr );
336: return( True );
337:
1.1.1.2 misho 338: case ' ':
339: case '\t':
340: /* A directive divides at the first space or tab. */
341: if (*bufr == '&') {
342: bufr[end++] = '\0';
343: i = vstart = end;
344: c = EatWhitespace(InFile);
345: if (c == '=')
346: c = EatWhitespace(InFile);
347: break;
348: }
349: /* FALL THROUGH */
350:
1.1 misho 351: default:
352: if( isspace( c ) ) /* One ' ' per whitespace region. */
353: {
354: bufr[end] = ' ';
355: i = end + 1;
356: c = EatWhitespace( InFile );
357: }
358: else /* All others verbatim. */
359: {
360: bufr[i++] = c;
361: end = i;
362: c = getc( InFile );
363: }
364: }
365: }
366:
367: /* Now parse the value. */
368: while( (EOF !=c) && (c > 0) )
369: {
370:
371: if( i > (bSize - 2) ) /* Make sure there's enough room. */
372: {
373: bSize += BUFR_INC;
374: bufr = realloc_array( bufr, char, bSize );
375: }
376:
377: switch( c )
378: {
379: case '\r': /* Explicitly remove '\r' because the older */
380: c = getc( InFile ); /* version called fgets_slash() which also */
381: break; /* removes them. */
382:
383: case '\n': /* Marks end of value unless there's a '\'. */
384: i = Continuation( bufr, i );
385: if( i < 0 )
386: c = 0;
387: else
388: {
389: for( end = i; end >= 0 && isSpace(bufr + end); end-- )
390: ;
391: c = getc( InFile );
392: }
393: break;
394:
395: default: /* All others verbatim. Note that spaces do */
396: bufr[i++] = c; /* not advance <end>. This allows trimming */
397: if( !isspace( c ) ) /* of whitespace at the end of the line. */
398: end = i;
399: c = getc( InFile );
400: break;
401: }
402: }
403: bufr[end] = '\0'; /* End of value. */
404:
405: return( pfunc( bufr, &bufr[vstart] ) ); /* Pass name & value to pfunc(). */
406: } /* Parameter */
407:
1.1.1.2 misho 408: static int name_cmp(const void *n1, const void *n2)
409: {
410: return strcmp(*(char * const *)n1, *(char * const *)n2);
411: }
412:
413: static int include_config(char *include, int manage_globals)
414: {
415: STRUCT_STAT sb;
416: char *match = manage_globals ? "*.conf" : "*.inc";
417: int ret;
418:
419: if (do_stat(include, &sb) < 0) {
420: rsyserr(FLOG, errno, "unable to stat config file \"%s\"", include);
421: return 0;
422: }
423:
424: if (S_ISREG(sb.st_mode)) {
425: if (manage_globals && the_sfunc)
426: the_sfunc("]push");
427: ret = pm_process(include, the_sfunc, the_pfunc);
428: if (manage_globals && the_sfunc)
429: the_sfunc("]pop");
430: } else if (S_ISDIR(sb.st_mode)) {
431: char buf[MAXPATHLEN], **bpp;
432: item_list conf_list;
433: struct dirent *di;
434: size_t j;
435: DIR *d;
436:
437: if (!(d = opendir(include))) {
438: rsyserr(FLOG, errno, "unable to open config dir \"%s\"", include);
439: return 0;
440: }
441:
442: memset(&conf_list, 0, sizeof conf_list);
443:
444: while ((di = readdir(d)) != NULL) {
445: char *dname = d_name(di);
446: if (!wildmatch(match, dname))
447: continue;
448: bpp = EXPAND_ITEM_LIST(&conf_list, char *, 32);
449: pathjoin(buf, sizeof buf, include, dname);
450: *bpp = strdup(buf);
451: }
452: closedir(d);
453:
454: if (!(bpp = conf_list.items))
455: return 1;
456:
457: if (conf_list.count > 1)
458: qsort(bpp, conf_list.count, sizeof (char *), name_cmp);
459:
460: for (j = 0, ret = 1; j < conf_list.count; j++) {
461: if (manage_globals && the_sfunc)
462: the_sfunc(j == 0 ? "]push" : "]reset");
463: if ((ret = pm_process(bpp[j], the_sfunc, the_pfunc)) != 1)
464: break;
465: }
466:
467: if (manage_globals && the_sfunc)
468: the_sfunc("]pop");
469:
470: for (j = 0; j < conf_list.count; j++)
471: free(bpp[j]);
472: free(bpp);
473: } else
474: ret = 0;
475:
476: return ret;
477: }
478:
479: static int parse_directives(char *name, char *val)
480: {
481: if (strcasecmp(name, "&include") == 0)
482: return include_config(val, 1);
483: if (strcasecmp(name, "&merge") == 0)
484: return include_config(val, 0);
485: rprintf(FLOG, "Unknown directive: %s.\n", name);
486: return 0;
487: }
488:
489: static int Parse( FILE *InFile,
1.1 misho 490: BOOL (*sfunc)(char *),
491: BOOL (*pfunc)(char *, char *) )
492: /* ------------------------------------------------------------------------ **
493: * Scan & parse the input.
494: *
495: * Input: InFile - Input source.
496: * sfunc - Function to be called when a section name is scanned.
497: * See Section().
498: * pfunc - Function to be called when a parameter is scanned.
499: * See Parameter().
500: *
1.1.1.2 misho 501: * Output: 1 if the file was successfully scanned, 2 if the file was
502: * scanned until a section header with no section function, else 0.
1.1 misho 503: *
504: * Notes: The input can be viewed in terms of 'lines'. There are four
505: * types of lines:
506: * Blank - May contain whitespace, otherwise empty.
507: * Comment - First non-whitespace character is a ';' or '#'.
508: * The remainder of the line is ignored.
509: * Section - First non-whitespace character is a '['.
510: * Parameter - The default case.
1.1.1.2 misho 511: *
1.1 misho 512: * ------------------------------------------------------------------------ **
513: */
514: {
515: int c;
516:
517: c = EatWhitespace( InFile );
518: while( (EOF != c) && (c > 0) )
519: {
520: switch( c )
521: {
522: case '\n': /* Blank line. */
523: c = EatWhitespace( InFile );
524: break;
525:
526: case ';': /* Comment line. */
527: case '#':
528: c = EatComment( InFile );
529: break;
530:
531: case '[': /* Section Header. */
1.1.1.2 misho 532: if (!sfunc)
533: return 2;
534: if( !Section( InFile, sfunc ) )
535: return 0;
536: c = EatWhitespace( InFile );
537: break;
1.1 misho 538:
539: case '\\': /* Bogus backslash. */
540: c = EatWhitespace( InFile );
541: break;
542:
1.1.1.2 misho 543: case '&': /* Handle directives */
544: the_sfunc = sfunc;
545: the_pfunc = pfunc;
546: c = Parameter( InFile, parse_directives, c );
547: if (c != 1)
548: return c;
549: c = EatWhitespace( InFile );
550: break;
551:
1.1 misho 552: default: /* Parameter line. */
553: if( !Parameter( InFile, pfunc, c ) )
1.1.1.2 misho 554: return 0;
1.1 misho 555: c = EatWhitespace( InFile );
556: break;
557: }
558: }
1.1.1.2 misho 559: return 1;
1.1 misho 560: } /* Parse */
561:
562: static FILE *OpenConfFile( char *FileName )
563: /* ------------------------------------------------------------------------ **
1.1.1.2 misho 564: * Open a config file.
1.1 misho 565: *
566: * Input: FileName - The pathname of the config file to be opened.
567: *
568: * Output: A pointer of type (FILE *) to the opened file, or NULL if the
569: * file could not be opened.
570: *
571: * ------------------------------------------------------------------------ **
572: */
573: {
574: FILE *OpenedFile;
575: char *func = "params.c:OpenConfFile() -";
576:
577: if( NULL == FileName || 0 == *FileName )
578: {
1.1.1.2 misho 579: rprintf(FLOG, "%s No config filename specified.\n", func);
1.1 misho 580: return( NULL );
581: }
582:
583: OpenedFile = fopen( FileName, "r" );
584: if( NULL == OpenedFile )
585: {
1.1.1.2 misho 586: rsyserr(FLOG, errno, "unable to open config file \"%s\"",
1.1 misho 587: FileName);
588: }
589:
590: return( OpenedFile );
591: } /* OpenConfFile */
592:
1.1.1.2 misho 593: int pm_process( char *FileName,
1.1 misho 594: BOOL (*sfunc)(char *),
595: BOOL (*pfunc)(char *, char *) )
596: /* ------------------------------------------------------------------------ **
597: * Process the named parameter file.
598: *
599: * Input: FileName - The pathname of the parameter file to be opened.
600: * sfunc - A pointer to a function that will be called when
601: * a section name is discovered.
602: * pfunc - A pointer to a function that will be called when
603: * a parameter name and value are discovered.
604: *
1.1.1.2 misho 605: * Output: 1 if the file was successfully parsed, 2 if parsing ended at a
606: * section header w/o a section function, else 0.
1.1 misho 607: *
608: * ------------------------------------------------------------------------ **
609: */
610: {
611: int result;
612: FILE *InFile;
613: char *func = "params.c:pm_process() -";
614:
615: InFile = OpenConfFile( FileName ); /* Open the config file. */
616: if( NULL == InFile )
617: return( False );
618:
619: if( NULL != bufr ) /* If we already have a buffer */
620: result = Parse( InFile, sfunc, pfunc ); /* (recursive call), then just */
621: /* use it. */
622:
623: else /* If we don't have a buffer */
624: { /* allocate one, then parse, */
625: bSize = BUFR_INC; /* then free. */
626: bufr = new_array( char, bSize );
627: result = Parse( InFile, sfunc, pfunc );
628: free( bufr );
629: bufr = NULL;
630: bSize = 0;
631: }
632:
633: fclose(InFile);
634:
635: if( !result ) /* Generic failure. */
636: {
637: rprintf(FLOG, "%s Failed. Error returned from params.c:parse().\n", func);
1.1.1.2 misho 638: return 0;
1.1 misho 639: }
640:
1.1.1.2 misho 641: return result;
1.1 misho 642: } /* pm_process */
643:
644: /* -------------------------------------------------------------------------- */
645:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>