1 /* 2 * $Id: rc.c,v 1.49 2011/10/15 00:56:44 tom Exp $ 3 * 4 * rc.c -- routines for processing the configuration file 5 * 6 * Copyright 2000-2010,2011 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 218 if (str[0] != '(' || lastch(str) != ')') { 219 if ((i = find_color(str)) >= 0) { 220 *fg = dlg_color_table[i].fg; 221 *bg = dlg_color_table[i].bg; 222 *hl = dlg_color_table[i].hilite; 223 return 0; 224 } 225 return -1; /* invalid representation */ 226 } 227 228 /* remove the parenthesis */ 229 strcpy(tempstr, str + 1); 230 lastch(tempstr) = '\0'; 231 232 /* get foreground and background */ 233 234 while (1) { 235 /* skip white space before fg/bg string */ 236 i = skip_whitespace(tempstr, i); 237 if (tempstr[i] == '\0') 238 return -1; /* invalid representation */ 239 part = tempstr + i; /* set 'part' to start of fg/bg string */ 240 241 /* find end of fg/bg string */ 242 while (!whitespace(tempstr[i]) && tempstr[i] != ',' 243 && tempstr[i] != '\0') 244 i++; 245 246 if (tempstr[i] == '\0') 247 return -1; /* invalid representation */ 248 else if (whitespace(tempstr[i])) { /* not yet ',' */ 249 tempstr[i++] = '\0'; 250 251 /* skip white space before ',' */ 252 i = skip_whitespace(tempstr, i); 253 if (tempstr[i] != ',') 254 return -1; /* invalid representation */ 255 } 256 tempstr[i++] = '\0'; /* skip the ',' */ 257 for (j = 0; j < COLOR_COUNT && dlg_strcmp(part, color_names[j].name); 258 j++) ; 259 if (j == COLOR_COUNT) /* invalid color name */ 260 return -1; 261 if (get_fg) { 262 *fg = color_names[j].value; 263 get_fg = 0; /* next we have to get the background */ 264 } else { 265 *bg = color_names[j].value; 266 break; 267 } 268 } /* got foreground and background */ 269 270 /* get highlight */ 271 272 /* skip white space before highlight string */ 273 i = skip_whitespace(tempstr, i); 274 if (tempstr[i] == '\0') 275 return -1; /* invalid representation */ 276 part = tempstr + i; /* set 'part' to start of highlight string */ 277 278 /* trim trailing white space from highlight string */ 279 i = (int) strlen(part) - 1; 280 while (whitespace(part[i]) && i > 0) 281 i--; 282 part[i + 1] = '\0'; 283 284 if (!dlg_strcmp(part, "ON")) 285 *hl = TRUE; 286 else if (!dlg_strcmp(part, "OFF")) 287 *hl = FALSE; 288 else 289 return -1; /* invalid highlight value */ 290 291 return 0; 292 } 293 #endif /* HAVE_COLOR */ 294 295 /* 296 * Check if the line begins with a special keyword; if so, return true while 297 * pointing params to its parameters. 298 */ 299 static int 300 begins_with(char *line, const char *keyword, char **params) 301 { 302 int i = skip_whitespace(line, 0); 303 int j = skip_keyword(line, i); 304 305 if ((j - i) == (int) strlen(keyword)) { 306 char save = line[j]; 307 line[j] = 0; 308 if (!dlg_strcmp(keyword, line + i)) { 309 *params = line + skip_whitespace(line, j + 1); 310 return 1; 311 } 312 line[j] = save; 313 } 314 315 return 0; 316 } 317 318 /* 319 * Parse a line in the configuration file 320 * 321 * Each line is of the form: "variable = value". On exit, 'var' will contain 322 * the variable name, and 'value' will contain the value string. 323 * 324 * Return values: 325 * 326 * LINE_EMPTY - line is blank or comment 327 * LINE_EQUALS - line contains "variable = value" 328 * LINE_ERROR - syntax error in line 329 */ 330 static PARSE_LINE 331 parse_line(char *line, char **var, char **value) 332 { 333 int i = 0; 334 335 /* ignore white space at beginning of line */ 336 i = skip_whitespace(line, i); 337 338 if (line[i] == '\0') /* line is blank */ 339 return LINE_EMPTY; 340 else if (line[i] == '#') /* line is comment */ 341 return LINE_EMPTY; 342 else if (line[i] == '=') /* variable names cannot start with a '=' */ 343 return LINE_ERROR; 344 345 /* set 'var' to variable name */ 346 *var = line + i++; /* skip to next character */ 347 348 /* find end of variable name */ 349 while (!whitespace(line[i]) && line[i] != '=' && line[i] != '\0') 350 i++; 351 352 if (line[i] == '\0') /* syntax error */ 353 return LINE_ERROR; 354 else if (line[i] == '=') 355 line[i++] = '\0'; 356 else { 357 line[i++] = '\0'; 358 359 /* skip white space before '=' */ 360 i = skip_whitespace(line, i); 361 362 if (line[i] != '=') /* syntax error */ 363 return LINE_ERROR; 364 else 365 i++; /* skip the '=' */ 366 } 367 368 /* skip white space after '=' */ 369 i = skip_whitespace(line, i); 370 371 if (line[i] == '\0') 372 return LINE_ERROR; 373 else 374 *value = line + i; /* set 'value' to value string */ 375 376 /* trim trailing white space from 'value' */ 377 i = (int) strlen(*value) - 1; 378 while (whitespace((*value)[i]) && i > 0) 379 i--; 380 (*value)[i + 1] = '\0'; 381 382 return LINE_EQUALS; /* no syntax error in line */ 383 } 384 385 /* 386 * Create the configuration file 387 */ 388 void 389 dlg_create_rc(const char *filename) 390 { 391 unsigned i; 392 FILE *rc_file; 393 394 if ((rc_file = fopen(filename, "wt")) == NULL) 395 dlg_exiterr("Error opening file for writing in dlg_create_rc()."); 396 397 fprintf(rc_file, "#\n\ 398 # Run-time configuration file for dialog\n\ 399 #\n\ 400 # Automatically generated by \"dialog --create-rc <file>\"\n\ 401 #\n\ 402 #\n\ 403 # Types of values:\n\ 404 #\n\ 405 # Number - <number>\n\ 406 # String - \"string\"\n\ 407 # Boolean - <ON|OFF>\n" 408 #ifdef HAVE_COLOR 409 "\ 410 # Attribute - (foreground,background,highlight?)\n" 411 #endif 412 ); 413 414 /* Print an entry for each configuration variable */ 415 for (i = 0; i < VAR_COUNT; i++) { 416 fprintf(rc_file, "\n# %s\n", vars[i].comment); 417 switch (vars[i].type) { 418 case VAL_INT: 419 fprintf(rc_file, "%s = %d\n", vars[i].name, 420 *((int *) vars[i].var)); 421 break; 422 case VAL_STR: 423 fprintf(rc_file, "%s = \"%s\"\n", vars[i].name, 424 (char *) vars[i].var); 425 break; 426 case VAL_BOOL: 427 fprintf(rc_file, "%s = %s\n", vars[i].name, 428 *((bool *) vars[i].var) ? "ON" : "OFF"); 429 break; 430 } 431 } 432 #ifdef HAVE_COLOR 433 for (i = 0; i < (unsigned) dlg_color_count(); ++i) { 434 char buffer[MAX_LEN + 1]; 435 unsigned j; 436 bool repeat = FALSE; 437 438 fprintf(rc_file, "\n# %s\n", dlg_color_table[i].comment); 439 for (j = 0; j != i; ++j) { 440 if (dlg_color_table[i].fg == dlg_color_table[j].fg 441 && dlg_color_table[i].bg == dlg_color_table[j].bg 442 && dlg_color_table[i].hilite == dlg_color_table[j].hilite) { 443 fprintf(rc_file, "%s = %s\n", 444 dlg_color_table[i].name, 445 dlg_color_table[j].name); 446 repeat = TRUE; 447 break; 448 } 449 } 450 451 if (!repeat) { 452 fprintf(rc_file, "%s = %s\n", dlg_color_table[i].name, 453 attr_to_str(buffer, 454 dlg_color_table[i].fg, 455 dlg_color_table[i].bg, 456 dlg_color_table[i].hilite)); 457 } 458 } 459 #endif /* HAVE_COLOR */ 460 dlg_dump_keys(rc_file); 461 462 (void) fclose(rc_file); 463 } 464 465 /* 466 * Parse the configuration file and set up variables 467 */ 468 int 469 dlg_parse_rc(void) 470 { 471 int i; 472 int l = 1; 473 PARSE_LINE parse; 474 char str[MAX_LEN + 1]; 475 char *var; 476 char *value; 477 char *tempptr; 478 int result = 0; 479 FILE *rc_file = 0; 480 char *params; 481 482 /* 483 * At startup, dialog determines the settings to use as follows: 484 * 485 * a) if the environment variable $DIALOGRC is set, its value determines 486 * the name of the configuration file. 487 * 488 * b) if the file in (a) can't be found, use the file $HOME/.dialogrc 489 * as the configuration file. 490 * 491 * c) if the file in (b) can't be found, try using the GLOBALRC file. 492 * Usually this will be /etc/dialogrc. 493 * 494 * d) if the file in (c) cannot be found, use the compiled-in defaults. 495 */ 496 497 /* try step (a) */ 498 if ((tempptr = getenv("DIALOGRC")) != NULL) 499 rc_file = fopen(tempptr, "rt"); 500 501 if (rc_file == NULL) { /* step (a) failed? */ 502 /* try step (b) */ 503 if ((tempptr = getenv("HOME")) != NULL 504 && strlen(tempptr) < MAX_LEN - (sizeof(DIALOGRC) + 3)) { 505 if (tempptr[0] == '\0' || lastch(tempptr) == '/') 506 sprintf(str, "%s%s", tempptr, DIALOGRC); 507 else 508 sprintf(str, "%s/%s", tempptr, DIALOGRC); 509 rc_file = fopen(tempptr = str, "rt"); 510 } 511 } 512 513 if (rc_file == NULL) { /* step (b) failed? */ 514 /* try step (c) */ 515 strcpy(str, GLOBALRC); 516 if ((rc_file = fopen(tempptr = str, "rt")) == NULL) 517 return 0; /* step (c) failed, use default values */ 518 } 519 520 DLG_TRACE(("opened rc file \"%s\"\n", tempptr)); 521 /* Scan each line and set variables */ 522 while ((result == 0) && (fgets(str, MAX_LEN, rc_file) != NULL)) { 523 DLG_TRACE(("rc:%s", str)); 524 if (*str == '\0' || lastch(str) != '\n') { 525 /* ignore rest of file if line too long */ 526 fprintf(stderr, "\nParse error: line %d of configuration" 527 " file too long.\n", l); 528 result = -1; /* parse aborted */ 529 break; 530 } 531 532 lastch(str) = '\0'; 533 if (begins_with(str, "bindkey", ¶ms)) { 534 if (!dlg_parse_bindkey(params)) { 535 fprintf(stderr, "\nParse error: line %d of configuration\n", l); 536 result = -1; 537 } 538 continue; 539 } 540 parse = parse_line(str, &var, &value); /* parse current line */ 541 542 switch (parse) { 543 case LINE_EMPTY: /* ignore blank lines and comments */ 544 break; 545 case LINE_EQUALS: 546 /* search table for matching config variable name */ 547 if ((i = find_vars(var)) >= 0) { 548 switch (vars[i].type) { 549 case VAL_INT: 550 *((int *) vars[i].var) = atoi(value); 551 break; 552 case VAL_STR: 553 if (!isquote(value[0]) || !isquote(lastch(value)) 554 || strlen(value) < 2) { 555 fprintf(stderr, "\nParse error: string value " 556 "expected at line %d of configuration " 557 "file.\n", l); 558 result = -1; /* parse aborted */ 559 } else { 560 /* remove the (") quotes */ 561 value++; 562 lastch(value) = '\0'; 563 strcpy((char *) vars[i].var, value); 564 } 565 break; 566 case VAL_BOOL: 567 if (!dlg_strcmp(value, "ON")) 568 *((bool *) vars[i].var) = TRUE; 569 else if (!dlg_strcmp(value, "OFF")) 570 *((bool *) vars[i].var) = FALSE; 571 else { 572 fprintf(stderr, "\nParse error: boolean value " 573 "expected at line %d of configuration " 574 "file (found %s).\n", l, value); 575 result = -1; /* parse aborted */ 576 } 577 break; 578 } 579 #ifdef HAVE_COLOR 580 } else if ((i = find_color(var)) >= 0) { 581 int fg = 0; 582 int bg = 0; 583 int hl = 0; 584 if (str_to_attr(value, &fg, &bg, &hl) == -1) { 585 fprintf(stderr, "\nParse error: attribute " 586 "value expected at line %d of configuration " 587 "file.\n", l); 588 result = -1; /* parse aborted */ 589 } else { 590 dlg_color_table[i].fg = fg; 591 dlg_color_table[i].bg = bg; 592 dlg_color_table[i].hilite = hl; 593 } 594 } else { 595 #endif /* HAVE_COLOR */ 596 fprintf(stderr, "\nParse error: unknown variable " 597 "at line %d of configuration file:\n\t%s\n", l, var); 598 result = -1; /* parse aborted */ 599 } 600 break; 601 case LINE_ERROR: 602 fprintf(stderr, "\nParse error: syntax error at line %d of " 603 "configuration file.\n", l); 604 result = -1; /* parse aborted */ 605 break; 606 } 607 l++; /* next line */ 608 } 609 610 (void) fclose(rc_file); 611 return result; 612 } 613