1/*
2Copyright 1987, 1998  The Open Group
3
4Permission to use, copy, modify, distribute, and sell this software and its
5documentation for any purpose is hereby granted without fee, provided that
6the above copyright notice appear in all copies and that both that
7copyright notice and this permission notice appear in supporting
8documentation.
9
10The above copyright notice and this permission notice shall be included in
11all copies or substantial portions of the Software.
12
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
16OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
17AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
20Except as contained in this notice, the name of The Open Group shall not be
21used in advertising or otherwise to promote the sale, use or other dealings
22in this Software without prior written authorization from The Open Group.
23 * Copyright 1990, 1991 Network Computing Devices;
24 * Portions Copyright 1987 by Digital Equipment Corporation
25 *
26 * Permission to use, copy, modify, distribute, and sell this software and its
27 * documentation for any purpose is hereby granted without fee, provided that
28 * the above copyright notice appear in all copies and that both that
29 * copyright notice and this permission notice appear in supporting
30 * documentation, and that the names of Network Computing Devices,
31 * or Digital not be used in advertising or
32 * publicity pertaining to distribution of the software without specific,
33 * written prior permission.  Network Computing Devices, or Digital
34 * make no representations about the
35 * suitability of this software for any purpose.  It is provided "as is"
36 * without express or implied warranty.
37 *
38 * NETWORK COMPUTING DEVICES, AND DIGITAL DISCLAIM ALL WARRANTIES WITH
39 * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
40 * AND FITNESS, IN NO EVENT SHALL NETWORK COMPUTING DEVICES, OR DIGITAL BE
41 * LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
42 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
43 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
44 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
45 *
46 */
47
48#include	"config.h"
49
50#include	<stdio.h>
51#include	<stdlib.h>
52#include	<ctype.h>
53#include	<X11/Xtrans/Xtrans.h>
54#include	<X11/Xos.h>
55#include	"misc.h"
56#include	"configstr.h"
57#include	"osdep.h"
58#include	"globals.h"
59#include	"access.h"
60#include	"difsutils.h"
61#include	"difs.h"
62#include        <X11/fonts/libxfont2.h>
63
64/* libXfont/src/bitmap/snfstr.h */
65extern void SnfSetFormat(int bit, int byte, int glyph, int scan);
66
67static const char * const default_config_files[] = {
68#ifdef DEFAULT_CONFIG_FILE
69    DEFAULT_CONFIG_FILE,
70#else
71    "/usr/lib/X11/fs/config",
72#endif
73    NULL
74};
75
76static char *font_catalogue = NULL;
77
78static char *config_set_int(ConfigOptionPtr parm, char *val);
79static char *config_set_bool(ConfigOptionPtr parm, char *val);
80static char *config_set_catalogue(ConfigOptionPtr parm, char *val);
81static char *config_set_glyph_caching_mode(ConfigOptionPtr parm, char *val);
82static char *config_set_list(ConfigOptionPtr parm, char *val);
83static char *config_set_file(ConfigOptionPtr parm, char *val);
84static char *config_set_resolutions(ConfigOptionPtr parm, char *val);
85static char *config_set_ignored_transports(ConfigOptionPtr parm, char *val);
86static char *config_set_snf_format(ConfigOptionPtr parm, char *val);
87
88/* these need to be in lower case and alphabetical order so a
89 * binary search lookup can be used
90 */
91static ConfigOptionRec config_options[] = {
92    {"alternate-servers", config_set_list},
93    {"catalogue", config_set_catalogue},
94    {"client-limit", config_set_int},
95    {"clone-self", config_set_bool},
96    {"default-point-size", config_set_int},
97    {"default-resolutions", config_set_resolutions},
98    {"deferglyphs", config_set_glyph_caching_mode},
99    {"error-file", config_set_file},
100    {"no-listen", config_set_ignored_transports},
101    {"port", config_set_int},
102    {"server-number", config_set_int},
103    {"snf-format", config_set_snf_format},
104    {"trusted-clients", config_set_list},
105    {"use-syslog", config_set_bool},
106    {NULL, NULL},
107};
108
109/* max size in bytes of config file */
110#define	CONFIG_MAX_FILESIZE	32767
111
112#define	CONFIG_ERR_MEMORY \
113    "CONFIG: insufficient memory to load configuration file \"%s\"\n"
114#define	CONFIG_ERR_OPEN "CONFIG: can't open configuration file \"%s\"\n"
115#define	CONFIG_ERR_READ "CONFIG: error reading configuration file \"%s\"\n"
116#define	CONFIG_ERR_VALUE "CONFIG: bad value \"%s\" for parameter \"%s\"\n"
117#define	CONFIG_ERR_UNKNOWN "CONFIG: unknown parameter \"%s\"\n"
118#define	CONFIG_ERR_NOEQUALS "CONFIG: missing '=' after parameter \"%s\"\n"
119#define	CONFIG_ERR_RANGE "CONFIG: value out of range for parameter \"%s\"\n"
120#define	CONFIG_ERR_SYNTAX "CONFIG: syntax error near parameter \"%s\"\n"
121#define	CONFIG_ERR_NOVALUE "CONFIG: missing value for parameter \"%s\"\n"
122#define	CONFIG_ERR_EXTRAVALUE "CONFIG: extra value for parameter \"%s\"\n"
123
124#define	iseol(c)	((c) == '\n' || (c) == '\r' || (c) == '\f')
125#define	skip_whitespace(c)	while(isspace(*(c)) || *(c) == ',') (c)++;
126#define	skip_val(c)	while(!isspace(*(c)) && *(c) != ',' && *(c) != '\0')\
127						(c) ++;
128#define	skip_list_val(c)	while(!isspace(*(c)) && *(c) != '\0')\
129						(c) ++;
130#define	blank_comment(c)	while (!iseol(*(c)) && *(c) != '\0')	\
131						*(c)++= ' ';
132
133static char *
134next_assign(char *c)
135{
136    int         nesting = 0;
137
138    while (*c != '\0') {
139	if (*c == '(')
140	    nesting++;
141	else if (*c == ')')
142	    nesting--;
143	else if (*c == '=' && nesting == 0)
144	    return c;
145	c++;
146    }
147    return (char *) 0;
148}
149
150static void
151strip_comments(char *data)
152{
153    char       *c;
154
155    c = data;
156    while ((c = strchr(c, '#')) != NULL) {
157	if (c == data || *(c - 1) != '\\') {
158	    blank_comment(c);
159	} else {
160	    c++;
161	}
162    }
163}
164
165static ConfigOptionPtr
166match_param_name(char *name)
167{
168    int         pos,
169                rc,
170                low,
171                high;
172
173    low = 0;
174    high = sizeof(config_options) / sizeof(ConfigOptionRec) - 2;
175    pos = high >> 1;
176
177    while (low <= high) {
178	rc = strcmp(name, config_options[pos].parm_name);
179	if (rc == 0) {
180	    return &config_options[pos];
181	} else if (rc < 0) {
182	    high = pos - 1;
183	} else {
184	    low = pos + 1;
185	}
186	pos = ((high + low) >> 1);
187    }
188    return NULL;
189}
190
191static int
192parse_config(char *data)
193{
194    char       *c,
195               *val = NULL,
196               *next_eq,
197               *consumed,
198               *p;
199    char        param_name[64];
200    Bool        equals_missing;
201    ConfigOptionPtr param;
202
203    c = data;
204    skip_whitespace(c);
205
206    while (*c != '\0') {
207	equals_missing = FALSE;
208
209	/* get parm name in lower case */
210	p = c;
211	while (isalnum(*c) || *c == '-') {
212	    if (isupper(*c))
213		*c = tolower(*c);
214	    c++;
215	}
216	memcpy(param_name, p, min(sizeof(param_name), (int) (c - p)));
217	param_name[(int) (c - p)] = '\0';
218
219	/* check for junk */
220	if (!isspace(*c) && *c != '=') {
221	    ErrorF(CONFIG_ERR_SYNTAX, param_name);
222	    /* eat garbage */
223	    while (!isspace(*c) && *c != '=' && *c != '\0')
224		c++;
225	}
226	skip_whitespace(c);
227	if (*c != '=') {
228	    ErrorF(CONFIG_ERR_NOEQUALS, param_name);
229	    equals_missing = TRUE;
230	} else {
231	    c++;
232	}
233
234	skip_whitespace(c);
235
236	/* find next assignment to guess where the value ends */
237	if ((next_eq = next_assign(c)) != NULL) {
238	    /* back up over whitespace */
239	    for (val = next_eq - 1; val >= c &&
240		    (isspace(*val) || *val == ',');
241		    val--);
242
243	    /* back over parm name */
244	    for (; val >= c && (isalnum(*val) || *val == '-'); val--);
245
246	    if (val <= c) {
247		/* no value, ignore */
248		ErrorF(CONFIG_ERR_NOVALUE, param_name);
249		continue;
250	    }
251	    *val = '\0';
252	} else if (*c == '\0') {
253	    /* no value, ignore */
254	    ErrorF(CONFIG_ERR_NOVALUE, param_name);
255	    continue;
256	}
257	/* match parm name */
258	if (equals_missing) {
259	    equals_missing = FALSE;
260	} else if ((param = match_param_name(param_name)) == NULL) {
261	    ErrorF(CONFIG_ERR_UNKNOWN, param_name);
262	} else {
263	    consumed = (param->set_func) (param, c);
264
265	    skip_whitespace(consumed);
266	    if (*consumed != '\0') {
267		ErrorF(CONFIG_ERR_EXTRAVALUE,
268		       param_name);
269	    }
270	}
271
272	if (next_eq != NULL)
273	    c = val + 1;
274	else			/* last setting */
275	    break;
276    }
277    return FSSuccess;
278}
279
280/*
281 * handles anything that should be set once the file is parsed
282 */
283void
284SetConfigValues(void)
285{
286    int         err,
287                num;
288
289    if (font_catalogue == NULL) {
290	FatalError("font catalogue is missing/empty\n");
291    }
292
293    err = SetFontCatalogue(font_catalogue, &num);
294    if (err != FSSuccess) {
295	FatalError("element #%d (starting at 0) of font path is bad or has a bad font:\n\"%s\"\n",
296		   num, font_catalogue);
297    }
298    InitErrors();
299    FSfree((char *) font_catalogue);
300    font_catalogue = NULL;
301}
302
303
304/* If argument is NULL, uses first file found from default_config_files */
305int
306ReadConfigFile(const char *filename)
307{
308    FILE       *fp = NULL;
309    int         ret;
310    int         len;
311    int         i;
312    char       *data;
313
314    data = (char *) FSalloc(CONFIG_MAX_FILESIZE);
315    if (!data) {
316	ErrorF(CONFIG_ERR_MEMORY, filename);
317	return FSBadAlloc;
318    }
319    if (filename != NULL) {
320	fp = fopen(filename, "r");
321	if (fp == NULL) {
322	    ErrorF(CONFIG_ERR_OPEN, filename);
323	}
324    } else {
325	for (i = 0; default_config_files[i] != NULL; i++) {
326	    filename = default_config_files[i];
327	    if ((fp = fopen(filename, "r")) != NULL) {
328		if (configfilename == NULL) {
329		    configfilename = strdup(filename); /* save for clones */
330		}
331		break;
332	    }
333	}
334	if (fp == NULL) {
335	    for (i = 0; default_config_files[i] != NULL; i++) {
336		ErrorF(CONFIG_ERR_OPEN, default_config_files[i]);
337	    }
338	}
339    }
340    if (fp == NULL) {
341	FSfree(data);
342	return FSBadName;
343    }
344    ret = fread(data, sizeof(char), CONFIG_MAX_FILESIZE, fp);
345    if (ret <= 0) {
346	FSfree(data);
347	(void) fclose(fp);
348	ErrorF(CONFIG_ERR_READ, filename);
349	return FSBadName;
350    }
351    len = ftell(fp);
352    len = min(len, CONFIG_MAX_FILESIZE);
353    data[len] = '\0';		/* NULL terminate the data */
354
355    (void) fclose(fp);
356
357    strip_comments(data);
358    ret = parse_config(data);
359
360    FSfree(data);
361
362    return ret;
363}
364
365struct nameVal {
366    const char *name;
367    int         val;
368};
369
370static char *
371config_parse_nameVal (
372    ConfigOptionPtr parm,
373    char       *c,
374    int        *ret,
375    int		*pval,
376    struct nameVal   *name_val)
377{
378    char       *start,
379                t;
380    int         i,
381                len;
382
383    start = c;
384    skip_val(c);
385    t = *c;
386    *c = '\0';
387    len = c - start;
388
389    for (i = 0; name_val[i].name; i++) {
390	if (!strncmpnocase(start, name_val[i].name, len)) {
391	    *pval = name_val[i].val;
392	    *ret = 0;
393	    *c = t;
394	    return c;
395	}
396    }
397    ErrorF(CONFIG_ERR_VALUE, start, parm->parm_name);
398    *c = t;
399    *ret = -1;
400    return c;
401}
402
403static char *
404config_parse_bool (
405    ConfigOptionPtr parm,
406    char	*c,
407    int		*ret,
408    Bool	*pval)
409{
410    static struct nameVal bool_val[] = {
411    	    { "yes",   TRUE },
412    	    { "on",    TRUE },
413    	    { "1",     TRUE },
414    	    { "true",  TRUE },
415    	    { "no",    FALSE },
416    	    { "off",   FALSE },
417    	    { "0",     FALSE },
418    	    { "false", FALSE },
419    	    { (char *) 0, 0 },
420    };
421    return config_parse_nameVal (parm, c, ret, pval, bool_val);
422}
423
424static char *
425config_parse_int(
426    ConfigOptionPtr parm,
427    char       *c,
428    int        *ret,
429    int        *pval)
430{
431    char       *start,
432                t;
433
434    start = c;
435    while (*c != '\0' && !isspace(*c) && *c != ',') {
436	if (!isdigit(*c)) {	/* error */
437	    skip_val(c);
438	    t = *c;
439	    *c = '\0';
440	    ErrorF(CONFIG_ERR_VALUE, start, parm->parm_name);
441	    *ret = -1;
442	    *c = t;
443	    return c;
444	}
445	c++;
446    }
447    t = *c;
448    *c = '\0';
449    *ret = 0;
450    *pval = atoi(start);
451    *c = t;
452    return c;
453}
454
455
456/* config option sets */
457/* these have to know how to do the real work and tweak the proper things */
458static char *
459config_set_int(
460    ConfigOptionPtr parm,
461    char       *val)
462{
463    int         ival,
464                ret;
465
466    val = config_parse_int(parm, val, &ret, &ival);
467    if (ret == -1)
468	return val;
469
470    /* now do individual attribute checks */
471    if (!strcmp(parm->parm_name, "port") && !portFromCmdline) {
472	ListenPort = ival;
473    } else if (!strcmp(parm->parm_name, "client-limit")) {
474	AccessSetConnectionLimit(ival);
475    } else if (!strcmp(parm->parm_name, "default-point-size")) {
476	SetDefaultPointSize(ival);
477    }
478    return val;
479}
480
481static char *
482config_set_bool(
483    ConfigOptionPtr parm,
484    char       *val)
485{
486    int
487                ret;
488    Bool        bval;
489
490    val = config_parse_bool(parm, val, &ret, &bval);
491    if (ret == -1)
492	return val;
493
494    /* now do individual attribute checks */
495    if (!strcmp(parm->parm_name, "use-syslog")) {
496	UseSyslog = bval;
497    } else if (!strcmp(parm->parm_name, "clone-self")) {
498	CloneSelf = bval;
499    }
500    return val;
501}
502
503static char *
504config_set_file(
505    ConfigOptionPtr parm,
506    char       *val)
507{
508    char       *start = val,
509                t;
510
511    skip_val(val);
512    t = *val;
513    *val = '\0';
514    if (!strcmp(parm->parm_name, "error-file")) {
515	memmove( ErrorFile, start, val - start + 1);
516    }
517    *val = t;
518    return val;
519}
520
521static char *
522config_set_catalogue(
523    ConfigOptionPtr parm,
524    char       *val)
525{
526    char       *b;
527
528    if (!strcmp(parm->parm_name, "catalogue")) {
529	/* stash it for later */
530	FSfree((char *) font_catalogue);	/* dump any previous one */
531	b = font_catalogue = (char *) FSalloc(strlen(val) + 1);
532	if (!font_catalogue)
533	    FatalError("insufficient memory for font catalogue\n");
534	while (*val) {		/* remove all the gunk */
535	    if (!isspace(*val)) {
536		*b++ = *val;
537	    }
538	    val++;
539	}
540	*b = '\0';
541    }
542    return val;
543}
544
545static char *
546config_set_list(
547    ConfigOptionPtr parm,
548    char       *val)
549{
550    char       *start = val,
551                t;
552
553    skip_list_val(val);
554    t = *val;
555    *val = '\0';
556    if (!strcmp(parm->parm_name, "alternate-servers")) {
557	SetAlternateServers(start);
558    }
559    *val = t;
560    return val;
561}
562
563static char *
564config_set_ignored_transports(
565    ConfigOptionPtr parm,
566    char       *val)
567{
568    char       *start = val,
569                t;
570
571    skip_list_val(val);
572    t = *val;
573    *val = '\0';
574    _FontTransNoListen(start);
575    *val = t;
576    return val;
577}
578
579static char *
580config_set_glyph_caching_mode(
581    ConfigOptionPtr parm,
582    char       *val)
583{
584    char       *start = val,
585                t;
586
587    skip_list_val(val);
588    t = *val;
589    *val = '\0';
590    if (!strcmp(parm->parm_name, "deferglyphs")) {
591	xfont2_parse_glyph_caching_mode(start);
592    }
593    *val = t;
594    return val;
595}
596
597static char *
598config_set_resolutions(
599    ConfigOptionPtr parm,
600    char       *val)
601{
602    char       *start = val,
603                t;
604    int         err;
605
606    skip_list_val(val);
607    t = *val;
608    *val = '\0';
609    if (!strcmp(parm->parm_name, "default-resolutions")) {
610	err = SetDefaultResolutions(start);
611	if (err != FSSuccess) {
612	    FatalError("bogus resolution list \"%s\"\n", start);
613	}
614    }
615    *val = t;
616    return val;
617}
618
619
620static char *
621config_parse_endian(
622    ConfigOptionPtr parm,
623    char       *c,
624    int        *ret,
625    int		*pval)
626{
627    static struct nameVal endian_val[] = {
628	{ "lsb",      LSBFirst },
629	{ "little",   LSBFirst },
630	{ "lsbfirst", LSBFirst },
631	{ "msb",      MSBFirst },
632	{ "big",      MSBFirst },
633	{ "msbfirst", MSBFirst },
634	{ (char *) 0, 0 },
635    };
636    return config_parse_nameVal (parm, c, ret, pval, endian_val);
637}
638
639/* ARGSUSED */
640static char *
641config_set_snf_format (
642    ConfigOptionPtr parm,
643    char	    *val)
644{
645    int	    bit, byte, glyph, scan;
646    int	    ret;
647
648    val = config_parse_endian (parm, val, &ret, &bit);
649    if (ret == -1)
650	return val;
651    skip_whitespace (val);
652    val = config_parse_endian (parm, val, &ret, &byte);
653    if (ret == -1)
654	return val;
655    skip_whitespace (val);
656    val = config_parse_int (parm, val, &ret, &glyph);
657    if (ret == -1)
658	return val;
659    skip_whitespace (val);
660    val = config_parse_int (parm, val, &ret, &scan);
661    if (ret == -1)
662	return val;
663#ifdef XFONT_SNFFORMAT
664    SnfSetFormat (bit, byte, glyph, scan);
665#endif
666    return val;
667}
668