File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / rsync / params.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Fri Feb 17 15:09:30 2012 UTC (12 years, 4 months ago) by misho
Branches: rsync, MAIN
CVS tags: rsync3_0_9p0, RSYNC3_0_9, HEAD
rsync

    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>