Annotation of embedaddon/rsync/params.c, revision 1.1
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"
! 78:
! 79: /* -------------------------------------------------------------------------- **
! 80: * Constants...
! 81: */
! 82:
! 83: #define BUFR_INC 1024
! 84:
! 85:
! 86: /* -------------------------------------------------------------------------- **
! 87: * Variables...
! 88: *
! 89: * bufr - pointer to a global buffer. This is probably a kludge,
! 90: * but it was the nicest kludge I could think of (for now).
! 91: * bSize - The size of the global buffer <bufr>.
! 92: */
! 93:
! 94: static char *bufr = NULL;
! 95: static int bSize = 0;
! 96:
! 97: /* -------------------------------------------------------------------------- **
! 98: * Functions...
! 99: */
! 100:
! 101: static int EatWhitespace( FILE *InFile )
! 102: /* ------------------------------------------------------------------------ **
! 103: * Scan past whitespace (see ctype(3C)) and return the first non-whitespace
! 104: * character, or newline, or EOF.
! 105: *
! 106: * Input: InFile - Input source.
! 107: *
! 108: * Output: The next non-whitespace character in the input stream.
! 109: *
! 110: * Notes: Because the config files use a line-oriented grammar, we
! 111: * explicitly exclude the newline character from the list of
! 112: * whitespace characters.
! 113: * - Note that both EOF (-1) and the nul character ('\0') are
! 114: * considered end-of-file markers.
! 115: *
! 116: * ------------------------------------------------------------------------ **
! 117: */
! 118: {
! 119: int c;
! 120:
! 121: for( c = getc( InFile ); isspace( c ) && ('\n' != c); c = getc( InFile ) )
! 122: ;
! 123: return( c );
! 124: } /* EatWhitespace */
! 125:
! 126: static int EatComment( FILE *InFile )
! 127: /* ------------------------------------------------------------------------ **
! 128: * Scan to the end of a comment.
! 129: *
! 130: * Input: InFile - Input source.
! 131: *
! 132: * Output: The character that marks the end of the comment. Normally,
! 133: * this will be a newline, but it *might* be an EOF.
! 134: *
! 135: * Notes: Because the config files use a line-oriented grammar, we
! 136: * explicitly exclude the newline character from the list of
! 137: * whitespace characters.
! 138: * - Note that both EOF (-1) and the nul character ('\0') are
! 139: * considered end-of-file markers.
! 140: *
! 141: * ------------------------------------------------------------------------ **
! 142: */
! 143: {
! 144: int c;
! 145:
! 146: for( c = getc( InFile ); ('\n'!=c) && (EOF!=c) && (c>0); c = getc( InFile ) )
! 147: ;
! 148: return( c );
! 149: } /* EatComment */
! 150:
! 151: static int Continuation( char *line, int pos )
! 152: /* ------------------------------------------------------------------------ **
! 153: * Scan backards within a string to discover if the last non-whitespace
! 154: * character is a line-continuation character ('\\').
! 155: *
! 156: * Input: line - A pointer to a buffer containing the string to be
! 157: * scanned.
! 158: * pos - This is taken to be the offset of the end of the
! 159: * string. This position is *not* scanned.
! 160: *
! 161: * Output: The offset of the '\\' character if it was found, or -1 to
! 162: * indicate that it was not.
! 163: *
! 164: * ------------------------------------------------------------------------ **
! 165: */
! 166: {
! 167: pos--;
! 168: while( pos >= 0 && isSpace(line + pos) )
! 169: pos--;
! 170:
! 171: return( ((pos >= 0) && ('\\' == line[pos])) ? pos : -1 );
! 172: } /* Continuation */
! 173:
! 174:
! 175: static BOOL Section( FILE *InFile, BOOL (*sfunc)(char *) )
! 176: /* ------------------------------------------------------------------------ **
! 177: * Scan a section name, and pass the name to function sfunc().
! 178: *
! 179: * Input: InFile - Input source.
! 180: * sfunc - Pointer to the function to be called if the section
! 181: * name is successfully read.
! 182: *
! 183: * Output: True if the section name was read and True was returned from
! 184: * <sfunc>. False if <sfunc> failed or if a lexical error was
! 185: * encountered.
! 186: *
! 187: * ------------------------------------------------------------------------ **
! 188: */
! 189: {
! 190: int c;
! 191: int i;
! 192: int end;
! 193: char *func = "params.c:Section() -";
! 194:
! 195: i = 0; /* <i> is the offset of the next free byte in bufr[] and */
! 196: end = 0; /* <end> is the current "end of string" offset. In most */
! 197: /* cases these will be the same, but if the last */
! 198: /* character written to bufr[] is a space, then <end> */
! 199: /* will be one less than <i>. */
! 200:
! 201: c = EatWhitespace( InFile ); /* We've already got the '['. Scan */
! 202: /* past initial white space. */
! 203:
! 204: while( (EOF != c) && (c > 0) )
! 205: {
! 206:
! 207: /* Check that the buffer is big enough for the next character. */
! 208: if( i > (bSize - 2) )
! 209: {
! 210: bSize += BUFR_INC;
! 211: bufr = realloc_array( bufr, char, bSize );
! 212: if( NULL == bufr )
! 213: {
! 214: rprintf(FLOG, "%s Memory re-allocation failure.", func);
! 215: return( False );
! 216: }
! 217: }
! 218:
! 219: /* Handle a single character. */
! 220: switch( c )
! 221: {
! 222: case ']': /* Found the closing bracket. */
! 223: bufr[end] = '\0';
! 224: if( 0 == end ) /* Don't allow an empty name. */
! 225: {
! 226: rprintf(FLOG, "%s Empty section name in configuration file.\n", func );
! 227: return( False );
! 228: }
! 229: if( !sfunc( bufr ) ) /* Got a valid name. Deal with it. */
! 230: return( False );
! 231: (void)EatComment( InFile ); /* Finish off the line. */
! 232: return( True );
! 233:
! 234: case '\n': /* Got newline before closing ']'. */
! 235: i = Continuation( bufr, i ); /* Check for line continuation. */
! 236: if( i < 0 )
! 237: {
! 238: bufr[end] = '\0';
! 239: rprintf(FLOG, "%s Badly formed line in configuration file: %s\n",
! 240: func, bufr );
! 241: return( False );
! 242: }
! 243: end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
! 244: c = getc( InFile ); /* Continue with next line. */
! 245: break;
! 246:
! 247: default: /* All else are a valid name chars. */
! 248: if( isspace( c ) ) /* One space per whitespace region. */
! 249: {
! 250: bufr[end] = ' ';
! 251: i = end + 1;
! 252: c = EatWhitespace( InFile );
! 253: }
! 254: else /* All others copy verbatim. */
! 255: {
! 256: bufr[i++] = c;
! 257: end = i;
! 258: c = getc( InFile );
! 259: }
! 260: }
! 261: }
! 262:
! 263: /* We arrive here if we've met the EOF before the closing bracket. */
! 264: rprintf(FLOG, "%s Unexpected EOF in the configuration file: %s\n", func, bufr );
! 265: return( False );
! 266: } /* Section */
! 267:
! 268: static BOOL Parameter( FILE *InFile, BOOL (*pfunc)(char *, char *), int c )
! 269: /* ------------------------------------------------------------------------ **
! 270: * Scan a parameter name and value, and pass these two fields to pfunc().
! 271: *
! 272: * Input: InFile - The input source.
! 273: * pfunc - A pointer to the function that will be called to
! 274: * process the parameter, once it has been scanned.
! 275: * c - The first character of the parameter name, which
! 276: * would have been read by Parse(). Unlike a comment
! 277: * line or a section header, there is no lead-in
! 278: * character that can be discarded.
! 279: *
! 280: * Output: True if the parameter name and value were scanned and processed
! 281: * successfully, else False.
! 282: *
! 283: * Notes: This function is in two parts. The first loop scans the
! 284: * parameter name. Internal whitespace is compressed, and an
! 285: * equal sign (=) terminates the token. Leading and trailing
! 286: * whitespace is discarded. The second loop scans the parameter
! 287: * value. When both have been successfully identified, they are
! 288: * passed to pfunc() for processing.
! 289: *
! 290: * ------------------------------------------------------------------------ **
! 291: */
! 292: {
! 293: int i = 0; /* Position within bufr. */
! 294: int end = 0; /* bufr[end] is current end-of-string. */
! 295: int vstart = 0; /* Starting position of the parameter value. */
! 296: char *func = "params.c:Parameter() -";
! 297:
! 298: /* Read the parameter name. */
! 299: while( 0 == vstart ) /* Loop until we've found the start of the value. */
! 300: {
! 301:
! 302: if( i > (bSize - 2) ) /* Ensure there's space for next char. */
! 303: {
! 304: bSize += BUFR_INC;
! 305: bufr = realloc_array( bufr, char, bSize );
! 306: if( NULL == bufr )
! 307: {
! 308: rprintf(FLOG, "%s Memory re-allocation failure.", func) ;
! 309: return( False );
! 310: }
! 311: }
! 312:
! 313: switch( c )
! 314: {
! 315: case '=': /* Equal sign marks end of param name. */
! 316: if( 0 == end ) /* Don't allow an empty name. */
! 317: {
! 318: rprintf(FLOG, "%s Invalid parameter name in config. file.\n", func );
! 319: return( False );
! 320: }
! 321: bufr[end++] = '\0'; /* Mark end of string & advance. */
! 322: i = end; /* New string starts here. */
! 323: vstart = end; /* New string is parameter value. */
! 324: bufr[i] = '\0'; /* New string is nul, for now. */
! 325: break;
! 326:
! 327: case '\n': /* Find continuation char, else error. */
! 328: i = Continuation( bufr, i );
! 329: if( i < 0 )
! 330: {
! 331: bufr[end] = '\0';
! 332: rprintf(FLOG, "%s Ignoring badly formed line in configuration file: %s\n",
! 333: func, bufr );
! 334: return( True );
! 335: }
! 336: end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
! 337: c = getc( InFile ); /* Read past eoln. */
! 338: break;
! 339:
! 340: case '\0': /* Shouldn't have EOF within param name. */
! 341: case EOF:
! 342: bufr[i] = '\0';
! 343: rprintf(FLOG, "%s Unexpected end-of-file at: %s\n", func, bufr );
! 344: return( True );
! 345:
! 346: default:
! 347: if( isspace( c ) ) /* One ' ' per whitespace region. */
! 348: {
! 349: bufr[end] = ' ';
! 350: i = end + 1;
! 351: c = EatWhitespace( InFile );
! 352: }
! 353: else /* All others verbatim. */
! 354: {
! 355: bufr[i++] = c;
! 356: end = i;
! 357: c = getc( InFile );
! 358: }
! 359: }
! 360: }
! 361:
! 362: /* Now parse the value. */
! 363: c = EatWhitespace( InFile ); /* Again, trim leading whitespace. */
! 364: while( (EOF !=c) && (c > 0) )
! 365: {
! 366:
! 367: if( i > (bSize - 2) ) /* Make sure there's enough room. */
! 368: {
! 369: bSize += BUFR_INC;
! 370: bufr = realloc_array( bufr, char, bSize );
! 371: if( NULL == bufr )
! 372: {
! 373: rprintf(FLOG, "%s Memory re-allocation failure.", func) ;
! 374: return( False );
! 375: }
! 376: }
! 377:
! 378: switch( c )
! 379: {
! 380: case '\r': /* Explicitly remove '\r' because the older */
! 381: c = getc( InFile ); /* version called fgets_slash() which also */
! 382: break; /* removes them. */
! 383:
! 384: case '\n': /* Marks end of value unless there's a '\'. */
! 385: i = Continuation( bufr, i );
! 386: if( i < 0 )
! 387: c = 0;
! 388: else
! 389: {
! 390: for( end = i; end >= 0 && isSpace(bufr + end); end-- )
! 391: ;
! 392: c = getc( InFile );
! 393: }
! 394: break;
! 395:
! 396: default: /* All others verbatim. Note that spaces do */
! 397: bufr[i++] = c; /* not advance <end>. This allows trimming */
! 398: if( !isspace( c ) ) /* of whitespace at the end of the line. */
! 399: end = i;
! 400: c = getc( InFile );
! 401: break;
! 402: }
! 403: }
! 404: bufr[end] = '\0'; /* End of value. */
! 405:
! 406: return( pfunc( bufr, &bufr[vstart] ) ); /* Pass name & value to pfunc(). */
! 407: } /* Parameter */
! 408:
! 409: static BOOL Parse( FILE *InFile,
! 410: BOOL (*sfunc)(char *),
! 411: BOOL (*pfunc)(char *, char *) )
! 412: /* ------------------------------------------------------------------------ **
! 413: * Scan & parse the input.
! 414: *
! 415: * Input: InFile - Input source.
! 416: * sfunc - Function to be called when a section name is scanned.
! 417: * See Section().
! 418: * pfunc - Function to be called when a parameter is scanned.
! 419: * See Parameter().
! 420: *
! 421: * Output: True if the file was successfully scanned, else False.
! 422: *
! 423: * Notes: The input can be viewed in terms of 'lines'. There are four
! 424: * types of lines:
! 425: * Blank - May contain whitespace, otherwise empty.
! 426: * Comment - First non-whitespace character is a ';' or '#'.
! 427: * The remainder of the line is ignored.
! 428: * Section - First non-whitespace character is a '['.
! 429: * Parameter - The default case.
! 430: *
! 431: * ------------------------------------------------------------------------ **
! 432: */
! 433: {
! 434: int c;
! 435:
! 436: c = EatWhitespace( InFile );
! 437: while( (EOF != c) && (c > 0) )
! 438: {
! 439: switch( c )
! 440: {
! 441: case '\n': /* Blank line. */
! 442: c = EatWhitespace( InFile );
! 443: break;
! 444:
! 445: case ';': /* Comment line. */
! 446: case '#':
! 447: c = EatComment( InFile );
! 448: break;
! 449:
! 450: case '[': /* Section Header. */
! 451: if (!sfunc) return True;
! 452: if( !Section( InFile, sfunc ) )
! 453: return( False );
! 454: c = EatWhitespace( InFile );
! 455: break;
! 456:
! 457: case '\\': /* Bogus backslash. */
! 458: c = EatWhitespace( InFile );
! 459: break;
! 460:
! 461: default: /* Parameter line. */
! 462: if( !Parameter( InFile, pfunc, c ) )
! 463: return( False );
! 464: c = EatWhitespace( InFile );
! 465: break;
! 466: }
! 467: }
! 468: return( True );
! 469: } /* Parse */
! 470:
! 471: static FILE *OpenConfFile( char *FileName )
! 472: /* ------------------------------------------------------------------------ **
! 473: * Open a configuration file.
! 474: *
! 475: * Input: FileName - The pathname of the config file to be opened.
! 476: *
! 477: * Output: A pointer of type (FILE *) to the opened file, or NULL if the
! 478: * file could not be opened.
! 479: *
! 480: * ------------------------------------------------------------------------ **
! 481: */
! 482: {
! 483: FILE *OpenedFile;
! 484: char *func = "params.c:OpenConfFile() -";
! 485:
! 486: if( NULL == FileName || 0 == *FileName )
! 487: {
! 488: rprintf(FLOG, "%s No configuration filename specified.\n", func);
! 489: return( NULL );
! 490: }
! 491:
! 492: OpenedFile = fopen( FileName, "r" );
! 493: if( NULL == OpenedFile )
! 494: {
! 495: rsyserr(FLOG, errno, "unable to open configuration file \"%s\"",
! 496: FileName);
! 497: }
! 498:
! 499: return( OpenedFile );
! 500: } /* OpenConfFile */
! 501:
! 502: BOOL pm_process( char *FileName,
! 503: BOOL (*sfunc)(char *),
! 504: BOOL (*pfunc)(char *, char *) )
! 505: /* ------------------------------------------------------------------------ **
! 506: * Process the named parameter file.
! 507: *
! 508: * Input: FileName - The pathname of the parameter file to be opened.
! 509: * sfunc - A pointer to a function that will be called when
! 510: * a section name is discovered.
! 511: * pfunc - A pointer to a function that will be called when
! 512: * a parameter name and value are discovered.
! 513: *
! 514: * Output: TRUE if the file was successfully parsed, else FALSE.
! 515: *
! 516: * ------------------------------------------------------------------------ **
! 517: */
! 518: {
! 519: int result;
! 520: FILE *InFile;
! 521: char *func = "params.c:pm_process() -";
! 522:
! 523: InFile = OpenConfFile( FileName ); /* Open the config file. */
! 524: if( NULL == InFile )
! 525: return( False );
! 526:
! 527: if( NULL != bufr ) /* If we already have a buffer */
! 528: result = Parse( InFile, sfunc, pfunc ); /* (recursive call), then just */
! 529: /* use it. */
! 530:
! 531: else /* If we don't have a buffer */
! 532: { /* allocate one, then parse, */
! 533: bSize = BUFR_INC; /* then free. */
! 534: bufr = new_array( char, bSize );
! 535: if( NULL == bufr )
! 536: {
! 537: rprintf(FLOG, "%s memory allocation failure.\n", func);
! 538: fclose(InFile);
! 539: return( False );
! 540: }
! 541: result = Parse( InFile, sfunc, pfunc );
! 542: free( bufr );
! 543: bufr = NULL;
! 544: bSize = 0;
! 545: }
! 546:
! 547: fclose(InFile);
! 548:
! 549: if( !result ) /* Generic failure. */
! 550: {
! 551: rprintf(FLOG, "%s Failed. Error returned from params.c:parse().\n", func);
! 552: return( False );
! 553: }
! 554:
! 555: return( True ); /* Generic success. */
! 556: } /* pm_process */
! 557:
! 558: /* -------------------------------------------------------------------------- */
! 559:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>