Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / DtSearch / ocf.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  *   COMPONENT_NAME: austext
25  *
26  *   FUNCTIONS: add_a_keytype
27  *              dumpboth
28  *              dumpoef
29  *              load_ocf
30  *              obsolete_keyword
31  *              ocfopen
32  *              parse_boolean
33  *              read_database
34  *              read_filepath
35  *              read_keytypes
36  *              read_maxhits
37  *              read_path
38  *              read_rest_of_line
39  *              set_boolbit
40  *              set_boolint
41  *              set_fileio
42  *              set_int
43  *              set_long
44  *
45  *   ORIGINS: 27
46  *
47  *   (C) COPYRIGHT International Business Machines Corp. 1991,1995
48  *   All Rights Reserved
49  *   US Government Users Restricted Rights - Use, duplication or
50  *   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
51  */
52 /*************************** OCF.C ***************************
53  * $TOG: ocf.c /main/9 1998/04/17 11:26:02 mgreess $
54  * August 1991.
55  * Load_ocf() reads an engine's "site configuration file" (.ocf),
56  * parses its contents, and returns a ptr to a list of
57  * DBLKs containing the file's info.
58  * Returns TRUE if .ocf file format is acceptable and everything worked.
59  * Returns FALSE if file contains fatal errors.  If file is ok, also
60  * returns a list of dblks in global usrblk and sets some global variables.
61  * The name and path of config file can be specified
62  * or generated in a number of convenient ways.
63  * The theory is: as many options as possible are in the config file
64  * rather than command lines, etc.
65  * Note: User configuration files are no longer supported.
66  * 
67  * $Log$
68  * Revision 2.6  1996/03/05  21:12:01  miker
69  * Reversed meaning of ascii_charmap test.
70  *
71  * Revision 2.5  1996/03/05  18:02:46  miker
72  * Repl ctype functions with refs to ascii_charmap and COMMENT_CHARS.
73  *
74  * Revision 2.4  1996/02/05  16:34:57  miker
75  * Silently try old file name when looking for site config file.
76  *
77  * Revision 2.3  1995/10/25  15:39:33  miker
78  * Added prolog.
79  *
80  * Revision 2.2  1995/10/03  21:40:15  miker
81  * Cosmetic msg change only.
82  *
83  * Revision 2.1  1995/09/22  21:30:12  miker
84  * Freeze DtSearch 0.1, AusText 2.1.8
85  *
86  * Revision 1.3  1995/09/05  21:41:59  miker
87  * Changed environment variable OCFPATH to DTSROCFPATH.
88  *
89  * Revision 1.2  1995/09/05  18:46:13  miker
90  * Moved all documentation to ocf.doc.
91  *
92  * Revision 1.1  1995/08/31  21:07:25  miker
93  * Initial revision (renamed ocf.c from loadocf.c).
94  *
95  * *Log: loadocf.c,v *
96  * Revision 1.19  1995/07/19  21:12:34  miker
97  * 2.1.6c: Cleaned up documentation to match actual current capabilities.
98  * Obsoleted MAILFEATURE and PRINTSERVER keywords.
99  *
100  * Revision 1.18  1995/07/18  22:52:17  miker
101  * Added ability to open prespecified OE_sitecnfg_fname.
102  */
103
104 #include "SearchE.h"
105 #include <stdlib.h>
106 #define X_INCLUDE_STRING_H
107 #define XOS_USE_NO_LOCKING
108 #include <X11/Xos_r.h>
109 #include <errno.h>
110 #include <unistd.h>     /* for POSIX getcwd() */
111 #include <sys/stat.h>
112
113 /*****#define DEBUG_DUMP******/
114 /********#define DEBUG_OEF**********/
115
116 #define PROGNAME        "OCF"
117 #define DELIMITERS      " =,\t\n"
118 #define NEGLOGHALF      (-.69314718F)   /* negative natural log of .5 */
119 #define FNAME_SITECONFIG_OLD    "austext.ocf"
120 #define MS_misc         1
121 #define MS_loadocf      8
122
123 /*-------------------- STATIC GLOBALS ----------------------*/
124 static DBLK    *db;     /* curr database/dict */
125 static char    *cfgfname;
126 static char    *sprintbufp;
127 static char    *token;
128 static int      fatal_error;
129 static int      debug_ocf = FALSE;
130
131 /*-------------------- ENGINE GLOBALS ----------------------
132  * These are globals which must be accessed by both sides. 
133  * Globals unique to the engine are in oe.c.
134  */
135 char           *OE_server_dir = NULL;
136 char           *OE_inittab_dir = NULL;
137
138 /*-------------------- OEF TABLE ----------------------*/
139 /*** (struct typdef located in oe.h)
140         {
141         char    *id;                     keyword identifier
142         char    **OEFptr;                addr of variable to change
143         char    previously_specified;    bool ensures only one spec
144         } OEFTAB;
145 ********/
146 OEFTAB          oef_table[] =
147 {
148     "AUDIT", &OEF_audit, FALSE,
149     "DISCARD", &OEF_discard, FALSE,
150     "NEWS", &OEF_news, FALSE,
151     "NOTESNOT", &OEF_notesnot, FALSE,
152     "NOTESSEM", &OEF_notessem, FALSE,
153     "README", &OEF_readme, FALSE,
154     NULL, NULL, FALSE   /* end of list */
155 };
156
157
158 #ifdef DEBUG_DUMP       /* for debugging only */
159 /****************************************/
160 /*                                      */
161 /*               dumpboth               */
162 /*                                      */
163 /****************************************/
164 static void     dumpboth (void)
165 {
166     char            typestring[4 * MAX_KTCOUNT + 2];
167     int             i;
168     char           *ptr;
169     LLIST          *m;
170     DBLK           *d;
171
172     printf ("\nDUMPBOTH> fatal_error=%d, msglist=%p, dblist=%p\n",
173         fatal_error, ausapi_msglist, usrblk.dblist);
174     printf ("%s\n", DtSrGetMessages ());
175     DtSrFreeMessages ();
176     for (d = usrblk.dblist; d != NULL; d = d->link) {
177         ptr = typestring;
178         for (i = 0; i < d->ktcount; i++) {
179             *ptr++ = (d->keytypes[i].is_selected) ? '*' : ' ';
180             *ptr++ = d->keytypes[i].ktchar;
181             *ptr++ = ' ';
182         }
183         *ptr = 0;
184         printf ("DBLK at %p link=%p name='%s' max=%d\n"
185             "    keytypes='%s', path='%s'\n",
186             d, d->link, d->name, d->maxhits,
187             typestring, NULLORSTR (d->path));
188     }
189     printf ("Push any key to continue...");
190
191     *typestring = '\0';
192     fgets (typestring, sizeof(typestring), stdin);
193     if (strlen(typestring) && typestring[strlen(typestring)-1] == '\n')
194       typestring[strlen(typestring)-1] = '\0';
195
196     printf ("\n\n\n");
197     return;
198 }  /* dumpboth() */
199
200 #endif
201
202 #ifdef DEBUG_OEF        /* for debugging only */
203 /****************************************/
204 /*                                      */
205 /*               dumpoef                */
206 /*                                      */
207 /****************************************/
208 static void     dumpoef (char *before_after)
209 {
210     OEFTAB         *oef;
211     fprintf (aa_stderr,
212         "\toef_table[] %s (* = changed strings)\n",
213         before_after);
214     for (oef = oef_table; oef->id != NULL; oef++) {
215         fprintf (aa_stderr, "\t%10s = %c'%s'\n",
216             oef->id,
217             (oef->previously_specified) ? '*' : ' ',
218             *(oef->OEFptr));
219     }
220     fputc ('\n', aa_stderr);
221     return;
222 }  /* dumpoef() */
223
224 #endif
225
226
227 /****************************************/
228 /*                                      */
229 /*             parse_boolean            */
230 /*                                      */
231 /****************************************/
232 /* Forces token to upper case.
233  * Returns +1 for TRUE, -1 for FALSE, and 0 for neither.
234  */
235 static int      parse_boolean (char *token)
236 {
237     /* Completely missing token defaults to affirmative */
238     if (token == NULL)
239         return +1;
240
241     strupr (token);
242     switch (token[0]) {
243         case 'O':       /* on, off */
244             if (token[1] == 'N')
245                 return +1;
246             else if (token[1] == 'F')
247                 return -1;
248             else
249                 return 0;
250
251         case 'T':       /* true */
252         case 'Y':       /* yes */
253         case 'E':       /* enabled */
254         case '1':       /* (one) */
255             return +1;
256
257         case 'F':       /* false */
258         case 'N':       /* no */
259         case 'D':       /* disabled */
260         case '0':       /* (zero) */
261             return -1;
262
263         default:
264             return 0;
265     }
266 }  /* parse_boolean() */
267
268 /****************************************/
269 /*                                      */
270 /*             read_database            */
271 /*                                      */
272 /****************************************/
273 /* Validates next token as database name.
274  * If not valid name, returns FALSE.
275  * Otherwise chains down through the database list
276  * looking for the one whose name matches the token.
277  * If a match is found, sets db to it, returns TRUE.
278  * If no match is found, creates and initializes
279  * new structure, sets db to it, returns TRUE.
280  */
281 static int      read_database (char *keyword, _Xstrtokparams *strtok_buf)
282 {
283     DBLK          **linkptr;
284
285     if ((token = _XStrtok(NULL, DELIMITERS, *strtok_buf)) == NULL) {
286         sprintf (sprintbufp,
287             catgets (dtsearch_catd, MS_loadocf, 477,
288                 "%s %s: Missing database name after '%s' keyword.\n"),
289             PROGNAME"477", cfgfname, keyword);
290         DtSearchAddMessage (sprintbufp);
291         fatal_error = TRUE;
292         return FALSE;
293     }
294
295     if (strlen (token) > 8) {
296         sprintf (sprintbufp,
297             catgets (dtsearch_catd, MS_loadocf, 184,
298             "%s %s: More than 8 characters in Database name '%.16s'.\n"),
299             PROGNAME"184", cfgfname, token);
300         DtSearchAddMessage (sprintbufp);
301         fatal_error = TRUE;
302         return FALSE;
303     }
304
305     /* Chain down the list looking for previously defined database structure */
306     linkptr = &usrblk.dblist;
307     for (db = usrblk.dblist; db != NULL; db = db->link) {
308         if (strcmp (db->name, token) == 0)
309             return TRUE;
310         linkptr = &db->link;
311     }
312     /* Didn't find matching dblk so allocate and initialize new one at
313      * end of list (linkptr).  Use system defaults for most values.
314      */
315     if (debug_ocf)
316         fprintf (aa_stderr, PROGNAME "204 "
317             "%s: creating dblk for '%s'\n", cfgfname, token);
318     db = austext_malloc (sizeof (DBLK) + 8, PROGNAME "205", NULL);
319     memset (db, 0, sizeof (DBLK));
320     strcpy (db->name, token);
321     db->is_selected = TRUE;
322     *linkptr = db;
323
324     return TRUE;
325 }  /* read_database() */
326
327
328 /****************************************/
329 /*                                      */
330 /*            set_boolbit               */
331 /*                                      */
332 /****************************************/
333 static void     set_boolbit (long *flagvar, long mask, char *flagname,
334                              _Xstrtokparams *strtok_buf)
335 {
336     int             i;
337
338     token = _XStrtok(NULL, DELIMITERS, *strtok_buf);
339     i = parse_boolean (token);
340     if (i > 0)
341         *flagvar |= mask;       /* switch ON */
342     else if (i < 0)
343         *flagvar &= ~mask;      /* switch OFF */
344     else {
345         sprintf (sprintbufp,
346             catgets (dtsearch_catd, MS_loadocf, 352,
347             "%s %s: Invalid %s value, line ignored.\n"),
348             PROGNAME"352", cfgfname, flagname);
349         DtSearchAddMessage (sprintbufp);
350     }
351     return;
352 }  /* set_boolbit() */
353
354
355
356 /****************************************/
357 /*                                      */
358 /*              set_int                 */
359 /*                                      */
360 /****************************************/
361 static void     set_int (int *intvar, char *keyword, _Xstrtokparams *strtok_buf)
362 {
363     int             myint;
364
365     if ((token = _XStrtok(NULL, DELIMITERS, *strtok_buf)) == NULL) {
366         token = catgets (dtsearch_catd, MS_loadocf, 140, "(missing)");
367 ERR_MSG:
368         sprintf (sprintbufp, catgets (dtsearch_catd, MS_loadocf, 610,
369             "%s %s: %s value is '%s'. "
370             "Should be positive integer. Line ignored."),
371             PROGNAME"844", cfgfname, keyword, token);
372         DtSearchAddMessage (sprintbufp);
373         return;
374     }
375     if ((myint = atoi (token)) <= 0L)
376         goto ERR_MSG;
377     *intvar = myint;
378     return;
379 }  /* set_int() */
380
381
382 /****************************************/
383 /*                                      */
384 /*              set_long                */
385 /*                                      */
386 /****************************************/
387 static void     set_long (long *longvar, char *keyword, 
388                           _Xstrtokparams *strtok_buf)
389 {
390     long            longi;
391
392     if ((token = _XStrtok(NULL, DELIMITERS, *strtok_buf)) == NULL) {
393         token = catgets (dtsearch_catd, MS_loadocf, 140, "(missing)");
394 ERR_MSG:
395         sprintf (sprintbufp, catgets (dtsearch_catd, MS_loadocf, 610,
396             "%s %s: %s value is '%s'. "
397             "Should be positive integer. Line ignored."),
398             PROGNAME"610", cfgfname, keyword, token);
399         DtSearchAddMessage (sprintbufp);
400         return;
401     }
402     if ((longi = atol (token)) <= 0L)
403         goto ERR_MSG;
404     *longvar = longi;
405     return;
406 }  /* set_long() */
407
408
409 /****************************************/
410 /*                                      */
411 /*             set_boolint              */
412 /*                                      */
413 /****************************************/
414 static void     set_boolint (int *boolint, char *keyword,
415                              _Xstrtokparams *strtok_buf)
416 {
417     int             i;
418
419     token = _XStrtok(NULL, DELIMITERS, *strtok_buf);
420     i = parse_boolean (token);
421     if (i > 0)
422         *boolint = TRUE;        /* switch ON */
423     else if (i < 0)
424         *boolint = FALSE;       /* switch OFF */
425     else {
426         sprintf (sprintbufp,
427             catgets (dtsearch_catd, MS_loadocf, 352,
428             "%s %s: Invalid %s value, line ignored.\n"),
429             PROGNAME"352", cfgfname, keyword);
430         DtSearchAddMessage (sprintbufp);
431     }
432     return;
433 }  /* set_boolint() */
434
435 /****************************************/
436 /*                                      */
437 /*            set_fileio                */
438 /*                                      */
439 /****************************************/
440 /* sets OE_fileio string pointer */
441 static void     set_fileio (_Xstrtokparams *strtok_buf)
442 {
443     char           *ptr;
444
445     /* if a value is missing, presume -ON */
446     if ((token = _XStrtok(NULL, DELIMITERS, *strtok_buf)) == NULL) {
447         OE_fileio = "-ON";
448         return;
449     }
450
451     /* check for hardcoded path name (anything that doesn't begin with '-') */
452     if (token[0] != '-') {
453         OE_fileio = austext_malloc (strlen (token) + 2, PROGNAME "592", NULL);
454         strcpy (OE_fileio, token);
455         return;
456     }
457
458     strupr (token);
459     if (strcmp (token, "-ON") == 0)
460         OE_fileio = "-ON";
461     else if (strcmp (token, "-OFF") == 0)
462         OE_fileio = "-OFF";
463     else if (strcmp (token, "-HOME") == 0)
464         OE_fileio = "-HOME";
465     else {
466         sprintf (sprintbufp, catgets (dtsearch_catd, MS_loadocf, 607,
467             "%s %s: Invalid FILEIO value.  "
468             "User file reads and writes prohibited."),
469             PROGNAME"607", cfgfname);
470         DtSearchAddMessage (sprintbufp);
471         OE_fileio = "-OFF";
472     }
473     return;
474 }  /* set_fileio() */
475
476
477 /****************************************/
478 /*                                      */
479 /*          read_rest_of_line           */
480 /*                                      */
481 /****************************************/
482 /* Malloc a copy of the rest of the input line after
483  * the current token and set passed string ptr to it.
484  */
485 static void     read_rest_of_line (char *keyword, char **passed_ptr,
486                                    _Xstrtokparams *strtok_buf)
487 {
488     int             len;
489
490     if ((token = _XStrtok(NULL, "\n", *strtok_buf)) == NULL) {
491         sprintf (sprintbufp, catgets (dtsearch_catd, MS_loadocf, 1007,
492             "%s%s: Empty %s string ignored."),
493             PROGNAME"1007 ", cfgfname, keyword);
494         DtSearchAddMessage (sprintbufp);
495         return;
496     }
497     *passed_ptr = austext_malloc (strlen(token) + 4, PROGNAME"1020", NULL);
498     strcpy (*passed_ptr, token);
499     return;
500 }  /* read_rest_of_line() */ 
501
502
503 /****************************************/
504 /*                                      */
505 /*            read_filepath             */
506 /*                                      */
507 /****************************************/
508 static void     read_filepath (_Xstrtokparams *strtok_buf)
509 {
510     char           *errp;
511     char           *newpath;
512     int             tabx;
513     int             toklen;
514
515     /* Second token on line should be valid id word.
516      * Set tabx to matching oef table entry.
517      */
518     if ((token = _XStrtok(NULL, DELIMITERS, *strtok_buf)) == NULL) {
519         token = catgets (dtsearch_catd, MS_loadocf, 142, "rest of line");
520         errp = catgets (dtsearch_catd, MS_loadocf, 143, "is empty");
521
522 IGNORE_FILEPATH:
523         sprintf (sprintbufp,
524             catgets (dtsearch_catd, MS_loadocf, 903,
525             "%s %s: FILEPATH '%s' %s. Line ignored."),
526             PROGNAME"903", cfgfname, token, errp);
527         DtSearchAddMessage (sprintbufp);
528         return;
529     }
530     strupr (token);
531     for (tabx = 0; oef_table[tabx].id != NULL; tabx++) {
532         if (strcmp (oef_table[tabx].id, token) == 0)
533             break;
534     }
535     if (oef_table[tabx].id == NULL) {
536         errp = catgets (dtsearch_catd, MS_loadocf, 165, "unknown id");
537         goto IGNORE_FILEPATH;
538     }
539     if (oef_table[tabx].previously_specified) {
540         errp = catgets (dtsearch_catd, MS_loadocf, 166, "previously specified");
541         goto IGNORE_FILEPATH;
542     }
543
544     /* Third token should be either a directory path (ends in slash)
545      * or a directory path plus a file name (does not end in slash).
546      */
547     if ((token = _XStrtok(NULL, DELIMITERS, *strtok_buf)) == NULL) {
548         token = oef_table[tabx].id;
549         errp = catgets (dtsearch_catd, MS_loadocf, 168,
550             "missing path specification");
551         goto IGNORE_FILEPATH;
552     }
553     toklen = strlen (token);
554
555     /* Allocate buffer for new path string and initialize it.
556      * Leave enough extra room for the default, dos-format file name,
557      * i.e. 8 chars, a period, and a 3 char extension.
558      */
559     newpath = austext_malloc (toklen + 20, PROGNAME "956", NULL);
560     strcpy (newpath, token);
561     if (token[toklen - 1] == LOCAL_SLASH) {
562         strncpy (newpath + toklen, *(oef_table[tabx].OEFptr), 12);
563         newpath[toklen + 12] = 0;
564     }
565
566     /* Setting the previously_specified flag not only prevents multiple
567      * specifications in a single file, but also permits user files
568      * to override site files for specific ids because loadocf is always
569      * called by the ui before it is called by oe initialization.
570      */
571     *(oef_table[tabx].OEFptr) = newpath;
572     oef_table[tabx].previously_specified = TRUE;
573     return;
574 }  /* read_filepath() */
575
576
577 /****************************************/
578 /*                                      */
579 /*             read_path                */
580 /*                                      */
581 /****************************************/
582 static void     read_path (_Xstrtokparams *strtok_buf)
583 {
584 #ifdef DEBUG_DUMP
585                     printf (PROGNAME "217> Entering read_path().\n");
586 #endif
587
588     /* parse the path for this dictionary/database */
589     if ((token = _XStrtok(NULL, DELIMITERS, *strtok_buf)) == NULL) {
590         sprintf (sprintbufp, catgets (dtsearch_catd, MS_loadocf, 221,
591             "%s %s: Database '%s', No PATH specified, statement ignored.\n"),
592             PROGNAME"221", cfgfname, db->name);
593         DtSearchAddMessage (sprintbufp);
594         return;
595     }
596     db->path = austext_malloc (strlen (token) + 4, PROGNAME "244", NULL);
597     strcpy (db->path, token);
598     ensure_end_slash (db->path);
599     return;
600 }  /* read_path() */
601
602
603 /****************************************/
604 /*                                      */
605 /*            obsolete_keyword          */
606 /*                                      */
607 /****************************************/
608 /* append warning msg: passed keyword no longer used */
609 static void     obsolete_keyword (char *keyword)
610 {
611     sprintf (sprintbufp, catgets (dtsearch_catd, MS_loadocf, 1,
612         "%1$s%2$s: %3$s keyword is obsolete."),
613         PROGNAME "001 ", cfgfname, keyword);
614     DtSearchAddMessage (sprintbufp);
615     return;
616 }  /* obsolete_keyword() */
617
618
619 /****************************************/
620 /*                                      */
621 /*            read_maxhits              */
622 /*                                      */
623 /****************************************/
624 static void     read_maxhits (_Xstrtokparams *strtok_buf)
625 {
626     int             i;
627
628 #ifdef DEBUG_DUMP
629     printf (PROGNAME "292> Entering read_maxhits().\n");
630 #endif
631
632     /* parse the max number of hits for this dictionary */
633     if ((token = _XStrtok(NULL, DELIMITERS, *strtok_buf)) == NULL) {
634 INVALID_MAXHITS:
635         sprintf (sprintbufp,
636             catgets (dtsearch_catd, MS_loadocf, 304,
637             "%s %s: Database '%s', invalid MAXHITS value.\n"),
638             PROGNAME"304", cfgfname, db->name);
639         DtSearchAddMessage (sprintbufp);
640         fatal_error = TRUE;
641         return;
642     }
643     i = atoi (token);
644     if (i <= 0)
645         goto INVALID_MAXHITS;
646     else
647         db->maxhits = i;
648     return;
649 }  /* read_maxhits() */
650
651
652 /****************************************/
653 /*                                      */
654 /*            add_a_keytype             */
655 /*                                      */
656 /****************************************/
657 /* Subroutine of read_keytypes().
658  * Concatenates a new DtSrKeytype node to end of current
659  * db->keytypes array (in effect, remallocs db->keytypes).
660  * Initializes ktchar with passed character.
661  * Initializes name string with a generic label derived
662  * from ktchar, which may later be changed by read_keytypes().
663  * Keeps db->ktcount current.
664  * If duplicate ktchar already exists, adds warning msg to
665  * msglist, but changes nothing and returns index of old node.
666  * Returns index of DtSrKeytype node or -1 on error.
667  */
668 static int      add_a_keytype (char ktchar)
669 {
670     DtSrKeytype        *kt;
671     int             i, newindex;
672
673     /* Make sure we have not exceeded max number of keytypes */
674     if (db->ktcount >= MAX_KTCOUNT) {
675         sprintf (sprintbufp,
676             catgets (dtsearch_catd, MS_loadocf, 732,
677             "%s %s: Database '%s', No more than %d keytypes allowed.\n"),
678             PROGNAME"732", cfgfname, db->name, MAX_KTCOUNT);
679         DtSearchAddMessage (sprintbufp);
680         fatal_error = TRUE;
681         return -1;
682     }
683
684     /* Make sure ktchar is alphanumeric */
685     if ( (ascii_charmap[ktchar] & (CONSONANT | VOWEL | NUMERAL)) == 0) {
686         if (!isprint (ktchar))
687             ktchar = '?';
688         sprintf (sprintbufp,
689             catgets (dtsearch_catd, MS_loadocf, 684,
690             "%s %s: Database '%s',\n  Invalid KEYTYPE character '%c'."),
691             PROGNAME"684", cfgfname, db->name, ktchar);
692         DtSearchAddMessage (sprintbufp);
693         fatal_error = TRUE;
694         return -1;
695     }
696
697     /* Test for duplicate, preexisting keytype */
698     kt = db->keytypes;
699     for (i = 0; i < db->ktcount; i++)
700         if (ktchar == kt[i].ktchar) {
701             sprintf (sprintbufp,
702                 catgets (dtsearch_catd, MS_loadocf, 1002,
703                 "%s %s: Database '%s', Duplicate KEYTYPE character '%c'.\n"),
704                 PROGNAME"1002", cfgfname, db->name, ktchar);
705             DtSearchAddMessage (sprintbufp);
706             return i;
707         }
708
709     /* Append valid new keytype to the keytypes array */
710     i = sizeof(DtSrKeytype) * db->ktcount;      /* size of current array */
711     kt = austext_malloc (i + sizeof(DtSrKeytype) + 4, PROGNAME "699", NULL);
712     if (db->keytypes != NULL) {
713         memcpy (kt, db->keytypes, i);
714         free (db->keytypes);
715     }
716     newindex = db->ktcount;
717     kt[newindex].is_selected = TRUE;
718     kt[newindex].ktchar = ktchar;
719     sprintf (kt[newindex].name,
720         catgets (dtsearch_catd, MS_loadocf, 457, "'%c' Records"), ktchar);
721
722     /* Warn about inaccessible lowercase ktchars */
723     if ( ((ascii_charmap[ktchar] & 0xff) != ktchar) && OE_uppercase_keys ) {
724         sprintf (sprintbufp,
725             catgets (dtsearch_catd, MS_loadocf, 1011,
726             "%s %s: Database '%s': Records with lowercase\n"
727             "  KEYTYPE character '%c' will be inaccessible.\n"
728             "  Set UPPERKEYS = FALSE if lowercase keys are intended."),
729             PROGNAME"1011", cfgfname, db->name, ktchar);
730         DtSearchAddMessage (sprintbufp);
731     }
732
733     db->keytypes = kt;
734     db->ktcount++;
735     return newindex;
736 }  /* add_a_keytype() */
737
738
739 /****************************************/
740 /*                                      */
741 /*            read_keytypes             */
742 /*                                      */
743 /****************************************/
744 /* Builds keytypes arrays in current dblk.
745  * Parameter 'how_many' limits the number keytype additions.
746  * For example if how_many == 1, only 1 keytype will be added
747  * and the balance of the line will be ignored.
748  * We also quit adding keytypes when the total reaches
749  * MAX_KTCOUNT or when we hit end of line or commments.
750  */
751 static void     read_keytypes (int how_many, _Xstrtokparams *strtok_buf)
752 {
753     /* 'last_ktchar' is index to db->keytypes.  if last_ktchar < 0 
754      * then last token was not a ktchar, or it was an invalid ktchar.
755      */
756     int             last_ktchar = -1;
757     DtSrKeytype        *kt;
758     char           *charptr;
759
760 #ifdef DEBUG_DUMP
761     printf (PROGNAME "361> Entering read_keytypes().\n");
762 #endif
763
764     /* parsing loop for each keytypes token */
765     while ((token = _XStrtok(NULL, DELIMITERS, *strtok_buf)) != NULL) {
766         /* quit when comments begin */
767         if (*token == '*' || *token == ':')
768             break;
769
770         /* Test for ktchar token (beginning with a single quote) */
771         if (*token == '\'') {
772             if (how_many <= 0)
773                 break;  /* don't exceed our quota */
774             last_ktchar = add_a_keytype (token[1]);
775             if (last_ktchar >= 0)
776                 how_many--;
777             continue;
778         }
779
780         /*
781          * Token is a name string.  If the last token was NOT a ktchar in
782          * single quotes, or it was an invalid ktchar, then create a new
783          * keytypes entry with the ktchar equal to the first char of this
784          * label. If any err, don't update the name string. 
785          */
786         if (last_ktchar < 0) {
787             if (how_many <= 0)
788                 break;  /* don't exceed our quota */
789             last_ktchar = add_a_keytype (token[0]);
790             if (last_ktchar < 0)
791                 continue;
792             else
793                 how_many--;
794         }
795
796         /*
797          * Update the appropriate table entry with the label string.
798          * Convert underscores to spaces. 
799          */
800         kt = &db->keytypes[last_ktchar];
801         strncpy (kt->name, token, DtSrMAX_KTNAME);
802         kt->name[DtSrMAX_KTNAME] = 0;
803         for (charptr = kt->name; *charptr != 0; charptr++)
804             if (*charptr == '_')
805                 *charptr = ' ';
806
807         last_ktchar = -1;
808     }   /* end token parsing loop */
809
810     return;
811 }  /* read_keytypes() */
812
813
814 /****************************************/
815 /*                                      */
816 /*               ocfopen                */
817 /*                                      */
818 /****************************************/
819 /* Subroutine of load_ocf().
820  * Attempts to open config file created by concatenating
821  * file name to passed pathname prefix.
822  * If both fields are NULL, tries to open fully qualified
823  * fname prespecified in OE_sitecnfg_fname.
824  * Messages may be appended if some information, like an
825  * environment variable, was found but still couldn't open file.
826  * Loads OE_sitecnfg... fields after successful sitecnfg open.
827  * Returns TRUE if fopen() successful, else FALSE.
828  */
829
830 static int      ocfopen (char *prefix, char *fname, FILE ** stream)
831 {
832     char            fullname[1024];
833     char           *cptr;
834     FILE           *fptr;
835     struct stat     statbuf;
836     int             is_prespecified_fname = (prefix == NULL && fname == NULL);
837
838     /*
839      * Build a complete path/file name from passed args into fullname
840      * buffer. 
841      */
842     if (is_prespecified_fname) {
843         strncpy (fullname, OE_sitecnfg_fname, sizeof (fullname));
844         fullname[sizeof (fullname) - 1] = 0;
845         goto GOT_FULLNAME;
846     }
847
848     if (prefix == NULL) {
849         if (debug_ocf)
850             fprintf (aa_stderr, " --> open not attempted: null prefix.\n");
851         return FALSE;
852     }
853
854     strncpy (fullname, prefix, sizeof (fullname));
855     fullname[sizeof (fullname) - 2] = 0;
856     strcpy (ensure_end_slash (fullname), fname);
857
858
859 GOT_FULLNAME:
860     fptr = fopen (fullname, "rt");
861     if (debug_ocf) {
862         if (fptr == NULL)
863             fprintf (aa_stderr, " --> %s\n", strerror (errno));
864         else
865             fprintf (aa_stderr, " --> open succeeded!\n");
866     }
867
868     /*
869      * If open failed: (1) Append information message if error is
870      * something other than 'cant find file'. (Always retn msg if user
871      * prespecified file name). (2) Return now. 
872      */
873     if (fptr == NULL) {
874         if (errno != ENOENT || is_prespecified_fname) {
875             sprintf (sprintbufp, catgets (dtsearch_catd, MS_loadocf, 414,
876                 "%s Unable to open configuration file '%s':\n  %s."),
877                 PROGNAME"414", fullname, strerror(errno));
878             DtSearchAddMessage (sprintbufp);
879         }
880         return FALSE;
881     }
882
883     /* Load OE_... fields associated with site config */
884     if (!is_prespecified_fname) {
885         OE_sitecnfg_fname = austext_malloc (strlen (fullname) + 2,
886             PROGNAME "941", NULL);
887         strcpy (OE_sitecnfg_fname, fullname);
888     }
889     if (fstat (fileno (fptr), &statbuf) == -1) {
890         sprintf (sprintbufp, catgets (dtsearch_catd, MS_loadocf, 1404,
891             "%s Status unavailable for configuration file '%s': %s"),
892             PROGNAME"1404", fullname, strerror(errno));
893         DtSearchAddMessage (sprintbufp);
894         return FALSE;
895     }
896     OE_sitecnfg_mtime = statbuf.st_mtime;
897
898     /* Save file name globally for error msgs */
899     strcpy (cfgfname, fullname);
900     *stream = fptr;
901     return TRUE;
902 }  /* ocfopen() */
903
904
905 /****************************************/
906 /*                                      */
907 /*              load_ocf                */
908 /*                                      */
909 /****************************************/
910 /* This is the only function in this module seen by the outside world.
911  * Returns TRUE if .ocf file format is acceptable and everything worked.
912  * Returns FALSE if file contains fatal errors.  If file is ok, also
913  * returns a list of dblks in global usrblk and sets some global variables.
914  */
915 int             load_ocf (void)
916 {
917     int             i;
918     char           *ptr, *p, *q;
919     char            sprintbuf[1024];
920     char            cfgfnamebuf[1024];
921     char            inbuf[1024];
922     char            uprtoken[128];
923     FILE           *stream;
924     _Xstrtokparams  strtok_buf;
925
926     /* Initialize program globals */
927     usrblk.dblist = NULL;       /* just to be sure */
928     fatal_error = FALSE;
929     sprintbufp = sprintbuf;
930     cfgfname = cfgfnamebuf;
931
932     debug_ocf = (usrblk.debug & USRDBG_RARE);
933
934 #ifdef DEBUG_DUMP
935     puts ("\nentering load_ocf");
936     dumpboth ();
937 #endif
938
939 #ifdef DEBUG_OEF
940     dumpoef ("BEFORE");
941 #endif
942
943     /* Construct the full pathname/filename and open it.
944      * Try various optional path prefixes depending on
945      * existence of certain environment and global variables.
946      * Also silently try the old filename in each directory.
947      * If the site config file is never found, return FALSE,
948      * meaning 'fatal error', and an error msg.
949      *
950      * (1) The first place to look is a prespecified, fully qualified
951      * name passed by the initialization function.  That occurs when
952      * the global OE_sitecnfg_fname, which otherwise would be NULL,
953      * points to the passed arg.  It's a fatal error if this name
954      * was specified but cannot be opened.
955      */
956     if (OE_sitecnfg_fname) {
957         if (debug_ocf)
958             fprintf (aa_stderr,
959                 PROGNAME "1446 ocfopen '%s', prespecified file name.\n",
960                 OE_sitecnfg_fname);
961         if (ocfopen (NULL, NULL, &stream))
962             goto OCFOPEN_OK;
963         else
964             return FALSE;
965     }
966
967     /* (2) try to find file in DTSROCFPATH directory */
968     p = getenv ("DTSROCFPATH");
969     if (debug_ocf)
970         fprintf (aa_stderr, PROGNAME "1753 ocfopen '%s'. DTSROCFPATH='%s'.\n",
971             FNAME_SITECONFIG, NULLORSTR (p));
972     if (ocfopen (p, FNAME_SITECONFIG, &stream))
973         goto OCFOPEN_OK;
974     if (ocfopen (p, FNAME_SITECONFIG_OLD, &stream))
975         goto OCFOPEN_OK;
976
977     /* (3) try to find file in current working directory */
978     if (getcwd (inbuf, sizeof (inbuf)) == NULL)
979         strcpy (inbuf, "./");   /* default to 'local' dir */
980     else if (inbuf[0] == 0)
981         strcpy (inbuf, "/");    /* presume 'root' dir */
982     if (debug_ocf)
983         fprintf (aa_stderr, PROGNAME "1771 ocfopen '%s'. cwd='%s'.\n",
984             FNAME_SITECONFIG, inbuf);
985     if (ocfopen (inbuf, FNAME_SITECONFIG, &stream))
986         goto OCFOPEN_OK;
987     if (ocfopen (inbuf, FNAME_SITECONFIG_OLD, &stream))
988         goto OCFOPEN_OK;
989
990     /* (4) try to find file in HOME directory */
991     q = getenv ("HOME");
992     if (debug_ocf)
993         fprintf (aa_stderr, "ocfopen '%s'. HOME='%s'.\n",
994             FNAME_SITECONFIG, NULLORSTR (q));
995     if (ocfopen (q, FNAME_SITECONFIG, &stream))
996         goto OCFOPEN_OK;
997     if (ocfopen (q, FNAME_SITECONFIG_OLD, &stream))
998         goto OCFOPEN_OK;
999
1000     /* (5) try to find file in server daemon's inittab directory */
1001     if (debug_ocf)
1002         fprintf (aa_stderr, "ocfopen '%s'. OE_inittab_dir='%s'.\n",
1003             FNAME_SITECONFIG, NULLORSTR (OE_inittab_dir));
1004     if (ocfopen (OE_inittab_dir, FNAME_SITECONFIG, &stream))
1005         goto OCFOPEN_OK;
1006     if (ocfopen (OE_inittab_dir, FNAME_SITECONFIG_OLD, &stream))
1007         goto OCFOPEN_OK;
1008
1009     /* (6) try to find file in server daemon's inetd.conf directory */
1010     if (debug_ocf)
1011         fprintf (aa_stderr, "ocfopen '%s'. OE_server_dir='%s'.\n",
1012             FNAME_SITECONFIG, NULLORSTR (OE_server_dir));
1013     if (ocfopen (OE_server_dir, FNAME_SITECONFIG, &stream))
1014         goto OCFOPEN_OK;
1015     if (ocfopen (OE_server_dir, FNAME_SITECONFIG_OLD, &stream))
1016         goto OCFOPEN_OK;
1017
1018     /* If we can't find site config on OE side we must quit.  */
1019     sprintf (sprintbuf, catgets (dtsearch_catd, MS_loadocf, 1643,
1020             "%1$sCannot find or open '%2$s'\n"
1021             "  configuration file in any of the following locations:\n"
1022             "    DTSROCFPATH directory =     %3$s,\n"
1023             "    current working directory = %4$s,\n"
1024             "    HOME directory =            %5$s,\n"
1025             "    /etc/inittab directory =    %6$s\n"
1026             "    /etc/inetd.conf directory = %7$s\n"
1027         )
1028         ,PROGNAME "1643 "
1029         ,FNAME_SITECONFIG
1030         ,(p) ? p : catgets (dtsearch_catd, MS_misc, 16,
1031             "<no environment variable>")
1032         ,inbuf
1033         ,(q) ? q : catgets (dtsearch_catd, MS_misc, 16,
1034             "<no environment variable>")
1035         ,(OE_inittab_dir) ? OE_inittab_dir :
1036             catgets (dtsearch_catd, MS_misc, 17,
1037             "<no server daemon>")
1038         ,(OE_server_dir) ? OE_server_dir :
1039             catgets (dtsearch_catd, MS_misc, 17,
1040             "<no server daemon>")
1041         );
1042     DtSearchAddMessage (sprintbuf);
1043     return FALSE;
1044
1045 OCFOPEN_OK:
1046
1047     /* MAIN LOOP ---- Read each line from file */
1048     *inbuf = '\0';
1049     while (fgets (inbuf, sizeof (inbuf) - 1, stream) != NULL) {
1050         /* terminate string just to be sure */
1051         if (strlen(inbuf) && inbuf[strlen(inbuf)-1] == '\n')
1052           inbuf[strlen(inbuf)-1] = '\0';
1053
1054         /* test for comment line */
1055         if (strchr (COMMENT_CHARS, inbuf[0]))
1056             continue;
1057
1058         /* parse KEYWORD */
1059         if ((token = _XStrtok(inbuf, DELIMITERS, strtok_buf)) == NULL)
1060             continue;
1061         strcpy (uprtoken, token);
1062         strupr (uprtoken);
1063
1064         if (strcmp (uprtoken, "PATH") == 0) {
1065             if (read_database (uprtoken, &strtok_buf))
1066                 read_path (&strtok_buf);
1067             continue;
1068         }
1069         if (strcmp (uprtoken, "KEYTYPES") == 0) {
1070             if (read_database (uprtoken, &strtok_buf))
1071                 read_keytypes (MAX_KTCOUNT, &strtok_buf);
1072             continue;
1073         }
1074         if (strcmp (uprtoken, "KEYTYPE") == 0) {
1075             if (read_database (uprtoken, &strtok_buf))
1076                 read_keytypes (1, &strtok_buf);
1077             continue;
1078         }
1079         if (strcmp (uprtoken, "FILEIO") == 0) {
1080             set_fileio (&strtok_buf);
1081             continue;
1082         }
1083         if (strcmp (uprtoken, "USERNOTES") == 0) {
1084             set_boolint (&OE_enable_usernotes, uprtoken, &strtok_buf);
1085             continue;
1086         }
1087         if (strcmp (uprtoken, "MARKDEL") == 0) {
1088             set_boolint (&OE_enable_markdel, uprtoken, &strtok_buf);
1089             continue;
1090         }
1091         if (strcmp (uprtoken, "FASTDECODE") == 0) {
1092             set_boolint (&OE_fastdecode, uprtoken, &strtok_buf);
1093             continue;
1094         }
1095         if (strcmp (uprtoken, "NOITERATE") == 0) {
1096             set_boolbit (&OE_flags, OE_NO_ITERATE, uprtoken, &strtok_buf);
1097             continue;
1098         }
1099         if (strcmp (uprtoken, "UPPERKEYS") == 0) {
1100             set_boolint (&OE_uppercase_keys, uprtoken, &strtok_buf);
1101             continue;
1102         }
1103         if (strcmp (uprtoken, "AUDIT") == 0) {
1104             set_boolbit (&OE_flags, OE_AUDIT, uprtoken, &strtok_buf);
1105             continue;
1106         }
1107         if (strcmp (uprtoken, "FILEPATH") == 0) {
1108             read_filepath (&strtok_buf);
1109             continue;
1110         }
1111         if (strcmp (uprtoken, "WHITLIM") == 0) {
1112             set_long (&OE_words_hitlimit, uprtoken, &strtok_buf);
1113             continue;
1114         }
1115         if (strcmp (token, "d3bug") == 0) {
1116             usrblk.debug |= atol (token + strlen (token) + 1);
1117             printf (PROGNAME "1630 %s: usrblk.debug = %ld, x%08lx.\n",
1118                 cfgfname, usrblk.debug, usrblk.debug);
1119             continue;
1120         }
1121         if (strcmp (uprtoken, "MAXHITS") == 0) {
1122             if (read_database (uprtoken, &strtok_buf))
1123                 read_maxhits (&strtok_buf);
1124             continue;
1125         }
1126         if (strcmp (uprtoken, "LABEL") == 0) {
1127             if (read_database (uprtoken, &strtok_buf))
1128                 read_rest_of_line (uprtoken, &db->label, &strtok_buf);
1129             continue;
1130         }
1131
1132         /* if we made it this far, the first token is unknown */
1133         sprintf (sprintbufp,
1134             catgets (dtsearch_catd, MS_loadocf, 495,
1135             "%s %s: '%.16s' is invalid keyword.\n"),
1136             PROGNAME"495", cfgfname, token);
1137         DtSearchAddMessage (sprintbufp);
1138
1139 #ifdef DEBUG_DUMP
1140         dumpboth ();
1141 #endif
1142     }   /* ...keep reading next line in file until eof */
1143
1144     fclose (stream);
1145
1146     /* In a site config file, SOME database has to have been specified.
1147      * Also for each database, KEYTYPES must be present and perfect.
1148      * If no keytypes survived the KEYTYPES parse for each database,
1149      * or if there was no KEYTYPES line for a database
1150      * in site config file, remove the dblk and mark fatal error.
1151      */
1152     if (usrblk.dblist == NULL) {
1153         sprintf (sprintbufp, catgets (dtsearch_catd, MS_loadocf, 1869,
1154             "%s %s: Configuration file must specify at least one database."),
1155             PROGNAME"1869", cfgfname);
1156         DtSearchAddMessage (sprintbufp);
1157         fatal_error = TRUE;
1158     }
1159     else
1160         for (db = usrblk.dblist; db != NULL; db = db->link) {
1161             if (db->ktcount <= 0) {
1162                 sprintf (sprintbufp, catgets (dtsearch_catd, MS_loadocf, 986,
1163                     "%s %s: KEYTYPES missing for database '%s'."),
1164                     PROGNAME"986", cfgfname, db->name);
1165                 DtSearchAddMessage (sprintbufp);
1166                 cutnode_llist ((LLIST *)db, (LLIST **)&usrblk.dblist);
1167                 free (db);
1168                 fatal_error = TRUE;
1169             }
1170         }
1171
1172 #ifdef DEBUG_OEF
1173     dumpoef ("AFTER");
1174 #endif
1175
1176 #ifdef DEBUG_DUMP
1177     printf (PROGNAME "516 leaving load_ocf().\n");
1178     dumpboth ();
1179 #endif
1180
1181     if (fatal_error)
1182         return FALSE;
1183     usrblk.dblk = usrblk.dblist;        /* init usrblk.dblk */
1184     return TRUE;
1185 }  /* load_ocf() */
1186
1187 /*************************** OCF.C ****************************/