Annotation of embedaddon/rsync/params.c, revision 1.1.1.2
1.1 misho 1: /* This modules is based on the params.c module from Samba, written by Karl Auer
2: and much modifed by Christopher Hertel. */
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
62: * to single spaces. Leading and trailing whitespace is stipped from
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: /* ------------------------------------------------------------------------ **
156: * Scan backards within a string to discover if the last non-whitespace
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: if( NULL == bufr )
216: {
217: rprintf(FLOG, "%s Memory re-allocation failure.", func);
218: return( False );
219: }
220: }
221:
222: /* Handle a single character. */
223: switch( c )
224: {
225: case ']': /* Found the closing bracket. */
226: bufr[end] = '\0';
227: if( 0 == end ) /* Don't allow an empty name. */
228: {
1.1.1.2 ! misho 229: rprintf(FLOG, "%s Empty section name in config file.\n", func );
1.1 misho 230: return( False );
231: }
232: if( !sfunc( bufr ) ) /* Got a valid name. Deal with it. */
233: return( False );
234: (void)EatComment( InFile ); /* Finish off the line. */
235: return( True );
236:
237: case '\n': /* Got newline before closing ']'. */
238: i = Continuation( bufr, i ); /* Check for line continuation. */
239: if( i < 0 )
240: {
241: bufr[end] = '\0';
1.1.1.2 ! misho 242: rprintf(FLOG, "%s Badly formed line in config file: %s\n",
1.1 misho 243: func, bufr );
244: return( False );
245: }
246: end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
247: c = getc( InFile ); /* Continue with next line. */
248: break;
249:
250: default: /* All else are a valid name chars. */
251: if( isspace( c ) ) /* One space per whitespace region. */
252: {
253: bufr[end] = ' ';
254: i = end + 1;
255: c = EatWhitespace( InFile );
256: }
257: else /* All others copy verbatim. */
258: {
259: bufr[i++] = c;
260: end = i;
261: c = getc( InFile );
262: }
263: }
264: }
265:
266: /* We arrive here if we've met the EOF before the closing bracket. */
1.1.1.2 ! misho 267: rprintf(FLOG, "%s Unexpected EOF in the config file: %s\n", func, bufr );
1.1 misho 268: return( False );
269: } /* Section */
270:
271: static BOOL Parameter( FILE *InFile, BOOL (*pfunc)(char *, char *), int c )
272: /* ------------------------------------------------------------------------ **
273: * Scan a parameter name and value, and pass these two fields to pfunc().
274: *
275: * Input: InFile - The input source.
276: * pfunc - A pointer to the function that will be called to
277: * process the parameter, once it has been scanned.
278: * c - The first character of the parameter name, which
279: * would have been read by Parse(). Unlike a comment
280: * line or a section header, there is no lead-in
281: * character that can be discarded.
282: *
283: * Output: True if the parameter name and value were scanned and processed
284: * successfully, else False.
285: *
286: * Notes: This function is in two parts. The first loop scans the
287: * parameter name. Internal whitespace is compressed, and an
288: * equal sign (=) terminates the token. Leading and trailing
289: * whitespace is discarded. The second loop scans the parameter
290: * value. When both have been successfully identified, they are
291: * passed to pfunc() for processing.
292: *
293: * ------------------------------------------------------------------------ **
294: */
295: {
296: int i = 0; /* Position within bufr. */
297: int end = 0; /* bufr[end] is current end-of-string. */
298: int vstart = 0; /* Starting position of the parameter value. */
299: char *func = "params.c:Parameter() -";
300:
301: /* Read the parameter name. */
302: while( 0 == vstart ) /* Loop until we've found the start of the value. */
303: {
304:
305: if( i > (bSize - 2) ) /* Ensure there's space for next char. */
306: {
307: bSize += BUFR_INC;
308: bufr = realloc_array( bufr, char, bSize );
309: if( NULL == bufr )
310: {
311: rprintf(FLOG, "%s Memory re-allocation failure.", func) ;
312: return( False );
313: }
314: }
315:
316: switch( c )
317: {
318: case '=': /* Equal sign marks end of param name. */
319: if( 0 == end ) /* Don't allow an empty name. */
320: {
1.1.1.2 ! misho 321: rprintf(FLOG, "%s Invalid parameter name in config file.\n", func );
1.1 misho 322: return( False );
323: }
324: bufr[end++] = '\0'; /* Mark end of string & advance. */
1.1.1.2 ! misho 325: i = vstart = end; /* New string starts here. */
! 326: c = EatWhitespace(InFile);
1.1 misho 327: break;
328:
329: case '\n': /* Find continuation char, else error. */
330: i = Continuation( bufr, i );
331: if( i < 0 )
332: {
333: bufr[end] = '\0';
1.1.1.2 ! misho 334: rprintf(FLOG, "%s Ignoring badly formed line in config file: %s\n",
1.1 misho 335: func, bufr );
336: return( True );
337: }
338: end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
339: c = getc( InFile ); /* Read past eoln. */
340: break;
341:
342: case '\0': /* Shouldn't have EOF within param name. */
343: case EOF:
344: bufr[i] = '\0';
345: rprintf(FLOG, "%s Unexpected end-of-file at: %s\n", func, bufr );
346: return( True );
347:
1.1.1.2 ! misho 348: case ' ':
! 349: case '\t':
! 350: /* A directive divides at the first space or tab. */
! 351: if (*bufr == '&') {
! 352: bufr[end++] = '\0';
! 353: i = vstart = end;
! 354: c = EatWhitespace(InFile);
! 355: if (c == '=')
! 356: c = EatWhitespace(InFile);
! 357: break;
! 358: }
! 359: /* FALL THROUGH */
! 360:
1.1 misho 361: default:
362: if( isspace( c ) ) /* One ' ' per whitespace region. */
363: {
364: bufr[end] = ' ';
365: i = end + 1;
366: c = EatWhitespace( InFile );
367: }
368: else /* All others verbatim. */
369: {
370: bufr[i++] = c;
371: end = i;
372: c = getc( InFile );
373: }
374: }
375: }
376:
377: /* Now parse the value. */
378: while( (EOF !=c) && (c > 0) )
379: {
380:
381: if( i > (bSize - 2) ) /* Make sure there's enough room. */
382: {
383: bSize += BUFR_INC;
384: bufr = realloc_array( bufr, char, bSize );
385: if( NULL == bufr )
386: {
387: rprintf(FLOG, "%s Memory re-allocation failure.", func) ;
388: return( False );
389: }
390: }
391:
392: switch( c )
393: {
394: case '\r': /* Explicitly remove '\r' because the older */
395: c = getc( InFile ); /* version called fgets_slash() which also */
396: break; /* removes them. */
397:
398: case '\n': /* Marks end of value unless there's a '\'. */
399: i = Continuation( bufr, i );
400: if( i < 0 )
401: c = 0;
402: else
403: {
404: for( end = i; end >= 0 && isSpace(bufr + end); end-- )
405: ;
406: c = getc( InFile );
407: }
408: break;
409:
410: default: /* All others verbatim. Note that spaces do */
411: bufr[i++] = c; /* not advance <end>. This allows trimming */
412: if( !isspace( c ) ) /* of whitespace at the end of the line. */
413: end = i;
414: c = getc( InFile );
415: break;
416: }
417: }
418: bufr[end] = '\0'; /* End of value. */
419:
420: return( pfunc( bufr, &bufr[vstart] ) ); /* Pass name & value to pfunc(). */
421: } /* Parameter */
422:
1.1.1.2 ! misho 423: static int name_cmp(const void *n1, const void *n2)
! 424: {
! 425: return strcmp(*(char * const *)n1, *(char * const *)n2);
! 426: }
! 427:
! 428: static int include_config(char *include, int manage_globals)
! 429: {
! 430: STRUCT_STAT sb;
! 431: char *match = manage_globals ? "*.conf" : "*.inc";
! 432: int ret;
! 433:
! 434: if (do_stat(include, &sb) < 0) {
! 435: rsyserr(FLOG, errno, "unable to stat config file \"%s\"", include);
! 436: return 0;
! 437: }
! 438:
! 439: if (S_ISREG(sb.st_mode)) {
! 440: if (manage_globals && the_sfunc)
! 441: the_sfunc("]push");
! 442: ret = pm_process(include, the_sfunc, the_pfunc);
! 443: if (manage_globals && the_sfunc)
! 444: the_sfunc("]pop");
! 445: } else if (S_ISDIR(sb.st_mode)) {
! 446: char buf[MAXPATHLEN], **bpp;
! 447: item_list conf_list;
! 448: struct dirent *di;
! 449: size_t j;
! 450: DIR *d;
! 451:
! 452: if (!(d = opendir(include))) {
! 453: rsyserr(FLOG, errno, "unable to open config dir \"%s\"", include);
! 454: return 0;
! 455: }
! 456:
! 457: memset(&conf_list, 0, sizeof conf_list);
! 458:
! 459: while ((di = readdir(d)) != NULL) {
! 460: char *dname = d_name(di);
! 461: if (!wildmatch(match, dname))
! 462: continue;
! 463: bpp = EXPAND_ITEM_LIST(&conf_list, char *, 32);
! 464: pathjoin(buf, sizeof buf, include, dname);
! 465: *bpp = strdup(buf);
! 466: }
! 467: closedir(d);
! 468:
! 469: if (!(bpp = conf_list.items))
! 470: return 1;
! 471:
! 472: if (conf_list.count > 1)
! 473: qsort(bpp, conf_list.count, sizeof (char *), name_cmp);
! 474:
! 475: for (j = 0, ret = 1; j < conf_list.count; j++) {
! 476: if (manage_globals && the_sfunc)
! 477: the_sfunc(j == 0 ? "]push" : "]reset");
! 478: if ((ret = pm_process(bpp[j], the_sfunc, the_pfunc)) != 1)
! 479: break;
! 480: }
! 481:
! 482: if (manage_globals && the_sfunc)
! 483: the_sfunc("]pop");
! 484:
! 485: for (j = 0; j < conf_list.count; j++)
! 486: free(bpp[j]);
! 487: free(bpp);
! 488: } else
! 489: ret = 0;
! 490:
! 491: return ret;
! 492: }
! 493:
! 494: static int parse_directives(char *name, char *val)
! 495: {
! 496: if (strcasecmp(name, "&include") == 0)
! 497: return include_config(val, 1);
! 498: if (strcasecmp(name, "&merge") == 0)
! 499: return include_config(val, 0);
! 500: rprintf(FLOG, "Unknown directive: %s.\n", name);
! 501: return 0;
! 502: }
! 503:
! 504: static int Parse( FILE *InFile,
1.1 misho 505: BOOL (*sfunc)(char *),
506: BOOL (*pfunc)(char *, char *) )
507: /* ------------------------------------------------------------------------ **
508: * Scan & parse the input.
509: *
510: * Input: InFile - Input source.
511: * sfunc - Function to be called when a section name is scanned.
512: * See Section().
513: * pfunc - Function to be called when a parameter is scanned.
514: * See Parameter().
515: *
1.1.1.2 ! misho 516: * Output: 1 if the file was successfully scanned, 2 if the file was
! 517: * scanned until a section header with no section function, else 0.
1.1 misho 518: *
519: * Notes: The input can be viewed in terms of 'lines'. There are four
520: * types of lines:
521: * Blank - May contain whitespace, otherwise empty.
522: * Comment - First non-whitespace character is a ';' or '#'.
523: * The remainder of the line is ignored.
524: * Section - First non-whitespace character is a '['.
525: * Parameter - The default case.
1.1.1.2 ! misho 526: *
1.1 misho 527: * ------------------------------------------------------------------------ **
528: */
529: {
530: int c;
531:
532: c = EatWhitespace( InFile );
533: while( (EOF != c) && (c > 0) )
534: {
535: switch( c )
536: {
537: case '\n': /* Blank line. */
538: c = EatWhitespace( InFile );
539: break;
540:
541: case ';': /* Comment line. */
542: case '#':
543: c = EatComment( InFile );
544: break;
545:
546: case '[': /* Section Header. */
1.1.1.2 ! misho 547: if (!sfunc)
! 548: return 2;
! 549: if( !Section( InFile, sfunc ) )
! 550: return 0;
! 551: c = EatWhitespace( InFile );
! 552: break;
1.1 misho 553:
554: case '\\': /* Bogus backslash. */
555: c = EatWhitespace( InFile );
556: break;
557:
1.1.1.2 ! misho 558: case '&': /* Handle directives */
! 559: the_sfunc = sfunc;
! 560: the_pfunc = pfunc;
! 561: c = Parameter( InFile, parse_directives, c );
! 562: if (c != 1)
! 563: return c;
! 564: c = EatWhitespace( InFile );
! 565: break;
! 566:
1.1 misho 567: default: /* Parameter line. */
568: if( !Parameter( InFile, pfunc, c ) )
1.1.1.2 ! misho 569: return 0;
1.1 misho 570: c = EatWhitespace( InFile );
571: break;
572: }
573: }
1.1.1.2 ! misho 574: return 1;
1.1 misho 575: } /* Parse */
576:
577: static FILE *OpenConfFile( char *FileName )
578: /* ------------------------------------------------------------------------ **
1.1.1.2 ! misho 579: * Open a config file.
1.1 misho 580: *
581: * Input: FileName - The pathname of the config file to be opened.
582: *
583: * Output: A pointer of type (FILE *) to the opened file, or NULL if the
584: * file could not be opened.
585: *
586: * ------------------------------------------------------------------------ **
587: */
588: {
589: FILE *OpenedFile;
590: char *func = "params.c:OpenConfFile() -";
591:
592: if( NULL == FileName || 0 == *FileName )
593: {
1.1.1.2 ! misho 594: rprintf(FLOG, "%s No config filename specified.\n", func);
1.1 misho 595: return( NULL );
596: }
597:
598: OpenedFile = fopen( FileName, "r" );
599: if( NULL == OpenedFile )
600: {
1.1.1.2 ! misho 601: rsyserr(FLOG, errno, "unable to open config file \"%s\"",
1.1 misho 602: FileName);
603: }
604:
605: return( OpenedFile );
606: } /* OpenConfFile */
607:
1.1.1.2 ! misho 608: int pm_process( char *FileName,
1.1 misho 609: BOOL (*sfunc)(char *),
610: BOOL (*pfunc)(char *, char *) )
611: /* ------------------------------------------------------------------------ **
612: * Process the named parameter file.
613: *
614: * Input: FileName - The pathname of the parameter file to be opened.
615: * sfunc - A pointer to a function that will be called when
616: * a section name is discovered.
617: * pfunc - A pointer to a function that will be called when
618: * a parameter name and value are discovered.
619: *
1.1.1.2 ! misho 620: * Output: 1 if the file was successfully parsed, 2 if parsing ended at a
! 621: * section header w/o a section function, else 0.
1.1 misho 622: *
623: * ------------------------------------------------------------------------ **
624: */
625: {
626: int result;
627: FILE *InFile;
628: char *func = "params.c:pm_process() -";
629:
630: InFile = OpenConfFile( FileName ); /* Open the config file. */
631: if( NULL == InFile )
632: return( False );
633:
634: if( NULL != bufr ) /* If we already have a buffer */
635: result = Parse( InFile, sfunc, pfunc ); /* (recursive call), then just */
636: /* use it. */
637:
638: else /* If we don't have a buffer */
639: { /* allocate one, then parse, */
640: bSize = BUFR_INC; /* then free. */
641: bufr = new_array( char, bSize );
642: if( NULL == bufr )
643: {
644: rprintf(FLOG, "%s memory allocation failure.\n", func);
645: fclose(InFile);
646: return( False );
647: }
648: result = Parse( InFile, sfunc, pfunc );
649: free( bufr );
650: bufr = NULL;
651: bSize = 0;
652: }
653:
654: fclose(InFile);
655:
656: if( !result ) /* Generic failure. */
657: {
658: rprintf(FLOG, "%s Failed. Error returned from params.c:parse().\n", func);
1.1.1.2 ! misho 659: return 0;
1.1 misho 660: }
661:
1.1.1.2 ! misho 662: return result;
1.1 misho 663: } /* pm_process */
664:
665: /* -------------------------------------------------------------------------- */
666:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>