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