First test with an external encoding.
[nvi.git] / common / options.c
blobfa0fd0f29aabb4ae4d2e25ea7d53bc44d5b6ac64
1 /*-
2 * Copyright (c) 1991, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1991, 1993, 1994, 1995, 1996
5 * Keith Bostic. All rights reserved.
7 * See the LICENSE file for redistribution information.
8 */
10 #include "config.h"
12 #ifndef lint
13 static const char sccsid[] = "$Id: options.c,v 10.57 2000/07/16 20:49:29 skimo Exp $ (Berkeley) $Date: 2000/07/16 20:49:29 $";
14 #endif /* not lint */
16 #include <sys/types.h>
17 #include <sys/queue.h>
18 #include <sys/stat.h>
19 #include <sys/time.h>
21 #include <bitstring.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
30 #include "common.h"
31 #include "../vi/vi.h"
32 #include "pathnames.h"
34 static int opts_abbcmp __P((const void *, const void *));
35 static int opts_cmp __P((const void *, const void *));
36 static int opts_print __P((SCR *, OPTLIST const *));
39 * O'Reilly noted options and abbreviations are from "Learning the VI Editor",
40 * Fifth Edition, May 1992. There's no way of knowing what systems they are
41 * actually from.
43 * HPUX noted options and abbreviations are from "The Ultimate Guide to the
44 * VI and EX Text Editors", 1990.
46 OPTLIST const optlist[] = {
47 /* O_ALTWERASE 4.4BSD */
48 {"altwerase", f_altwerase, OPT_0BOOL, 0},
49 /* O_AUTOINDENT 4BSD */
50 {"autoindent", NULL, OPT_0BOOL, 0},
51 /* O_AUTOPRINT 4BSD */
52 {"autoprint", NULL, OPT_1BOOL, 0},
53 /* O_AUTOWRITE 4BSD */
54 {"autowrite", NULL, OPT_0BOOL, 0},
55 /* O_BACKUP 4.4BSD */
56 {"backup", NULL, OPT_STR, 0},
57 /* O_BEAUTIFY 4BSD */
58 {"beautify", NULL, OPT_0BOOL, 0},
59 /* O_CDPATH 4.4BSD */
60 {"cdpath", NULL, OPT_STR, 0},
61 /* O_CEDIT 4.4BSD */
62 {"cedit", NULL, OPT_STR, 0},
63 /* O_COLUMNS 4.4BSD */
64 {"columns", f_columns, OPT_NUM, OPT_NOSAVE},
65 /* O_COMMENT 4.4BSD */
66 {"comment", NULL, OPT_0BOOL, 0},
67 /* O_DIRECTORY 4BSD */
68 {"directory", NULL, OPT_STR, 0},
69 /* O_EDCOMPATIBLE 4BSD */
70 {"edcompatible",NULL, OPT_0BOOL, 0},
71 /* O_ESCAPETIME 4.4BSD */
72 {"escapetime", NULL, OPT_NUM, 0},
73 /* O_ERRORBELLS 4BSD */
74 {"errorbells", NULL, OPT_0BOOL, 0},
75 /* O_EXRC System V (undocumented) */
76 {"exrc", NULL, OPT_0BOOL, 0},
77 /* O_EXTENDED 4.4BSD */
78 {"extended", f_recompile, OPT_0BOOL, 0},
79 /* O_FILEC 4.4BSD */
80 {"filec", NULL, OPT_STR, 0},
81 /* O_FILEENCODING */
82 {"fileencoding",f_fileencoding, OPT_STR, 0},
83 /* O_FLASH HPUX */
84 {"flash", NULL, OPT_1BOOL, 0},
85 /* O_HARDTABS 4BSD */
86 {"hardtabs", NULL, OPT_NUM, 0},
87 /* O_ICLOWER 4.4BSD */
88 {"iclower", f_recompile, OPT_0BOOL, 0},
89 /* O_IGNORECASE 4BSD */
90 {"ignorecase", f_recompile, OPT_0BOOL, 0},
91 /* O_KEYTIME 4.4BSD */
92 {"keytime", NULL, OPT_NUM, 0},
93 /* O_LEFTRIGHT 4.4BSD */
94 {"leftright", f_reformat, OPT_0BOOL, 0},
95 /* O_LINES 4.4BSD */
96 {"lines", f_lines, OPT_NUM, OPT_NOSAVE},
97 /* O_LISP 4BSD
98 * XXX
99 * When the lisp option is implemented, delete the OPT_NOSAVE flag,
100 * so that :mkexrc dumps it.
102 {"lisp", f_lisp, OPT_0BOOL, OPT_NOSAVE},
103 /* O_LIST 4BSD */
104 {"list", f_reformat, OPT_0BOOL, 0},
105 /* O_LOCKFILES 4.4BSD
106 * XXX
107 * Locking isn't reliable enough over NFS to require it, in addition,
108 * it's a serious startup performance problem over some remote links.
110 {"lock", NULL, OPT_1BOOL, 0},
111 /* O_MAGIC 4BSD */
112 {"magic", NULL, OPT_1BOOL, 0},
113 /* O_MATCHTIME 4.4BSD */
114 {"matchtime", NULL, OPT_NUM, 0},
115 /* O_MESG 4BSD */
116 {"mesg", NULL, OPT_1BOOL, 0},
117 /* O_MODELINE 4BSD
118 * !!!
119 * This has been documented in historical systems as both "modeline"
120 * and as "modelines". Regardless of the name, this option represents
121 * a security problem of mammoth proportions, not to mention a stunning
122 * example of what your intro CS professor referred to as the perils of
123 * mixing code and data. Don't add it, or I will kill you.
125 {"modeline", NULL, OPT_0BOOL, OPT_NOSET},
126 /* O_MSGCAT 4.4BSD */
127 {"msgcat", f_msgcat, OPT_STR, 0},
128 /* O_NOPRINT 4.4BSD */
129 {"noprint", f_print, OPT_STR, 0},
130 /* O_NUMBER 4BSD */
131 {"number", f_reformat, OPT_0BOOL, 0},
132 /* O_OCTAL 4.4BSD */
133 {"octal", f_print, OPT_0BOOL, 0},
134 /* O_OPEN 4BSD */
135 {"open", NULL, OPT_1BOOL, 0},
136 /* O_OPTIMIZE 4BSD */
137 {"optimize", NULL, OPT_1BOOL, 0},
138 /* O_PARAGRAPHS 4BSD */
139 {"paragraphs", f_paragraph, OPT_STR, 0},
140 /* O_PATH 4.4BSD */
141 {"path", NULL, OPT_STR, 0},
142 /* O_PRINT 4.4BSD */
143 {"print", f_print, OPT_STR, 0},
144 /* O_PROMPT 4BSD */
145 {"prompt", NULL, OPT_1BOOL, 0},
146 /* O_READONLY 4BSD (undocumented) */
147 {"readonly", f_readonly, OPT_0BOOL, OPT_ALWAYS},
148 /* O_RECDIR 4.4BSD */
149 {"recdir", NULL, OPT_STR, 0},
150 /* O_REDRAW 4BSD */
151 {"redraw", NULL, OPT_0BOOL, 0},
152 /* O_REMAP 4BSD */
153 {"remap", NULL, OPT_1BOOL, 0},
154 /* O_REPORT 4BSD */
155 {"report", NULL, OPT_NUM, 0},
156 /* O_RULER 4.4BSD */
157 {"ruler", NULL, OPT_0BOOL, 0},
158 /* O_SCROLL 4BSD */
159 {"scroll", NULL, OPT_NUM, 0},
160 /* O_SEARCHINCR 4.4BSD */
161 {"searchincr", NULL, OPT_0BOOL, 0},
162 /* O_SECTIONS 4BSD */
163 {"sections", f_section, OPT_STR, 0},
164 /* O_SECURE 4.4BSD */
165 {"secure", NULL, OPT_0BOOL, OPT_NOUNSET},
166 /* O_SHELL 4BSD */
167 {"shell", NULL, OPT_STR, 0},
168 /* O_SHELLMETA 4.4BSD */
169 {"shellmeta", NULL, OPT_STR, 0},
170 /* O_SHIFTWIDTH 4BSD */
171 {"shiftwidth", NULL, OPT_NUM, OPT_NOZERO},
172 /* O_SHOWMATCH 4BSD */
173 {"showmatch", NULL, OPT_0BOOL, 0},
174 /* O_SHOWMODE 4.4BSD */
175 {"showmode", NULL, OPT_0BOOL, 0},
176 /* O_SIDESCROLL 4.4BSD */
177 {"sidescroll", NULL, OPT_NUM, OPT_NOZERO},
178 /* O_SLOWOPEN 4BSD */
179 {"slowopen", NULL, OPT_0BOOL, 0},
180 /* O_SOURCEANY 4BSD (undocumented)
181 * !!!
182 * Historic vi, on startup, source'd $HOME/.exrc and ./.exrc, if they
183 * were owned by the user. The sourceany option was an undocumented
184 * feature of historic vi which permitted the startup source'ing of
185 * .exrc files the user didn't own. This is an obvious security problem,
186 * and we ignore the option.
188 {"sourceany", NULL, OPT_0BOOL, OPT_NOSET},
189 /* O_TABSTOP 4BSD */
190 {"tabstop", f_reformat, OPT_NUM, OPT_NOZERO},
191 /* O_TAGLENGTH 4BSD */
192 {"taglength", NULL, OPT_NUM, 0},
193 /* O_TAGS 4BSD */
194 {"tags", NULL, OPT_STR, 0},
195 /* O_TERM 4BSD
196 * !!!
197 * By default, the historic vi always displayed information about two
198 * options, redraw and term. Term seems sufficient.
200 {"term", NULL, OPT_STR, OPT_ADISP|OPT_NOSAVE},
201 /* O_TERSE 4BSD */
202 {"terse", NULL, OPT_0BOOL, 0},
203 /* O_TILDEOP 4.4BSD */
204 {"tildeop", NULL, OPT_0BOOL, 0},
205 /* O_TIMEOUT 4BSD (undocumented) */
206 {"timeout", NULL, OPT_1BOOL, 0},
207 /* O_TTYWERASE 4.4BSD */
208 {"ttywerase", f_ttywerase, OPT_0BOOL, 0},
209 /* O_VERBOSE 4.4BSD */
210 {"verbose", NULL, OPT_0BOOL, 0},
211 /* O_W1200 4BSD */
212 {"w1200", f_w1200, OPT_NUM, OPT_NDISP|OPT_NOSAVE},
213 /* O_W300 4BSD */
214 {"w300", f_w300, OPT_NUM, OPT_NDISP|OPT_NOSAVE},
215 /* O_W9600 4BSD */
216 {"w9600", f_w9600, OPT_NUM, OPT_NDISP|OPT_NOSAVE},
217 /* O_WARN 4BSD */
218 {"warn", NULL, OPT_1BOOL, 0},
219 /* O_WINDOW 4BSD */
220 {"window", f_window, OPT_NUM, 0},
221 /* O_WINDOWNAME 4BSD */
222 {"windowname", NULL, OPT_0BOOL, 0},
223 /* O_WRAPLEN 4.4BSD */
224 {"wraplen", NULL, OPT_NUM, 0},
225 /* O_WRAPMARGIN 4BSD */
226 {"wrapmargin", NULL, OPT_NUM, 0},
227 /* O_WRAPSCAN 4BSD */
228 {"wrapscan", NULL, OPT_1BOOL, 0},
229 /* O_WRITEANY 4BSD */
230 {"writeany", NULL, OPT_0BOOL, 0},
231 {NULL},
234 typedef struct abbrev {
235 char *name;
236 int offset;
237 } OABBREV;
239 static OABBREV const abbrev[] = {
240 {"ai", O_AUTOINDENT}, /* 4BSD */
241 {"ap", O_AUTOPRINT}, /* 4BSD */
242 {"aw", O_AUTOWRITE}, /* 4BSD */
243 {"bf", O_BEAUTIFY}, /* 4BSD */
244 {"co", O_COLUMNS}, /* 4.4BSD */
245 {"dir", O_DIRECTORY}, /* 4BSD */
246 {"eb", O_ERRORBELLS}, /* 4BSD */
247 {"ed", O_EDCOMPATIBLE}, /* 4BSD */
248 {"ex", O_EXRC}, /* System V (undocumented) */
249 {"fe", O_FILEENCODING},
250 {"ht", O_HARDTABS}, /* 4BSD */
251 {"ic", O_IGNORECASE}, /* 4BSD */
252 {"li", O_LINES}, /* 4.4BSD */
253 {"modelines", O_MODELINE}, /* HPUX */
254 {"nu", O_NUMBER}, /* 4BSD */
255 {"opt", O_OPTIMIZE}, /* 4BSD */
256 {"para", O_PARAGRAPHS}, /* 4BSD */
257 {"re", O_REDRAW}, /* O'Reilly */
258 {"ro", O_READONLY}, /* 4BSD (undocumented) */
259 {"scr", O_SCROLL}, /* 4BSD (undocumented) */
260 {"sect", O_SECTIONS}, /* O'Reilly */
261 {"sh", O_SHELL}, /* 4BSD */
262 {"slow", O_SLOWOPEN}, /* 4BSD */
263 {"sm", O_SHOWMATCH}, /* 4BSD */
264 {"smd", O_SHOWMODE}, /* 4BSD */
265 {"sw", O_SHIFTWIDTH}, /* 4BSD */
266 {"tag", O_TAGS}, /* 4BSD (undocumented) */
267 {"tl", O_TAGLENGTH}, /* 4BSD */
268 {"to", O_TIMEOUT}, /* 4BSD (undocumented) */
269 {"ts", O_TABSTOP}, /* 4BSD */
270 {"tty", O_TERM}, /* 4BSD (undocumented) */
271 {"ttytype", O_TERM}, /* 4BSD (undocumented) */
272 {"w", O_WINDOW}, /* O'Reilly */
273 {"wa", O_WRITEANY}, /* 4BSD */
274 {"wi", O_WINDOW}, /* 4BSD (undocumented) */
275 {"wl", O_WRAPLEN}, /* 4.4BSD */
276 {"wm", O_WRAPMARGIN}, /* 4BSD */
277 {"ws", O_WRAPSCAN}, /* 4BSD */
278 {NULL},
282 * opts_init --
283 * Initialize some of the options.
285 * PUBLIC: int opts_init __P((SCR *, int *));
288 opts_init(sp, oargs)
289 SCR *sp;
290 int *oargs;
292 ARGS *argv[2], a, b;
293 OPTLIST const *op;
294 u_long isset, v;
295 int cnt, optindx;
296 char *s;
297 char b1[1024];
298 CHAR_T b2[1024];
299 CHAR_T *wp;
300 size_t wlen;
302 a.bp = b2;
303 b.bp = NULL;
304 a.len = b.len = 0;
305 argv[0] = &a;
306 argv[1] = &b;
308 /* Set numeric and string default values. */
309 #define OI(indx, str) { \
310 CHAR2INT(sp, str, strlen(str) + 1, wp, wlen); \
311 a.len = wlen - 1; \
312 (void)MEMCPYW(b2, wp, a.len + 1); \
313 if (opts_set(sp, argv, NULL)) { \
314 optindx = indx; \
315 goto err; \
319 * Indirect global options to global space. Specifically, set up
320 * terminal, lines, columns first, they're used by other options.
321 * Note, don't set the flags until we've set up the indirection.
323 if (o_set(sp, O_TERM, 0, NULL, GO_TERM))
324 goto err;
325 F_SET(&sp->opts[O_TERM], OPT_GLOBAL);
326 if (o_set(sp, O_LINES, 0, NULL, GO_LINES))
327 goto err;
328 F_SET(&sp->opts[O_LINES], OPT_GLOBAL);
329 if (o_set(sp, O_COLUMNS, 0, NULL, GO_COLUMNS))
330 goto err;
331 F_SET(&sp->opts[O_COLUMNS], OPT_GLOBAL);
332 if (o_set(sp, O_SECURE, 0, NULL, GO_SECURE))
333 goto err;
334 F_SET(&sp->opts[O_SECURE], OPT_GLOBAL);
336 /* Initialize string values. */
337 (void)snprintf(b1, sizeof(b1),
338 "cdpath=%s", (s = getenv("CDPATH")) == NULL ? ":" : s);
339 OI(O_CDPATH, b1);
342 * !!!
343 * Vi historically stored temporary files in /var/tmp. We store them
344 * in /tmp by default, hoping it's a memory based file system. There
345 * are two ways to change this -- the user can set either the directory
346 * option or the TMPDIR environmental variable.
348 (void)snprintf(b1, sizeof(b1),
349 "directory=%s", (s = getenv("TMPDIR")) == NULL ? _PATH_TMP : s);
350 OI(O_DIRECTORY, b1);
351 OI(O_ESCAPETIME, "escapetime=1");
352 OI(O_KEYTIME, "keytime=6");
353 OI(O_MATCHTIME, "matchtime=7");
354 (void)snprintf(b1, sizeof(b1), "msgcat=%s", _PATH_MSGCAT);
355 OI(O_MSGCAT, b1);
356 OI(O_REPORT, "report=5");
357 OI(O_PARAGRAPHS, "paragraphs=IPLPPPQPP LIpplpipbp");
358 (void)snprintf(b1, sizeof(b1), "path=%s", "");
359 OI(O_PATH, b1);
360 (void)snprintf(b1, sizeof(b1), "recdir=%s", _PATH_PRESERVE);
361 OI(O_RECDIR, b1);
362 OI(O_SECTIONS, "sections=NHSHH HUnhsh");
363 (void)snprintf(b1, sizeof(b1),
364 "shell=%s", (s = getenv("SHELL")) == NULL ? _PATH_BSHELL : s);
365 OI(O_SHELL, b1);
366 OI(O_SHELLMETA, "shellmeta=~{[*?$`'\"\\");
367 OI(O_SHIFTWIDTH, "shiftwidth=8");
368 OI(O_SIDESCROLL, "sidescroll=16");
369 OI(O_TABSTOP, "tabstop=8");
370 (void)snprintf(b1, sizeof(b1), "tags=%s", _PATH_TAGS);
371 OI(O_TAGS, b1);
374 * XXX
375 * Initialize O_SCROLL here, after term; initializing term should
376 * have created a LINES/COLUMNS value.
378 if ((v = (O_VAL(sp, O_LINES) - 1) / 2) == 0)
379 v = 1;
380 (void)snprintf(b1, sizeof(b1), "scroll=%ld", v);
381 OI(O_SCROLL, b1);
384 * The default window option values are:
385 * 8 if baud rate <= 600
386 * 16 if baud rate <= 1200
387 * LINES - 1 if baud rate > 1200
389 * Note, the windows option code will correct any too-large value
390 * or when the O_LINES value is 1.
392 if (sp->gp->scr_baud(sp, &v))
393 return (1);
394 if (v <= 600)
395 v = 8;
396 else if (v <= 1200)
397 v = 16;
398 else if ((v = O_VAL(sp, O_LINES) - 1) == 0)
399 v = 1;
401 (void)snprintf(b1, sizeof(b1), "window=%lu", v);
402 OI(O_WINDOW, b1);
405 * Set boolean default values, and copy all settings into the default
406 * information. OS_NOFREE is set, we're copying, not replacing.
408 for (op = optlist, cnt = 0; op->name != NULL; ++op, ++cnt)
409 switch (op->type) {
410 case OPT_0BOOL:
411 break;
412 case OPT_1BOOL:
413 O_SET(sp, cnt);
414 O_D_SET(sp, cnt);
415 break;
416 case OPT_NUM:
417 o_set(sp, cnt, OS_DEF, NULL, O_VAL(sp, cnt));
418 break;
419 case OPT_STR:
420 if (O_STR(sp, cnt) != NULL && o_set(sp, cnt,
421 OS_DEF | OS_NOFREE | OS_STRDUP, O_STR(sp, cnt), 0))
422 goto err;
423 break;
424 default:
425 abort();
429 * !!!
430 * Some options can be initialized by the command name or the
431 * command-line arguments. They don't set the default values,
432 * it's historic practice.
434 for (; *oargs != -1; ++oargs)
435 OI(*oargs, optlist[*oargs].name);
436 #undef OI
439 * Inform the underlying screen of the initial values of the
440 * edit options.
442 for (op = optlist, cnt = 0; op->name != NULL; ++op, ++cnt) {
443 isset = O_ISSET(sp, cnt);
444 (void)sp->gp->scr_optchange(sp, cnt, O_STR(sp, cnt), &isset);
446 return (0);
448 err: msgq(sp, M_ERR,
449 "031|Unable to set default %s option", optlist[optindx].name);
450 return (1);
454 * opts_set --
455 * Change the values of one or more options.
457 * PUBLIC: int opts_set __P((SCR *, ARGS *[], char *));
460 opts_set(sp, argv, usage)
461 SCR *sp;
462 ARGS *argv[];
463 char *usage;
465 enum optdisp disp;
466 enum nresult nret;
467 OPTLIST const *op;
468 OPTION *spo;
469 u_long isset, turnoff, value;
470 int ch, equals, nf, nf2, offset, qmark, rval;
471 CHAR_T *endp, *name, *p, *sep, *t;
472 char *p2, *t2;
473 char *np;
474 size_t nlen;
476 disp = NO_DISPLAY;
477 for (rval = 0; argv[0]->len != 0; ++argv) {
478 static CHAR_T all[] = {'a', 'l', 'l', 0};
480 * The historic vi dumped the options for each occurrence of
481 * "all" in the set list. Puhleeze.
483 if (!memcmp(argv[0]->bp, all, sizeof(all))) {
484 disp = ALL_DISPLAY;
485 continue;
488 /* Find equals sign or question mark. */
489 for (sep = NULL, equals = qmark = 0,
490 p = name = argv[0]->bp; (ch = *p) != '\0'; ++p)
491 if (ch == '=' || ch == '?') {
492 if (p == name) {
493 if (usage != NULL)
494 msgq(sp, M_ERR,
495 "032|Usage: %s", usage);
496 return (1);
498 sep = p;
499 if (ch == '=')
500 equals = 1;
501 else
502 qmark = 1;
503 break;
506 turnoff = 0;
507 op = NULL;
508 if (sep != NULL)
509 *sep++ = '\0';
511 /* Search for the name, then name without any leading "no". */
512 INT2CHAR(sp, name, v_strlen(name) + 1, np, nlen);
513 if ((op = opts_search(np)) == NULL &&
514 name[0] == 'n' && name[1] == 'o') {
515 turnoff = 1;
516 name += 2;
517 INT2CHAR(sp, name, v_strlen(name) + 1, np, nlen);
518 op = opts_search(np);
520 if (op == NULL) {
521 INT2CHAR(sp, name, v_strlen(name) + 1, np, nlen);
522 opts_nomatch(sp, np);
523 rval = 1;
524 continue;
527 /* Find current option values. */
528 offset = op - optlist;
529 spo = sp->opts + offset;
532 * !!!
533 * Historically, the question mark could be a separate
534 * argument.
536 if (!equals && !qmark &&
537 argv[1]->len == 1 && argv[1]->bp[0] == '?') {
538 ++argv;
539 qmark = 1;
542 /* Set name, value. */
543 switch (op->type) {
544 case OPT_0BOOL:
545 case OPT_1BOOL:
546 /* Some options may not be reset. */
547 if (F_ISSET(op, OPT_NOUNSET) && turnoff) {
548 msgq_wstr(sp, M_ERR, name,
549 "291|set: the %s option may not be turned off");
550 rval = 1;
551 break;
554 /* Some options may not be set. */
555 if (F_ISSET(op, OPT_NOSET) && !turnoff) {
556 msgq_wstr(sp, M_ERR, name,
557 "313|set: the %s option may never be turned on");
558 rval = 1;
559 break;
562 if (equals) {
563 msgq_wstr(sp, M_ERR, name,
564 "034|set: [no]%s option doesn't take a value");
565 rval = 1;
566 break;
568 if (qmark) {
569 if (!disp)
570 disp = SELECT_DISPLAY;
571 F_SET(spo, OPT_SELECTED);
572 break;
576 * Do nothing if the value is unchanged, the underlying
577 * functions can be expensive.
579 isset = !turnoff;
580 if (!F_ISSET(op, OPT_ALWAYS))
581 if (isset) {
582 if (O_ISSET(sp, offset))
583 break;
584 } else
585 if (!O_ISSET(sp, offset))
586 break;
588 /* Report to subsystems. */
589 if (op->func != NULL &&
590 op->func(sp, spo, NULL, &isset) ||
591 ex_optchange(sp, offset, NULL, &isset) ||
592 v_optchange(sp, offset, NULL, &isset) ||
593 sp->gp->scr_optchange(sp, offset, NULL, &isset)) {
594 rval = 1;
595 break;
598 /* Set the value. */
599 if (isset)
600 O_SET(sp, offset);
601 else
602 O_CLR(sp, offset);
603 break;
604 case OPT_NUM:
605 if (turnoff) {
606 msgq_wstr(sp, M_ERR, name,
607 "035|set: %s option isn't a boolean");
608 rval = 1;
609 break;
611 if (qmark || !equals) {
612 if (!disp)
613 disp = SELECT_DISPLAY;
614 F_SET(spo, OPT_SELECTED);
615 break;
618 if (!isdigit(sep[0]))
619 goto badnum;
620 if ((nret =
621 nget_uslong(sp, &value, sep, &endp, 10)) != NUM_OK) {
622 INT2CHAR(sp, name, v_strlen(name) + 1,
623 np, nlen);
624 p2 = msg_print(sp, np, &nf);
625 INT2CHAR(sp, sep, v_strlen(sep) + 1,
626 np, nlen);
627 t2 = msg_print(sp, np, &nf2);
628 switch (nret) {
629 case NUM_ERR:
630 msgq(sp, M_SYSERR,
631 "036|set: %s option: %s", p2, t2);
632 break;
633 case NUM_OVER:
634 msgq(sp, M_ERR,
635 "037|set: %s option: %s: value overflow", p2, t2);
636 break;
637 case NUM_OK:
638 case NUM_UNDER:
639 abort();
641 if (nf)
642 FREE_SPACE(sp, p2, 0);
643 if (nf2)
644 FREE_SPACE(sp, t2, 0);
645 rval = 1;
646 break;
648 if (*endp && !isblank(*endp)) {
649 badnum: INT2CHAR(sp, name, v_strlen(name) + 1,
650 np, nlen);
651 p2 = msg_print(sp, np, &nf);
652 INT2CHAR(sp, sep, v_strlen(sep) + 1,
653 np, nlen);
654 t2 = msg_print(sp, np, &nf2);
655 msgq(sp, M_ERR,
656 "038|set: %s option: %s is an illegal number", p2, t2);
657 if (nf)
658 FREE_SPACE(sp, p2, 0);
659 if (nf2)
660 FREE_SPACE(sp, t2, 0);
661 rval = 1;
662 break;
665 /* Some options may never be set to zero. */
666 if (F_ISSET(op, OPT_NOZERO) && value == 0) {
667 msgq_wstr(sp, M_ERR, name,
668 "314|set: the %s option may never be set to 0");
669 rval = 1;
670 break;
674 * Do nothing if the value is unchanged, the underlying
675 * functions can be expensive.
677 if (!F_ISSET(op, OPT_ALWAYS) &&
678 O_VAL(sp, offset) == value)
679 break;
681 /* Report to subsystems. */
682 INT2CHAR(sp, sep, v_strlen(sep) + 1, np, nlen);
683 if (op->func != NULL &&
684 op->func(sp, spo, np, &value) ||
685 ex_optchange(sp, offset, np, &value) ||
686 v_optchange(sp, offset, np, &value) ||
687 sp->gp->scr_optchange(sp, offset, np, &value)) {
688 rval = 1;
689 break;
692 /* Set the value. */
693 if (o_set(sp, offset, 0, NULL, value))
694 rval = 1;
695 break;
696 case OPT_STR:
697 if (turnoff) {
698 msgq_wstr(sp, M_ERR, name,
699 "039|set: %s option isn't a boolean");
700 rval = 1;
701 break;
703 if (qmark || !equals) {
704 if (!disp)
705 disp = SELECT_DISPLAY;
706 F_SET(spo, OPT_SELECTED);
707 break;
711 * Do nothing if the value is unchanged, the underlying
712 * functions can be expensive.
714 INT2CHAR(sp, sep, v_strlen(sep) + 1, np, nlen);
715 if (!F_ISSET(op, OPT_ALWAYS) &&
716 O_STR(sp, offset) != NULL &&
717 !strcmp(O_STR(sp, offset), np))
718 break;
720 /* Report to subsystems. */
721 if (op->func != NULL &&
722 op->func(sp, spo, np, NULL) ||
723 ex_optchange(sp, offset, np, NULL) ||
724 v_optchange(sp, offset, np, NULL) ||
725 sp->gp->scr_optchange(sp, offset, np, NULL)) {
726 rval = 1;
727 break;
730 /* Set the value. */
731 if (o_set(sp, offset, OS_STRDUP, np, 0))
732 rval = 1;
733 break;
734 default:
735 abort();
738 if (disp != NO_DISPLAY)
739 opts_dump(sp, disp);
740 return (rval);
744 * o_set --
745 * Set an option's value.
747 * PUBLIC: int o_set __P((SCR *, int, u_int, char *, u_long));
750 o_set(sp, opt, flags, str, val)
751 SCR *sp;
752 int opt;
753 u_int flags;
754 char *str;
755 u_long val;
757 OPTION *op;
759 /* Set a pointer to the options area. */
760 op = F_ISSET(&sp->opts[opt], OPT_GLOBAL) ?
761 &sp->gp->opts[sp->opts[opt].o_cur.val] : &sp->opts[opt];
763 /* Copy the string, if requested. */
764 if (LF_ISSET(OS_STRDUP) && (str = strdup(str)) == NULL) {
765 msgq(sp, M_SYSERR, NULL);
766 return (1);
769 /* Free the previous string, if requested, and set the value. */
770 if LF_ISSET(OS_DEF)
771 if (LF_ISSET(OS_STR | OS_STRDUP)) {
772 if (!LF_ISSET(OS_NOFREE) && op->o_def.str != NULL)
773 free(op->o_def.str);
774 op->o_def.str = str;
775 } else
776 op->o_def.val = val;
777 else
778 if (LF_ISSET(OS_STR | OS_STRDUP)) {
779 if (!LF_ISSET(OS_NOFREE) && op->o_cur.str != NULL)
780 free(op->o_cur.str);
781 op->o_cur.str = str;
782 } else
783 op->o_cur.val = val;
784 return (0);
788 * opts_empty --
789 * Return 1 if the string option is invalid, 0 if it's OK.
791 * PUBLIC: int opts_empty __P((SCR *, int, int));
794 opts_empty(sp, off, silent)
795 SCR *sp;
796 int off, silent;
798 char *p;
800 if ((p = O_STR(sp, off)) == NULL || p[0] == '\0') {
801 if (!silent)
802 msgq_str(sp, M_ERR, optlist[off].name,
803 "305|No %s edit option specified");
804 return (1);
806 return (0);
810 * opts_dump --
811 * List the current values of selected options.
813 * PUBLIC: void opts_dump __P((SCR *, enum optdisp));
815 void
816 opts_dump(sp, type)
817 SCR *sp;
818 enum optdisp type;
820 OPTLIST const *op;
821 int base, b_num, cnt, col, colwidth, curlen, s_num;
822 int numcols, numrows, row;
823 int b_op[O_OPTIONCOUNT], s_op[O_OPTIONCOUNT];
824 char nbuf[20];
825 CHAR_T *kp;
828 * Options are output in two groups -- those that fit in a column and
829 * those that don't. Output is done on 6 character "tab" boundaries
830 * for no particular reason. (Since we don't output tab characters,
831 * we can ignore the terminal's tab settings.) Ignore the user's tab
832 * setting because we have no idea how reasonable it is.
834 * Find a column width we can live with, testing from 10 columns to 1.
836 for (numcols = 10; numcols > 1; --numcols) {
837 colwidth = sp->cols / numcols & ~(STANDARD_TAB - 1);
838 if (colwidth >= 10) {
839 colwidth =
840 (colwidth + STANDARD_TAB) & ~(STANDARD_TAB - 1);
841 numcols = sp->cols / colwidth;
842 break;
844 colwidth = 0;
848 * Get the set of options to list, entering them into
849 * the column list or the overflow list.
851 for (b_num = s_num = 0, op = optlist; op->name != NULL; ++op) {
852 cnt = op - optlist;
854 /* If OPT_NDISP set, it's never displayed. */
855 if (F_ISSET(op, OPT_NDISP))
856 continue;
858 switch (type) {
859 case ALL_DISPLAY: /* Display all. */
860 break;
861 case CHANGED_DISPLAY: /* Display changed. */
862 /* If OPT_ADISP set, it's always "changed". */
863 if (F_ISSET(op, OPT_ADISP))
864 break;
865 switch (op->type) {
866 case OPT_0BOOL:
867 case OPT_1BOOL:
868 case OPT_NUM:
869 if (O_VAL(sp, cnt) == O_D_VAL(sp, cnt))
870 continue;
871 break;
872 case OPT_STR:
873 if (O_STR(sp, cnt) == O_D_STR(sp, cnt) ||
874 O_D_STR(sp, cnt) != NULL &&
875 !strcmp(O_STR(sp, cnt), O_D_STR(sp, cnt)))
876 continue;
877 break;
879 break;
880 case SELECT_DISPLAY: /* Display selected. */
881 if (!F_ISSET(&sp->opts[cnt], OPT_SELECTED))
882 continue;
883 break;
884 default:
885 case NO_DISPLAY:
886 abort();
888 F_CLR(&sp->opts[cnt], OPT_SELECTED);
890 curlen = strlen(op->name);
891 switch (op->type) {
892 case OPT_0BOOL:
893 case OPT_1BOOL:
894 if (!O_ISSET(sp, cnt))
895 curlen += 2;
896 break;
897 case OPT_NUM:
898 (void)snprintf(nbuf,
899 sizeof(nbuf), "%ld", O_VAL(sp, cnt));
900 curlen += strlen(nbuf);
901 break;
902 case OPT_STR:
903 if (O_STR(sp, cnt) != NULL)
904 curlen += strlen(O_STR(sp, cnt));
905 curlen += 3;
906 break;
908 /* Offset by 2 so there's a gap. */
909 if (curlen <= colwidth - 2)
910 s_op[s_num++] = cnt;
911 else
912 b_op[b_num++] = cnt;
915 if (s_num > 0) {
916 /* Figure out the number of rows. */
917 if (s_num > numcols) {
918 numrows = s_num / numcols;
919 if (s_num % numcols)
920 ++numrows;
921 } else
922 numrows = 1;
924 /* Display the options in sorted order. */
925 for (row = 0; row < numrows;) {
926 for (base = row, col = 0; col < numcols; ++col) {
927 cnt = opts_print(sp, &optlist[s_op[base]]);
928 if ((base += numrows) >= s_num)
929 break;
930 (void)ex_printf(sp, "%*s",
931 (int)(colwidth - cnt), "");
933 if (++row < numrows || b_num)
934 (void)ex_puts(sp, "\n");
938 for (row = 0; row < b_num;) {
939 (void)opts_print(sp, &optlist[b_op[row]]);
940 if (++row < b_num)
941 (void)ex_puts(sp, "\n");
943 (void)ex_puts(sp, "\n");
947 * opts_print --
948 * Print out an option.
950 static int
951 opts_print(sp, op)
952 SCR *sp;
953 OPTLIST const *op;
955 int curlen, offset;
957 curlen = 0;
958 offset = op - optlist;
959 switch (op->type) {
960 case OPT_0BOOL:
961 case OPT_1BOOL:
962 curlen += ex_printf(sp,
963 "%s%s", O_ISSET(sp, offset) ? "" : "no", op->name);
964 break;
965 case OPT_NUM:
966 curlen += ex_printf(sp, "%s=%ld", op->name, O_VAL(sp, offset));
967 break;
968 case OPT_STR:
969 curlen += ex_printf(sp, "%s=\"%s\"", op->name,
970 O_STR(sp, offset) == NULL ? "" : O_STR(sp, offset));
971 break;
973 return (curlen);
977 * opts_save --
978 * Write the current configuration to a file.
980 * PUBLIC: int opts_save __P((SCR *, FILE *));
983 opts_save(sp, fp)
984 SCR *sp;
985 FILE *fp;
987 OPTLIST const *op;
988 int ch, cnt;
989 char *p;
991 for (op = optlist; op->name != NULL; ++op) {
992 if (F_ISSET(op, OPT_NOSAVE))
993 continue;
994 cnt = op - optlist;
995 switch (op->type) {
996 case OPT_0BOOL:
997 case OPT_1BOOL:
998 if (O_ISSET(sp, cnt))
999 (void)fprintf(fp, "set %s\n", op->name);
1000 else
1001 (void)fprintf(fp, "set no%s\n", op->name);
1002 break;
1003 case OPT_NUM:
1004 (void)fprintf(fp,
1005 "set %s=%-3ld\n", op->name, O_VAL(sp, cnt));
1006 break;
1007 case OPT_STR:
1008 if (O_STR(sp, cnt) == NULL)
1009 break;
1010 (void)fprintf(fp, "set ");
1011 for (p = op->name; (ch = *p) != '\0'; ++p) {
1012 if (isblank(ch) || ch == '\\')
1013 (void)putc('\\', fp);
1014 (void)putc(ch, fp);
1016 (void)putc('=', fp);
1017 for (p = O_STR(sp, cnt); (ch = *p) != '\0'; ++p) {
1018 if (isblank(ch) || ch == '\\')
1019 (void)putc('\\', fp);
1020 (void)putc(ch, fp);
1022 (void)putc('\n', fp);
1023 break;
1025 if (ferror(fp)) {
1026 msgq(sp, M_SYSERR, NULL);
1027 return (1);
1030 return (0);
1034 * opts_search --
1035 * Search for an option.
1037 * PUBLIC: OPTLIST const *opts_search __P((char *));
1039 OPTLIST const *
1040 opts_search(name)
1041 char *name;
1043 OPTLIST const *op, *found;
1044 OABBREV atmp, *ap;
1045 OPTLIST otmp;
1046 size_t len;
1048 /* Check list of abbreviations. */
1049 atmp.name = name;
1050 if ((ap = bsearch(&atmp, abbrev, sizeof(abbrev) / sizeof(OABBREV) - 1,
1051 sizeof(OABBREV), opts_abbcmp)) != NULL)
1052 return (optlist + ap->offset);
1054 /* Check list of options. */
1055 otmp.name = name;
1056 if ((op = bsearch(&otmp, optlist, sizeof(optlist) / sizeof(OPTLIST) - 1,
1057 sizeof(OPTLIST), opts_cmp)) != NULL)
1058 return (op);
1061 * Check to see if the name is the prefix of one (and only one)
1062 * option. If so, return the option.
1064 len = strlen(name);
1065 for (found = NULL, op = optlist; op->name != NULL; ++op) {
1066 if (op->name[0] < name[0])
1067 continue;
1068 if (op->name[0] > name[0])
1069 break;
1070 if (!memcmp(op->name, name, len)) {
1071 if (found != NULL)
1072 return (NULL);
1073 found = op;
1076 return (found);
1080 * opts_nomatch --
1081 * Standard nomatch error message for options.
1083 * PUBLIC: void opts_nomatch __P((SCR *, char *));
1085 void
1086 opts_nomatch(sp, name)
1087 SCR *sp;
1088 char *name;
1090 msgq_str(sp, M_ERR, name,
1091 "033|set: no %s option: 'set all' gives all option values");
1094 static int
1095 opts_abbcmp(a, b)
1096 const void *a, *b;
1098 return(strcmp(((OABBREV *)a)->name, ((OABBREV *)b)->name));
1101 static int
1102 opts_cmp(a, b)
1103 const void *a, *b;
1105 return(strcmp(((OPTLIST *)a)->name, ((OPTLIST *)b)->name));
1109 * opts_copy --
1110 * Copy a screen's OPTION array.
1112 * PUBLIC: int opts_copy __P((SCR *, SCR *));
1115 opts_copy(orig, sp)
1116 SCR *orig, *sp;
1118 int cnt, rval;
1120 /* Copy most everything without change. */
1121 memcpy(sp->opts, orig->opts, sizeof(orig->opts));
1123 /* Copy the string edit options. */
1124 for (cnt = rval = 0; cnt < O_OPTIONCOUNT; ++cnt) {
1125 if (optlist[cnt].type != OPT_STR ||
1126 F_ISSET(&optlist[cnt], OPT_GLOBAL))
1127 continue;
1129 * If never set, or already failed, NULL out the entries --
1130 * have to continue after failure, otherwise would have two
1131 * screens referencing the same memory.
1133 if (rval || O_STR(sp, cnt) == NULL) {
1134 o_set(sp, cnt, OS_NOFREE | OS_STR, NULL, 0);
1135 o_set(sp, cnt, OS_DEF | OS_NOFREE | OS_STR, NULL, 0);
1136 continue;
1139 /* Copy the current string. */
1140 if (o_set(sp, cnt, OS_NOFREE | OS_STRDUP, O_STR(sp, cnt), 0)) {
1141 o_set(sp, cnt, OS_DEF | OS_NOFREE | OS_STR, NULL, 0);
1142 goto nomem;
1145 /* Copy the default string. */
1146 if (O_D_STR(sp, cnt) != NULL && o_set(sp, cnt,
1147 OS_DEF | OS_NOFREE | OS_STRDUP, O_D_STR(sp, cnt), 0)) {
1148 nomem: msgq(orig, M_SYSERR, NULL);
1149 rval = 1;
1152 return (rval);
1156 * opts_free --
1157 * Free all option strings
1159 * PUBLIC: void opts_free __P((SCR *));
1161 void
1162 opts_free(sp)
1163 SCR *sp;
1165 int cnt;
1167 for (cnt = 0; cnt < O_OPTIONCOUNT; ++cnt) {
1168 if (optlist[cnt].type != OPT_STR ||
1169 F_ISSET(&optlist[cnt], OPT_GLOBAL))
1170 continue;
1171 if (O_STR(sp, cnt) != NULL)
1172 free(O_STR(sp, cnt));
1173 if (O_D_STR(sp, cnt) != NULL)
1174 free(O_D_STR(sp, cnt));