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