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