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
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_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
201 typedef struct abbrev {
202 char *name;
203 int offset;
204 } OABBREV;
205
206 static 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 */
249 int
opts_init(SCR * sp,int * oargs)250 opts_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
393 err: 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 */
404 int
opts_set(SCR * sp,ARGS * argv[],char * usage)405 opts_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)) {
583 badnum: 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 */
697 int
o_set(SCR * sp,int opt,u_int flags,char * str,u_long val)698 o_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 */
736 int
opts_empty(SCR * sp,int off,int silent)737 opts_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 */
756 void
opts_dump(SCR * sp,enum optdisp type)757 opts_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 */
888 static int
opts_print(SCR * sp,OPTLIST const * op)889 opts_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 */
918 int
opts_save(SCR * sp,FILE * fp)919 opts_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 */
973 OPTLIST const *
opts_search(char * name)974 opts_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 */
1018 void
opts_nomatch(SCR * sp,char * name)1019 opts_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
1025 static int
opts_abbcmp(const void * a,const void * b)1026 opts_abbcmp(const void *a, const void *b)
1027 {
1028 return(strcmp(((OABBREV *)a)->name, ((OABBREV *)b)->name));
1029 }
1030
1031 static int
opts_cmp(const void * a,const void * b)1032 opts_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 */
1043 int
opts_copy(SCR * orig,SCR * sp)1044 opts_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)) {
1076 nomem: 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 */
1089 void
opts_free(SCR * sp)1090 opts_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