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