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