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