2 * $Id: rc.c,v 1.62 2022/07/28 08:17:21 tom Exp $
4 * rc.c -- routines for processing the configuration file
6 * Copyright 2000-2020,2022 Thomas E. Dickey
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.
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.
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.
23 * An earlier version of this program lists as authors
24 * Savio Lam (lam836@cs.cuhk.hk)
27 #include <dlg_internals.h>
31 #include <dlg_colors.h>
40 #define MAX_TOKEN MIN_TOKEN
43 #define UNKNOWN_COLOR -2
46 * For matching color names with color values
48 static const color_names_st color_names
[] =
50 #ifdef HAVE_USE_DEFAULT_COLORS
53 {"BLACK", COLOR_BLACK
},
55 {"GREEN", COLOR_GREEN
},
56 {"YELLOW", COLOR_YELLOW
},
58 {"MAGENTA", COLOR_MAGENTA
},
60 {"WHITE", COLOR_WHITE
},
62 #define COLOR_COUNT TableSize(color_names)
63 #endif /* HAVE_COLOR */
65 #define GLOBALRC "/etc/dialogrc"
66 #define DIALOGRC ".dialogrc"
73 /* Type of line in configuration file */
80 /* number of configuration variables */
81 #define VAR_COUNT TableSize(vars)
83 /* check if character is string quoting characters */
84 #define isquote(c) ((c) == '"' || (c) == '\'')
86 /* get last character of string */
87 #define lastch(str) str[strlen(str)-1]
90 * Configuration variables
93 const char *name
; /* name of configuration variable as in DIALOGRC */
94 void *var
; /* address of actual variable to change */
95 int type
; /* type of value */
96 const char *comment
; /* comment to put in "rc" file */
100 * This table should contain only references to dialog_state, since dialog_vars
101 * is reset specially in dialog.c before each widget.
103 static const vars_st vars
[] =
106 &dialog_state
.aspect_ratio
,
108 "Set aspect-ration."},
111 &dialog_state
.separate_str
,
113 "Set separator (for multiple widgets output)."},
116 &dialog_state
.tab_len
,
118 "Set tab-length (for textbox tab-conversion)."},
121 &dialog_state
.visit_items
,
123 "Make tab-traversal for checklist, etc., include the list."},
126 &dialog_state
.use_scrollbar
,
128 "Show scrollbar in dialog boxes?"},
132 &dialog_state
.use_shadow
,
134 "Shadow dialog boxes? This also turns on color."},
137 &dialog_state
.use_colors
,
139 "Turn color support ON or OFF"},
140 #endif /* HAVE_COLOR */
144 skip_whitespace(char *str
, int n
)
146 while (isblank(UCH(str
[n
])) && str
[n
] != '\0')
152 skip_keyword(char *str
, int n
)
154 while (isalnum(UCH(str
[n
])) && str
[n
] != '\0')
160 trim_token(char **tok
)
162 char *tmp
= *tok
+ skip_whitespace(*tok
, 0);
166 while (*tmp
!= '\0' && !isblank(UCH(*tmp
)))
173 from_boolean(const char *str
)
177 if (str
!= NULL
&& *str
!= '\0') {
178 if (!dlg_strcmp(str
, "ON")) {
180 } else if (!dlg_strcmp(str
, "OFF")) {
188 from_color_name(const char *str
)
190 int code
= UNKNOWN_COLOR
;
192 if (str
!= NULL
&& *str
!= '\0') {
195 for (i
= 0; i
< COLOR_COUNT
; ++i
) {
196 if (!dlg_strcmp(str
, color_names
[i
].name
)) {
197 code
= color_names
[i
].value
;
206 find_vars(char *name
)
211 for (i
= 0; i
< VAR_COUNT
; i
++) {
212 if (dlg_strcmp(vars
[i
].name
, name
) == 0) {
222 find_color(char *name
)
226 int limit
= dlg_color_count();
228 for (i
= 0; i
< limit
; i
++) {
229 if (dlg_strcmp(dlg_color_table
[i
].name
, name
) == 0) {
238 to_color_name(int code
)
240 const char *result
= "?";
242 for (n
= 0; n
< TableSize(color_names
); ++n
) {
243 if (code
== color_names
[n
].value
) {
244 result
= color_names
[n
].name
;
254 return code
? "ON" : "OFF";
258 * Extract the foreground, background and highlight values from an attribute
259 * represented as a string in one of these forms:
261 * "(foreground,background,highlight,underline,reverse)"
262 * "(foreground,background,highlight,underline)"
263 * "(foreground,background,highlight)"
267 str_to_attr(char *str
, DIALOG_COLORS
* result
)
269 char *tokens
[MAX_TOKEN
+ 1];
270 char tempstr
[MAX_LEN
+ 1];
273 size_t tok_count
= 0;
275 memset(result
, 0, sizeof(*result
));
280 if (str
[0] != L_PAREN
|| lastch(str
) != R_PAREN
) {
283 if ((ret
= find_color(str
)) >= 0) {
284 *result
= dlg_color_table
[ret
];
287 /* invalid representation */
291 /* remove the parenthesis */
293 if (have
> MAX_LEN
) {
298 memcpy(tempstr
, str
+ 1, have
);
299 tempstr
[have
] = '\0';
301 /* parse comma-separated tokens, allow up to
302 * one more than max tokens to detect extras */
303 while (tok_count
< TableSize(tokens
)) {
305 tokens
[tok_count
++] = &tempstr
[i
];
307 while (tempstr
[i
] != '\0' && tempstr
[i
] != ',')
310 if (tempstr
[i
] == '\0')
316 if (tok_count
< MIN_TOKEN
|| tok_count
> MAX_TOKEN
) {
317 /* invalid representation */
321 for (i
= 0; i
< tok_count
; ++i
)
322 trim_token(&tokens
[i
]);
325 if (UNKNOWN_COLOR
== (result
->fg
= from_color_name(tokens
[0]))
326 || UNKNOWN_COLOR
== (result
->bg
= from_color_name(tokens
[1]))
327 || UNKNOWN_COLOR
== (result
->hilite
= from_boolean(tokens
[2]))
329 || (tok_count
>= 4 && (result
->ul
= from_boolean(tokens
[3])) == -1)
330 || (tok_count
>= 5 && (result
->rv
= from_boolean(tokens
[4])) == -1)
331 #endif /* HAVE_RC_FILE2 */
333 /* invalid representation */
339 #endif /* HAVE_COLOR */
342 * Check if the line begins with a special keyword; if so, return true while
343 * pointing params to its parameters.
346 begins_with(char *line
, const char *keyword
, char **params
)
348 int i
= skip_whitespace(line
, 0);
349 int j
= skip_keyword(line
, i
);
351 if ((j
- i
) == (int) strlen(keyword
)) {
354 if (!dlg_strcmp(keyword
, line
+ i
)) {
355 *params
= line
+ skip_whitespace(line
, j
+ 1);
365 * Parse a line in the configuration file
367 * Each line is of the form: "variable = value". On exit, 'var' will contain
368 * the variable name, and 'value' will contain the value string.
372 * LINE_EMPTY - line is blank or comment
373 * LINE_EQUALS - line contains "variable = value"
374 * LINE_ERROR - syntax error in line
377 parse_line(char *line
, char **var
, char **value
)
381 /* ignore white space at beginning of line */
382 i
= skip_whitespace(line
, i
);
384 if (line
[i
] == '\0') /* line is blank */
386 else if (line
[i
] == '#') /* line is comment */
388 else if (line
[i
] == '=') /* variable names cannot start with a '=' */
391 /* set 'var' to variable name */
392 *var
= line
+ i
++; /* skip to next character */
394 /* find end of variable name */
395 while (!isblank(UCH(line
[i
])) && line
[i
] != '=' && line
[i
] != '\0')
398 if (line
[i
] == '\0') /* syntax error */
400 else if (line
[i
] == '=')
405 /* skip white space before '=' */
406 i
= skip_whitespace(line
, i
);
408 if (line
[i
] != '=') /* syntax error */
411 i
++; /* skip the '=' */
414 /* skip white space after '=' */
415 i
= skip_whitespace(line
, i
);
420 *value
= line
+ i
; /* set 'value' to value string */
422 /* trim trailing white space from 'value' */
423 i
= (int) strlen(*value
) - 1;
424 while (isblank(UCH((*value
)[i
])) && i
> 0)
426 (*value
)[i
+ 1] = '\0';
428 return LINE_EQUALS
; /* no syntax error in line */
432 * Create the configuration file
435 dlg_create_rc(const char *filename
)
440 if ((rc_file
= fopen(filename
, "wt")) == NULL
)
441 dlg_exiterr("Error opening file for writing in dlg_create_rc().");
443 fprintf(rc_file
, "#\n\
444 # Run-time configuration file for dialog\n\
446 # Automatically generated by \"dialog --create-rc <file>\"\n\
449 # Types of values:\n\
451 # Number - <number>\n\
452 # String - \"string\"\n\
453 # Boolean - <ON|OFF>\n"
457 # Attribute - (foreground,background,highlight?,underline?,reverse?)\n"
458 #else /* HAVE_RC_FILE2 */
460 # Attribute - (foreground,background,highlight?)\n"
461 #endif /* HAVE_RC_FILE2 */
462 #endif /* HAVE_COLOR */
465 /* Print an entry for each configuration variable */
466 for (i
= 0; i
< VAR_COUNT
; i
++) {
467 fprintf(rc_file
, "\n# %s\n", vars
[i
].comment
);
468 switch (vars
[i
].type
) {
470 fprintf(rc_file
, "%s = %d\n", vars
[i
].name
,
471 *((int *) vars
[i
].var
));
474 fprintf(rc_file
, "%s = \"%s\"\n", vars
[i
].name
,
475 (char *) vars
[i
].var
);
478 fprintf(rc_file
, "%s = %s\n", vars
[i
].name
,
479 *((bool *) vars
[i
].var
) ? "ON" : "OFF");
484 for (i
= 0; i
< (unsigned) dlg_color_count(); ++i
) {
488 fprintf(rc_file
, "\n# %s\n", dlg_color_table
[i
].comment
);
489 for (j
= 0; j
!= i
; ++j
) {
490 if (dlg_color_table
[i
].fg
== dlg_color_table
[j
].fg
491 && dlg_color_table
[i
].bg
== dlg_color_table
[j
].bg
492 && dlg_color_table
[i
].hilite
== dlg_color_table
[j
].hilite
) {
493 fprintf(rc_file
, "%s = %s\n",
494 dlg_color_table
[i
].name
,
495 dlg_color_table
[j
].name
);
502 fprintf(rc_file
, "%s = %c", dlg_color_table
[i
].name
, L_PAREN
);
503 fprintf(rc_file
, "%s", to_color_name(dlg_color_table
[i
].fg
));
504 fprintf(rc_file
, ",%s", to_color_name(dlg_color_table
[i
].bg
));
505 fprintf(rc_file
, ",%s", to_boolean(dlg_color_table
[i
].hilite
));
507 if (dlg_color_table
[i
].ul
|| dlg_color_table
[i
].rv
)
508 fprintf(rc_file
, ",%s", to_boolean(dlg_color_table
[i
].ul
));
509 if (dlg_color_table
[i
].rv
)
510 fprintf(rc_file
, ",%s", to_boolean(dlg_color_table
[i
].rv
));
511 #endif /* HAVE_RC_FILE2 */
512 fprintf(rc_file
, "%c\n", R_PAREN
);
515 #endif /* HAVE_COLOR */
516 dlg_dump_keys(rc_file
);
518 (void) fclose(rc_file
);
522 report_error(const char *filename
, int line_no
, const char *msg
)
524 fprintf(stderr
, "%s:%d: %s\n", filename
, line_no
, msg
);
525 dlg_trace_msg("%s:%d: %s\n", filename
, line_no
, msg
);
529 * Parse the configuration file and set up variables
537 char str
[MAX_LEN
+ 1];
546 * At startup, dialog determines the settings to use as follows:
548 * a) if the environment variable $DIALOGRC is set, its value determines
549 * the name of the configuration file.
551 * b) if the file in (a) can't be found, use the file $HOME/.dialogrc
552 * as the configuration file.
554 * c) if the file in (b) can't be found, try using the GLOBALRC file.
555 * Usually this will be /etc/dialogrc.
557 * d) if the file in (c) cannot be found, use the compiled-in defaults.
561 if ((filename
= dlg_getenv_str("DIALOGRC")) != NULL
)
562 rc_file
= fopen(filename
, "rt");
564 if (rc_file
== NULL
) { /* step (a) failed? */
566 if ((filename
= dlg_getenv_str("HOME")) != NULL
567 && strlen(filename
) < MAX_LEN
- (sizeof(DIALOGRC
) + 3)) {
568 if (filename
[0] == '\0' || lastch(filename
) == '/')
569 sprintf(str
, "%s%s", filename
, DIALOGRC
);
571 sprintf(str
, "%s/%s", filename
, DIALOGRC
);
572 rc_file
= fopen(filename
= str
, "rt");
576 if (rc_file
== NULL
) { /* step (b) failed? */
578 strcpy(str
, GLOBALRC
);
579 if ((rc_file
= fopen(filename
= str
, "rt")) == NULL
)
580 return 0; /* step (c) failed, use default values */
583 DLG_TRACE(("# opened rc file \"%s\"\n", filename
));
584 /* Scan each line and set variables */
585 while ((result
== 0) && (fgets(str
, MAX_LEN
, rc_file
) != NULL
)) {
586 DLG_TRACE(("#\t%s", str
));
587 if (*str
== '\0' || lastch(str
) != '\n') {
588 /* ignore rest of file if line too long */
589 report_error(filename
, l
, "line too long");
590 result
= -1; /* parse aborted */
595 if (begins_with(str
, "bindkey", ¶ms
)) {
596 if (!dlg_parse_bindkey(params
)) {
597 report_error(filename
, l
, "invalid bindkey");
602 parse
= parse_line(str
, &var
, &value
); /* parse current line */
605 case LINE_EMPTY
: /* ignore blank lines and comments */
608 /* search table for matching config variable name */
609 if ((i
= find_vars(var
)) >= 0) {
610 switch (vars
[i
].type
) {
612 *((int *) vars
[i
].var
) = atoi(value
);
615 if (!isquote(value
[0]) || !isquote(lastch(value
))
616 || strlen(value
) < 2) {
617 report_error(filename
, l
, "expected string value");
618 result
= -1; /* parse aborted */
620 /* remove the (") quotes */
622 lastch(value
) = '\0';
623 strcpy((char *) vars
[i
].var
, value
);
627 if (!dlg_strcmp(value
, "ON"))
628 *((bool *) vars
[i
].var
) = TRUE
;
629 else if (!dlg_strcmp(value
, "OFF"))
630 *((bool *) vars
[i
].var
) = FALSE
;
632 report_error(filename
, l
, "expected boolean value");
633 result
= -1; /* parse aborted */
638 } else if ((i
= find_color(var
)) >= 0) {
640 if (str_to_attr(value
, &temp
) == -1) {
641 report_error(filename
, l
, "expected attribute value");
642 result
= -1; /* parse aborted */
644 dlg_color_table
[i
].fg
= temp
.fg
;
645 dlg_color_table
[i
].bg
= temp
.bg
;
646 dlg_color_table
[i
].hilite
= temp
.hilite
;
648 dlg_color_table
[i
].ul
= temp
.ul
;
649 dlg_color_table
[i
].rv
= temp
.rv
;
650 #endif /* HAVE_RC_FILE2 */
653 #endif /* HAVE_COLOR */
654 report_error(filename
, l
, "unknown variable");
655 result
= -1; /* parse aborted */
659 report_error(filename
, l
, "syntax error");
660 result
= -1; /* parse aborted */
666 (void) fclose(rc_file
);