1 /*
2 * $Id: rc.c,v 1.53 2018/05/31 20:32:15 tom Exp $
3 *
4 * rc.c -- routines for processing the configuration file
5 *
6 * Copyright 2000-2012,2018 Thomas E. Dickey
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License, version 2.1
10 * as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to
19 * Free Software Foundation, Inc.
20 * 51 Franklin St., Fifth Floor
21 * Boston, MA 02110, USA.
22 *
23 * An earlier version of this program lists as authors
24 * Savio Lam (lam836@cs.cuhk.hk)
25 */
26
27 #include <dialog.h>
28
29 #include <dlg_keys.h>
30
31 #ifdef HAVE_COLOR
32 #include <dlg_colors.h>
33
34 /*
35 * For matching color names with color values
36 */
37 static const color_names_st color_names[] =
38 {
39 #ifdef HAVE_USE_DEFAULT_COLORS
40 {"DEFAULT", -1},
41 #endif
42 {"BLACK", COLOR_BLACK},
43 {"RED", COLOR_RED},
44 {"GREEN", COLOR_GREEN},
45 {"YELLOW", COLOR_YELLOW},
46 {"BLUE", COLOR_BLUE},
47 {"MAGENTA", COLOR_MAGENTA},
48 {"CYAN", COLOR_CYAN},
49 {"WHITE", COLOR_WHITE},
50 }; /* color names */
51 #define COLOR_COUNT (sizeof(color_names) / sizeof(color_names[0]))
52 #endif /* HAVE_COLOR */
53
54 #define GLOBALRC "/etc/dialogrc"
55 #define DIALOGRC ".dialogrc"
56
57 /* Types of values */
58 #define VAL_INT 0
59 #define VAL_STR 1
60 #define VAL_BOOL 2
61
62 /* Type of line in configuration file */
63 typedef enum {
64 LINE_ERROR = -1,
65 LINE_EQUALS,
66 LINE_EMPTY
67 } PARSE_LINE;
68
69 /* number of configuration variables */
70 #define VAR_COUNT (sizeof(vars) / sizeof(vars_st))
71
72 /* check if character is string quoting characters */
73 #define isquote(c) ((c) == '"' || (c) == '\'')
74
75 /* get last character of string */
76 #define lastch(str) str[strlen(str)-1]
77
78 /*
79 * Configuration variables
80 */
81 typedef struct {
82 const char *name; /* name of configuration variable as in DIALOGRC */
83 void *var; /* address of actual variable to change */
84 int type; /* type of value */
85 const char *comment; /* comment to put in "rc" file */
86 } vars_st;
87
88 /*
89 * This table should contain only references to dialog_state, since dialog_vars
90 * is reset specially in dialog.c before each widget.
91 */
92 static const vars_st vars[] =
93 {
94 {"aspect",
95 &dialog_state.aspect_ratio,
96 VAL_INT,
97 "Set aspect-ration."},
98
99 {"separate_widget",
100 &dialog_state.separate_str,
101 VAL_STR,
102 "Set separator (for multiple widgets output)."},
103
104 {"tab_len",
105 &dialog_state.tab_len,
106 VAL_INT,
107 "Set tab-length (for textbox tab-conversion)."},
108
109 {"visit_items",
110 &dialog_state.visit_items,
111 VAL_BOOL,
112 "Make tab-traversal for checklist, etc., include the list."},
113
114 #ifdef HAVE_COLOR
115 {"use_shadow",
116 &dialog_state.use_shadow,
117 VAL_BOOL,
118 "Shadow dialog boxes? This also turns on color."},
119
120 {"use_colors",
121 &dialog_state.use_colors,
122 VAL_BOOL,
123 "Turn color support ON or OFF"},
124 #endif /* HAVE_COLOR */
125 }; /* vars */
126
127 static int
skip_whitespace(char * str,int n)128 skip_whitespace(char *str, int n)
129 {
130 while (isblank(UCH(str[n])) && str[n] != '\0')
131 n++;
132 return n;
133 }
134
135 static int
skip_keyword(char * str,int n)136 skip_keyword(char *str, int n)
137 {
138 while (isalnum(UCH(str[n])) && str[n] != '\0')
139 n++;
140 return n;
141 }
142
143 static int
find_vars(char * name)144 find_vars(char *name)
145 {
146 int result = -1;
147 unsigned i;
148
149 for (i = 0; i < VAR_COUNT; i++) {
150 if (dlg_strcmp(vars[i].name, name) == 0) {
151 result = (int) i;
152 break;
153 }
154 }
155 return result;
156 }
157
158 #ifdef HAVE_COLOR
159 static int
find_color(char * name)160 find_color(char *name)
161 {
162 int result = -1;
163 int i;
164 int limit = dlg_color_count();
165
166 for (i = 0; i < limit; i++) {
167 if (dlg_strcmp(dlg_color_table[i].name, name) == 0) {
168 result = i;
169 break;
170 }
171 }
172 return result;
173 }
174
175 /*
176 * Convert an attribute to a string representation like this:
177 *
178 * "(foreground,background,highlight)"
179 */
180 static char *
attr_to_str(char * str,int fg,int bg,int hl)181 attr_to_str(char *str, int fg, int bg, int hl)
182 {
183 int i;
184
185 strcpy(str, "(");
186 /* foreground */
187 for (i = 0; fg != color_names[i].value; i++) ;
188 strcat(str, color_names[i].name);
189 strcat(str, ",");
190
191 /* background */
192 for (i = 0; bg != color_names[i].value; i++) ;
193 strcat(str, color_names[i].name);
194
195 /* highlight */
196 strcat(str, hl ? ",ON)" : ",OFF)");
197
198 return str;
199 }
200
201 /*
202 * Extract the foreground, background and highlight values from an attribute
203 * represented as a string in one of two forms:
204 *
205 * "(foreground,background,highlight)"
206 " "xxxx_color"
207 */
208 static int
str_to_attr(char * str,int * fg,int * bg,int * hl)209 str_to_attr(char *str, int *fg, int *bg, int *hl)
210 {
211 int i = 0, get_fg = 1;
212 unsigned j;
213 char tempstr[MAX_LEN + 1], *part;
214 size_t have;
215
216 if (str[0] != '(' || lastch(str) != ')') {
217 if ((i = find_color(str)) >= 0) {
218 *fg = dlg_color_table[i].fg;
219 *bg = dlg_color_table[i].bg;
220 *hl = dlg_color_table[i].hilite;
221 return 0;
222 }
223 return -1; /* invalid representation */
224 }
225
226 /* remove the parenthesis */
227 have = strlen(str);
228 if (have > MAX_LEN) {
229 have = MAX_LEN - 1;
230 } else {
231 have -= 2;
232 }
233 memcpy(tempstr, str + 1, have);
234 tempstr[have] = '\0';
235
236 /* get foreground and background */
237
238 while (1) {
239 /* skip white space before fg/bg string */
240 i = skip_whitespace(tempstr, i);
241 if (tempstr[i] == '\0')
242 return -1; /* invalid representation */
243 part = tempstr + i; /* set 'part' to start of fg/bg string */
244
245 /* find end of fg/bg string */
246 while (!isblank(UCH(tempstr[i])) && tempstr[i] != ','
247 && tempstr[i] != '\0')
248 i++;
249
250 if (tempstr[i] == '\0')
251 return -1; /* invalid representation */
252 else if (isblank(UCH(tempstr[i]))) { /* not yet ',' */
253 tempstr[i++] = '\0';
254
255 /* skip white space before ',' */
256 i = skip_whitespace(tempstr, i);
257 if (tempstr[i] != ',')
258 return -1; /* invalid representation */
259 }
260 tempstr[i++] = '\0'; /* skip the ',' */
261 for (j = 0; j < COLOR_COUNT && dlg_strcmp(part, color_names[j].name);
262 j++) ;
263 if (j == COLOR_COUNT) /* invalid color name */
264 return -1;
265 if (get_fg) {
266 *fg = color_names[j].value;
267 get_fg = 0; /* next we have to get the background */
268 } else {
269 *bg = color_names[j].value;
270 break;
271 }
272 } /* got foreground and background */
273
274 /* get highlight */
275
276 /* skip white space before highlight string */
277 i = skip_whitespace(tempstr, i);
278 if (tempstr[i] == '\0')
279 return -1; /* invalid representation */
280 part = tempstr + i; /* set 'part' to start of highlight string */
281
282 /* trim trailing white space from highlight string */
283 i = (int) strlen(part) - 1;
284 while (isblank(UCH(part[i])) && i > 0)
285 i--;
286 part[i + 1] = '\0';
287
288 if (!dlg_strcmp(part, "ON"))
289 *hl = TRUE;
290 else if (!dlg_strcmp(part, "OFF"))
291 *hl = FALSE;
292 else
293 return -1; /* invalid highlight value */
294
295 return 0;
296 }
297 #endif /* HAVE_COLOR */
298
299 /*
300 * Check if the line begins with a special keyword; if so, return true while
301 * pointing params to its parameters.
302 */
303 static int
begins_with(char * line,const char * keyword,char ** params)304 begins_with(char *line, const char *keyword, char **params)
305 {
306 int i = skip_whitespace(line, 0);
307 int j = skip_keyword(line, i);
308
309 if ((j - i) == (int) strlen(keyword)) {
310 char save = line[j];
311 line[j] = 0;
312 if (!dlg_strcmp(keyword, line + i)) {
313 *params = line + skip_whitespace(line, j + 1);
314 return 1;
315 }
316 line[j] = save;
317 }
318
319 return 0;
320 }
321
322 /*
323 * Parse a line in the configuration file
324 *
325 * Each line is of the form: "variable = value". On exit, 'var' will contain
326 * the variable name, and 'value' will contain the value string.
327 *
328 * Return values:
329 *
330 * LINE_EMPTY - line is blank or comment
331 * LINE_EQUALS - line contains "variable = value"
332 * LINE_ERROR - syntax error in line
333 */
334 static PARSE_LINE
parse_line(char * line,char ** var,char ** value)335 parse_line(char *line, char **var, char **value)
336 {
337 int i = 0;
338
339 /* ignore white space at beginning of line */
340 i = skip_whitespace(line, i);
341
342 if (line[i] == '\0') /* line is blank */
343 return LINE_EMPTY;
344 else if (line[i] == '#') /* line is comment */
345 return LINE_EMPTY;
346 else if (line[i] == '=') /* variable names cannot start with a '=' */
347 return LINE_ERROR;
348
349 /* set 'var' to variable name */
350 *var = line + i++; /* skip to next character */
351
352 /* find end of variable name */
353 while (!isblank(UCH(line[i])) && line[i] != '=' && line[i] != '\0')
354 i++;
355
356 if (line[i] == '\0') /* syntax error */
357 return LINE_ERROR;
358 else if (line[i] == '=')
359 line[i++] = '\0';
360 else {
361 line[i++] = '\0';
362
363 /* skip white space before '=' */
364 i = skip_whitespace(line, i);
365
366 if (line[i] != '=') /* syntax error */
367 return LINE_ERROR;
368 else
369 i++; /* skip the '=' */
370 }
371
372 /* skip white space after '=' */
373 i = skip_whitespace(line, i);
374
375 if (line[i] == '\0')
376 return LINE_ERROR;
377 else
378 *value = line + i; /* set 'value' to value string */
379
380 /* trim trailing white space from 'value' */
381 i = (int) strlen(*value) - 1;
382 while (isblank(UCH((*value)[i])) && i > 0)
383 i--;
384 (*value)[i + 1] = '\0';
385
386 return LINE_EQUALS; /* no syntax error in line */
387 }
388
389 /*
390 * Create the configuration file
391 */
392 void
dlg_create_rc(const char * filename)393 dlg_create_rc(const char *filename)
394 {
395 unsigned i;
396 FILE *rc_file;
397
398 if ((rc_file = fopen(filename, "wt")) == NULL)
399 dlg_exiterr("Error opening file for writing in dlg_create_rc().");
400
401 fprintf(rc_file, "#\n\
402 # Run-time configuration file for dialog\n\
403 #\n\
404 # Automatically generated by \"dialog --create-rc <file>\"\n\
405 #\n\
406 #\n\
407 # Types of values:\n\
408 #\n\
409 # Number - <number>\n\
410 # String - \"string\"\n\
411 # Boolean - <ON|OFF>\n"
412 #ifdef HAVE_COLOR
413 "\
414 # Attribute - (foreground,background,highlight?)\n"
415 #endif
416 );
417
418 /* Print an entry for each configuration variable */
419 for (i = 0; i < VAR_COUNT; i++) {
420 fprintf(rc_file, "\n# %s\n", vars[i].comment);
421 switch (vars[i].type) {
422 case VAL_INT:
423 fprintf(rc_file, "%s = %d\n", vars[i].name,
424 *((int *) vars[i].var));
425 break;
426 case VAL_STR:
427 fprintf(rc_file, "%s = \"%s\"\n", vars[i].name,
428 (char *) vars[i].var);
429 break;
430 case VAL_BOOL:
431 fprintf(rc_file, "%s = %s\n", vars[i].name,
432 *((bool *) vars[i].var) ? "ON" : "OFF");
433 break;
434 }
435 }
436 #ifdef HAVE_COLOR
437 for (i = 0; i < (unsigned) dlg_color_count(); ++i) {
438 char buffer[MAX_LEN + 1];
439 unsigned j;
440 bool repeat = FALSE;
441
442 fprintf(rc_file, "\n# %s\n", dlg_color_table[i].comment);
443 for (j = 0; j != i; ++j) {
444 if (dlg_color_table[i].fg == dlg_color_table[j].fg
445 && dlg_color_table[i].bg == dlg_color_table[j].bg
446 && dlg_color_table[i].hilite == dlg_color_table[j].hilite) {
447 fprintf(rc_file, "%s = %s\n",
448 dlg_color_table[i].name,
449 dlg_color_table[j].name);
450 repeat = TRUE;
451 break;
452 }
453 }
454
455 if (!repeat) {
456 fprintf(rc_file, "%s = %s\n", dlg_color_table[i].name,
457 attr_to_str(buffer,
458 dlg_color_table[i].fg,
459 dlg_color_table[i].bg,
460 dlg_color_table[i].hilite));
461 }
462 }
463 #endif /* HAVE_COLOR */
464 dlg_dump_keys(rc_file);
465
466 (void) fclose(rc_file);
467 }
468
469 /*
470 * Parse the configuration file and set up variables
471 */
472 int
dlg_parse_rc(void)473 dlg_parse_rc(void)
474 {
475 int i;
476 int l = 1;
477 PARSE_LINE parse;
478 char str[MAX_LEN + 1];
479 char *var;
480 char *value;
481 char *tempptr;
482 int result = 0;
483 FILE *rc_file = 0;
484 char *params;
485
486 /*
487 * At startup, dialog determines the settings to use as follows:
488 *
489 * a) if the environment variable $DIALOGRC is set, its value determines
490 * the name of the configuration file.
491 *
492 * b) if the file in (a) can't be found, use the file $HOME/.dialogrc
493 * as the configuration file.
494 *
495 * c) if the file in (b) can't be found, try using the GLOBALRC file.
496 * Usually this will be /etc/dialogrc.
497 *
498 * d) if the file in (c) cannot be found, use the compiled-in defaults.
499 */
500
501 /* try step (a) */
502 if ((tempptr = getenv("DIALOGRC")) != NULL)
503 rc_file = fopen(tempptr, "rt");
504
505 if (rc_file == NULL) { /* step (a) failed? */
506 /* try step (b) */
507 if ((tempptr = getenv("HOME")) != NULL
508 && strlen(tempptr) < MAX_LEN - (sizeof(DIALOGRC) + 3)) {
509 if (tempptr[0] == '\0' || lastch(tempptr) == '/')
510 sprintf(str, "%s%s", tempptr, DIALOGRC);
511 else
512 sprintf(str, "%s/%s", tempptr, DIALOGRC);
513 rc_file = fopen(tempptr = str, "rt");
514 }
515 }
516
517 if (rc_file == NULL) { /* step (b) failed? */
518 /* try step (c) */
519 strcpy(str, GLOBALRC);
520 if ((rc_file = fopen(tempptr = str, "rt")) == NULL)
521 return 0; /* step (c) failed, use default values */
522 }
523
524 DLG_TRACE(("# opened rc file \"%s\"\n", tempptr));
525 /* Scan each line and set variables */
526 while ((result == 0) && (fgets(str, MAX_LEN, rc_file) != NULL)) {
527 DLG_TRACE(("#\t%s", str));
528 if (*str == '\0' || lastch(str) != '\n') {
529 /* ignore rest of file if line too long */
530 fprintf(stderr, "\nParse error: line %d of configuration"
531 " file too long.\n", l);
532 result = -1; /* parse aborted */
533 break;
534 }
535
536 lastch(str) = '\0';
537 if (begins_with(str, "bindkey", ¶ms)) {
538 if (!dlg_parse_bindkey(params)) {
539 fprintf(stderr, "\nParse error: line %d of configuration\n", l);
540 result = -1;
541 }
542 continue;
543 }
544 parse = parse_line(str, &var, &value); /* parse current line */
545
546 switch (parse) {
547 case LINE_EMPTY: /* ignore blank lines and comments */
548 break;
549 case LINE_EQUALS:
550 /* search table for matching config variable name */
551 if ((i = find_vars(var)) >= 0) {
552 switch (vars[i].type) {
553 case VAL_INT:
554 *((int *) vars[i].var) = atoi(value);
555 break;
556 case VAL_STR:
557 if (!isquote(value[0]) || !isquote(lastch(value))
558 || strlen(value) < 2) {
559 fprintf(stderr, "\nParse error: string value "
560 "expected at line %d of configuration "
561 "file.\n", l);
562 result = -1; /* parse aborted */
563 } else {
564 /* remove the (") quotes */
565 value++;
566 lastch(value) = '\0';
567 strcpy((char *) vars[i].var, value);
568 }
569 break;
570 case VAL_BOOL:
571 if (!dlg_strcmp(value, "ON"))
572 *((bool *) vars[i].var) = TRUE;
573 else if (!dlg_strcmp(value, "OFF"))
574 *((bool *) vars[i].var) = FALSE;
575 else {
576 fprintf(stderr, "\nParse error: boolean value "
577 "expected at line %d of configuration "
578 "file (found %s).\n", l, value);
579 result = -1; /* parse aborted */
580 }
581 break;
582 }
583 #ifdef HAVE_COLOR
584 } else if ((i = find_color(var)) >= 0) {
585 int fg = 0;
586 int bg = 0;
587 int hl = 0;
588 if (str_to_attr(value, &fg, &bg, &hl) == -1) {
589 fprintf(stderr, "\nParse error: attribute "
590 "value expected at line %d of configuration "
591 "file.\n", l);
592 result = -1; /* parse aborted */
593 } else {
594 dlg_color_table[i].fg = fg;
595 dlg_color_table[i].bg = bg;
596 dlg_color_table[i].hilite = hl;
597 }
598 } else {
599 #endif /* HAVE_COLOR */
600 fprintf(stderr, "\nParse error: unknown variable "
601 "at line %d of configuration file:\n\t%s\n", l, var);
602 result = -1; /* parse aborted */
603 }
604 break;
605 case LINE_ERROR:
606 fprintf(stderr, "\nParse error: syntax error at line %d of "
607 "configuration file.\n", l);
608 result = -1; /* parse aborted */
609 break;
610 }
611 l++; /* next line */
612 }
613
614 (void) fclose(rc_file);
615 return result;
616 }
617