]> git.sur5r.net Git - openldap/blob - servers/slapd/config.c
1fc3cf52ea18e5ad1424516b88f947e918ffd72a
[openldap] / servers / slapd / config.c
1 /* config.c - configuration file handling routines */
2
3 #include "portable.h"
4
5 #include <stdio.h>
6 #ifdef HAVE_LOCALE_H
7 #include <locale.h>
8 #endif
9
10 #include <ac/string.h>
11 #include <ac/ctype.h>
12 #include <ac/socket.h>
13
14 #include "ldap_defaults.h"
15 #include "slap.h"
16
17 #define MAXARGS 100
18
19 /*
20  * defaults for various global variables
21  */
22 int             defsize = SLAPD_DEFAULT_SIZELIMIT;
23 int             deftime = SLAPD_DEFAULT_TIMELIMIT;
24 struct acl      *global_acl = NULL;
25 int             global_default_access = ACL_READ;
26 char            *replogfile;
27 int             global_lastmod;
28 char            *ldap_srvtab = "";
29
30 char   *slapd_pid_file  = NULL;
31 char   *slapd_args_file = NULL;
32
33 static char     *fp_getline(FILE *fp, int *lineno);
34 static void     fp_getline_init(int *lineno);
35 static int      fp_parse_line(char *line, int *argcp, char **argv);
36
37 static char     *strtok_quote(char *line, char *sep);
38
39 int
40 read_config( char *fname )
41 {
42         FILE    *fp;
43         char    *line, *savefname, *saveline;
44         int     cargc, savelineno;
45         char    *cargv[MAXARGS];
46         int     lineno, i;
47
48         static BackendInfo *bi = NULL;
49         static BackendDB        *be = NULL;
50
51         if ( (fp = fopen( fname, "r" )) == NULL ) {
52                 ldap_syslog = 1;
53                 Debug( LDAP_DEBUG_ANY,
54                     "could not open config file \"%s\" - absolute path?\n",
55                     fname, 0, 0 );
56                 perror( fname );
57                 return 1;
58         }
59
60         Debug( LDAP_DEBUG_CONFIG, "reading config file %s\n", fname, 0, 0 );
61
62         if ( schema_init( ) != 0 ) {
63                 Debug( LDAP_DEBUG_ANY,
64                     "error initializing the schema\n",
65                     0, 0, 0 );
66                 return( 1 );
67         }
68
69         fp_getline_init( &lineno );
70
71         while ( (line = fp_getline( fp, &lineno )) != NULL ) {
72                 /* skip comments and blank lines */
73                 if ( line[0] == '#' || line[0] == '\0' ) {
74                         continue;
75                 }
76
77                 Debug( LDAP_DEBUG_CONFIG, "line %d (%s)\n", lineno, line, 0 );
78
79                 /* fp_parse_line is destructive, we save a copy */
80                 saveline = ch_strdup( line );
81
82                 if ( fp_parse_line( line, &cargc, cargv ) != 0 ) {
83                         return( 1 );
84                 }
85
86                 if ( cargc < 1 ) {
87                         Debug( LDAP_DEBUG_ANY,
88                             "%s: line %d: bad config line (ignored)\n",
89                             fname, lineno, 0 );
90                         continue;
91                 }
92
93                 if ( strcasecmp( cargv[0], "backend" ) == 0 ) {
94                         if ( cargc < 2 ) {
95                                 Debug( LDAP_DEBUG_ANY,
96                 "%s: line %d: missing type in \"backend <type>\" line\n",
97                                     fname, lineno, 0 );
98                                 return( 1 );
99                         }
100
101                         if( be != NULL ) {
102                                 Debug( LDAP_DEBUG_ANY,
103 "%s: line %d: backend line must appear before any database definition\n",
104                                     fname, lineno, 0 );
105                                 return( 1 );
106                         }
107
108                         bi = backend_info( cargv[1] );
109
110                 /* start of a new database definition */
111                 } else if ( strcasecmp( cargv[0], "database" ) == 0 ) {
112                         if ( cargc < 2 ) {
113                                 Debug( LDAP_DEBUG_ANY,
114                 "%s: line %d: missing type in \"database <type>\" line\n",
115                                     fname, lineno, 0 );
116                                 return( 1 );
117                         }
118                         bi = NULL;
119                         be = backend_db_init( cargv[1] );
120
121                 /* assign a default depth limit for alias deref */
122                 be->be_maxDerefDepth = SLAPD_DEFAULT_MAXDEREFDEPTH; 
123
124                 /* get pid file name */
125                 } else if ( strcasecmp( cargv[0], "pidfile" ) == 0 ) {
126                         if ( cargc < 2 ) {
127                                 Debug( LDAP_DEBUG_ANY,
128             "%s: line %d: missing file name in \"pidfile <file>\" line\n",
129                                     fname, lineno, 0 );
130                                 return( 1 );
131                         }
132
133                         slapd_pid_file = ch_strdup( cargv[1] );
134
135                 /* get args file name */
136                 } else if ( strcasecmp( cargv[0], "argsfile" ) == 0 ) {
137                         if ( cargc < 2 ) {
138                                 Debug( LDAP_DEBUG_ANY,
139             "%s: line %d: missing file name in \"argsfile <file>\" line\n",
140                                     fname, lineno, 0 );
141                                 return( 1 );
142                         }
143
144                         slapd_args_file = ch_strdup( cargv[1] );
145
146                 /* set size limit */
147                 } else if ( strcasecmp( cargv[0], "sizelimit" ) == 0 ) {
148                         if ( cargc < 2 ) {
149                                 Debug( LDAP_DEBUG_ANY,
150             "%s: line %d: missing limit in \"sizelimit <limit>\" line\n",
151                                     fname, lineno, 0 );
152                                 return( 1 );
153                         }
154                         if ( be == NULL ) {
155                                 defsize = atoi( cargv[1] );
156                         } else {
157                                 be->be_sizelimit = atoi( cargv[1] );
158                         }
159
160                 /* set time limit */
161                 } else if ( strcasecmp( cargv[0], "timelimit" ) == 0 ) {
162                         if ( cargc < 2 ) {
163                                 Debug( LDAP_DEBUG_ANY,
164             "%s: line %d: missing limit in \"timelimit <limit>\" line\n",
165                                     fname, lineno, 0 );
166                                 return( 1 );
167                         }
168                         if ( be == NULL ) {
169                                 deftime = atoi( cargv[1] );
170                         } else {
171                                 be->be_timelimit = atoi( cargv[1] );
172                         }
173
174                 /* set database suffix */
175                 } else if ( strcasecmp( cargv[0], "suffix" ) == 0 ) {
176                         if ( cargc < 2 ) {
177                                 Debug( LDAP_DEBUG_ANY,
178                     "%s: line %d: missing dn in \"suffix <dn>\" line\n",
179                                     fname, lineno, 0 );
180                                 return( 1 );
181                         } else if ( cargc > 2 ) {
182                                 Debug( LDAP_DEBUG_ANY,
183     "%s: line %d: extra cruft after <dn> in \"suffix %s\" line (ignored)\n",
184                                     fname, lineno, cargv[1] );
185                         }
186                         if ( be == NULL ) {
187                                 Debug( LDAP_DEBUG_ANY,
188 "%s: line %d: suffix line must appear inside a database definition (ignored)\n",
189                                     fname, lineno, 0 );
190                         } else {
191                                 char *dn = ch_strdup( cargv[1] );
192                                 (void) dn_normalize( dn );
193                                 charray_add( &be->be_suffix, dn );
194                                 (void) dn_upcase( dn );
195                                 charray_add( &be->be_nsuffix, dn );
196                                 free( dn );
197                         }
198
199                 /* set database suffixAlias */
200                 } else if ( strcasecmp( cargv[0], "suffixAlias" ) == 0 ) {
201                         if ( cargc < 2 ) {
202                                 Debug( LDAP_DEBUG_ANY,
203                     "%s: line %d: missing alias and aliased_dn in \"suffixAlias <alias> <aliased_dn>\" line\n",
204                                     fname, lineno, 0 );
205                                 return( 1 );
206                         } else if ( cargc < 3 ) {
207                                 Debug( LDAP_DEBUG_ANY,
208                     "%s: line %d: missing aliased_dn in \"suffixAlias <alias> <aliased_dn>\" line\n",
209                                     fname, lineno, 0 );
210                                 return( 1 );
211                         } else if ( cargc > 3 ) {
212                                 Debug( LDAP_DEBUG_ANY,
213     "%s: line %d: extra cruft in suffixAlias line (ignored)\n",
214                                     fname, lineno, 0 );
215                         }
216                         if ( be == NULL ) {
217                                 Debug( LDAP_DEBUG_ANY,
218 "%s: line %d: suffixAlias line must appear inside a database definition (ignored)\n",
219                                     fname, lineno, 0 );
220                         } else {
221                                 char *alias, *aliased_dn;
222
223                                                                 alias = ch_strdup( cargv[1] );
224                                 (void) dn_normalize( alias );
225
226                                 aliased_dn = ch_strdup( cargv[2] );
227                                 (void) dn_normalize( aliased_dn );
228
229
230                                                                 if ( strcasecmp( alias, aliased_dn) == 0 ) {
231                                         Debug( LDAP_DEBUG_ANY,
232 "%s: line %d: suffixAlias %s is not different from aliased dn (ignored)\n",
233                                     fname, lineno, alias );
234                                                                 } else {
235                                         (void) dn_normalize_case( alias );
236                                         (void) dn_normalize_case( aliased_dn );
237                                         charray_add( &be->be_suffixAlias, alias );
238                                         charray_add( &be->be_suffixAlias, aliased_dn );
239                                                                 }
240
241                                                                 free(alias);
242                                                                 free(aliased_dn);
243                         }
244
245                /* set max deref depth */
246                } else if ( strcasecmp( cargv[0], "maxDerefDepth" ) == 0 ) {
247                        if ( cargc < 2 ) {
248                                Debug( LDAP_DEBUG_ANY,
249                    "%s: line %d: missing depth in \"maxDerefDepth <depth>\" line\n",
250                                    fname, lineno, 0 );
251                                return( 1 );
252                        }
253                        if ( be == NULL ) {
254                                Debug( LDAP_DEBUG_ANY,
255 "%s: line %d: depth line must appear inside a database definition (ignored)\n",
256                                    fname, lineno, 0 );
257                        } else {
258                            be->be_maxDerefDepth = atoi (cargv[1]);
259                        }
260
261
262                 /* set magic "root" dn for this database */
263                 } else if ( strcasecmp( cargv[0], "rootdn" ) == 0 ) {
264                         if ( cargc < 2 ) {
265                                 Debug( LDAP_DEBUG_ANY,
266                     "%s: line %d: missing dn in \"rootdn <dn>\" line\n",
267                                     fname, lineno, 0 );
268                                 return( 1 );
269                         }
270                         if ( be == NULL ) {
271                                 Debug( LDAP_DEBUG_ANY,
272 "%s: line %d: rootdn line must appear inside a database definition (ignored)\n",
273                                     fname, lineno, 0 );
274                         } else {
275                                 be->be_root_dn = ch_strdup( cargv[1] );
276                                 be->be_root_ndn = dn_normalize_case( ch_strdup( cargv[1] ) );
277                         }
278
279                 /* set super-secret magic database password */
280                 } else if ( strcasecmp( cargv[0], "rootpw" ) == 0 ) {
281                         if ( cargc < 2 ) {
282                                 Debug( LDAP_DEBUG_ANY,
283             "%s: line %d: missing passwd in \"rootpw <passwd>\" line\n",
284                                     fname, lineno, 0 );
285                                 return( 1 );
286                         }
287                         if ( be == NULL ) {
288                                 Debug( LDAP_DEBUG_ANY,
289 "%s: line %d: rootpw line must appear inside a database definition (ignored)\n",
290                                     fname, lineno, 0 );
291                         } else {
292                                 be->be_root_pw = ch_strdup( cargv[1] );
293                         }
294
295                 /* make this database read-only */
296                 } else if ( strcasecmp( cargv[0], "readonly" ) == 0 ) {
297                         if ( cargc < 2 ) {
298                                 Debug( LDAP_DEBUG_ANY,
299             "%s: line %d: missing on|off in \"readonly <on|off>\" line\n",
300                                     fname, lineno, 0 );
301                                 return( 1 );
302                         }
303                         if ( be == NULL ) {
304                                 Debug( LDAP_DEBUG_ANY,
305 "%s: line %d: readonly line must appear inside a database definition (ignored)\n",
306                                     fname, lineno, 0 );
307                         } else {
308                                 if ( strcasecmp( cargv[1], "on" ) == 0 ) {
309                                         be->be_readonly = 1;
310                                 } else {
311                                         be->be_readonly = 0;
312                                 }
313                         }
314
315                 /* where to send clients when we don't hold it */
316                 } else if ( strcasecmp( cargv[0], "referral" ) == 0 ) {
317                         if ( cargc < 2 ) {
318                                 Debug( LDAP_DEBUG_ANY,
319                     "%s: line %d: missing URL in \"referral <URL>\" line\n",
320                                     fname, lineno, 0 );
321                                 return( 1 );
322                         }
323                         default_referral = (char *) ch_malloc( strlen( cargv[1] )
324                             + sizeof("Referral:\n") + 1 );
325                         strcpy( default_referral, "Referral:\n" );
326                         strcat( default_referral, cargv[1] );
327
328                 /* specify locale */
329                 } else if ( strcasecmp( cargv[0], "locale" ) == 0 ) {
330 #ifdef HAVE_LOCALE_H
331                         char *locale;
332                         if ( cargc < 2 ) {
333                                 Debug( LDAP_DEBUG_ANY,
334         "%s: line %d: missing locale in \"locale <name | on | off>\" line\n",
335                                        fname, lineno, 0 );
336                                 return( 1 );
337                         }
338
339                         locale = (strcasecmp(   cargv[1], "on"  ) == 0 ? ""
340                                   : strcasecmp( cargv[1], "off" ) == 0 ? "C"
341                                   : ch_strdup( cargv[1] )                    );
342
343                         if ( setlocale( LC_CTYPE, locale ) == 0 ) {
344                                 Debug( LDAP_DEBUG_ANY,
345                                        (*locale
346                                         ? "%s: line %d: bad locale \"%s\"\n"
347                                         : "%s: line %d: bad locale\n"),
348                                        fname, lineno, locale );
349                                 return( 1 );
350                         }
351 #else
352                         Debug( LDAP_DEBUG_ANY,
353                                "%s: line %d: \"locale\" unsupported\n",
354                                fname, lineno, 0 );
355                         return( 1 );
356 #endif
357                 /* specify an objectclass */
358                 } else if ( strcasecmp( cargv[0], "objectclass" ) == 0 ) {
359                         if ( *cargv[1] == '(' ) {
360                                 char * p;
361                                 p = strchr(saveline,'(');
362                                 parse_oc( fname, lineno, p );
363                         } else {
364                                 parse_oc_old( be, fname, lineno, cargc, cargv );
365                         }
366
367                 /* specify an attribute */
368                 } else if ( strcasecmp( cargv[0], "attribute" ) == 0 ) {
369                         if ( *cargv[1] == '(' ) {
370                                 char * p;
371                                 p = strchr(saveline,'(');
372                                 parse_at( fname, lineno, p );
373                         } else {
374                                 attr_syntax_config( fname, lineno, cargc - 1,
375                                     &cargv[1] );
376                         }
377
378                 /* turn on/off schema checking */
379                 } else if ( strcasecmp( cargv[0], "schemacheck" ) == 0 ) {
380                         if ( cargc < 2 ) {
381                                 Debug( LDAP_DEBUG_ANY,
382     "%s: line %d: missing on|off in \"schemacheck <on|off>\" line\n",
383                                     fname, lineno, 0 );
384                                 return( 1 );
385                         }
386                         if ( strcasecmp( cargv[1], "off" ) == 0 ) {
387                                 global_schemacheck = 0;
388                         } else {
389                                 global_schemacheck = 1;
390                         }
391
392                 /* specify access control info */
393                 } else if ( strcasecmp( cargv[0], "access" ) == 0 ) {
394                         parse_acl( be, fname, lineno, cargc, cargv );
395
396                 /* specify default access control info */
397                 } else if ( strcasecmp( cargv[0], "defaultaccess" ) == 0 ) {
398                         if ( cargc < 2 ) {
399                                 Debug( LDAP_DEBUG_ANY,
400             "%s: line %d: missing limit in \"defaultaccess <access>\" line\n",
401                                     fname, lineno, 0 );
402                                 return( 1 );
403                         }
404                         if ( be == NULL ) {
405                                 if ( (global_default_access =
406                                     str2access( cargv[1] )) == -1 ) {
407                                         Debug( LDAP_DEBUG_ANY,
408 "%s: line %d: bad access \"%s\" expecting [self]{none|compare|read|write}\n",
409                                             fname, lineno, cargv[1] );
410                                         return( 1 );
411                                 }
412                         } else {
413                                 if ( (be->be_dfltaccess =
414                                     str2access( cargv[1] )) == -1 ) {
415                                         Debug( LDAP_DEBUG_ANY,
416 "%s: line %d: bad access \"%s\" expecting [self]{none|compare|read|write}\n",
417                                             fname, lineno, cargv[1] );
418                                         return( 1 );
419                                 }
420                         }
421
422                 /* debug level to log things to syslog */
423                 } else if ( strcasecmp( cargv[0], "loglevel" ) == 0 ) {
424                         if ( cargc < 2 ) {
425                                 Debug( LDAP_DEBUG_ANY,
426                     "%s: line %d: missing level in \"loglevel <level>\" line\n",
427                                     fname, lineno, 0 );
428                                 return( 1 );
429                         }
430                         ldap_syslog = atoi( cargv[1] );
431
432                 /* list of replicas of the data in this backend (master only) */
433                 } else if ( strcasecmp( cargv[0], "replica" ) == 0 ) {
434                         if ( cargc < 2 ) {
435                                 Debug( LDAP_DEBUG_ANY,
436             "%s: line %d: missing host in \"replica <host[:port]>\" line\n",
437                                     fname, lineno, 0 );
438                                 return( 1 );
439                         }
440                         if ( be == NULL ) {
441                                 Debug( LDAP_DEBUG_ANY,
442 "%s: line %d: replica line must appear inside a database definition (ignored)\n",
443                                     fname, lineno, 0 );
444                         } else {
445                                 for ( i = 1; i < cargc; i++ ) {
446                                         if ( strncasecmp( cargv[i], "host=", 5 )
447                                             == 0 ) {
448                                                 charray_add( &be->be_replica,
449                                                              cargv[i] + 5 );
450                                                 break;
451                                         }
452                                 }
453                                 if ( i == cargc ) {
454                                         Debug( LDAP_DEBUG_ANY,
455                     "%s: line %d: missing host in \"replica\" line (ignored)\n",
456                                             fname, lineno, 0 );
457                                 }
458                         }
459
460                 /* dn of master entity allowed to write to replica */
461                 } else if ( strcasecmp( cargv[0], "updatedn" ) == 0 ) {
462                         if ( cargc < 2 ) {
463                                 Debug( LDAP_DEBUG_ANY,
464                     "%s: line %d: missing dn in \"updatedn <dn>\" line\n",
465                                     fname, lineno, 0 );
466                                 return( 1 );
467                         }
468                         if ( be == NULL ) {
469                                 Debug( LDAP_DEBUG_ANY,
470 "%s: line %d: updatedn line must appear inside a database definition (ignored)\n",
471                                     fname, lineno, 0 );
472                         } else {
473                                 be->be_update_ndn = ch_strdup( cargv[1] );
474                                 (void) dn_normalize_case( be->be_update_ndn );
475                         }
476
477                 /* replication log file to which changes are appended */
478                 } else if ( strcasecmp( cargv[0], "replogfile" ) == 0 ) {
479                         if ( cargc < 2 ) {
480                                 Debug( LDAP_DEBUG_ANY,
481             "%s: line %d: missing dn in \"replogfile <filename>\" line\n",
482                                     fname, lineno, 0 );
483                                 return( 1 );
484                         }
485                         if ( be ) {
486                                 be->be_replogfile = ch_strdup( cargv[1] );
487                         } else {
488                                 replogfile = ch_strdup( cargv[1] );
489                         }
490
491                 /* maintain lastmodified{by,time} attributes */
492                 } else if ( strcasecmp( cargv[0], "lastmod" ) == 0 ) {
493                         if ( cargc < 2 ) {
494                                 Debug( LDAP_DEBUG_ANY,
495             "%s: line %d: missing on|off in \"lastmod <on|off>\" line\n",
496                                     fname, lineno, 0 );
497                                 return( 1 );
498                         }
499                         if ( strcasecmp( cargv[1], "on" ) == 0 ) {
500                                 if ( be )
501                                         be->be_lastmod = ON;
502                                 else
503                                         global_lastmod = ON;
504                         } else {
505                                 if ( be )
506                                         be->be_lastmod = OFF;
507                                 else
508                                         global_lastmod = OFF;
509                         }
510
511                 /* include another config file */
512                 } else if ( strcasecmp( cargv[0], "include" ) == 0 ) {
513                         if ( cargc < 2 ) {
514                                 Debug( LDAP_DEBUG_ANY,
515     "%s: line %d: missing filename in \"include <filename>\" line\n",
516                                     fname, lineno, 0 );
517                                 return( 1 );
518                         }
519                         savefname = ch_strdup( cargv[1] );
520                         savelineno = lineno;
521
522                         if ( read_config( savefname ) != 0 ) {
523                                 return( 1 );
524                         }
525
526                         free( savefname );
527                         lineno = savelineno - 1;
528
529                 /* location of kerberos srvtab file */
530                 } else if ( strcasecmp( cargv[0], "srvtab" ) == 0 ) {
531                         if ( cargc < 2 ) {
532                                 Debug( LDAP_DEBUG_ANY,
533             "%s: line %d: missing filename in \"srvtab <filename>\" line\n",
534                                     fname, lineno, 0 );
535                                 return( 1 );
536                         }
537                         ldap_srvtab = ch_strdup( cargv[1] );
538
539                 /* pass anything else to the current backend info/db config routine */
540                 } else {
541                         if ( bi != NULL ) {
542                                 if ( bi->bi_config == 0 ) {
543                                         Debug( LDAP_DEBUG_ANY,
544 "%s: line %d: unknown directive \"%s\" inside backend info definition (ignored)\n",
545                                                 fname, lineno, cargv[0] );
546                                 } else {
547                                         if ( (*bi->bi_config)( bi, fname, lineno, cargc, cargv )
548                                                 != 0 )
549                                         {
550                                                 return( 1 );
551                                         }
552                                 }
553                         } else if ( be != NULL ) {
554                                 if ( be->be_config == 0 ) {
555                                         Debug( LDAP_DEBUG_ANY,
556 "%s: line %d: unknown directive \"%s\" inside backend database definition (ignored)\n",
557                                         fname, lineno, cargv[0] );
558                                 } else {
559                                         if ( (*be->be_config)( be, fname, lineno, cargc, cargv )
560                                                 != 0 )
561                                         {
562                                                 return( 1 );
563                                         }
564                                 }
565                         } else {
566                                 Debug( LDAP_DEBUG_ANY,
567 "%s: line %d: unknown directive \"%s\" outside backend info and database definitions (ignored)\n",
568                                     fname, lineno, cargv[0] );
569                         }
570                 }
571                 free( saveline );
572         }
573         fclose( fp );
574         return( 0 );
575 }
576
577 static int
578 fp_parse_line(
579     char        *line,
580     int         *argcp,
581     char        **argv
582 )
583 {
584         char *  token;
585
586         *argcp = 0;
587         for ( token = strtok_quote( line, " \t" ); token != NULL;
588             token = strtok_quote( NULL, " \t" ) ) {
589                 if ( *argcp == MAXARGS ) {
590                         Debug( LDAP_DEBUG_ANY, "Too many tokens (max %d)\n",
591                             MAXARGS, 0, 0 );
592                         return( 1 );
593                 }
594                 argv[(*argcp)++] = token;
595         }
596         argv[*argcp] = NULL;
597         return 0;
598 }
599
600 static char *
601 strtok_quote( char *line, char *sep )
602 {
603         int             inquote;
604         char            *tmp;
605         static char     *next;
606
607         if ( line != NULL ) {
608                 next = line;
609         }
610         while ( *next && strchr( sep, *next ) ) {
611                 next++;
612         }
613
614         if ( *next == '\0' ) {
615                 next = NULL;
616                 return( NULL );
617         }
618         tmp = next;
619
620         for ( inquote = 0; *next; ) {
621                 switch ( *next ) {
622                 case '"':
623                         if ( inquote ) {
624                                 inquote = 0;
625                         } else {
626                                 inquote = 1;
627                         }
628                         SAFEMEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
629                         break;
630
631                 case '\\':
632                         if ( next[1] )
633                                 SAFEMEMCPY( next,
634                                             next + 1, strlen( next + 1 ) + 1 );
635                         next++;         /* dont parse the escaped character */
636                         break;
637
638                 default:
639                         if ( ! inquote ) {
640                                 if ( strchr( sep, *next ) != NULL ) {
641                                         *next++ = '\0';
642                                         return( tmp );
643                                 }
644                         }
645                         next++;
646                         break;
647                 }
648         }
649
650         return( tmp );
651 }
652
653 static char     buf[BUFSIZ];
654 static char     *line;
655 static int      lmax, lcur;
656
657 #define CATLINE( buf )  { \
658         int     len; \
659         len = strlen( buf ); \
660         while ( lcur + len + 1 > lmax ) { \
661                 lmax += BUFSIZ; \
662                 line = (char *) ch_realloc( line, lmax ); \
663         } \
664         strcpy( line + lcur, buf ); \
665         lcur += len; \
666 }
667
668 static char *
669 fp_getline( FILE *fp, int *lineno )
670 {
671         char            *p;
672
673         lcur = 0;
674         CATLINE( buf );
675         (*lineno)++;
676
677         /* hack attack - keeps us from having to keep a stack of bufs... */
678         if ( strncasecmp( line, "include", 7 ) == 0 ) {
679                 buf[0] = '\0';
680                 return( line );
681         }
682
683         while ( fgets( buf, sizeof(buf), fp ) != NULL ) {
684                 if ( (p = strchr( buf, '\n' )) != NULL ) {
685                         *p = '\0';
686                 }
687                 if ( ! isspace( (unsigned char) buf[0] ) ) {
688                         return( line );
689                 }
690
691                 CATLINE( buf );
692                 (*lineno)++;
693         }
694         buf[0] = '\0';
695
696         return( line[0] ? line : NULL );
697 }
698
699 static void
700 fp_getline_init( int *lineno )
701 {
702         *lineno = -1;
703         buf[0] = '\0';
704 }