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