]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/dialog/rc.c
THIS BRANCH IS OBSOLETE, PLEASE READ:
[FreeBSD/FreeBSD.git] / contrib / dialog / rc.c
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
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
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
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
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 *
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
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
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
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
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
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", &params)) {
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 }