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", ¶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