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>