1 /* $OpenBSD: options.c,v 1.27 2019/05/21 09:24:58 martijn Exp $ */ 2 3 /*- 4 * Copyright (c) 1991, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * Copyright (c) 1991, 1993, 1994, 1995, 1996 7 * Keith Bostic. All rights reserved. 8 * 9 * See the LICENSE file for redistribution information. 10 */ 11 12 #include "config.h" 13 14 #include <sys/types.h> 15 #include <sys/queue.h> 16 #include <sys/stat.h> 17 #include <sys/time.h> 18 19 #include <bitstring.h> 20 #include <ctype.h> 21 #include <errno.h> 22 #include <limits.h> 23 #include <paths.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <unistd.h> 28 29 #include "common.h" 30 #include "../vi/vi.h" 31 #include "pathnames.h" 32 33 static int opts_abbcmp(const void *, const void *); 34 static int opts_cmp(const void *, const void *); 35 static int opts_print(SCR *, OPTLIST const *); 36 37 /* 38 * O'Reilly noted options and abbreviations are from "Learning the VI Editor", 39 * Fifth Edition, May 1992. There's no way of knowing what systems they are 40 * actually from. 41 * 42 * HPUX noted options and abbreviations are from "The Ultimate Guide to the 43 * VI and EX Text Editors", 1990. 44 */ 45 OPTLIST const optlist[] = { 46 /* O_ALTWERASE 4.4BSD */ 47 {"altwerase", f_altwerase, OPT_0BOOL, 0}, 48 /* O_AUTOINDENT 4BSD */ 49 {"autoindent", NULL, OPT_0BOOL, 0}, 50 /* O_AUTOPRINT 4BSD */ 51 {"autoprint", NULL, OPT_1BOOL, 0}, 52 /* O_AUTOWRITE 4BSD */ 53 {"autowrite", NULL, OPT_0BOOL, 0}, 54 /* O_BACKUP 4.4BSD */ 55 {"backup", NULL, OPT_STR, 0}, 56 /* O_BEAUTIFY 4BSD */ 57 {"beautify", NULL, OPT_0BOOL, 0}, 58 /* O_CDPATH 4.4BSD */ 59 {"cdpath", NULL, OPT_STR, 0}, 60 /* O_CEDIT 4.4BSD */ 61 {"cedit", NULL, OPT_STR, 0}, 62 /* O_COLUMNS 4.4BSD */ 63 {"columns", f_columns, OPT_NUM, OPT_NOSAVE}, 64 /* O_COMMENT 4.4BSD */ 65 {"comment", NULL, OPT_0BOOL, 0}, 66 /* O_EDCOMPATIBLE 4BSD */ 67 {"edcompatible",NULL, OPT_0BOOL, 0}, 68 /* O_ESCAPETIME 4.4BSD */ 69 {"escapetime", NULL, OPT_NUM, 0}, 70 /* O_ERRORBELLS 4BSD */ 71 {"errorbells", NULL, OPT_0BOOL, 0}, 72 /* O_EXRC System V (undocumented) */ 73 {"exrc", NULL, OPT_0BOOL, 0}, 74 /* O_EXTENDED 4.4BSD */ 75 {"extended", f_recompile, OPT_0BOOL, 0}, 76 /* O_FILEC 4.4BSD */ 77 {"filec", NULL, OPT_STR, 0}, 78 /* O_FLASH HPUX */ 79 {"flash", NULL, OPT_0BOOL, 0}, 80 /* O_HARDTABS 4BSD */ 81 {"hardtabs", NULL, OPT_NUM, 0}, 82 /* O_ICLOWER 4.4BSD */ 83 {"iclower", f_recompile, OPT_0BOOL, 0}, 84 /* O_IGNORECASE 4BSD */ 85 {"ignorecase", f_recompile, OPT_0BOOL, 0}, 86 /* O_KEYTIME 4.4BSD */ 87 {"keytime", NULL, OPT_NUM, 0}, 88 /* O_LEFTRIGHT 4.4BSD */ 89 {"leftright", f_reformat, OPT_0BOOL, 0}, 90 /* O_LINES 4.4BSD */ 91 {"lines", f_lines, OPT_NUM, OPT_NOSAVE}, 92 /* O_LIST 4BSD */ 93 {"list", f_reformat, OPT_0BOOL, 0}, 94 /* O_LOCKFILES 4.4BSD 95 * XXX 96 * Locking isn't reliable enough over NFS to require it, in addition, 97 * it's a serious startup performance problem over some remote links. 98 */ 99 {"lock", NULL, OPT_1BOOL, 0}, 100 /* O_MAGIC 4BSD */ 101 {"magic", NULL, OPT_1BOOL, 0}, 102 /* O_MATCHTIME 4.4BSD */ 103 {"matchtime", NULL, OPT_NUM, 0}, 104 /* O_MESG 4BSD */ 105 {"mesg", NULL, OPT_1BOOL, 0}, 106 /* O_NOPRINT 4.4BSD */ 107 {"noprint", f_print, OPT_STR, OPT_EARLYSET}, 108 /* O_NUMBER 4BSD */ 109 {"number", f_reformat, OPT_0BOOL, 0}, 110 /* O_OCTAL 4.4BSD */ 111 {"octal", f_print, OPT_0BOOL, OPT_EARLYSET}, 112 /* O_OPEN 4BSD */ 113 {"open", NULL, OPT_1BOOL, 0}, 114 /* O_PARAGRAPHS 4BSD */ 115 {"paragraphs", f_paragraph, OPT_STR, 0}, 116 /* O_PATH 4.4BSD */ 117 {"path", NULL, OPT_STR, 0}, 118 /* O_PRINT 4.4BSD */ 119 {"print", f_print, OPT_STR, OPT_EARLYSET}, 120 /* O_PROMPT 4BSD */ 121 {"prompt", NULL, OPT_1BOOL, 0}, 122 /* O_READONLY 4BSD (undocumented) */ 123 {"readonly", f_readonly, OPT_0BOOL, OPT_ALWAYS}, 124 /* O_RECDIR 4.4BSD */ 125 {"recdir", NULL, OPT_STR, 0}, 126 /* O_REMAP 4BSD */ 127 {"remap", NULL, OPT_1BOOL, 0}, 128 /* O_REPORT 4BSD */ 129 {"report", NULL, OPT_NUM, 0}, 130 /* O_RULER 4.4BSD */ 131 {"ruler", NULL, OPT_0BOOL, 0}, 132 /* O_SCROLL 4BSD */ 133 {"scroll", NULL, OPT_NUM, 0}, 134 /* O_SEARCHINCR 4.4BSD */ 135 {"searchincr", NULL, OPT_0BOOL, 0}, 136 /* O_SECTIONS 4BSD */ 137 {"sections", f_section, OPT_STR, 0}, 138 /* O_SECURE 4.4BSD */ 139 {"secure", f_secure, OPT_0BOOL, OPT_NOUNSET}, 140 /* O_SHELL 4BSD */ 141 {"shell", NULL, OPT_STR, 0}, 142 /* O_SHELLMETA 4.4BSD */ 143 {"shellmeta", NULL, OPT_STR, 0}, 144 /* O_SHIFTWIDTH 4BSD */ 145 {"shiftwidth", NULL, OPT_NUM, OPT_NOZERO}, 146 /* O_SHOWMATCH 4BSD */ 147 {"showmatch", NULL, OPT_0BOOL, 0}, 148 /* O_SHOWMODE 4.4BSD */ 149 {"showmode", NULL, OPT_0BOOL, 0}, 150 /* O_SIDESCROLL 4.4BSD */ 151 {"sidescroll", NULL, OPT_NUM, OPT_NOZERO}, 152 /* O_TABSTOP 4BSD */ 153 {"tabstop", f_reformat, OPT_NUM, OPT_NOZERO}, 154 /* O_TAGLENGTH 4BSD */ 155 {"taglength", NULL, OPT_NUM, 0}, 156 /* O_TAGS 4BSD */ 157 {"tags", NULL, OPT_STR, 0}, 158 /* O_TERM 4BSD 159 * !!! 160 * By default, the historic vi always displayed information about two 161 * options, redraw and term. Term seems sufficient. 162 */ 163 {"term", NULL, OPT_STR, OPT_ADISP|OPT_NOSAVE}, 164 /* O_TERSE 4BSD */ 165 {"terse", NULL, OPT_0BOOL, 0}, 166 /* O_TILDEOP 4.4BSD */ 167 {"tildeop", NULL, OPT_0BOOL, 0}, 168 /* O_TIMEOUT 4BSD (undocumented) */ 169 {"timeout", NULL, OPT_1BOOL, 0}, 170 /* O_TTYWERASE 4.4BSD */ 171 {"ttywerase", f_ttywerase, OPT_0BOOL, 0}, 172 /* O_VERBOSE 4.4BSD */ 173 {"verbose", NULL, OPT_0BOOL, 0}, 174 /* O_W1200 4BSD */ 175 {"w1200", f_w1200, OPT_NUM, OPT_NDISP|OPT_NOSAVE}, 176 /* O_W300 4BSD */ 177 {"w300", f_w300, OPT_NUM, OPT_NDISP|OPT_NOSAVE}, 178 /* O_W9600 4BSD */ 179 {"w9600", f_w9600, OPT_NUM, OPT_NDISP|OPT_NOSAVE}, 180 /* O_WARN 4BSD */ 181 {"warn", NULL, OPT_1BOOL, 0}, 182 /* O_WINDOW 4BSD */ 183 {"window", f_window, OPT_NUM, 0}, 184 /* O_WINDOWNAME 4BSD */ 185 {"windowname", NULL, OPT_0BOOL, 0}, 186 /* O_WRAPLEN 4.4BSD */ 187 {"wraplen", NULL, OPT_NUM, 0}, 188 /* O_WRAPMARGIN 4BSD */ 189 {"wrapmargin", NULL, OPT_NUM, 0}, 190 /* O_WRAPSCAN 4BSD */ 191 {"wrapscan", NULL, OPT_1BOOL, 0}, 192 /* O_WRITEANY 4BSD */ 193 {"writeany", NULL, OPT_0BOOL, 0}, 194 {NULL}, 195 }; 196 197 typedef struct abbrev { 198 char *name; 199 int offset; 200 } OABBREV; 201 202 static OABBREV const abbrev[] = { 203 {"ai", O_AUTOINDENT}, /* 4BSD */ 204 {"ap", O_AUTOPRINT}, /* 4BSD */ 205 {"aw", O_AUTOWRITE}, /* 4BSD */ 206 {"bf", O_BEAUTIFY}, /* 4BSD */ 207 {"co", O_COLUMNS}, /* 4.4BSD */ 208 {"eb", O_ERRORBELLS}, /* 4BSD */ 209 {"ed", O_EDCOMPATIBLE}, /* 4BSD */ 210 {"ex", O_EXRC}, /* System V (undocumented) */ 211 {"ht", O_HARDTABS}, /* 4BSD */ 212 {"ic", O_IGNORECASE}, /* 4BSD */ 213 {"li", O_LINES}, /* 4.4BSD */ 214 {"nu", O_NUMBER}, /* 4BSD */ 215 {"para", O_PARAGRAPHS}, /* 4BSD */ 216 {"ro", O_READONLY}, /* 4BSD (undocumented) */ 217 {"scr", O_SCROLL}, /* 4BSD (undocumented) */ 218 {"sect", O_SECTIONS}, /* O'Reilly */ 219 {"sh", O_SHELL}, /* 4BSD */ 220 {"sm", O_SHOWMATCH}, /* 4BSD */ 221 {"smd", O_SHOWMODE}, /* 4BSD */ 222 {"sw", O_SHIFTWIDTH}, /* 4BSD */ 223 {"tag", O_TAGS}, /* 4BSD (undocumented) */ 224 {"tl", O_TAGLENGTH}, /* 4BSD */ 225 {"to", O_TIMEOUT}, /* 4BSD (undocumented) */ 226 {"ts", O_TABSTOP}, /* 4BSD */ 227 {"tty", O_TERM}, /* 4BSD (undocumented) */ 228 {"ttytype", O_TERM}, /* 4BSD (undocumented) */ 229 {"w", O_WINDOW}, /* O'Reilly */ 230 {"wa", O_WRITEANY}, /* 4BSD */ 231 {"wi", O_WINDOW}, /* 4BSD (undocumented) */ 232 {"wl", O_WRAPLEN}, /* 4.4BSD */ 233 {"wm", O_WRAPMARGIN}, /* 4BSD */ 234 {"ws", O_WRAPSCAN}, /* 4BSD */ 235 {NULL}, 236 }; 237 238 /* 239 * opts_init -- 240 * Initialize some of the options. 241 * 242 * PUBLIC: int opts_init(SCR *, int *); 243 */ 244 int 245 opts_init(SCR *sp, int *oargs) 246 { 247 ARGS *argv[2], a, b; 248 OPTLIST const *op; 249 u_long v; 250 int optindx; 251 char *s, b1[1024]; 252 253 a.bp = b1; 254 b.bp = NULL; 255 a.len = b.len = 0; 256 argv[0] = &a; 257 argv[1] = &b; 258 259 /* Set numeric and string default values. */ 260 #define OI_b1(indx) { \ 261 a.len = strlen(b1); \ 262 if (opts_set(sp, argv, NULL)) { \ 263 optindx = indx; \ 264 goto err; \ 265 } \ 266 } 267 #define OI(indx, str) { \ 268 (void)strlcpy(b1, (str), sizeof(b1)); \ 269 OI_b1(indx); \ 270 } 271 272 /* 273 * Indirect global options to global space. Specifically, set up 274 * terminal, lines, columns first, they're used by other options. 275 * Note, don't set the flags until we've set up the indirection. 276 */ 277 if (o_set(sp, O_TERM, 0, NULL, GO_TERM)) { 278 optindx = O_TERM; 279 goto err; 280 } 281 F_SET(&sp->opts[O_TERM], OPT_GLOBAL); 282 if (o_set(sp, O_LINES, 0, NULL, GO_LINES)) { 283 optindx = O_LINES; 284 goto err; 285 } 286 F_SET(&sp->opts[O_LINES], OPT_GLOBAL); 287 if (o_set(sp, O_COLUMNS, 0, NULL, GO_COLUMNS)) { 288 optindx = O_COLUMNS; 289 goto err; 290 } 291 F_SET(&sp->opts[O_COLUMNS], OPT_GLOBAL); 292 if (o_set(sp, O_SECURE, 0, NULL, GO_SECURE)) { 293 optindx = O_SECURE; 294 goto err; 295 } 296 F_SET(&sp->opts[O_SECURE], OPT_GLOBAL); 297 298 /* Initialize string values. */ 299 (void)snprintf(b1, sizeof(b1), 300 "cdpath=%s", (s = getenv("CDPATH")) == NULL ? ":" : s); 301 OI_b1(O_CDPATH); 302 OI(O_ESCAPETIME, "escapetime=1"); 303 OI(O_FILEC, "filec=\t"); 304 OI(O_KEYTIME, "keytime=6"); 305 OI(O_MATCHTIME, "matchtime=7"); 306 OI(O_REPORT, "report=5"); 307 OI(O_PARAGRAPHS, "paragraphs=IPLPPPQPP LIpplpipbpBlBdPpLpIt"); 308 OI(O_PATH, "path="); 309 (void)snprintf(b1, sizeof(b1), "recdir=%s", _PATH_PRESERVE); 310 OI_b1(O_RECDIR); 311 OI(O_SECTIONS, "sections=NHSHH HUnhshShSs"); 312 (void)snprintf(b1, sizeof(b1), 313 "shell=%s", (s = getenv("SHELL")) == NULL ? _PATH_BSHELL : s); 314 OI_b1(O_SHELL); 315 OI(O_SHELLMETA, "shellmeta=~{[*?$`'\"\\"); 316 OI(O_SHIFTWIDTH, "shiftwidth=8"); 317 OI(O_SIDESCROLL, "sidescroll=16"); 318 OI(O_TABSTOP, "tabstop=8"); 319 (void)snprintf(b1, sizeof(b1), "tags=%s", _PATH_TAGS); 320 OI_b1(O_TAGS); 321 322 /* 323 * XXX 324 * Initialize O_SCROLL here, after term; initializing term should 325 * have created a LINES/COLUMNS value. 326 */ 327 if ((v = (O_VAL(sp, O_LINES) - 1) / 2) == 0) 328 v = 1; 329 (void)snprintf(b1, sizeof(b1), "scroll=%ld", v); 330 OI_b1(O_SCROLL); 331 332 /* 333 * The default window option values are: 334 * 8 if baud rate <= 600 335 * 16 if baud rate <= 1200 336 * LINES - 1 if baud rate > 1200 337 * 338 * Note, the windows option code will correct any too-large value 339 * or when the O_LINES value is 1. 340 */ 341 if (sp->gp->scr_baud(sp, &v)) 342 return (1); 343 if (v <= 600) 344 v = 8; 345 else if (v <= 1200) 346 v = 16; 347 else 348 v = O_VAL(sp, O_LINES) - 1; 349 (void)snprintf(b1, sizeof(b1), "window=%lu", v); 350 OI_b1(O_WINDOW); 351 352 /* 353 * Set boolean default values, and copy all settings into the default 354 * information. OS_NOFREE is set, we're copying, not replacing. 355 */ 356 for (op = optlist, optindx = 0; op->name != NULL; ++op, ++optindx) 357 switch (op->type) { 358 case OPT_0BOOL: 359 break; 360 case OPT_1BOOL: 361 O_SET(sp, optindx); 362 O_D_SET(sp, optindx); 363 break; 364 case OPT_NUM: 365 o_set(sp, optindx, OS_DEF, NULL, O_VAL(sp, optindx)); 366 break; 367 case OPT_STR: 368 if (O_STR(sp, optindx) != NULL && o_set(sp, optindx, 369 OS_DEF | OS_NOFREE | OS_STRDUP, O_STR(sp, optindx), 0)) 370 goto err; 371 break; 372 default: 373 abort(); 374 } 375 376 /* 377 * !!! 378 * Some options can be initialized by the command name or the 379 * command-line arguments. They don't set the default values, 380 * it's historic practice. 381 */ 382 for (; *oargs != -1; ++oargs) 383 OI(*oargs, optlist[*oargs].name); 384 return (0); 385 #undef OI 386 #undef OI_b1 387 388 err: msgq(sp, M_ERR, 389 "Unable to set default %s option", optlist[optindx].name); 390 return (1); 391 } 392 393 /* 394 * opts_set -- 395 * Change the values of one or more options. 396 * 397 * PUBLIC: int opts_set(SCR *, ARGS *[], char *); 398 */ 399 int 400 opts_set(SCR *sp, ARGS *argv[], char *usage) 401 { 402 enum optdisp disp; 403 enum nresult nret; 404 OPTLIST const *op; 405 OPTION *spo; 406 u_long value, turnoff; 407 int ch, equals, nf, nf2, offset, qmark, rval; 408 char *endp, *name, *p, *sep, *t; 409 410 disp = NO_DISPLAY; 411 for (rval = 0; argv[0]->len != 0; ++argv) { 412 /* 413 * The historic vi dumped the options for each occurrence of 414 * "all" in the set list. Puhleeze. 415 */ 416 if (!strcmp(argv[0]->bp, "all")) { 417 disp = ALL_DISPLAY; 418 continue; 419 } 420 421 /* Find equals sign or question mark. */ 422 for (sep = NULL, equals = qmark = 0, 423 p = name = argv[0]->bp; (ch = *p) != '\0'; ++p) 424 if (ch == '=' || ch == '?') { 425 if (p == name) { 426 if (usage != NULL) 427 msgq(sp, M_ERR, 428 "Usage: %s", usage); 429 return (1); 430 } 431 sep = p; 432 if (ch == '=') 433 equals = 1; 434 else 435 qmark = 1; 436 break; 437 } 438 439 turnoff = 0; 440 op = NULL; 441 if (sep != NULL) 442 *sep++ = '\0'; 443 444 /* Search for the name, then name without any leading "no". */ 445 if ((op = opts_search(name)) == NULL && 446 name[0] == 'n' && name[1] == 'o') { 447 turnoff = 1; 448 name += 2; 449 op = opts_search(name); 450 } 451 if (op == NULL) { 452 opts_nomatch(sp, name); 453 rval = 1; 454 continue; 455 } 456 457 /* Find current option values. */ 458 offset = op - optlist; 459 spo = sp->opts + offset; 460 461 /* 462 * !!! 463 * Historically, the question mark could be a separate 464 * argument. 465 */ 466 if (!equals && !qmark && 467 argv[1]->len == 1 && argv[1]->bp[0] == '?') { 468 ++argv; 469 qmark = 1; 470 } 471 472 /* Set name, value. */ 473 switch (op->type) { 474 case OPT_0BOOL: 475 case OPT_1BOOL: 476 /* Some options may not be reset. */ 477 if (F_ISSET(op, OPT_NOUNSET) && turnoff) { 478 msgq_str(sp, M_ERR, name, 479 "set: the %s option may not be turned off"); 480 rval = 1; 481 break; 482 } 483 484 if (equals) { 485 msgq_str(sp, M_ERR, name, 486 "set: [no]%s option doesn't take a value"); 487 rval = 1; 488 break; 489 } 490 if (qmark) { 491 if (!disp) 492 disp = SELECT_DISPLAY; 493 F_SET(spo, OPT_SELECTED); 494 break; 495 } 496 497 /* 498 * Do nothing if the value is unchanged, the underlying 499 * functions can be expensive. 500 */ 501 if (!F_ISSET(op, OPT_ALWAYS)) { 502 if (turnoff) { 503 if (!O_ISSET(sp, offset)) 504 break; 505 } else { 506 if (O_ISSET(sp, offset)) 507 break; 508 } 509 } 510 511 if (F_ISSET(op, OPT_EARLYSET)) { 512 /* Set the value. */ 513 if (turnoff) 514 O_CLR(sp, offset); 515 else 516 O_SET(sp, offset); 517 } 518 519 /* Report to subsystems. */ 520 if ((op->func != NULL && 521 op->func(sp, spo, NULL, &turnoff)) || 522 ex_optchange(sp, offset, NULL, &turnoff) || 523 v_optchange(sp, offset, NULL, &turnoff) || 524 sp->gp->scr_optchange(sp, offset, NULL, &turnoff)) { 525 rval = 1; 526 break; 527 } 528 529 if (!F_ISSET(op, OPT_EARLYSET)) { 530 /* Set the value. */ 531 if (turnoff) 532 O_CLR(sp, offset); 533 else 534 O_SET(sp, offset); 535 } 536 break; 537 case OPT_NUM: 538 if (turnoff) { 539 msgq_str(sp, M_ERR, name, 540 "set: %s option isn't a boolean"); 541 rval = 1; 542 break; 543 } 544 if (qmark || !equals) { 545 if (!disp) 546 disp = SELECT_DISPLAY; 547 F_SET(spo, OPT_SELECTED); 548 break; 549 } 550 551 if (!isdigit(sep[0])) 552 goto badnum; 553 if ((nret = 554 nget_uslong(&value, sep, &endp, 10)) != NUM_OK) { 555 p = msg_print(sp, name, &nf); 556 t = msg_print(sp, sep, &nf2); 557 switch (nret) { 558 case NUM_ERR: 559 msgq(sp, M_SYSERR, 560 "set: %s option: %s", p, t); 561 break; 562 case NUM_OVER: 563 msgq(sp, M_ERR, 564 "set: %s option: %s: value overflow", p, t); 565 break; 566 case NUM_OK: 567 case NUM_UNDER: 568 abort(); 569 } 570 if (nf) 571 FREE_SPACE(sp, p, 0); 572 if (nf2) 573 FREE_SPACE(sp, t, 0); 574 rval = 1; 575 break; 576 } 577 if (*endp && !isblank(*endp)) { 578 badnum: p = msg_print(sp, name, &nf); 579 t = msg_print(sp, sep, &nf2); 580 msgq(sp, M_ERR, 581 "set: %s option: %s is an illegal number", p, t); 582 if (nf) 583 FREE_SPACE(sp, p, 0); 584 if (nf2) 585 FREE_SPACE(sp, t, 0); 586 rval = 1; 587 break; 588 } 589 590 /* Some options may never be set to zero. */ 591 if (F_ISSET(op, OPT_NOZERO) && value == 0) { 592 msgq_str(sp, M_ERR, name, 593 "set: the %s option may never be set to 0"); 594 rval = 1; 595 break; 596 } 597 598 /* 599 * Do nothing if the value is unchanged, the underlying 600 * functions can be expensive. 601 */ 602 if (!F_ISSET(op, OPT_ALWAYS) && 603 O_VAL(sp, offset) == value) 604 break; 605 606 if (F_ISSET(op, OPT_EARLYSET)) { 607 /* Set the value. */ 608 if (o_set(sp, offset, 0, NULL, value)) { 609 rval = 1; 610 break; 611 } 612 } 613 614 /* Report to subsystems. */ 615 if ((op->func != NULL && 616 op->func(sp, spo, sep, &value)) || 617 ex_optchange(sp, offset, sep, &value) || 618 v_optchange(sp, offset, sep, &value) || 619 sp->gp->scr_optchange(sp, offset, sep, &value)) { 620 rval = 1; 621 break; 622 } 623 624 if (!F_ISSET(op, OPT_EARLYSET)) { 625 /* Set the value. */ 626 if (o_set(sp, offset, 0, NULL, value)) 627 rval = 1; 628 } 629 break; 630 case OPT_STR: 631 if (turnoff) { 632 msgq_str(sp, M_ERR, name, 633 "set: %s option isn't a boolean"); 634 rval = 1; 635 break; 636 } 637 if (qmark || !equals) { 638 if (!disp) 639 disp = SELECT_DISPLAY; 640 F_SET(spo, OPT_SELECTED); 641 break; 642 } 643 644 /* 645 * Do nothing if the value is unchanged, the underlying 646 * functions can be expensive. 647 */ 648 if (!F_ISSET(op, OPT_ALWAYS) && 649 O_STR(sp, offset) != NULL && 650 !strcmp(O_STR(sp, offset), sep)) 651 break; 652 653 if (F_ISSET(op, OPT_EARLYSET)) { 654 /* Set the value. */ 655 if (o_set(sp, offset, OS_STRDUP, sep, 0)) { 656 rval = 1; 657 break; 658 } 659 } 660 661 /* Report to subsystems. */ 662 if ((op->func != NULL && 663 op->func(sp, spo, sep, NULL)) || 664 ex_optchange(sp, offset, sep, NULL) || 665 v_optchange(sp, offset, sep, NULL) || 666 sp->gp->scr_optchange(sp, offset, sep, NULL)) { 667 rval = 1; 668 break; 669 } 670 671 if (!F_ISSET(op, OPT_EARLYSET)) { 672 /* Set the value. */ 673 if (o_set(sp, offset, OS_STRDUP, sep, 0)) 674 rval = 1; 675 } 676 break; 677 default: 678 abort(); 679 } 680 } 681 if (disp != NO_DISPLAY) 682 opts_dump(sp, disp); 683 return (rval); 684 } 685 686 /* 687 * o_set -- 688 * Set an option's value. 689 * 690 * PUBLIC: int o_set(SCR *, int, u_int, char *, u_long); 691 */ 692 int 693 o_set(SCR *sp, int opt, u_int flags, char *str, u_long val) 694 { 695 OPTION *op; 696 697 /* Set a pointer to the options area. */ 698 op = F_ISSET(&sp->opts[opt], OPT_GLOBAL) ? 699 &sp->gp->opts[sp->opts[opt].o_cur.val] : &sp->opts[opt]; 700 701 /* Copy the string, if requested. */ 702 if (LF_ISSET(OS_STRDUP) && (str = strdup(str)) == NULL) { 703 msgq(sp, M_SYSERR, NULL); 704 return (1); 705 } 706 707 /* Free the previous string, if requested, and set the value. */ 708 if (LF_ISSET(OS_DEF)) 709 if (LF_ISSET(OS_STR | OS_STRDUP)) { 710 if (!LF_ISSET(OS_NOFREE) && op->o_def.str != NULL) 711 free(op->o_def.str); 712 op->o_def.str = str; 713 } else 714 op->o_def.val = val; 715 else 716 if (LF_ISSET(OS_STR | OS_STRDUP)) { 717 if (!LF_ISSET(OS_NOFREE) && op->o_cur.str != NULL) 718 free(op->o_cur.str); 719 op->o_cur.str = str; 720 } else 721 op->o_cur.val = val; 722 return (0); 723 } 724 725 /* 726 * opts_empty -- 727 * Return 1 if the string option is invalid, 0 if it's OK. 728 * 729 * PUBLIC: int opts_empty(SCR *, int, int); 730 */ 731 int 732 opts_empty(SCR *sp, int off, int silent) 733 { 734 char *p; 735 736 if ((p = O_STR(sp, off)) == NULL || p[0] == '\0') { 737 if (!silent) 738 msgq_str(sp, M_ERR, optlist[off].name, 739 "No %s edit option specified"); 740 return (1); 741 } 742 return (0); 743 } 744 745 /* 746 * opts_dump -- 747 * List the current values of selected options. 748 * 749 * PUBLIC: void opts_dump(SCR *, enum optdisp); 750 */ 751 void 752 opts_dump(SCR *sp, enum optdisp type) 753 { 754 OPTLIST const *op; 755 int base, b_num, cnt, col, colwidth, curlen, s_num; 756 int numcols, numrows, row; 757 int b_op[O_OPTIONCOUNT], s_op[O_OPTIONCOUNT]; 758 char nbuf[20]; 759 760 /* 761 * Options are output in two groups -- those that fit in a column and 762 * those that don't. Output is done on 6 character "tab" boundaries 763 * for no particular reason. (Since we don't output tab characters, 764 * we can ignore the terminal's tab settings.) Ignore the user's tab 765 * setting because we have no idea how reasonable it is. 766 * 767 * Find a column width we can live with, testing from 10 columns to 1. 768 */ 769 for (numcols = 10; numcols > 1; --numcols) { 770 colwidth = sp->cols / numcols & ~(STANDARD_TAB - 1); 771 if (colwidth >= 10) { 772 colwidth = 773 (colwidth + STANDARD_TAB) & ~(STANDARD_TAB - 1); 774 numcols = sp->cols / colwidth; 775 break; 776 } 777 colwidth = 0; 778 } 779 780 /* 781 * Get the set of options to list, entering them into 782 * the column list or the overflow list. 783 */ 784 for (b_num = s_num = 0, op = optlist; op->name != NULL; ++op) { 785 cnt = op - optlist; 786 787 /* If OPT_NDISP set, it's never displayed. */ 788 if (F_ISSET(op, OPT_NDISP)) 789 continue; 790 791 switch (type) { 792 case ALL_DISPLAY: /* Display all. */ 793 break; 794 case CHANGED_DISPLAY: /* Display changed. */ 795 /* If OPT_ADISP set, it's always "changed". */ 796 if (F_ISSET(op, OPT_ADISP)) 797 break; 798 switch (op->type) { 799 case OPT_0BOOL: 800 case OPT_1BOOL: 801 case OPT_NUM: 802 if (O_VAL(sp, cnt) == O_D_VAL(sp, cnt)) 803 continue; 804 break; 805 case OPT_STR: 806 if (O_STR(sp, cnt) == O_D_STR(sp, cnt) || 807 (O_D_STR(sp, cnt) != NULL && 808 !strcmp(O_STR(sp, cnt), O_D_STR(sp, cnt)))) 809 continue; 810 break; 811 } 812 break; 813 case SELECT_DISPLAY: /* Display selected. */ 814 if (!F_ISSET(&sp->opts[cnt], OPT_SELECTED)) 815 continue; 816 break; 817 default: 818 case NO_DISPLAY: 819 abort(); 820 } 821 F_CLR(&sp->opts[cnt], OPT_SELECTED); 822 823 curlen = strlen(op->name); 824 switch (op->type) { 825 case OPT_0BOOL: 826 case OPT_1BOOL: 827 if (!O_ISSET(sp, cnt)) 828 curlen += 2; 829 break; 830 case OPT_NUM: 831 (void)snprintf(nbuf, 832 sizeof(nbuf), "%ld", O_VAL(sp, cnt)); 833 curlen += strlen(nbuf); 834 break; 835 case OPT_STR: 836 if (O_STR(sp, cnt) != NULL) 837 curlen += strlen(O_STR(sp, cnt)); 838 curlen += 3; 839 break; 840 } 841 /* Offset by 2 so there's a gap. */ 842 if (curlen <= colwidth - 2) 843 s_op[s_num++] = cnt; 844 else 845 b_op[b_num++] = cnt; 846 } 847 848 if (s_num > 0) { 849 /* Figure out the number of rows. */ 850 if (s_num > numcols) { 851 numrows = s_num / numcols; 852 if (s_num % numcols) 853 ++numrows; 854 } else 855 numrows = 1; 856 857 /* Display the options in sorted order. */ 858 for (row = 0; row < numrows;) { 859 for (base = row, col = 0; col < numcols; ++col) { 860 cnt = opts_print(sp, &optlist[s_op[base]]); 861 if ((base += numrows) >= s_num) 862 break; 863 (void)ex_printf(sp, "%*s", 864 (int)(colwidth - cnt), ""); 865 } 866 if (++row < numrows || b_num) 867 (void)ex_puts(sp, "\n"); 868 } 869 } 870 871 for (row = 0; row < b_num;) { 872 (void)opts_print(sp, &optlist[b_op[row]]); 873 if (++row < b_num) 874 (void)ex_puts(sp, "\n"); 875 } 876 (void)ex_puts(sp, "\n"); 877 } 878 879 /* 880 * opts_print -- 881 * Print out an option. 882 */ 883 static int 884 opts_print(SCR *sp, OPTLIST const *op) 885 { 886 int curlen, offset; 887 888 curlen = 0; 889 offset = op - optlist; 890 switch (op->type) { 891 case OPT_0BOOL: 892 case OPT_1BOOL: 893 curlen += ex_printf(sp, 894 "%s%s", O_ISSET(sp, offset) ? "" : "no", op->name); 895 break; 896 case OPT_NUM: 897 curlen += ex_printf(sp, "%s=%ld", op->name, O_VAL(sp, offset)); 898 break; 899 case OPT_STR: 900 curlen += ex_printf(sp, "%s=\"%s\"", op->name, 901 O_STR(sp, offset) == NULL ? "" : O_STR(sp, offset)); 902 break; 903 } 904 return (curlen); 905 } 906 907 /* 908 * opts_save -- 909 * Write the current configuration to a file. 910 * 911 * PUBLIC: int opts_save(SCR *, FILE *); 912 */ 913 int 914 opts_save(SCR *sp, FILE *fp) 915 { 916 OPTLIST const *op; 917 int ch, cnt; 918 char *p; 919 920 for (op = optlist; op->name != NULL; ++op) { 921 if (F_ISSET(op, OPT_NOSAVE)) 922 continue; 923 cnt = op - optlist; 924 switch (op->type) { 925 case OPT_0BOOL: 926 case OPT_1BOOL: 927 if (O_ISSET(sp, cnt)) 928 (void)fprintf(fp, "set %s\n", op->name); 929 else 930 (void)fprintf(fp, "set no%s\n", op->name); 931 break; 932 case OPT_NUM: 933 (void)fprintf(fp, 934 "set %s=%-3ld\n", op->name, O_VAL(sp, cnt)); 935 break; 936 case OPT_STR: 937 if (O_STR(sp, cnt) == NULL) 938 break; 939 (void)fprintf(fp, "set "); 940 for (p = op->name; (ch = *p) != '\0'; ++p) { 941 if (isblank(ch) || ch == '\\') 942 (void)putc('\\', fp); 943 (void)putc(ch, fp); 944 } 945 (void)putc('=', fp); 946 for (p = O_STR(sp, cnt); (ch = *p) != '\0'; ++p) { 947 if (isblank(ch) || ch == '\\') 948 (void)putc('\\', fp); 949 (void)putc(ch, fp); 950 } 951 (void)putc('\n', fp); 952 break; 953 } 954 if (ferror(fp)) { 955 msgq(sp, M_SYSERR, NULL); 956 return (1); 957 } 958 } 959 return (0); 960 } 961 962 /* 963 * opts_search -- 964 * Search for an option. 965 * 966 * PUBLIC: OPTLIST const *opts_search(char *); 967 */ 968 OPTLIST const * 969 opts_search(char *name) 970 { 971 OPTLIST const *op, *found; 972 OABBREV atmp, *ap; 973 OPTLIST otmp; 974 size_t len; 975 976 /* Check list of abbreviations. */ 977 atmp.name = name; 978 if ((ap = bsearch(&atmp, abbrev, sizeof(abbrev) / sizeof(OABBREV) - 1, 979 sizeof(OABBREV), opts_abbcmp)) != NULL) 980 return (optlist + ap->offset); 981 982 /* Check list of options. */ 983 otmp.name = name; 984 if ((op = bsearch(&otmp, optlist, sizeof(optlist) / sizeof(OPTLIST) - 1, 985 sizeof(OPTLIST), opts_cmp)) != NULL) 986 return (op); 987 988 /* 989 * Check to see if the name is the prefix of one (and only one) 990 * option. If so, return the option. 991 */ 992 len = strlen(name); 993 for (found = NULL, op = optlist; op->name != NULL; ++op) { 994 if (op->name[0] < name[0]) 995 continue; 996 if (op->name[0] > name[0]) 997 break; 998 if (!memcmp(op->name, name, len)) { 999 if (found != NULL) 1000 return (NULL); 1001 found = op; 1002 } 1003 } 1004 return (found); 1005 } 1006 1007 /* 1008 * opts_nomatch -- 1009 * Standard nomatch error message for options. 1010 * 1011 * PUBLIC: void opts_nomatch(SCR *, char *); 1012 */ 1013 void 1014 opts_nomatch(SCR *sp, char *name) 1015 { 1016 msgq_str(sp, M_ERR, name, 1017 "set: no %s option: 'set all' gives all option values"); 1018 } 1019 1020 static int 1021 opts_abbcmp(const void *a, const void *b) 1022 { 1023 return(strcmp(((OABBREV *)a)->name, ((OABBREV *)b)->name)); 1024 } 1025 1026 static int 1027 opts_cmp(const void *a, const void *b) 1028 { 1029 return(strcmp(((OPTLIST *)a)->name, ((OPTLIST *)b)->name)); 1030 } 1031 1032 /* 1033 * opts_copy -- 1034 * Copy a screen's OPTION array. 1035 * 1036 * PUBLIC: int opts_copy(SCR *, SCR *); 1037 */ 1038 int 1039 opts_copy(SCR *orig, SCR *sp) 1040 { 1041 int cnt, rval; 1042 1043 /* Copy most everything without change. */ 1044 memcpy(sp->opts, orig->opts, sizeof(orig->opts)); 1045 1046 /* Copy the string edit options. */ 1047 for (cnt = rval = 0; cnt < O_OPTIONCOUNT; ++cnt) { 1048 if (optlist[cnt].type != OPT_STR || 1049 F_ISSET(&optlist[cnt], OPT_GLOBAL)) 1050 continue; 1051 /* 1052 * If never set, or already failed, NULL out the entries -- 1053 * have to continue after failure, otherwise would have two 1054 * screens referencing the same memory. 1055 */ 1056 if (rval || O_STR(sp, cnt) == NULL) { 1057 o_set(sp, cnt, OS_NOFREE | OS_STR, NULL, 0); 1058 o_set(sp, cnt, OS_DEF | OS_NOFREE | OS_STR, NULL, 0); 1059 continue; 1060 } 1061 1062 /* Copy the current string. */ 1063 if (o_set(sp, cnt, OS_NOFREE | OS_STRDUP, O_STR(sp, cnt), 0)) { 1064 o_set(sp, cnt, OS_DEF | OS_NOFREE | OS_STR, NULL, 0); 1065 goto nomem; 1066 } 1067 1068 /* Copy the default string. */ 1069 if (O_D_STR(sp, cnt) != NULL && o_set(sp, cnt, 1070 OS_DEF | OS_NOFREE | OS_STRDUP, O_D_STR(sp, cnt), 0)) { 1071 nomem: msgq(orig, M_SYSERR, NULL); 1072 rval = 1; 1073 } 1074 } 1075 return (rval); 1076 } 1077 1078 /* 1079 * opts_free -- 1080 * Free all option strings 1081 * 1082 * PUBLIC: void opts_free(SCR *); 1083 */ 1084 void 1085 opts_free(SCR *sp) 1086 { 1087 int cnt; 1088 1089 for (cnt = 0; cnt < O_OPTIONCOUNT; ++cnt) { 1090 if (optlist[cnt].type != OPT_STR || 1091 F_ISSET(&optlist[cnt], OPT_GLOBAL)) 1092 continue; 1093 free(O_STR(sp, cnt)); 1094 free(O_D_STR(sp, cnt)); 1095 } 1096 } 1097