root/src/vfs/smbfs/helpers/param/params.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. EatWhitespace
  2. EatComment
  3. Continuation
  4. Section
  5. Parameter
  6. Parse
  7. OpenConfFile
  8. pm_process

   1 /* -------------------------------------------------------------------------- **
   2  * Microsoft Network Services for Unix, AKA., Andrew Tridgell's SAMBA.
   3  *
   4  * This module Copyright (C) 1990-1998 Karl Auer
   5  *
   6  * Rewritten almost completely by Christopher R. Hertel
   7  * at the University of Minnesota, September, 1997.
   8  * This module Copyright (C) 1997-1998 by the University of Minnesota
   9  * -------------------------------------------------------------------------- **
  10  *
  11  This file is part of the Midnight Commander.
  12 
  13  The Midnight Commander is free software: you can redistribute it
  14  and/or modify it under the terms of the GNU General Public License as
  15  published by the Free Software Foundation, either version 3 of the License,
  16  or (at your option) any later version.
  17 
  18  The Midnight Commander is distributed in the hope that it will be useful,
  19  but WITHOUT ANY WARRANTY; without even the implied warranty of
  20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21  GNU General Public License for more details.
  22 
  23  You should have received a copy of the GNU General Public License
  24  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  25 
  26  */
  27 
  28 /*
  29  * -------------------------------------------------------------------------- **
  30  *
  31  * Module name: params
  32  *
  33  * -------------------------------------------------------------------------- **
  34  *
  35  *  This module performs lexical analysis and initial parsing of a
  36  *  Windows-like parameter file.  It recognizes and handles four token
  37  *  types:  section-name, parameter-name, parameter-value, and
  38  *  end-of-file.  Comments and line continuation are handled
  39  *  internally.
  40  *
  41  *  The entry point to the module is function pm_process().  This
  42  *  function opens the source file, calls the Parse() function to parse
  43  *  the input, and then closes the file when either the EOF is reached
  44  *  or a fatal error is encountered.
  45  *
  46  *  A sample parameter file might look like this:
  47  *
  48  *  [section one]
  49  *  parameter one = value string
  50  *  parameter two = another value
  51  *  [section two]
  52  *  new parameter = some value or t'other
  53  *
  54  *  The parameter file is divided into sections by section headers:
  55  *  section names enclosed in square brackets (eg. [section one]).
  56  *  Each section contains parameter lines, each of which consist of a
  57  *  parameter name and value delimited by an equal sign.  Roughly, the
  58  *  syntax is:
  59  *
  60  *    <file>            :==  { <section> } EOF
  61  *
  62  *    <section>         :==  <section header> { <parameter line> }
  63  *
  64  *    <section header>  :==  '[' NAME ']'
  65  *
  66  *    <parameter line>  :==  NAME '=' VALUE '\n'
  67  *
  68  *  Blank lines and comment lines are ignored.  Comment lines are lines
  69  *  beginning with either a semicolon (';') or a pound sign ('#').
  70  *
  71  *  All whitespace in section names and parameter names is compressed
  72  *  to single spaces.  Leading and trailing whitespace is stipped from
  73  *  both names and values.
  74  *
  75  *  Only the first equals sign in a parameter line is significant.
  76  *  Parameter values may contain equals signs, square brackets and
  77  *  semicolons.  Internal whitespace is retained in parameter values,
  78  *  with the exception of the '\r' character, which is stripped for
  79  *  historic reasons.  Parameter names may not start with a left square
  80  *  bracket, an equal sign, a pound sign, or a semicolon, because these
  81  *  are used to identify other tokens.
  82  *
  83  * -------------------------------------------------------------------------- **
  84  */
  85 
  86 #include "includes.h"
  87 const char *unix_error_string (int error_num);
  88 
  89 /* -------------------------------------------------------------------------- **
  90  * Constants...
  91  */
  92 
  93 #define BUFR_INC 1024
  94 
  95 
  96 /* -------------------------------------------------------------------------- **
  97  * Variables...
  98  *
  99  *  DEBUGLEVEL  - The ubiquitous DEBUGLEVEL.  This determines which DEBUG()
 100  *                messages will be produced.
 101  *  bufr        - pointer to a global buffer.  This is probably a kludge,
 102  *                but it was the nicest kludge I could think of (for now).
 103  *  bSize       - The size of the global buffer <bufr>.
 104  */
 105 
 106 extern int DEBUGLEVEL;
 107 
 108 static char *bufr = NULL;
 109 static int bSize = 0;
 110 
 111 /* -------------------------------------------------------------------------- **
 112  * Functions...
 113  */
 114 
 115 static int
 116 EatWhitespace (FILE * InFile)
     /* [previous][next][first][last][top][bottom][index][help]  */
 117   /* ------------------------------------------------------------------------ **
 118    * Scan past whitespace (see ctype(3C)) and return the first non-whitespace
 119    * character, or newline, or EOF.
 120    *
 121    *  Input:  InFile  - Input source.
 122    *
 123    *  Output: The next non-whitespace character in the input stream.
 124    *
 125    *  Notes:  Because the config files use a line-oriented grammar, we
 126    *          explicitly exclude the newline character from the list of
 127    *          whitespace characters.
 128    *        - Note that both EOF (-1) and the nul character ('\0') are
 129    *          considered end-of-file markers.
 130    *
 131    * ------------------------------------------------------------------------ **
 132    */
 133 {
 134     int c;
 135 
 136     for (c = getc (InFile); isspace (c) && ('\n' != c); c = getc (InFile))
 137         ;
 138     return (c);
 139 }                               /* EatWhitespace */
 140 
 141 static int
 142 EatComment (FILE * InFile)
     /* [previous][next][first][last][top][bottom][index][help]  */
 143   /* ------------------------------------------------------------------------ **
 144    * Scan to the end of a comment.
 145    *
 146    *  Input:  InFile  - Input source.
 147    *
 148    *  Output: The character that marks the end of the comment.  Normally,
 149    *          this will be a newline, but it *might* be an EOF.
 150    *
 151    *  Notes:  Because the config files use a line-oriented grammar, we
 152    *          explicitly exclude the newline character from the list of
 153    *          whitespace characters.
 154    *        - Note that both EOF (-1) and the nul character ('\0') are
 155    *          considered end-of-file markers.
 156    *
 157    * ------------------------------------------------------------------------ **
 158    */
 159 {
 160     int c;
 161 
 162     for (c = getc (InFile); ('\n' != c) && (EOF != c) && (c > 0); c = getc (InFile))
 163         ;
 164     return (c);
 165 }                               /* EatComment */
 166 
 167 static int
 168 Continuation (char *line, int pos)
     /* [previous][next][first][last][top][bottom][index][help]  */
 169   /* ------------------------------------------------------------------------ **
 170    * Scan backwards within a string to discover if the last non-whitespace
 171    * character is a line-continuation character ('\\').
 172    *
 173    *  Input:  line  - A pointer to a buffer containing the string to be
 174    *                  scanned.
 175    *          pos   - This is taken to be the offset of the end of the
 176    *                  string.  This position is *not* scanned.
 177    *
 178    *  Output: The offset of the '\\' character if it was found, or -1 to
 179    *          indicate that it was not.
 180    *
 181    * ------------------------------------------------------------------------ **
 182    */
 183 {
 184     pos--;
 185     while ((pos >= 0) && isspace (line[pos]))
 186         pos--;
 187 
 188     return (((pos >= 0) && ('\\' == line[pos])) ? pos : -1);
 189 }                               /* Continuation */
 190 
 191 
 192 static BOOL
 193 Section (FILE * InFile, BOOL (*sfunc) (const char *))
     /* [previous][next][first][last][top][bottom][index][help]  */
 194   /* ------------------------------------------------------------------------ **
 195    * Scan a section name, and pass the name to function sfunc().
 196    *
 197    *  Input:  InFile  - Input source.
 198    *          sfunc   - Pointer to the function to be called if the section
 199    *                    name is successfully read.
 200    *
 201    *  Output: True if the section name was read and True was returned from
 202    *          <sfunc>.  False if <sfunc> failed or if a lexical error was
 203    *          encountered.
 204    *
 205    * ------------------------------------------------------------------------ **
 206    */
 207 {
 208     int c;
 209     int i;
 210     int end;
 211     const char *func = "params.c:Section() -";
 212 
 213     i = 0;                      /* <i> is the offset of the next free byte in bufr[] and  */
 214     end = 0;                    /* <end> is the current "end of string" offset.  In most  */
 215     /* cases these will be the same, but if the last          */
 216     /* character written to bufr[] is a space, then <end>     */
 217     /* will be one less than <i>.                             */
 218 
 219     c = EatWhitespace (InFile); /* We've already got the '['.  Scan */
 220     /* past initial white space.        */
 221 
 222     while ((EOF != c) && (c > 0))
 223     {
 224 
 225         /* Check that the buffer is big enough for the next character. */
 226         if (i > (bSize - 2))
 227         {
 228             bSize += BUFR_INC;
 229             bufr = Realloc (bufr, bSize);
 230             if (NULL == bufr)
 231             {
 232                 DEBUG (0, ("%s Memory re-allocation failure.", func));
 233                 return (False);
 234             }
 235         }
 236 
 237         /* Handle a single character. */
 238         switch (c)
 239         {
 240         case ']':              /* Found the closing bracket.         */
 241             bufr[end] = '\0';
 242             if (0 == end)       /* Don't allow an empty name.       */
 243             {
 244                 DEBUG (0, ("%s Empty section name in configuration file.\n", func));
 245                 return (False);
 246             }
 247             if (!sfunc (bufr))  /* Got a valid name.  Deal with it. */
 248                 return (False);
 249             (void) EatComment (InFile); /* Finish off the line.             */
 250             return (True);
 251 
 252         case '\n':             /* Got newline before closing ']'.    */
 253             i = Continuation (bufr, i); /* Check for line continuation.     */
 254             if (i < 0)
 255             {
 256                 bufr[end] = '\0';
 257                 DEBUG (0, ("%s Badly formed line in configuration file: %s\n", func, bufr));
 258                 return (False);
 259             }
 260             end = ((i > 0) && (' ' == bufr[i - 1])) ? (i - 1) : (i);
 261             c = getc (InFile);  /* Continue with next line.         */
 262             break;
 263 
 264         default:               /* All else are a valid name chars.   */
 265             if (isspace (c))    /* One space per whitespace region. */
 266             {
 267                 bufr[end] = ' ';
 268                 i = end + 1;
 269                 c = EatWhitespace (InFile);
 270             }
 271             else                /* All others copy verbatim.        */
 272             {
 273                 bufr[i++] = c;
 274                 end = i;
 275                 c = getc (InFile);
 276             }
 277         }
 278     }
 279 
 280     /* We arrive here if we've met the EOF before the closing bracket. */
 281     DEBUG (0, ("%s Unexpected EOF in the configuration file: %s\n", func, bufr));
 282     return (False);
 283 }                               /* Section */
 284 
 285 static BOOL
 286 Parameter (FILE * InFile, BOOL (*pfunc) (const char *, const char *), int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
 287   /* ------------------------------------------------------------------------ **
 288    * Scan a parameter name and value, and pass these two fields to pfunc().
 289    *
 290    *  Input:  InFile  - The input source.
 291    *          pfunc   - A pointer to the function that will be called to
 292    *                    process the parameter, once it has been scanned.
 293    *          c       - The first character of the parameter name, which
 294    *                    would have been read by Parse().  Unlike a comment
 295    *                    line or a section header, there is no lead-in
 296    *                    character that can be discarded.
 297    *
 298    *  Output: True if the parameter name and value were scanned and processed
 299    *          successfully, else False.
 300    *
 301    *  Notes:  This function is in two parts.  The first loop scans the
 302    *          parameter name.  Internal whitespace is compressed, and an
 303    *          equal sign (=) terminates the token.  Leading and trailing
 304    *          whitespace is discarded.  The second loop scans the parameter
 305    *          value.  When both have been successfully identified, they are
 306    *          passed to pfunc() for processing.
 307    *
 308    * ------------------------------------------------------------------------ **
 309    */
 310 {
 311     int i = 0;                  /* Position within bufr. */
 312     int end = 0;                /* bufr[end] is current end-of-string. */
 313     int vstart = 0;             /* Starting position of the parameter value. */
 314     const char *func = "params.c:Parameter() -";
 315 
 316     /* Read the parameter name. */
 317     while (0 == vstart)         /* Loop until we've found the start of the value. */
 318     {
 319 
 320         if (i > (bSize - 2))    /* Ensure there's space for next char.    */
 321         {
 322             bSize += BUFR_INC;
 323             bufr = Realloc (bufr, bSize);
 324             if (NULL == bufr)
 325             {
 326                 DEBUG (0, ("%s Memory re-allocation failure.", func));
 327                 return (False);
 328             }
 329         }
 330 
 331         switch (c)
 332         {
 333         case '=':              /* Equal sign marks end of param name. */
 334             if (0 == end)       /* Don't allow an empty name.      */
 335             {
 336                 DEBUG (0, ("%s Invalid parameter name in config. file.\n", func));
 337                 return (False);
 338             }
 339             bufr[end++] = '\0'; /* Mark end of string & advance.   */
 340             i = end;            /* New string starts here.         */
 341             vstart = end;       /* New string is parameter value.  */
 342             bufr[i] = '\0';     /* New string is nul, for now.     */
 343             break;
 344 
 345         case '\n':             /* Find continuation char, else error. */
 346             i = Continuation (bufr, i);
 347             if (i < 0)
 348             {
 349                 bufr[end] = '\0';
 350                 DEBUG (1, ("%s Ignoring badly formed line in configuration file: %s\n",
 351                            func, bufr));
 352                 return (True);
 353             }
 354             end = ((i > 0) && (' ' == bufr[i - 1])) ? (i - 1) : (i);
 355             c = getc (InFile);  /* Read past eoln.                   */
 356             break;
 357 
 358         case '\0':             /* Shouldn't have EOF within param name. */
 359         case EOF:
 360             bufr[i] = '\0';
 361             DEBUG (1, ("%s Unexpected end-of-file at: %s\n", func, bufr));
 362             return (True);
 363 
 364         default:
 365             if (isspace (c))    /* One ' ' per whitespace region.       */
 366             {
 367                 bufr[end] = ' ';
 368                 i = end + 1;
 369                 c = EatWhitespace (InFile);
 370             }
 371             else                /* All others verbatim.                 */
 372             {
 373                 bufr[i++] = c;
 374                 end = i;
 375                 c = getc (InFile);
 376             }
 377         }
 378     }
 379 
 380     /* Now parse the value. */
 381     c = EatWhitespace (InFile); /* Again, trim leading whitespace. */
 382     while ((EOF != c) && (c > 0))
 383     {
 384 
 385         if (i > (bSize - 2))    /* Make sure there's enough room. */
 386         {
 387             bSize += BUFR_INC;
 388             bufr = Realloc (bufr, bSize);
 389             if (NULL == bufr)
 390             {
 391                 DEBUG (0, ("%s Memory re-allocation failure.", func));
 392                 return (False);
 393             }
 394         }
 395 
 396         switch (c)
 397         {
 398         case '\r':             /* Explicitly remove '\r' because the older */
 399             c = getc (InFile);  /* version called fgets_slash() which also  */
 400             break;              /* removes them.                            */
 401 
 402         case '\n':             /* Marks end of value unless there's a '\'. */
 403             i = Continuation (bufr, i);
 404             if (i < 0)
 405                 c = 0;
 406             else
 407             {
 408                 for (end = i; (end >= 0) && isspace (bufr[end]); end--)
 409                     ;
 410                 c = getc (InFile);
 411             }
 412             break;
 413 
 414         default:               /* All others verbatim.  Note that spaces do */
 415             bufr[i++] = c;      /* not advance <end>.  This allows trimming  */
 416             if (!isspace (c))   /* of whitespace at the end of the line.     */
 417                 end = i;
 418             c = getc (InFile);
 419             break;
 420         }
 421     }
 422     bufr[end] = '\0';           /* End of value. */
 423 
 424     return (pfunc (bufr, &bufr[vstart]));       /* Pass name & value to pfunc().  */
 425 }                               /* Parameter */
 426 
 427 static BOOL
 428 Parse (FILE * InFile, BOOL (*sfunc) (const char *), BOOL (*pfunc) (const char *, const char *))
     /* [previous][next][first][last][top][bottom][index][help]  */
 429   /* ------------------------------------------------------------------------ **
 430    * Scan & parse the input.
 431    *
 432    *  Input:  InFile  - Input source.
 433    *          sfunc   - Function to be called when a section name is scanned.
 434    *                    See Section().
 435    *          pfunc   - Function to be called when a parameter is scanned.
 436    *                    See Parameter().
 437    *
 438    *  Output: True if the file was successfully scanned, else False.
 439    *
 440    *  Notes:  The input can be viewed in terms of 'lines'.  There are four
 441    *          types of lines:
 442    *            Blank      - May contain whitespace, otherwise empty.
 443    *            Comment    - First non-whitespace character is a ';' or '#'.
 444    *                         The remainder of the line is ignored.
 445    *            Section    - First non-whitespace character is a '['.
 446    *            Parameter  - The default case.
 447    * 
 448    * ------------------------------------------------------------------------ **
 449    */
 450 {
 451     int c;
 452 
 453     c = EatWhitespace (InFile);
 454     while ((EOF != c) && (c > 0))
 455     {
 456         switch (c)
 457         {
 458         case '\n':             /* Blank line. */
 459             c = EatWhitespace (InFile);
 460             break;
 461 
 462         case ';':              /* Comment line. */
 463         case '#':
 464             c = EatComment (InFile);
 465             break;
 466 
 467         case '[':              /* Section Header. */
 468             if (!Section (InFile, sfunc))
 469                 return (False);
 470             c = EatWhitespace (InFile);
 471             break;
 472 
 473         case '\\':             /* Bogus backslash. */
 474             c = EatWhitespace (InFile);
 475             break;
 476 
 477         default:               /* Parameter line. */
 478             if (!Parameter (InFile, pfunc, c))
 479                 return (False);
 480             c = EatWhitespace (InFile);
 481             break;
 482         }
 483     }
 484     return (True);
 485 }                               /* Parse */
 486 
 487 static FILE *
 488 OpenConfFile (const char *FileName)
     /* [previous][next][first][last][top][bottom][index][help]  */
 489   /* ------------------------------------------------------------------------ **
 490    * Open a configuration file.
 491    *
 492    *  Input:  FileName  - The pathname of the config file to be opened.
 493    *
 494    *  Output: A pointer of type (FILE *) to the opened file, or NULL if the
 495    *          file could not be opened.
 496    *
 497    * ------------------------------------------------------------------------ **
 498    */
 499 {
 500     FILE *OpenedFile;
 501     const char *func = "params.c:OpenConfFile() -";
 502     extern BOOL in_client;
 503     int lvl = in_client ? 1 : 0;
 504 
 505     if (NULL == FileName || 0 == *FileName)
 506     {
 507         DEBUG (lvl, ("%s No configuration filename specified.\n", func));
 508         return (NULL);
 509     }
 510 
 511     OpenedFile = sys_fopen (FileName, "r");
 512     if (NULL == OpenedFile)
 513     {
 514         DEBUG (lvl,
 515                ("%s Unable to open configuration file \"%s\":\n\t%s\n",
 516                 func, FileName, unix_error_string (errno)));
 517     }
 518 
 519     return (OpenedFile);
 520 }                               /* OpenConfFile */
 521 
 522 BOOL
 523 pm_process (const char *FileName,
     /* [previous][next][first][last][top][bottom][index][help]  */
 524             BOOL (*sfunc) (const char *), BOOL (*pfunc) (const char *, const char *))
 525   /* ------------------------------------------------------------------------ **
 526    * Process the named parameter file.
 527    *
 528    *  Input:  FileName  - The pathname of the parameter file to be opened.
 529    *          sfunc     - A pointer to a function that will be called when
 530    *                      a section name is discovered.
 531    *          pfunc     - A pointer to a function that will be called when
 532    *                      a parameter name and value are discovered.
 533    *
 534    *  Output: TRUE if the file was successfully parsed, else FALSE.
 535    *
 536    * ------------------------------------------------------------------------ **
 537    */
 538 {
 539     int result;
 540     FILE *InFile;
 541     const char *func = "params.c:pm_process() -";
 542 
 543     InFile = OpenConfFile (FileName);   /* Open the config file. */
 544     if (NULL == InFile)
 545         return (False);
 546 
 547     DEBUG (3, ("%s Processing configuration file \"%s\"\n", func, FileName));
 548 
 549     if (NULL != bufr)           /* If we already have a buffer */
 550         result = Parse (InFile, sfunc, pfunc);  /* (recursive call), then just */
 551     /* use it.                     */
 552 
 553     else                        /* If we don't have a buffer   */
 554     {                           /* allocate one, then parse,   */
 555         bSize = BUFR_INC;       /* then free.                  */
 556         bufr = (char *) malloc (bSize);
 557         if (NULL == bufr)
 558         {
 559             DEBUG (0, ("%s memory allocation failure.\n", func));
 560             fclose (InFile);
 561             return (False);
 562         }
 563         result = Parse (InFile, sfunc, pfunc);
 564         free (bufr);
 565         bufr = NULL;
 566         bSize = 0;
 567     }
 568 
 569     fclose (InFile);
 570 
 571     if (!result)                /* Generic failure. */
 572     {
 573         DEBUG (0, ("%s Failed.  Error returned from params.c:parse().\n", func));
 574         return (False);
 575     }
 576 
 577     return (True);              /* Generic success. */
 578 }                               /* pm_process */
 579 
 580 /* -------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */