params.c

説明を見る。
00001 /*
00002   This modules is based on the params.c module from Samba, written by Karl Auer
00003   and much modifed by Christopher Hertel.
00004 
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00018  *
00019  * -------------------------------------------------------------------------- **
00020  *
00021  * Module name: params
00022  *
00023  * -------------------------------------------------------------------------- **
00024  *
00025  *  This module performs lexical analysis and initial parsing of a
00026  *  Windows-like parameter file.  It recognizes and handles four token
00027  *  types:  section-name, parameter-name, parameter-value, and
00028  *  end-of-file.  Comments and line continuation are handled
00029  *  internally.
00030  *
00031  *  The entry point to the module is function pm_process().  This
00032  *  function opens the source file, calls the Parse() function to parse
00033  *  the input, and then closes the file when either the EOF is reached
00034  *  or a fatal error is encountered.
00035  *
00036  *  A sample parameter file might look like this:
00037  *
00038  *  [section one]
00039  *  parameter one = value string
00040  *  parameter two = another value
00041  *  [section two]
00042  *  new parameter = some value or t'other
00043  *
00044  *  The parameter file is divided into sections by section headers:
00045  *  section names enclosed in square brackets (eg. [section one]).
00046  *  Each section contains parameter lines, each of which consist of a
00047  *  parameter name and value delimited by an equal sign.  Roughly, the
00048  *  syntax is:
00049  *
00050  *    <file>            :==  { <section> } EOF
00051  *
00052  *    <section>         :==  <section header> { <parameter line> }
00053  *
00054  *    <section header>  :==  '[' NAME ']'
00055  *
00056  *    <parameter line>  :==  NAME '=' VALUE '\n'
00057  *
00058  *  Blank lines and comment lines are ignored.  Comment lines are lines
00059  *  beginning with either a semicolon (';') or a pound sign ('#').
00060  *
00061  *  All whitespace in section names and parameter names is compressed
00062  *  to single spaces.  Leading and trailing whitespace is stipped from
00063  *  both names and values.
00064  *
00065  *  Only the first equals sign in a parameter line is significant.
00066  *  Parameter values may contain equals signs, square brackets and
00067  *  semicolons.  Internal whitespace is retained in parameter values,
00068  *  with the exception of the '\r' character, which is stripped for
00069  *  historic reasons.  Parameter names may not start with a left square
00070  *  bracket, an equal sign, a pound sign, or a semicolon, because these
00071  *  are used to identify other tokens.
00072  *
00073  * -------------------------------------------------------------------------- **
00074  */
00075 
00076 #include "rsync.h"
00077 
00078 /* -------------------------------------------------------------------------- **
00079  * Constants...
00080  */
00081 
00082 #define BUFR_INC 1024
00083 
00084 
00085 /* -------------------------------------------------------------------------- **
00086  * Variables...
00087  *
00088  *  bufr        - pointer to a global buffer.  This is probably a kludge,
00089  *                but it was the nicest kludge I could think of (for now).
00090  *  bSize       - The size of the global buffer <bufr>.
00091  */
00092 
00093 static char *bufr  = NULL;
00094 static int   bSize = 0;
00095 
00096 /* -------------------------------------------------------------------------- **
00097  * Functions...
00098  */
00099 
00100 static int EatWhitespace( FILE *InFile )
00101   /* ------------------------------------------------------------------------ **
00102    * Scan past whitespace (see ctype(3C)) and return the first non-whitespace
00103    * character, or newline, or EOF.
00104    *
00105    *  Input:  InFile  - Input source.
00106    *
00107    *  Output: The next non-whitespace character in the input stream.
00108    *
00109    *  Notes:  Because the config files use a line-oriented grammar, we
00110    *          explicitly exclude the newline character from the list of
00111    *          whitespace characters.
00112    *        - Note that both EOF (-1) and the nul character ('\0') are
00113    *          considered end-of-file markers.
00114    *
00115    * ------------------------------------------------------------------------ **
00116    */
00117   {
00118   int c;
00119 
00120   for( c = getc( InFile ); isspace( c ) && ('\n' != c); c = getc( InFile ) )
00121     ;
00122   return( c );
00123   } /* EatWhitespace */
00124 
00125 static int EatComment( FILE *InFile )
00126   /* ------------------------------------------------------------------------ **
00127    * Scan to the end of a comment.
00128    *
00129    *  Input:  InFile  - Input source.
00130    *
00131    *  Output: The character that marks the end of the comment.  Normally,
00132    *          this will be a newline, but it *might* be an EOF.
00133    *
00134    *  Notes:  Because the config files use a line-oriented grammar, we
00135    *          explicitly exclude the newline character from the list of
00136    *          whitespace characters.
00137    *        - Note that both EOF (-1) and the nul character ('\0') are
00138    *          considered end-of-file markers.
00139    *
00140    * ------------------------------------------------------------------------ **
00141    */
00142   {
00143   int c;
00144 
00145   for( c = getc( InFile ); ('\n'!=c) && (EOF!=c) && (c>0); c = getc( InFile ) )
00146     ;
00147   return( c );
00148   } /* EatComment */
00149 
00150 static int Continuation( char *line, int pos )
00151   /* ------------------------------------------------------------------------ **
00152    * Scan backards within a string to discover if the last non-whitespace
00153    * character is a line-continuation character ('\\').
00154    *
00155    *  Input:  line  - A pointer to a buffer containing the string to be
00156    *                  scanned.
00157    *          pos   - This is taken to be the offset of the end of the
00158    *                  string.  This position is *not* scanned.
00159    *
00160    *  Output: The offset of the '\\' character if it was found, or -1 to
00161    *          indicate that it was not.
00162    *
00163    * ------------------------------------------------------------------------ **
00164    */
00165   {
00166   pos--;
00167   while( (pos >= 0) && isspace(((unsigned char *)line)[pos]) )
00168      pos--;
00169 
00170   return( ((pos >= 0) && ('\\' == line[pos])) ? pos : -1 );
00171   } /* Continuation */
00172 
00173 
00174 static BOOL Section( FILE *InFile, BOOL (*sfunc)(char *) )
00175   /* ------------------------------------------------------------------------ **
00176    * Scan a section name, and pass the name to function sfunc().
00177    *
00178    *  Input:  InFile  - Input source.
00179    *          sfunc   - Pointer to the function to be called if the section
00180    *                    name is successfully read.
00181    *
00182    *  Output: True if the section name was read and True was returned from
00183    *          <sfunc>.  False if <sfunc> failed or if a lexical error was
00184    *          encountered.
00185    *
00186    * ------------------------------------------------------------------------ **
00187    */
00188   {
00189   int   c;
00190   int   i;
00191   int   end;
00192   char *func  = "params.c:Section() -";
00193 
00194   i = 0;      /* <i> is the offset of the next free byte in bufr[] and  */
00195   end = 0;    /* <end> is the current "end of string" offset.  In most  */
00196               /* cases these will be the same, but if the last          */
00197               /* character written to bufr[] is a space, then <end>     */
00198               /* will be one less than <i>.                             */
00199 
00200   c = EatWhitespace( InFile );    /* We've already got the '['.  Scan */
00201                                   /* past initial white space.        */
00202 
00203   while( (EOF != c) && (c > 0) )
00204     {
00205 
00206     /* Check that the buffer is big enough for the next character. */
00207     if( i > (bSize - 2) )
00208       {
00209       bSize += BUFR_INC;
00210       bufr   = realloc_array( bufr, char, bSize );
00211       if( NULL == bufr )
00212         {
00213         rprintf(FERROR, "%s Memory re-allocation failure.", func);
00214         return( False );
00215         }
00216       }
00217 
00218     /* Handle a single character. */
00219     switch( c )
00220       {
00221       case ']':                       /* Found the closing bracket.         */
00222         bufr[end] = '\0';
00223         if( 0 == end )                  /* Don't allow an empty name.       */
00224           {
00225           rprintf(FERROR, "%s Empty section name in configuration file.\n", func );
00226           return( False );
00227           }
00228         if( !sfunc( bufr ) )            /* Got a valid name.  Deal with it. */
00229           return( False );
00230         (void)EatComment( InFile );     /* Finish off the line.             */
00231         return( True );
00232 
00233       case '\n':                      /* Got newline before closing ']'.    */
00234         i = Continuation( bufr, i );    /* Check for line continuation.     */
00235         if( i < 0 )
00236           {
00237           bufr[end] = '\0';
00238           rprintf(FERROR, "%s Badly formed line in configuration file: %s\n",
00239                    func, bufr );
00240           return( False );
00241           }
00242         end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
00243         c = getc( InFile );             /* Continue with next line.         */
00244         break;
00245 
00246       default:                        /* All else are a valid name chars.   */
00247         if( isspace( c ) )              /* One space per whitespace region. */
00248           {
00249           bufr[end] = ' ';
00250           i = end + 1;
00251           c = EatWhitespace( InFile );
00252           }
00253         else                            /* All others copy verbatim.        */
00254           {
00255           bufr[i++] = c;
00256           end = i;
00257           c = getc( InFile );
00258           }
00259       }
00260     }
00261 
00262   /* We arrive here if we've met the EOF before the closing bracket. */
00263   rprintf(FERROR, "%s Unexpected EOF in the configuration file: %s\n", func, bufr );
00264   return( False );
00265   } /* Section */
00266 
00267 static BOOL Parameter( FILE *InFile, BOOL (*pfunc)(char *, char *), int c )
00268   /* ------------------------------------------------------------------------ **
00269    * Scan a parameter name and value, and pass these two fields to pfunc().
00270    *
00271    *  Input:  InFile  - The input source.
00272    *          pfunc   - A pointer to the function that will be called to
00273    *                    process the parameter, once it has been scanned.
00274    *          c       - The first character of the parameter name, which
00275    *                    would have been read by Parse().  Unlike a comment
00276    *                    line or a section header, there is no lead-in
00277    *                    character that can be discarded.
00278    *
00279    *  Output: True if the parameter name and value were scanned and processed
00280    *          successfully, else False.
00281    *
00282    *  Notes:  This function is in two parts.  The first loop scans the
00283    *          parameter name.  Internal whitespace is compressed, and an
00284    *          equal sign (=) terminates the token.  Leading and trailing
00285    *          whitespace is discarded.  The second loop scans the parameter
00286    *          value.  When both have been successfully identified, they are
00287    *          passed to pfunc() for processing.
00288    *
00289    * ------------------------------------------------------------------------ **
00290    */
00291   {
00292   int   i       = 0;    /* Position within bufr. */
00293   int   end     = 0;    /* bufr[end] is current end-of-string. */
00294   int   vstart  = 0;    /* Starting position of the parameter value. */
00295   char *func    = "params.c:Parameter() -";
00296 
00297   /* Read the parameter name. */
00298   while( 0 == vstart )  /* Loop until we've found the start of the value. */
00299     {
00300 
00301     if( i > (bSize - 2) )       /* Ensure there's space for next char.    */
00302       {
00303       bSize += BUFR_INC;
00304       bufr   = realloc_array( bufr, char, bSize );
00305       if( NULL == bufr )
00306         {
00307         rprintf(FERROR, "%s Memory re-allocation failure.", func) ;
00308         return( False );
00309         }
00310       }
00311 
00312     switch( c )
00313       {
00314       case '=':                 /* Equal sign marks end of param name. */
00315         if( 0 == end )              /* Don't allow an empty name.      */
00316           {
00317           rprintf(FERROR, "%s Invalid parameter name in config. file.\n", func );
00318           return( False );
00319           }
00320         bufr[end++] = '\0';         /* Mark end of string & advance.   */
00321         i       = end;              /* New string starts here.         */
00322         vstart  = end;              /* New string is parameter value.  */
00323         bufr[i] = '\0';             /* New string is nul, for now.     */
00324         break;
00325 
00326       case '\n':                /* Find continuation char, else error. */
00327         i = Continuation( bufr, i );
00328         if( i < 0 )
00329           {
00330           bufr[end] = '\0';
00331           rprintf(FERROR, "%s Ignoring badly formed line in configuration file: %s\n",
00332                    func, bufr );
00333           return( True );
00334           }
00335         end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
00336         c = getc( InFile );       /* Read past eoln.                   */
00337         break;
00338 
00339       case '\0':                /* Shouldn't have EOF within param name. */
00340       case EOF:
00341         bufr[i] = '\0';
00342         rprintf(FERROR, "%s Unexpected end-of-file at: %s\n", func, bufr );
00343         return( True );
00344 
00345       default:
00346         if( isspace( c ) )     /* One ' ' per whitespace region.       */
00347           {
00348           bufr[end] = ' ';
00349           i = end + 1;
00350           c = EatWhitespace( InFile );
00351           }
00352         else                   /* All others verbatim.                 */
00353           {
00354           bufr[i++] = c;
00355           end = i;
00356           c = getc( InFile );
00357           }
00358       }
00359     }
00360 
00361   /* Now parse the value. */
00362   c = EatWhitespace( InFile );  /* Again, trim leading whitespace. */
00363   while( (EOF !=c) && (c > 0) )
00364     {
00365 
00366     if( i > (bSize - 2) )       /* Make sure there's enough room. */
00367       {
00368       bSize += BUFR_INC;
00369       bufr   = realloc_array( bufr, char, bSize );
00370       if( NULL == bufr )
00371         {
00372         rprintf(FERROR, "%s Memory re-allocation failure.", func) ;
00373         return( False );
00374         }
00375       }
00376 
00377     switch( c )
00378       {
00379       case '\r':              /* Explicitly remove '\r' because the older */
00380         c = getc( InFile );   /* version called fgets_slash() which also  */
00381         break;                /* removes them.                            */
00382 
00383       case '\n':              /* Marks end of value unless there's a '\'. */
00384         i = Continuation( bufr, i );
00385         if( i < 0 )
00386           c = 0;
00387         else
00388           {
00389           for( end = i; (end >= 0) && isspace(((unsigned char *) bufr)[end]); end-- )
00390             ;
00391           c = getc( InFile );
00392           }
00393         break;
00394 
00395       default:               /* All others verbatim.  Note that spaces do */
00396         bufr[i++] = c;       /* not advance <end>.  This allows trimming  */
00397         if( !isspace( c ) )  /* of whitespace at the end of the line.     */
00398           end = i;
00399         c = getc( InFile );
00400         break;
00401       }
00402     }
00403   bufr[end] = '\0';          /* End of value. */
00404 
00405   return( pfunc( bufr, &bufr[vstart] ) );   /* Pass name & value to pfunc().  */
00406   } /* Parameter */
00407 
00408 static BOOL Parse( FILE *InFile,
00409                    BOOL (*sfunc)(char *),
00410                    BOOL (*pfunc)(char *, char *) )
00411   /* ------------------------------------------------------------------------ **
00412    * Scan & parse the input.
00413    *
00414    *  Input:  InFile  - Input source.
00415    *          sfunc   - Function to be called when a section name is scanned.
00416    *                    See Section().
00417    *          pfunc   - Function to be called when a parameter is scanned.
00418    *                    See Parameter().
00419    *
00420    *  Output: True if the file was successfully scanned, else False.
00421    *
00422    *  Notes:  The input can be viewed in terms of 'lines'.  There are four
00423    *          types of lines:
00424    *            Blank      - May contain whitespace, otherwise empty.
00425    *            Comment    - First non-whitespace character is a ';' or '#'.
00426    *                         The remainder of the line is ignored.
00427    *            Section    - First non-whitespace character is a '['.
00428    *            Parameter  - The default case.
00429    * 
00430    * ------------------------------------------------------------------------ **
00431    */
00432   {
00433   int    c;
00434 
00435   c = EatWhitespace( InFile );
00436   while( (EOF != c) && (c > 0) )
00437     {
00438     switch( c )
00439       {
00440       case '\n':                        /* Blank line. */
00441         c = EatWhitespace( InFile );
00442         break;
00443 
00444       case ';':                         /* Comment line. */
00445       case '#':
00446         c = EatComment( InFile );
00447         break;
00448 
00449       case '[':                         /* Section Header. */
00450               if (!sfunc) return True;
00451               if( !Section( InFile, sfunc ) )
00452                       return( False );
00453               c = EatWhitespace( InFile );
00454               break;
00455 
00456       case '\\':                        /* Bogus backslash. */
00457         c = EatWhitespace( InFile );
00458         break;
00459 
00460       default:                          /* Parameter line. */
00461         if( !Parameter( InFile, pfunc, c ) )
00462           return( False );
00463         c = EatWhitespace( InFile );
00464         break;
00465       }
00466     }
00467   return( True );
00468   } /* Parse */
00469 
00470 static FILE *OpenConfFile( char *FileName )
00471   /* ------------------------------------------------------------------------ **
00472    * Open a configuration file.
00473    *
00474    *  Input:  FileName  - The pathname of the config file to be opened.
00475    *
00476    *  Output: A pointer of type (FILE *) to the opened file, or NULL if the
00477    *          file could not be opened.
00478    *
00479    * ------------------------------------------------------------------------ **
00480    */
00481   {
00482   FILE *OpenedFile;
00483   char *func = "params.c:OpenConfFile() -";
00484 
00485   if( NULL == FileName || 0 == *FileName )
00486     {
00487     rprintf(FERROR,"%s No configuration filename specified.\n", func);
00488     return( NULL );
00489     }
00490 
00491   OpenedFile = fopen( FileName, "r" );
00492   if( NULL == OpenedFile )
00493     {
00494     rsyserr(FERROR, errno, "unable to open configuration file \"%s\"",
00495             FileName);
00496     }
00497 
00498   return( OpenedFile );
00499   } /* OpenConfFile */
00500 
00501 BOOL pm_process( char *FileName,
00502                  BOOL (*sfunc)(char *),
00503                  BOOL (*pfunc)(char *, char *) )
00504   /* ------------------------------------------------------------------------ **
00505    * Process the named parameter file.
00506    *
00507    *  Input:  FileName  - The pathname of the parameter file to be opened.
00508    *          sfunc     - A pointer to a function that will be called when
00509    *                      a section name is discovered.
00510    *          pfunc     - A pointer to a function that will be called when
00511    *                      a parameter name and value are discovered.
00512    *
00513    *  Output: TRUE if the file was successfully parsed, else FALSE.
00514    *
00515    * ------------------------------------------------------------------------ **
00516    */
00517   {
00518   int   result;
00519   FILE *InFile;
00520   char *func = "params.c:pm_process() -";
00521 
00522   InFile = OpenConfFile( FileName );          /* Open the config file. */
00523   if( NULL == InFile )
00524     return( False );
00525 
00526   if( NULL != bufr )                          /* If we already have a buffer */
00527     result = Parse( InFile, sfunc, pfunc );   /* (recursive call), then just */
00528                                               /* use it.                     */
00529 
00530   else                                        /* If we don't have a buffer   */
00531     {                                         /* allocate one, then parse,   */
00532     bSize = BUFR_INC;                         /* then free.                  */
00533     bufr = new_array( char, bSize );
00534     if( NULL == bufr )
00535       {
00536       rprintf(FERROR,"%s memory allocation failure.\n", func);
00537       fclose(InFile);
00538       return( False );
00539       }
00540     result = Parse( InFile, sfunc, pfunc );
00541     free( bufr );
00542     bufr  = NULL;
00543     bSize = 0;
00544     }
00545 
00546   fclose(InFile);
00547 
00548   if( !result )                               /* Generic failure. */
00549     {
00550     rprintf(FERROR,"%s Failed.  Error returned from params.c:parse().\n", func);
00551     return( False );
00552     }
00553 
00554   return( True );                             /* Generic success. */
00555   } /* pm_process */
00556 
00557 /* -------------------------------------------------------------------------- */
00558 

rsyncに対してSat Dec 5 19:45:41 2009に生成されました。  doxygen 1.4.7