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