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