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