]> git.sur5r.net Git - openldap/blob - servers/slapd/config.c
992d5ff658593bf4ebb19f14bfd68489630f5920
[openldap] / servers / slapd / config.c
1 /* config.c - configuration file handling routines */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11
12 #include <ac/string.h>
13 #include <ac/ctype.h>
14 #include <ac/socket.h>
15
16 #include "ldap_pvt.h"
17 #include "slap.h"
18
19 #define MAXARGS 200
20
21 /*
22  * defaults for various global variables
23  */
24 int             defsize = SLAPD_DEFAULT_SIZELIMIT;
25 int             deftime = SLAPD_DEFAULT_TIMELIMIT;
26 AccessControl   *global_acl = NULL;
27 slap_access_t           global_default_access = ACL_READ;
28 slap_mask_t             global_restrictops = 0;
29 slap_mask_t             global_allows = 0;
30 slap_mask_t             global_disallows = 0;
31 slap_mask_t             global_requires = 0;
32 slap_ssf_set_t  global_ssf_set;
33 char            *replogfile;
34 int             global_lastmod = ON;
35 int             global_idletimeout = 0;
36 char    *global_host = NULL;
37 char    *global_realm = NULL;
38 char    *global_ucdata_path = NULL;
39 char            *ldap_srvtab = "";
40 char            *default_passwd_hash;
41 char            *default_search_base = NULL;
42 char            *default_search_nbase = NULL;
43
44 char   *slapd_pid_file  = NULL;
45 char   *slapd_args_file = NULL;
46
47 int nSaslRegexp = 0;
48 SaslRegexp_t *SaslRegexp = NULL;
49
50 static char     *fp_getline(FILE *fp, int *lineno);
51 static void     fp_getline_init(int *lineno);
52 static int      fp_parse_line(char *line, int *argcp, char **argv);
53
54 static char     *strtok_quote(char *line, char *sep);
55
56 int
57 read_config( const char *fname )
58 {
59         FILE    *fp;
60         char    *line, *savefname, *saveline;
61         int     cargc, savelineno;
62         char    *cargv[MAXARGS+1];
63         int     lineno, i;
64 #ifdef HAVE_TLS
65         int rc;
66 #endif
67         struct berval *vals[2];
68         struct berval val;
69
70         static BackendInfo *bi = NULL;
71         static BackendDB        *be = NULL;
72
73         vals[0] = &val;
74         vals[1] = NULL;
75
76         if ( (fp = fopen( fname, "r" )) == NULL ) {
77                 ldap_syslog = 1;
78                 Debug( LDAP_DEBUG_ANY,
79                     "could not open config file \"%s\" - absolute path?\n",
80                     fname, 0, 0 );
81                 perror( fname );
82                 return 1;
83         }
84
85         Debug( LDAP_DEBUG_CONFIG, "reading config file %s\n", fname, 0, 0 );
86
87         fp_getline_init( &lineno );
88
89         while ( (line = fp_getline( fp, &lineno )) != NULL ) {
90                 /* skip comments and blank lines */
91                 if ( line[0] == '#' || line[0] == '\0' ) {
92                         continue;
93                 }
94
95                 Debug( LDAP_DEBUG_CONFIG, "line %d (%s)\n", lineno, line, 0 );
96
97                 /* fp_parse_line is destructive, we save a copy */
98                 saveline = ch_strdup( line );
99
100                 if ( fp_parse_line( line, &cargc, cargv ) != 0 ) {
101                         return( 1 );
102                 }
103
104                 if ( cargc < 1 ) {
105                         Debug( LDAP_DEBUG_ANY,
106                             "%s: line %d: bad config line (ignored)\n",
107                             fname, lineno, 0 );
108                         continue;
109                 }
110
111                 if ( strcasecmp( cargv[0], "backend" ) == 0 ) {
112                         if ( cargc < 2 ) {
113                                 Debug( LDAP_DEBUG_ANY,
114                 "%s: line %d: missing type in \"backend <type>\" line\n",
115                                     fname, lineno, 0 );
116                                 return( 1 );
117                         }
118
119                         if( be != NULL ) {
120                                 Debug( LDAP_DEBUG_ANY,
121 "%s: line %d: backend line must appear before any database definition\n",
122                                     fname, lineno, 0 );
123                                 return( 1 );
124                         }
125
126                         bi = backend_info( cargv[1] );
127
128                         if( bi == NULL ) {
129                                 Debug( LDAP_DEBUG_ANY,
130                                         "backend %s initialization failed.\n",
131                                     cargv[1], 0, 0 );
132                                 return( 1 );
133                         }
134
135                 /* start of a new database definition */
136                 } else if ( strcasecmp( cargv[0], "database" ) == 0 ) {
137                         if ( cargc < 2 ) {
138                                 Debug( LDAP_DEBUG_ANY,
139                 "%s: line %d: missing type in \"database <type>\" line\n",
140                                     fname, lineno, 0 );
141                                 return( 1 );
142                         }
143
144                         bi = NULL;
145                         be = backend_db_init( cargv[1] );
146
147                         if( be == NULL ) {
148                                 Debug( LDAP_DEBUG_ANY,
149                                         "database %s initialization failed.\n",
150                                     cargv[1], 0, 0 );
151                                 return( 1 );
152                         }
153
154                 /* set thread concurrency */
155                 } else if ( strcasecmp( cargv[0], "concurrency" ) == 0 ) {
156                         int c;
157                         if ( cargc < 2 ) {
158                                 Debug( LDAP_DEBUG_ANY,
159             "%s: line %d: missing level in \"concurrency <level>\" line\n",
160                                     fname, lineno, 0 );
161                                 return( 1 );
162                         }
163
164                         c = atoi( cargv[1] );
165
166                         if( c < 1 ) {
167                                 Debug( LDAP_DEBUG_ANY,
168             "%s: line %d: invalid level (%d) in \"concurrency <level>\" line\n",
169                                     fname, lineno, c );
170                                 return( 1 );
171                         }
172
173                         ldap_pvt_thread_set_concurrency( c );
174
175                 /* default search base */
176                 } else if ( strcasecmp( cargv[0], "defaultSearchBase" ) == 0 ) {
177                         if ( cargc < 2 ) {
178                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
179                                         "missing dn in \"defaultSearchBase <dn>\" line\n",
180                                         fname, lineno, 0 );
181                                 return 1;
182
183                         } else if ( cargc > 2 ) {
184                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
185                                         "extra cruft after <dn> in \"defaultSearchBase %s\", "
186                                         "line (ignored)\n",
187                                         fname, lineno, cargv[1] );
188                         }
189
190                         if ( bi != NULL || be != NULL ) {
191                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
192                                         "defaultSearchBaase line must appear prior to "
193                                         "any backend or database definition\n",
194                                     fname, lineno, 0 );
195                                 return 1;
196                         }
197
198                         if ( default_search_nbase != NULL ) {
199                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
200                                         "default search base \"%s\" already defined "
201                                         "(discarding old)\n",
202                                         fname, lineno, default_search_base );
203                                 free( default_search_base );
204                                 free( default_search_nbase );
205                         }
206
207                         default_search_base = ch_strdup( cargv[1] );
208                         default_search_nbase = ch_strdup( cargv[1] );
209
210                         if( dn_normalize( default_search_nbase ) == NULL ) {
211                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
212                                         "invalid default search base \"%s\"\n",
213                                         fname, lineno, default_search_base );
214                                 return 1;
215                         }
216                
217                 /* set maximum threads in thread pool */
218                 } else if ( strcasecmp( cargv[0], "threads" ) == 0 ) {
219                         int c;
220                         if ( cargc < 2 ) {
221                                 Debug( LDAP_DEBUG_ANY,
222             "%s: line %d: missing count in \"threads <count>\" line\n",
223                                     fname, lineno, 0 );
224                                 return( 1 );
225                         }
226
227                         c = atoi( cargv[1] );
228
229                         if( c < 0 ) {
230                                 Debug( LDAP_DEBUG_ANY,
231             "%s: line %d: invalid level (%d) in \"threads <count>\" line\n",
232                                     fname, lineno, c );
233                                 return( 1 );
234                         }
235
236                         ldap_pvt_thread_pool_maxthreads( &connection_pool, c );
237
238                 /* get pid file name */
239                 } else if ( strcasecmp( cargv[0], "pidfile" ) == 0 ) {
240                         if ( cargc < 2 ) {
241                                 Debug( LDAP_DEBUG_ANY,
242             "%s: line %d: missing file name in \"pidfile <file>\" line\n",
243                                     fname, lineno, 0 );
244                                 return( 1 );
245                         }
246
247                         slapd_pid_file = ch_strdup( cargv[1] );
248
249                 /* get args file name */
250                 } else if ( strcasecmp( cargv[0], "argsfile" ) == 0 ) {
251                         if ( cargc < 2 ) {
252                                 Debug( LDAP_DEBUG_ANY,
253             "%s: line %d: missing file name in \"argsfile <file>\" line\n",
254                                     fname, lineno, 0 );
255                                 return( 1 );
256                         }
257
258                         slapd_args_file = ch_strdup( cargv[1] );
259
260                 /* default password hash */
261                 } else if ( strcasecmp( cargv[0], "password-hash" ) == 0 ) {
262                         if ( cargc < 2 ) {
263                                 Debug( LDAP_DEBUG_ANY,
264             "%s: line %d: missing hash in \"password-hash <hash>\" line\n",
265                                     fname, lineno, 0 );
266                                 return( 1 );
267                         }
268                         if ( default_passwd_hash != NULL ) {
269                                 Debug( LDAP_DEBUG_ANY,
270                                         "%s: line %d: already set default password_hash!\n",
271                                         fname, lineno, 0 );
272                                 return 1;
273
274                         } else {
275                                 default_passwd_hash = ch_strdup( cargv[1] );
276                         }
277
278                 /* set SASL host */
279                 } else if ( strcasecmp( cargv[0], "sasl-host" ) == 0 ) {
280                         if ( cargc < 2 ) {
281                                 Debug( LDAP_DEBUG_ANY,
282             "%s: line %d: missing host in \"sasl-host <host>\" line\n",
283                                     fname, lineno, 0 );
284                                 return( 1 );
285                         }
286
287                         if ( global_host != NULL ) {
288                                 Debug( LDAP_DEBUG_ANY,
289                                         "%s: line %d: already set sasl-host!\n",
290                                         fname, lineno, 0 );
291                                 return 1;
292
293                         } else {
294                                 global_host = ch_strdup( cargv[1] );
295                         }
296
297                 /* set SASL realm */
298                 } else if ( strcasecmp( cargv[0], "sasl-realm" ) == 0 ) {
299                         if ( cargc < 2 ) {
300                                 Debug( LDAP_DEBUG_ANY,
301             "%s: line %d: missing realm in \"sasl-realm <realm>\" line\n",
302                                     fname, lineno, 0 );
303                                 return( 1 );
304                         }
305
306                         if ( global_realm != NULL ) {
307                                 Debug( LDAP_DEBUG_ANY,
308                                         "%s: line %d: already set sasl-realm!\n",
309                                         fname, lineno, 0 );
310                                 return 1;
311
312                         } else {
313                                 global_realm = ch_strdup( cargv[1] );
314                         }
315
316                 } else if ( !strcasecmp( cargv[0], "saslregexp" ) ) {
317                         int rc;
318                         if ( cargc != 3 ) {
319                                 Debug( LDAP_DEBUG_ANY, 
320                                 "%s: line %d: need 2 args in \"saslregexp <match> <replace>\"\n",
321                                     fname, lineno, 0 );
322                                 return( 1 );
323                         }
324                         rc = slap_sasl_regexp_config( cargv[1], cargv[2] );
325                         if ( rc ) {
326                                 return rc;
327                         }
328
329                 /* SASL security properties */
330                 } else if ( strcasecmp( cargv[0], "sasl-secprops" ) == 0 ) {
331                         char *txt;
332
333                         if ( cargc < 2 ) {
334                                 Debug( LDAP_DEBUG_ANY,
335             "%s: line %d: missing flags in \"sasl-secprops <properties>\" line\n",
336                                     fname, lineno, 0 );
337                                 return 1;
338                         }
339
340                         txt = slap_sasl_secprops( cargv[1] );
341                         if ( txt != NULL ) {
342                                 Debug( LDAP_DEBUG_ANY,
343             "%s: line %d: sasl-secprops: %s\n",
344                                     fname, lineno, txt );
345                                 return 1;
346                         }
347
348                 /* set UCDATA path */
349                 } else if ( strcasecmp( cargv[0], "ucdata-path" ) == 0 ) {
350                         if ( cargc < 2 ) {
351                                 Debug( LDAP_DEBUG_ANY,
352             "%s: line %d: missing path in \"ucdata-path <path>\" line\n",
353                                     fname, lineno, 0 );
354                                 return( 1 );
355                         }
356
357                         if ( global_ucdata_path != NULL ) {
358                                 Debug( LDAP_DEBUG_ANY,
359                                         "%s: line %d: already set ucdata-path!\n",
360                                         fname, lineno, 0 );
361                                 return 1;
362
363                         } else {
364                                 global_ucdata_path = ch_strdup( cargv[1] );
365                         }
366
367                 /* set time limit */
368                 } else if ( strcasecmp( cargv[0], "sizelimit" ) == 0 ) {
369                         if ( cargc < 2 ) {
370                                 Debug( LDAP_DEBUG_ANY,
371             "%s: line %d: missing limit in \"sizelimit <limit>\" line\n",
372                                     fname, lineno, 0 );
373                                 return( 1 );
374                         }
375                         if ( be == NULL ) {
376                                 defsize = atoi( cargv[1] );
377                         } else {
378                                 be->be_sizelimit = atoi( cargv[1] );
379                         }
380
381                 /* set time limit */
382                 } else if ( strcasecmp( cargv[0], "timelimit" ) == 0 ) {
383                         if ( cargc < 2 ) {
384                                 Debug( LDAP_DEBUG_ANY,
385             "%s: line %d: missing limit in \"timelimit <limit>\" line\n",
386                                     fname, lineno, 0 );
387                                 return( 1 );
388                         }
389                         if ( be == NULL ) {
390                                 deftime = atoi( cargv[1] );
391                         } else {
392                                 be->be_timelimit = atoi( cargv[1] );
393                         }
394
395                 /* set database suffix */
396                 } else if ( strcasecmp( cargv[0], "suffix" ) == 0 ) {
397                         Backend *tmp_be;
398                         if ( cargc < 2 ) {
399                                 Debug( LDAP_DEBUG_ANY,
400                     "%s: line %d: missing dn in \"suffix <dn>\" line\n",
401                                     fname, lineno, 0 );
402                                 return( 1 );
403                         } else if ( cargc > 2 ) {
404                                 Debug( LDAP_DEBUG_ANY,
405     "%s: line %d: extra cruft after <dn> in \"suffix %s\" line (ignored)\n",
406                                     fname, lineno, cargv[1] );
407                         }
408                         if ( be == NULL ) {
409                                 Debug( LDAP_DEBUG_ANY,
410 "%s: line %d: suffix line must appear inside a database definition (ignored)\n",
411                                     fname, lineno, 0 );
412                         } else if ( ( tmp_be = select_backend( cargv[1] ) ) == be ) {
413                                 Debug( LDAP_DEBUG_ANY,
414 "%s: line %d: suffix already served by this backend (ignored)\n",
415                                     fname, lineno, 0 );
416                         } else if ( tmp_be  != NULL ) {
417                                 Debug( LDAP_DEBUG_ANY,
418 "%s: line %d: suffix already served by a preceeding backend \"%s\" (ignored)\n",
419                                     fname, lineno, tmp_be->be_suffix[0] );
420                         } else {
421                                 char *dn = ch_strdup( cargv[1] );
422                                 if( dn_validate( dn ) == NULL ) {
423                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
424                                                 "suffix DN invalid \"%s\"\n",
425                                         fname, lineno, cargv[1] );
426                                         return 1;
427
428                                 } else if( *dn == '\0' && default_search_nbase != NULL ) {
429                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
430                                                 "suffix DN empty and default "
431                                                 "search base provided \"%s\" (assuming okay)\n",
432                                         fname, lineno, default_search_base );
433                                 }
434                                 charray_add( &be->be_suffix, dn );
435                                 (void) ldap_pvt_str2upper( dn );
436                                 charray_add( &be->be_nsuffix, dn );
437                                 free( dn );
438                         }
439
440                 /* set database suffixAlias */
441                 } else if ( strcasecmp( cargv[0], "suffixAlias" ) == 0 ) {
442                         Backend *tmp_be;
443                         if ( cargc < 2 ) {
444                                 Debug( LDAP_DEBUG_ANY,
445 "%s: line %d: missing alias and aliased_dn in \"suffixAlias <alias> <aliased_dn>\" line\n",
446                                         fname, lineno, 0 );
447                                 return( 1 );
448                         } else if ( cargc < 3 ) {
449                                 Debug( LDAP_DEBUG_ANY,
450 "%s: line %d: missing aliased_dn in \"suffixAlias <alias> <aliased_dn>\" line\n",
451                                 fname, lineno, 0 );
452                                 return( 1 );
453                         } else if ( cargc > 3 ) {
454                                 Debug( LDAP_DEBUG_ANY,
455                                         "%s: line %d: extra cruft in suffixAlias line (ignored)\n",
456                                 fname, lineno, 0 );
457                         }
458
459                         if ( be == NULL ) {
460                                 Debug( LDAP_DEBUG_ANY,
461                                         "%s: line %d: suffixAlias line"
462                                         " must appear inside a database definition (ignored)\n",
463                                         fname, lineno, 0 );
464                         } else if ( (tmp_be = select_backend( cargv[1] )) != NULL ) {
465                                 Debug( LDAP_DEBUG_ANY,
466                                         "%s: line %d: suffixAlias served by"
467                                         "  a preceeding backend \"%s\" (ignored)\n",
468                                         fname, lineno, tmp_be->be_suffix[0] );
469
470                         } else if ( (tmp_be = select_backend( cargv[2] )) != NULL ) {
471                                 Debug( LDAP_DEBUG_ANY,
472                                         "%s: line %d: suffixAlias derefs to differnet backend"
473                                         "  a preceeding backend \"%s\" (ignored)\n",
474                                         fname, lineno, tmp_be->be_suffix[0] );
475
476                         } else {
477                                 char *alias, *aliased_dn;
478
479                                 alias = ch_strdup( cargv[1] );
480                                 (void) dn_normalize( alias );
481
482                                 aliased_dn = ch_strdup( cargv[2] );
483                                 (void) dn_normalize( aliased_dn );
484
485                                 charray_add( &be->be_suffixAlias, alias );
486                                 charray_add( &be->be_suffixAlias, aliased_dn );
487
488                                 free(alias);
489                                 free(aliased_dn);
490                         }
491
492                /* set max deref depth */
493                } else if ( strcasecmp( cargv[0], "maxDerefDepth" ) == 0 ) {
494                                         int i;
495                        if ( cargc < 2 ) {
496                                Debug( LDAP_DEBUG_ANY,
497                    "%s: line %d: missing depth in \"maxDerefDepth <depth>\" line\n",
498                                    fname, lineno, 0 );
499                                return( 1 );
500                        }
501                        if ( be == NULL ) {
502                                Debug( LDAP_DEBUG_ANY,
503 "%s: line %d: depth line must appear inside a database definition (ignored)\n",
504                                    fname, lineno, 0 );
505                        } else if ((i = atoi(cargv[1])) < 0) {
506                                Debug( LDAP_DEBUG_ANY,
507 "%s: line %d: depth must be positive (ignored)\n",
508                                    fname, lineno, 0 );
509
510                        } else {
511                            be->be_max_deref_depth = i;
512                                            }
513
514
515                 /* set magic "root" dn for this database */
516                 } else if ( strcasecmp( cargv[0], "rootdn" ) == 0 ) {
517                         if ( cargc < 2 ) {
518                                 Debug( LDAP_DEBUG_ANY,
519                     "%s: line %d: missing dn in \"rootdn <dn>\" line\n",
520                                     fname, lineno, 0 );
521                                 return( 1 );
522                         }
523                         if ( be == NULL ) {
524                                 Debug( LDAP_DEBUG_ANY,
525 "%s: line %d: rootdn line must appear inside a database definition (ignored)\n",
526                                     fname, lineno, 0 );
527                         } else {
528                                 be->be_root_dn = ch_strdup( cargv[1] );
529                                 be->be_root_ndn = ch_strdup( cargv[1] );
530
531                                 if( dn_normalize( be->be_root_ndn ) == NULL ) {
532                                         free( be->be_root_dn );
533                                         free( be->be_root_ndn );
534                                         Debug( LDAP_DEBUG_ANY,
535 "%s: line %d: rootdn DN is invalid\n",
536                                            fname, lineno, 0 );
537                                         return( 1 );
538                                 }
539                         }
540
541                 /* set super-secret magic database password */
542                 } else if ( strcasecmp( cargv[0], "rootpw" ) == 0 ) {
543                         if ( cargc < 2 ) {
544                                 Debug( LDAP_DEBUG_ANY,
545             "%s: line %d: missing passwd in \"rootpw <passwd>\" line\n",
546                                     fname, lineno, 0 );
547                                 return( 1 );
548                         }
549                         if ( be == NULL ) {
550                                 Debug( LDAP_DEBUG_ANY,
551 "%s: line %d: rootpw line must appear inside a database definition (ignored)\n",
552                                     fname, lineno, 0 );
553                         } else {
554                                 be->be_root_pw.bv_val = ch_strdup( cargv[1] );
555                                 be->be_root_pw.bv_len = strlen( be->be_root_pw.bv_val );
556                         }
557
558                 /* make this database read-only */
559                 } else if ( strcasecmp( cargv[0], "readonly" ) == 0 ) {
560                         if ( cargc < 2 ) {
561                                 Debug( LDAP_DEBUG_ANY,
562             "%s: line %d: missing on|off in \"readonly <on|off>\" line\n",
563                                     fname, lineno, 0 );
564                                 return( 1 );
565                         }
566                         if ( be == NULL ) {
567                                 if ( strcasecmp( cargv[1], "on" ) == 0 ) {
568                                         global_restrictops |= SLAP_RESTRICT_OP_WRITES;
569                                 } else {
570                                         global_restrictops &= ~SLAP_RESTRICT_OP_WRITES;
571                                 }
572                         } else {
573                                 if ( strcasecmp( cargv[1], "on" ) == 0 ) {
574                                         be->be_restrictops |= SLAP_RESTRICT_OP_WRITES;
575                                 } else {
576                                         be->be_restrictops &= ~SLAP_RESTRICT_OP_WRITES;
577                                 }
578                         }
579
580
581                 /* allow these features */
582                 } else if ( strcasecmp( cargv[0], "allows" ) == 0 ||
583                         strcasecmp( cargv[0], "allow" ) == 0 )
584                 {
585                         slap_mask_t     allows;
586
587                         if ( be != NULL ) {
588                                 Debug( LDAP_DEBUG_ANY,
589 "%s: line %d: allow line must appear prior to database definitions\n",
590                                     fname, lineno, 0 );
591                         }
592
593                         if ( cargc < 2 ) {
594                                 Debug( LDAP_DEBUG_ANY,
595             "%s: line %d: missing feature(s) in \"allow <features>\" line\n",
596                                     fname, lineno, 0 );
597                                 return( 1 );
598                         }
599
600                         allows = 0;
601
602                         for( i=1; i < cargc; i++ ) {
603                                 if( strcasecmp( cargv[i], "tls_2_anon" ) == 0 ) {
604                                         allows |= SLAP_ALLOW_TLS_2_ANON;
605
606                                 } else if( strcasecmp( cargv[i], "none" ) != 0 ) {
607                                         Debug( LDAP_DEBUG_ANY,
608                     "%s: line %d: unknown feature %s in \"allow <features>\" line\n",
609                                             fname, lineno, cargv[i] );
610                                         return( 1 );
611                                 }
612                         }
613
614                         global_allows = allows;
615
616                 /* disallow these features */
617                 } else if ( strcasecmp( cargv[0], "disallows" ) == 0 ||
618                         strcasecmp( cargv[0], "disallow" ) == 0 )
619                 {
620                         slap_mask_t     disallows;
621
622                         if ( be != NULL ) {
623                                 Debug( LDAP_DEBUG_ANY,
624 "%s: line %d: disallow line must appear prior to database definitions\n",
625                                     fname, lineno, 0 );
626                         }
627
628                         if ( cargc < 2 ) {
629                                 Debug( LDAP_DEBUG_ANY,
630             "%s: line %d: missing feature(s) in \"disallow <features>\" line\n",
631                                     fname, lineno, 0 );
632                                 return( 1 );
633                         }
634
635                         disallows = 0;
636
637                         for( i=1; i < cargc; i++ ) {
638                                 if( strcasecmp( cargv[i], "bind_v2" ) == 0 ) {
639                                         disallows |= SLAP_DISALLOW_BIND_V2;
640
641                                 } else if( strcasecmp( cargv[i], "bind_anon" ) == 0 ) {
642                                         disallows |= SLAP_DISALLOW_BIND_ANON;
643
644                                 } else if( strcasecmp( cargv[i], "bind_anon_cred" ) == 0 ) {
645                                         disallows |= SLAP_DISALLOW_BIND_ANON_CRED;
646
647                                 } else if( strcasecmp( cargv[i], "bind_anon_dn" ) == 0 ) {
648                                         disallows |= SLAP_DISALLOW_BIND_ANON_DN;
649
650                                 } else if( strcasecmp( cargv[i], "bind_simple" ) == 0 ) {
651                                         disallows |= SLAP_DISALLOW_BIND_SIMPLE;
652
653                                 } else if( strcasecmp( cargv[i], "bind_krbv4" ) == 0 ) {
654                                         disallows |= SLAP_DISALLOW_BIND_KRBV4;
655
656                                 } else if( strcasecmp( cargv[i], "tls_authc" ) == 0 ) {
657                                         disallows |= SLAP_DISALLOW_TLS_AUTHC;
658
659                                 } else if( strcasecmp( cargv[i], "none" ) != 0 ) {
660                                         Debug( LDAP_DEBUG_ANY,
661                     "%s: line %d: unknown feature %s in \"disallow <features>\" line\n",
662                                             fname, lineno, cargv[i] );
663                                         return( 1 );
664                                 }
665                         }
666
667                         global_disallows = disallows;
668
669                 /* require these features */
670                 } else if ( strcasecmp( cargv[0], "requires" ) == 0 ||
671                         strcasecmp( cargv[0], "require" ) == 0 )
672                 {
673                         slap_mask_t     requires;
674
675                         if ( cargc < 2 ) {
676                                 Debug( LDAP_DEBUG_ANY,
677             "%s: line %d: missing feature(s) in \"require <features>\" line\n",
678                                     fname, lineno, 0 );
679                                 return( 1 );
680                         }
681
682                         requires = 0;
683
684                         for( i=1; i < cargc; i++ ) {
685                                 if( strcasecmp( cargv[i], "bind" ) == 0 ) {
686                                         requires |= SLAP_REQUIRE_BIND;
687
688                                 } else if( strcasecmp( cargv[i], "LDAPv3" ) == 0 ) {
689                                         requires |= SLAP_REQUIRE_LDAP_V3;
690
691                                 } else if( strcasecmp( cargv[i], "authc" ) == 0 ) {
692                                         requires |= SLAP_REQUIRE_AUTHC;
693
694                                 } else if( strcasecmp( cargv[i], "SASL" ) == 0 ) {
695                                         requires |= SLAP_REQUIRE_SASL;
696
697                                 } else if( strcasecmp( cargv[i], "strong" ) == 0 ) {
698                                         requires |= SLAP_REQUIRE_STRONG;
699
700                                 } else if( strcasecmp( cargv[i], "none" ) != 0 ) {
701                                         Debug( LDAP_DEBUG_ANY,
702                     "%s: line %d: unknown feature %s in \"require <features>\" line\n",
703                                             fname, lineno, cargv[i] );
704                                         return( 1 );
705                                 }
706                         }
707
708                         if ( be == NULL ) {
709                                 global_requires = requires;
710                         } else {
711                                 be->be_requires = requires;
712                         }
713
714                 /* required security factors */
715                 } else if ( strcasecmp( cargv[0], "security" ) == 0 ) {
716                         slap_ssf_set_t *set;
717
718                         if ( cargc < 2 ) {
719                                 Debug( LDAP_DEBUG_ANY,
720             "%s: line %d: missing factor(s) in \"security <factors>\" line\n",
721                                     fname, lineno, 0 );
722                                 return( 1 );
723                         }
724
725                         if ( be == NULL ) {
726                                 set = &global_ssf_set;
727                         } else {
728                                 set = &be->be_ssf_set;
729                         }
730
731                         for( i=1; i < cargc; i++ ) {
732                                 if( strncasecmp( cargv[i], "ssf=",
733                                         sizeof("ssf") ) == 0 )
734                                 {
735                                         set->sss_ssf =
736                                                 atoi( &cargv[i][sizeof("ssf")] );
737
738                                 } else if( strncasecmp( cargv[i], "transport=",
739                                         sizeof("transport") ) == 0 )
740                                 {
741                                         set->sss_transport =
742                                                 atoi( &cargv[i][sizeof("transport")] );
743
744                                 } else if( strncasecmp( cargv[i], "tls=",
745                                         sizeof("tls") ) == 0 )
746                                 {
747                                         set->sss_tls =
748                                                 atoi( &cargv[i][sizeof("tls")] );
749
750                                 } else if( strncasecmp( cargv[i], "sasl=",
751                                         sizeof("sasl") ) == 0 )
752                                 {
753                                         set->sss_sasl =
754                                                 atoi( &cargv[i][sizeof("sasl")] );
755
756                                 } else if( strncasecmp( cargv[i], "update_ssf=",
757                                         sizeof("update_ssf") ) == 0 )
758                                 {
759                                         set->sss_update_ssf =
760                                                 atoi( &cargv[i][sizeof("update_ssf")] );
761
762                                 } else if( strncasecmp( cargv[i], "update_transport=",
763                                         sizeof("update_transport") ) == 0 )
764                                 {
765                                         set->sss_update_transport =
766                                                 atoi( &cargv[i][sizeof("update_transport")] );
767
768                                 } else if( strncasecmp( cargv[i], "update_tls=",
769                                         sizeof("update_tls") ) == 0 )
770                                 {
771                                         set->sss_update_tls =
772                                                 atoi( &cargv[i][sizeof("update_tls")] );
773
774                                 } else if( strncasecmp( cargv[i], "update_sasl=",
775                                         sizeof("update_sasl") ) == 0 )
776                                 {
777                                         set->sss_update_sasl =
778                                                 atoi( &cargv[i][sizeof("update_sasl")] );
779
780                                 } else {
781                                         Debug( LDAP_DEBUG_ANY,
782                     "%s: line %d: unknown factor %s in \"security <factors>\" line\n",
783                                             fname, lineno, cargv[i] );
784                                         return( 1 );
785                                 }
786                         }
787
788                 
789                 /* where to send clients when we don't hold it */
790                 } else if ( strcasecmp( cargv[0], "referral" ) == 0 ) {
791                         if ( cargc < 2 ) {
792                                 Debug( LDAP_DEBUG_ANY,
793                     "%s: line %d: missing URL in \"referral <URL>\" line\n",
794                                     fname, lineno, 0 );
795                                 return( 1 );
796                         }
797
798                         vals[0]->bv_val = cargv[1];
799                         vals[0]->bv_len = strlen( vals[0]->bv_val );
800                         value_add( &default_referral, vals );
801
802                 /* specify an Object Identifier macro */
803                 } else if ( strcasecmp( cargv[0], "objectidentifier" ) == 0 ) {
804                         parse_oidm( fname, lineno, cargc, cargv );
805
806                 /* specify an objectclass */
807                 } else if ( strcasecmp( cargv[0], "objectclass" ) == 0 ) {
808                         if ( *cargv[1] == '(' ) {
809                                 char * p;
810                                 p = strchr(saveline,'(');
811                                 parse_oc( fname, lineno, p, cargv );
812                         } else {
813                                 Debug( LDAP_DEBUG_ANY,
814     "%s: line %d: old objectclass format not supported.\n",
815                                     fname, lineno, 0 );
816                         }
817
818                 /* specify an attribute type */
819                 } else if (( strcasecmp( cargv[0], "attributetype" ) == 0 )
820                         || ( strcasecmp( cargv[0], "attribute" ) == 0 ))
821                 {
822                         if ( *cargv[1] == '(' ) {
823                                 char * p;
824                                 p = strchr(saveline,'(');
825                                 parse_at( fname, lineno, p, cargv );
826                         } else {
827                                 Debug( LDAP_DEBUG_ANY,
828     "%s: line %d: old attribute type format not supported.\n",
829                                     fname, lineno, 0 );
830                         }
831
832                 /* turn on/off schema checking */
833                 } else if ( strcasecmp( cargv[0], "schemacheck" ) == 0 ) {
834                         if ( cargc < 2 ) {
835                                 Debug( LDAP_DEBUG_ANY,
836     "%s: line %d: missing on|off in \"schemacheck <on|off>\" line\n",
837                                     fname, lineno, 0 );
838                                 return( 1 );
839                         }
840                         if ( strcasecmp( cargv[1], "off" ) == 0 ) {
841                                 global_schemacheck = 0;
842                         } else {
843                                 global_schemacheck = 1;
844                         }
845
846                 /* specify access control info */
847                 } else if ( strcasecmp( cargv[0], "access" ) == 0 ) {
848                         parse_acl( be, fname, lineno, cargc, cargv );
849
850                 /* specify default access control info */
851                 } else if ( strcasecmp( cargv[0], "defaultaccess" ) == 0 ) {
852                         slap_access_t access;
853
854                         if ( cargc < 2 ) {
855                                 Debug( LDAP_DEBUG_ANY,
856             "%s: line %d: missing limit in \"defaultaccess <access>\" line\n",
857                                     fname, lineno, 0 );
858                                 return( 1 );
859                         }
860
861                         access = str2access( cargv[1] );
862
863                         if ( access == ACL_INVALID_ACCESS ) {
864                                 Debug( LDAP_DEBUG_ANY,
865                                         "%s: line %d: bad access level \"%s\", "
866                                         "expecting none|auth|compare|search|read|write\n",
867                                     fname, lineno, cargv[1] );
868                                 return( 1 );
869                         }
870
871                         if ( be == NULL ) {
872                                 global_default_access = access;
873                         } else {
874                                 be->be_dfltaccess = access;
875                         }
876
877                 /* debug level to log things to syslog */
878                 } else if ( strcasecmp( cargv[0], "loglevel" ) == 0 ) {
879                         if ( cargc < 2 ) {
880                                 Debug( LDAP_DEBUG_ANY,
881                     "%s: line %d: missing level in \"loglevel <level>\" line\n",
882                                     fname, lineno, 0 );
883                                 return( 1 );
884                         }
885
886                         ldap_syslog = 0;
887
888                         for( i=1; i < cargc; i++ ) {
889                                 ldap_syslog += atoi( cargv[1] );
890                         }
891
892                 /* list of replicas of the data in this backend (master only) */
893                 } else if ( strcasecmp( cargv[0], "replica" ) == 0 ) {
894                         if ( cargc < 2 ) {
895                                 Debug( LDAP_DEBUG_ANY,
896             "%s: line %d: missing host in \"replica <host[:port]>\" line\n",
897                                     fname, lineno, 0 );
898                                 return( 1 );
899                         }
900                         if ( be == NULL ) {
901                                 Debug( LDAP_DEBUG_ANY,
902 "%s: line %d: replica line must appear inside a database definition (ignored)\n",
903                                     fname, lineno, 0 );
904                         } else {
905                                 for ( i = 1; i < cargc; i++ ) {
906                                         if ( strncasecmp( cargv[i], "host=", 5 )
907                                             == 0 ) {
908                                                 charray_add( &be->be_replica,
909                                                              cargv[i] + 5 );
910                                                 break;
911                                         }
912                                 }
913                                 if ( i == cargc ) {
914                                         Debug( LDAP_DEBUG_ANY,
915                     "%s: line %d: missing host in \"replica\" line (ignored)\n",
916                                             fname, lineno, 0 );
917                                 }
918                         }
919
920                 /* dn of master entity allowed to write to replica */
921                 } else if ( strcasecmp( cargv[0], "updatedn" ) == 0 ) {
922                         if ( cargc < 2 ) {
923                                 Debug( LDAP_DEBUG_ANY,
924                     "%s: line %d: missing dn in \"updatedn <dn>\" line\n",
925                                     fname, lineno, 0 );
926                                 return( 1 );
927                         }
928                         if ( be == NULL ) {
929                                 Debug( LDAP_DEBUG_ANY,
930 "%s: line %d: updatedn line must appear inside a database definition (ignored)\n",
931                                     fname, lineno, 0 );
932                         } else {
933                                 be->be_update_ndn = ch_strdup( cargv[1] );
934                                 if( dn_normalize( be->be_update_ndn ) == NULL ) {
935                                         Debug( LDAP_DEBUG_ANY,
936 "%s: line %d: updatedn DN is invalid\n",
937                                             fname, lineno, 0 );
938                                         return 1;
939                                 }
940                         }
941
942                 } else if ( strcasecmp( cargv[0], "updateref" ) == 0 ) {
943                         if ( cargc < 2 ) {
944                                 Debug( LDAP_DEBUG_ANY,
945                     "%s: line %d: missing dn in \"updateref <ldapurl>\" line\n",
946                                     fname, lineno, 0 );
947                                 return( 1 );
948                         }
949                         if ( be == NULL ) {
950                                 Debug( LDAP_DEBUG_ANY,
951 "%s: line %d: updateref line must appear inside a database definition (ignored)\n",
952                                     fname, lineno, 0 );
953                         } else if ( be->be_update_ndn == NULL ) {
954                                 Debug( LDAP_DEBUG_ANY,
955 "%s: line %d: updateref line must after updatedn (ignored)\n",
956                                     fname, lineno, 0 );
957                         } else {
958                                 vals[0]->bv_val = cargv[1];
959                                 vals[0]->bv_len = strlen( vals[0]->bv_val );
960                                 value_add( &be->be_update_refs, vals );
961                         }
962
963                 /* replication log file to which changes are appended */
964                 } else if ( strcasecmp( cargv[0], "replogfile" ) == 0 ) {
965                         if ( cargc < 2 ) {
966                                 Debug( LDAP_DEBUG_ANY,
967             "%s: line %d: missing dn in \"replogfile <filename>\" line\n",
968                                     fname, lineno, 0 );
969                                 return( 1 );
970                         }
971                         if ( be ) {
972                                 be->be_replogfile = ch_strdup( cargv[1] );
973                         } else {
974                                 replogfile = ch_strdup( cargv[1] );
975                         }
976
977                 /* maintain lastmodified{by,time} attributes */
978                 } else if ( strcasecmp( cargv[0], "lastmod" ) == 0 ) {
979                         if ( cargc < 2 ) {
980                                 Debug( LDAP_DEBUG_ANY,
981             "%s: line %d: missing on|off in \"lastmod <on|off>\" line\n",
982                                     fname, lineno, 0 );
983                                 return( 1 );
984                         }
985                         if ( strcasecmp( cargv[1], "on" ) == 0 ) {
986                                 if ( be )
987                                         be->be_lastmod = ON;
988                                 else
989                                         global_lastmod = ON;
990                         } else {
991                                 if ( be )
992                                         be->be_lastmod = OFF;
993                                 else
994                                         global_lastmod = OFF;
995                         }
996
997                 /* set idle timeout value */
998                 } else if ( strcasecmp( cargv[0], "idletimeout" ) == 0 ) {
999                         int i;
1000                         if ( cargc < 2 ) {
1001                                 Debug( LDAP_DEBUG_ANY,
1002             "%s: line %d: missing timeout value in \"idletimeout <seconds>\" line\n",
1003                                     fname, lineno, 0 );
1004                                 return( 1 );
1005                         }
1006
1007                         i = atoi( cargv[1] );
1008
1009                         if( i < 0 ) {
1010                                 Debug( LDAP_DEBUG_ANY,
1011             "%s: line %d: timeout value (%d) invalid \"idletimeout <seconds>\" line\n",
1012                                     fname, lineno, i );
1013                                 return( 1 );
1014                         }
1015
1016                         global_idletimeout = i;
1017
1018                 /* include another config file */
1019                 } else if ( strcasecmp( cargv[0], "include" ) == 0 ) {
1020                         if ( cargc < 2 ) {
1021                                 Debug( LDAP_DEBUG_ANY,
1022     "%s: line %d: missing filename in \"include <filename>\" line\n",
1023                                     fname, lineno, 0 );
1024                                 return( 1 );
1025                         }
1026                         savefname = ch_strdup( cargv[1] );
1027                         savelineno = lineno;
1028
1029                         if ( read_config( savefname ) != 0 ) {
1030                                 return( 1 );
1031                         }
1032
1033                         free( savefname );
1034                         lineno = savelineno - 1;
1035
1036                 /* location of kerberos srvtab file */
1037                 } else if ( strcasecmp( cargv[0], "srvtab" ) == 0 ) {
1038                         if ( cargc < 2 ) {
1039                                 Debug( LDAP_DEBUG_ANY,
1040             "%s: line %d: missing filename in \"srvtab <filename>\" line\n",
1041                                     fname, lineno, 0 );
1042                                 return( 1 );
1043                         }
1044                         ldap_srvtab = ch_strdup( cargv[1] );
1045
1046 #ifdef SLAPD_MODULES
1047                 } else if (strcasecmp( cargv[0], "moduleload") == 0 ) {
1048                    if ( cargc < 2 ) {
1049                       Debug( LDAP_DEBUG_ANY,
1050                              "%s: line %d: missing filename in \"moduleload <filename>\" line\n",
1051                              fname, lineno, 0 );
1052                       exit( EXIT_FAILURE );
1053                    }
1054                    if (module_load(cargv[1], cargc - 2, (cargc > 2) ? cargv + 2 : NULL)) {
1055                       Debug( LDAP_DEBUG_ANY,
1056                              "%s: line %d: failed to load or initialize module %s\n",
1057                              fname, lineno, cargv[1]);
1058                       exit( EXIT_FAILURE );
1059                    }
1060                 } else if (strcasecmp( cargv[0], "modulepath") == 0 ) {
1061                    if ( cargc != 2 ) {
1062                       Debug( LDAP_DEBUG_ANY,
1063                              "%s: line %d: missing path in \"modulepath <path>\" line\n",
1064                              fname, lineno, 0 );
1065                       exit( EXIT_FAILURE );
1066                    }
1067                    if (module_path( cargv[1] )) {
1068                       Debug( LDAP_DEBUG_ANY,
1069                              "%s: line %d: failed to set module search path to %s\n",
1070                              fname, lineno, cargv[1]);
1071                       exit( EXIT_FAILURE );
1072                    }
1073                    
1074 #endif /*SLAPD_MODULES*/
1075
1076 #ifdef HAVE_TLS
1077                 } else if ( !strcasecmp( cargv[0], "TLSProtocol" ) ) {
1078                         rc = ldap_pvt_tls_set_option( NULL,
1079                                                       LDAP_OPT_X_TLS_PROTOCOL,
1080                                                       cargv[1] );
1081                         if ( rc )
1082                                 return rc;
1083
1084                 } else if ( !strcasecmp( cargv[0], "TLSCipherSuite" ) ) {
1085                         rc = ldap_pvt_tls_set_option( NULL,
1086                                                       LDAP_OPT_X_TLS_CIPHER_SUITE,
1087                                                       cargv[1] );
1088                         if ( rc )
1089                                 return rc;
1090
1091                 } else if ( !strcasecmp( cargv[0], "TLSCertificateFile" ) ) {
1092                         rc = ldap_pvt_tls_set_option( NULL,
1093                                                       LDAP_OPT_X_TLS_CERTFILE,
1094                                                       cargv[1] );
1095                         if ( rc )
1096                                 return rc;
1097
1098                 } else if ( !strcasecmp( cargv[0], "TLSCertificateKeyFile" ) ) {
1099                         rc = ldap_pvt_tls_set_option( NULL,
1100                                                       LDAP_OPT_X_TLS_KEYFILE,
1101                                                       cargv[1] );
1102                         if ( rc )
1103                                 return rc;
1104
1105                 } else if ( !strcasecmp( cargv[0], "TLSCACertificatePath" ) ) {
1106                         rc = ldap_pvt_tls_set_option( NULL,
1107                                                       LDAP_OPT_X_TLS_CACERTDIR,
1108                                                       cargv[1] );
1109                         if ( rc )
1110                                 return rc;
1111
1112                 } else if ( !strcasecmp( cargv[0], "TLSCACertificateFile" ) ) {
1113                         rc = ldap_pvt_tls_set_option( NULL,
1114                                                       LDAP_OPT_X_TLS_CACERTFILE,
1115                                                       cargv[1] );
1116                         if ( rc )
1117                                 return rc;
1118                 } else if ( !strcasecmp( cargv[0], "TLSVerifyClient" ) ) {
1119                         rc = ldap_pvt_tls_set_option( NULL,
1120                                                       LDAP_OPT_X_TLS_REQUIRE_CERT,
1121                                                       cargv[1] );
1122                         if ( rc )
1123                                 return rc;
1124
1125 #endif
1126
1127                 /* pass anything else to the current backend info/db config routine */
1128                 } else {
1129                         if ( bi != NULL ) {
1130                                 if ( bi->bi_config == 0 ) {
1131                                         Debug( LDAP_DEBUG_ANY,
1132 "%s: line %d: unknown directive \"%s\" inside backend info definition (ignored)\n",
1133                                                 fname, lineno, cargv[0] );
1134                                 } else {
1135                                         if ( (*bi->bi_config)( bi, fname, lineno, cargc, cargv )
1136                                                 != 0 )
1137                                         {
1138                                                 return( 1 );
1139                                         }
1140                                 }
1141                         } else if ( be != NULL ) {
1142                                 if ( be->be_config == 0 ) {
1143                                         Debug( LDAP_DEBUG_ANY,
1144 "%s: line %d: unknown directive \"%s\" inside backend database definition (ignored)\n",
1145                                         fname, lineno, cargv[0] );
1146                                 } else {
1147                                         if ( (*be->be_config)( be, fname, lineno, cargc, cargv )
1148                                                 != 0 )
1149                                         {
1150                                                 return( 1 );
1151                                         }
1152                                 }
1153                         } else {
1154                                 Debug( LDAP_DEBUG_ANY,
1155 "%s: line %d: unknown directive \"%s\" outside backend info and database definitions (ignored)\n",
1156                                     fname, lineno, cargv[0] );
1157                         }
1158                 }
1159                 free( saveline );
1160         }
1161         fclose( fp );
1162         return( 0 );
1163 }
1164
1165 static int
1166 fp_parse_line(
1167     char        *line,
1168     int         *argcp,
1169     char        **argv
1170 )
1171 {
1172         char *  token;
1173
1174         *argcp = 0;
1175         for ( token = strtok_quote( line, " \t" ); token != NULL;
1176             token = strtok_quote( NULL, " \t" ) ) {
1177                 if ( *argcp == MAXARGS ) {
1178                         Debug( LDAP_DEBUG_ANY, "Too many tokens (max %d)\n",
1179                             MAXARGS, 0, 0 );
1180                         return( 1 );
1181                 }
1182                 argv[(*argcp)++] = token;
1183         }
1184         argv[*argcp] = NULL;
1185         return 0;
1186 }
1187
1188 static char *
1189 strtok_quote( char *line, char *sep )
1190 {
1191         int             inquote;
1192         char            *tmp;
1193         static char     *next;
1194
1195         if ( line != NULL ) {
1196                 next = line;
1197         }
1198         while ( *next && strchr( sep, *next ) ) {
1199                 next++;
1200         }
1201
1202         if ( *next == '\0' ) {
1203                 next = NULL;
1204                 return( NULL );
1205         }
1206         tmp = next;
1207
1208         for ( inquote = 0; *next; ) {
1209                 switch ( *next ) {
1210                 case '"':
1211                         if ( inquote ) {
1212                                 inquote = 0;
1213                         } else {
1214                                 inquote = 1;
1215                         }
1216                         AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
1217                         break;
1218
1219                 case '\\':
1220                         if ( next[1] )
1221                                 AC_MEMCPY( next,
1222                                             next + 1, strlen( next + 1 ) + 1 );
1223                         next++;         /* dont parse the escaped character */
1224                         break;
1225
1226                 default:
1227                         if ( ! inquote ) {
1228                                 if ( strchr( sep, *next ) != NULL ) {
1229                                         *next++ = '\0';
1230                                         return( tmp );
1231                                 }
1232                         }
1233                         next++;
1234                         break;
1235                 }
1236         }
1237
1238         return( tmp );
1239 }
1240
1241 static char     buf[BUFSIZ];
1242 static char     *line;
1243 static int      lmax, lcur;
1244
1245 #define CATLINE( buf )  { \
1246         int     len; \
1247         len = strlen( buf ); \
1248         while ( lcur + len + 1 > lmax ) { \
1249                 lmax += BUFSIZ; \
1250                 line = (char *) ch_realloc( line, lmax ); \
1251         } \
1252         strcpy( line + lcur, buf ); \
1253         lcur += len; \
1254 }
1255
1256 static char *
1257 fp_getline( FILE *fp, int *lineno )
1258 {
1259         char            *p;
1260
1261         lcur = 0;
1262         CATLINE( buf );
1263         (*lineno)++;
1264
1265         /* hack attack - keeps us from having to keep a stack of bufs... */
1266         if ( strncasecmp( line, "include", 7 ) == 0 ) {
1267                 buf[0] = '\0';
1268                 return( line );
1269         }
1270
1271         while ( fgets( buf, sizeof(buf), fp ) != NULL ) {
1272                 if ( (p = strchr( buf, '\n' )) != NULL ) {
1273                         *p = '\0';
1274                 }
1275                 if ( ! isspace( (unsigned char) buf[0] ) ) {
1276                         return( line );
1277                 }
1278
1279                 CATLINE( buf );
1280                 (*lineno)++;
1281         }
1282         buf[0] = '\0';
1283
1284         return( line[0] ? line : NULL );
1285 }
1286
1287 static void
1288 fp_getline_init( int *lineno )
1289 {
1290         *lineno = -1;
1291         buf[0] = '\0';
1292 }