119304Speter/*-
219304Speter * Copyright (c) 1991, 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1991, 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: options.c,v 10.73 2012/10/09 06:14:07 zy Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
1819304Speter#include <sys/stat.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <ctype.h>
2219304Speter#include <errno.h>
2319304Speter#include <limits.h>
2419304Speter#include <stdio.h>
2519304Speter#include <stdlib.h>
2619304Speter#include <string.h>
2719304Speter#include <unistd.h>
2819304Speter
2919304Speter#include "common.h"
3019304Speter#include "../vi/vi.h"
3119304Speter#include "pathnames.h"
3219304Speter
3319304Speterstatic int	 	 opts_abbcmp __P((const void *, const void *));
3419304Speterstatic int	 	 opts_cmp __P((const void *, const void *));
3519304Speterstatic int	 	 opts_print __P((SCR *, OPTLIST const *));
3619304Speter
37254225Speter#ifdef USE_WIDECHAR
38254225Speter#define OPT_WC	    0
39254225Speter#else
40254225Speter#define OPT_WC	    (OPT_NOSAVE | OPT_NDISP)
41254225Speter#endif
42254225Speter
4319304Speter/*
4419304Speter * O'Reilly noted options and abbreviations are from "Learning the VI Editor",
4519304Speter * Fifth Edition, May 1992.  There's no way of knowing what systems they are
4619304Speter * actually from.
4719304Speter *
4819304Speter * HPUX noted options and abbreviations are from "The Ultimate Guide to the
4919304Speter * VI and EX Text Editors", 1990.
5019304Speter */
5119304SpeterOPTLIST const optlist[] = {
5219304Speter/* O_ALTWERASE	  4.4BSD */
53254225Speter	{L("altwerase"),	f_altwerase,	OPT_0BOOL,	0},
5419304Speter/* O_AUTOINDENT	    4BSD */
55254225Speter	{L("autoindent"),	NULL,		OPT_0BOOL,	0},
5619304Speter/* O_AUTOPRINT	    4BSD */
57254225Speter	{L("autoprint"),	NULL,		OPT_1BOOL,	0},
5819304Speter/* O_AUTOWRITE	    4BSD */
59254225Speter	{L("autowrite"),	NULL,		OPT_0BOOL,	0},
6019304Speter/* O_BACKUP	  4.4BSD */
61254225Speter	{L("backup"),	NULL,		OPT_STR,	0},
6219304Speter/* O_BEAUTIFY	    4BSD */
63254225Speter	{L("beautify"),	NULL,		OPT_0BOOL,	0},
6419304Speter/* O_CDPATH	  4.4BSD */
65254225Speter	{L("cdpath"),	NULL,		OPT_STR,	0},
6619304Speter/* O_CEDIT	  4.4BSD */
67254225Speter	{L("cedit"),	NULL,		OPT_STR,	0},
6819304Speter/* O_COLUMNS	  4.4BSD */
69254225Speter	{L("columns"),	f_columns,	OPT_NUM,	OPT_NOSAVE},
70254225Speter/* O_COMBINED */
71254225Speter	{L("combined"),	NULL,		OPT_0BOOL,	OPT_NOSET|OPT_WC},
7219304Speter/* O_COMMENT	  4.4BSD */
73254225Speter	{L("comment"),	NULL,		OPT_0BOOL,	0},
74254225Speter/* O_TMPDIR	    4BSD */
75254225Speter	{L("directory"),	NULL,		OPT_STR,	0},
7619304Speter/* O_EDCOMPATIBLE   4BSD */
77254225Speter	{L("edcompatible"),NULL,		OPT_0BOOL,	0},
78254225Speter/* O_ERRORBELLS	    4BSD */
79254225Speter	{L("errorbells"),	NULL,		OPT_0BOOL,	0},
8019304Speter/* O_ESCAPETIME	  4.4BSD */
81254225Speter	{L("escapetime"),	NULL,		OPT_NUM,	0},
8219304Speter/* O_EXRC	System V (undocumented) */
83254225Speter	{L("exrc"),	NULL,		OPT_0BOOL,	0},
8419304Speter/* O_EXTENDED	  4.4BSD */
85254225Speter	{L("extended"),	f_recompile,	OPT_0BOOL,	0},
8619304Speter/* O_FILEC	  4.4BSD */
87254225Speter	{L("filec"),	NULL,		OPT_STR,	0},
88254225Speter/* O_FILEENCODING */
89254225Speter	{L("fileencoding"),f_encoding,	OPT_STR,	OPT_WC},
9019304Speter/* O_FLASH	    HPUX */
91254225Speter	{L("flash"),	NULL,		OPT_1BOOL,	0},
9219304Speter/* O_HARDTABS	    4BSD */
93254225Speter	{L("hardtabs"),	NULL,		OPT_NUM,	0},
9419304Speter/* O_ICLOWER	  4.4BSD */
95254225Speter	{L("iclower"),	f_recompile,	OPT_0BOOL,	0},
9619304Speter/* O_IGNORECASE	    4BSD */
97254225Speter	{L("ignorecase"),	f_recompile,	OPT_0BOOL,	0},
98254225Speter/* O_INPUTENCODING */
99254225Speter	{L("inputencoding"),f_encoding,	OPT_STR,	OPT_WC},
10019304Speter/* O_KEYTIME	  4.4BSD */
101254225Speter	{L("keytime"),	NULL,		OPT_NUM,	0},
10219304Speter/* O_LEFTRIGHT	  4.4BSD */
103254225Speter	{L("leftright"),	f_reformat,	OPT_0BOOL,	0},
10419304Speter/* O_LINES	  4.4BSD */
105254225Speter	{L("lines"),	f_lines,	OPT_NUM,	OPT_NOSAVE},
10619304Speter/* O_LISP	    4BSD
10719304Speter *	XXX
10819304Speter *	When the lisp option is implemented, delete the OPT_NOSAVE flag,
10919304Speter *	so that :mkexrc dumps it.
11019304Speter */
111254225Speter	{L("lisp"),	f_lisp,		OPT_0BOOL,	OPT_NOSAVE},
11219304Speter/* O_LIST	    4BSD */
113254225Speter	{L("list"),	f_reformat,	OPT_0BOOL,	0},
11419304Speter/* O_LOCKFILES	  4.4BSD
11519304Speter *	XXX
11619304Speter *	Locking isn't reliable enough over NFS to require it, in addition,
11719304Speter *	it's a serious startup performance problem over some remote links.
11819304Speter */
119254225Speter	{L("lock"),	NULL,		OPT_1BOOL,	0},
12019304Speter/* O_MAGIC	    4BSD */
121254225Speter	{L("magic"),	NULL,		OPT_1BOOL,	0},
122254225Speter/* O_MATCHCHARS	  NetBSD 2.0 */
123254225Speter	{L("matchchars"),	NULL,		OPT_STR,	OPT_PAIRS},
12419304Speter/* O_MATCHTIME	  4.4BSD */
125254225Speter	{L("matchtime"),	NULL,		OPT_NUM,	0},
12619304Speter/* O_MESG	    4BSD */
127254225Speter	{L("mesg"),	NULL,		OPT_1BOOL,	0},
12819304Speter/* O_MODELINE	    4BSD
12919304Speter *	!!!
13019304Speter *	This has been documented in historical systems as both "modeline"
13119304Speter *	and as "modelines".  Regardless of the name, this option represents
13219304Speter *	a security problem of mammoth proportions, not to mention a stunning
13319304Speter *	example of what your intro CS professor referred to as the perils of
13419304Speter *	mixing code and data.  Don't add it, or I will kill you.
13519304Speter */
136254225Speter	{L("modeline"),	NULL,		OPT_0BOOL,	OPT_NOSET},
13719304Speter/* O_MSGCAT	  4.4BSD */
138254225Speter	{L("msgcat"),	f_msgcat,	OPT_STR,	0},
13919304Speter/* O_NOPRINT	  4.4BSD */
140254225Speter	{L("noprint"),	f_print,	OPT_STR,	0},
14119304Speter/* O_NUMBER	    4BSD */
142254225Speter	{L("number"),	f_reformat,	OPT_0BOOL,	0},
14319304Speter/* O_OCTAL	  4.4BSD */
144254225Speter	{L("octal"),	f_print,	OPT_0BOOL,	0},
14519304Speter/* O_OPEN	    4BSD */
146254225Speter	{L("open"),	NULL,		OPT_1BOOL,	0},
14719304Speter/* O_OPTIMIZE	    4BSD */
148254225Speter	{L("optimize"),	NULL,		OPT_1BOOL,	0},
14919304Speter/* O_PARAGRAPHS	    4BSD */
150254225Speter	{L("paragraphs"),	NULL,		OPT_STR,	OPT_PAIRS},
15119304Speter/* O_PATH	  4.4BSD */
152254225Speter	{L("path"),	NULL,		OPT_STR,	0},
15319304Speter/* O_PRINT	  4.4BSD */
154254225Speter	{L("print"),	f_print,	OPT_STR,	0},
15519304Speter/* O_PROMPT	    4BSD */
156254225Speter	{L("prompt"),	NULL,		OPT_1BOOL,	0},
15719304Speter/* O_READONLY	    4BSD (undocumented) */
158254225Speter	{L("readonly"),	f_readonly,	OPT_0BOOL,	OPT_ALWAYS},
15919304Speter/* O_RECDIR	  4.4BSD */
160254225Speter	{L("recdir"),	NULL,		OPT_STR,	0},
16119304Speter/* O_REDRAW	    4BSD */
162254225Speter	{L("redraw"),	NULL,		OPT_0BOOL,	0},
16319304Speter/* O_REMAP	    4BSD */
164254225Speter	{L("remap"),	NULL,		OPT_1BOOL,	0},
16519304Speter/* O_REPORT	    4BSD */
166254225Speter	{L("report"),	NULL,		OPT_NUM,	0},
16719304Speter/* O_RULER	  4.4BSD */
168254225Speter	{L("ruler"),	NULL,		OPT_0BOOL,	0},
16919304Speter/* O_SCROLL	    4BSD */
170254225Speter	{L("scroll"),	NULL,		OPT_NUM,	0},
17119304Speter/* O_SEARCHINCR	  4.4BSD */
172254225Speter	{L("searchincr"),	NULL,		OPT_0BOOL,	0},
17319304Speter/* O_SECTIONS	    4BSD */
174254225Speter	{L("sections"),	NULL,		OPT_STR,	OPT_PAIRS},
17519304Speter/* O_SECURE	  4.4BSD */
176254225Speter	{L("secure"),	NULL,		OPT_0BOOL,	OPT_NOUNSET},
17719304Speter/* O_SHELL	    4BSD */
178254225Speter	{L("shell"),	NULL,		OPT_STR,	0},
17919304Speter/* O_SHELLMETA	  4.4BSD */
180254225Speter	{L("shellmeta"),	NULL,		OPT_STR,	0},
18119304Speter/* O_SHIFTWIDTH	    4BSD */
182254225Speter	{L("shiftwidth"),	NULL,		OPT_NUM,	OPT_NOZERO},
18319304Speter/* O_SHOWMATCH	    4BSD */
184254225Speter	{L("showmatch"),	NULL,		OPT_0BOOL,	0},
18519304Speter/* O_SHOWMODE	  4.4BSD */
186254225Speter	{L("showmode"),	NULL,		OPT_0BOOL,	0},
18719304Speter/* O_SIDESCROLL	  4.4BSD */
188254225Speter	{L("sidescroll"),	NULL,		OPT_NUM,	OPT_NOZERO},
18919304Speter/* O_SLOWOPEN	    4BSD  */
190254225Speter	{L("slowopen"),	NULL,		OPT_0BOOL,	0},
19119304Speter/* O_SOURCEANY	    4BSD (undocumented)
19219304Speter *	!!!
19319304Speter *	Historic vi, on startup, source'd $HOME/.exrc and ./.exrc, if they
19419304Speter *	were owned by the user.  The sourceany option was an undocumented
19519304Speter *	feature of historic vi which permitted the startup source'ing of
19619304Speter *	.exrc files the user didn't own.  This is an obvious security problem,
19719304Speter *	and we ignore the option.
19819304Speter */
199254225Speter	{L("sourceany"),	NULL,		OPT_0BOOL,	OPT_NOSET},
20019304Speter/* O_TABSTOP	    4BSD */
201254225Speter	{L("tabstop"),	f_reformat,	OPT_NUM,	OPT_NOZERO},
20219304Speter/* O_TAGLENGTH	    4BSD */
203254225Speter	{L("taglength"),	NULL,		OPT_NUM,	0},
20419304Speter/* O_TAGS	    4BSD */
205254225Speter	{L("tags"),	NULL,		OPT_STR,	0},
20619304Speter/* O_TERM	    4BSD
20719304Speter *	!!!
20819304Speter *	By default, the historic vi always displayed information about two
20919304Speter *	options, redraw and term.  Term seems sufficient.
21019304Speter */
211254225Speter	{L("term"),	NULL,		OPT_STR,	OPT_ADISP|OPT_NOSAVE},
21219304Speter/* O_TERSE	    4BSD */
213254225Speter	{L("terse"),	NULL,		OPT_0BOOL,	0},
21419304Speter/* O_TILDEOP      4.4BSD */
215254225Speter	{L("tildeop"),	NULL,		OPT_0BOOL,	0},
21619304Speter/* O_TIMEOUT	    4BSD (undocumented) */
217254225Speter	{L("timeout"),	NULL,		OPT_1BOOL,	0},
21819304Speter/* O_TTYWERASE	  4.4BSD */
219254225Speter	{L("ttywerase"),	f_ttywerase,	OPT_0BOOL,	0},
22019304Speter/* O_VERBOSE	  4.4BSD */
221254225Speter	{L("verbose"),	NULL,		OPT_0BOOL,	0},
22219304Speter/* O_W1200	    4BSD */
223254225Speter	{L("w1200"),	f_w1200,	OPT_NUM,	OPT_NDISP|OPT_NOSAVE},
22419304Speter/* O_W300	    4BSD */
225254225Speter	{L("w300"),	f_w300,		OPT_NUM,	OPT_NDISP|OPT_NOSAVE},
22619304Speter/* O_W9600	    4BSD */
227254225Speter	{L("w9600"),	f_w9600,	OPT_NUM,	OPT_NDISP|OPT_NOSAVE},
22819304Speter/* O_WARN	    4BSD */
229254225Speter	{L("warn"),	NULL,		OPT_1BOOL,	0},
23019304Speter/* O_WINDOW	    4BSD */
231254225Speter	{L("window"),	f_window,	OPT_NUM,	0},
23219304Speter/* O_WINDOWNAME	    4BSD */
233254225Speter	{L("windowname"),	NULL,		OPT_0BOOL,	0},
23419304Speter/* O_WRAPLEN	  4.4BSD */
235254225Speter	{L("wraplen"),	NULL,		OPT_NUM,	0},
23619304Speter/* O_WRAPMARGIN	    4BSD */
237254225Speter	{L("wrapmargin"),	NULL,		OPT_NUM,	0},
23819304Speter/* O_WRAPSCAN	    4BSD */
239254225Speter	{L("wrapscan"),	NULL,		OPT_1BOOL,	0},
24019304Speter/* O_WRITEANY	    4BSD */
241254225Speter	{L("writeany"),	NULL,		OPT_0BOOL,	0},
24219304Speter	{NULL},
24319304Speter};
24419304Speter
24519304Spetertypedef struct abbrev {
246254225Speter        CHAR_T *name;
24719304Speter        int offset;
24819304Speter} OABBREV;
24919304Speter
25019304Speterstatic OABBREV const abbrev[] = {
251254225Speter	{L("ai"),	O_AUTOINDENT},		/*     4BSD */
252254225Speter	{L("ap"),	O_AUTOPRINT},		/*     4BSD */
253254225Speter	{L("aw"),	O_AUTOWRITE},		/*     4BSD */
254254225Speter	{L("bf"),	O_BEAUTIFY},		/*     4BSD */
255254225Speter	{L("co"),	O_COLUMNS},		/*   4.4BSD */
256254225Speter	{L("dir"),	O_TMPDIR},		/*     4BSD */
257254225Speter	{L("eb"),	O_ERRORBELLS},		/*     4BSD */
258254225Speter	{L("ed"),	O_EDCOMPATIBLE},	/*     4BSD */
259254225Speter	{L("ex"),	O_EXRC},		/* System V (undocumented) */
260254225Speter	{L("fe"),	O_FILEENCODING},
261254225Speter	{L("ht"),	O_HARDTABS},		/*     4BSD */
262254225Speter	{L("ic"),	O_IGNORECASE},		/*     4BSD */
263254225Speter	{L("ie"),	O_INPUTENCODING},
264254225Speter	{L("li"),	O_LINES},		/*   4.4BSD */
265254225Speter	{L("modelines"),	O_MODELINE},		/*     HPUX */
266254225Speter	{L("nu"),	O_NUMBER},		/*     4BSD */
267254225Speter	{L("opt"),	O_OPTIMIZE},		/*     4BSD */
268254225Speter	{L("para"),	O_PARAGRAPHS},		/*     4BSD */
269254225Speter	{L("re"),	O_REDRAW},		/* O'Reilly */
270254225Speter	{L("ro"),	O_READONLY},		/*     4BSD (undocumented) */
271254225Speter	{L("scr"),	O_SCROLL},		/*     4BSD (undocumented) */
272254225Speter	{L("sect"),	O_SECTIONS},		/* O'Reilly */
273254225Speter	{L("sh"),	O_SHELL},		/*     4BSD */
274254225Speter	{L("slow"),	O_SLOWOPEN},		/*     4BSD */
275254225Speter	{L("sm"),	O_SHOWMATCH},		/*     4BSD */
276254225Speter	{L("smd"),	O_SHOWMODE},		/*     4BSD */
277254225Speter	{L("sw"),	O_SHIFTWIDTH},		/*     4BSD */
278254225Speter	{L("tag"),	O_TAGS},		/*     4BSD (undocumented) */
279254225Speter	{L("tl"),	O_TAGLENGTH},		/*     4BSD */
280254225Speter	{L("to"),	O_TIMEOUT},		/*     4BSD (undocumented) */
281254225Speter	{L("ts"),	O_TABSTOP},		/*     4BSD */
282254225Speter	{L("tty"),	O_TERM},		/*     4BSD (undocumented) */
283254225Speter	{L("ttytype"),	O_TERM},		/*     4BSD (undocumented) */
284254225Speter	{L("w"),	O_WINDOW},		/* O'Reilly */
285254225Speter	{L("wa"),	O_WRITEANY},		/*     4BSD */
286254225Speter	{L("wi"),	O_WINDOW},		/*     4BSD (undocumented) */
287254225Speter	{L("wl"),	O_WRAPLEN},		/*   4.4BSD */
288254225Speter	{L("wm"),	O_WRAPMARGIN},		/*     4BSD */
289254225Speter	{L("ws"),	O_WRAPSCAN},		/*     4BSD */
29019304Speter	{NULL},
29119304Speter};
29219304Speter
29319304Speter/*
29419304Speter * opts_init --
29519304Speter *	Initialize some of the options.
29619304Speter *
29719304Speter * PUBLIC: int opts_init __P((SCR *, int *));
29819304Speter */
29919304Speterint
300254225Speteropts_init(
301254225Speter	SCR *sp,
302254225Speter	int *oargs)
30319304Speter{
30419304Speter	ARGS *argv[2], a, b;
30519304Speter	OPTLIST const *op;
30619304Speter	u_long v;
307254225Speter	int cnt, optindx = 0;
308254225Speter	char *s;
309254225Speter	CHAR_T b2[1024];
31019304Speter
311254225Speter	a.bp = b2;
31219304Speter	b.bp = NULL;
31319304Speter	a.len = b.len = 0;
31419304Speter	argv[0] = &a;
31519304Speter	argv[1] = &b;
31619304Speter
31719304Speter	/* Set numeric and string default values. */
31819304Speter#define	OI(indx, str) {							\
319254225Speter	a.len = STRLEN(str);						\
320254225Speter	if ((CHAR_T*)str != b2)	  /* GCC puts strings in text-space. */	\
321254225Speter		(void)MEMCPY(b2, str, a.len+1);				\
32219304Speter	if (opts_set(sp, argv, NULL)) {					\
32319304Speter		 optindx = indx;					\
32419304Speter		goto err;						\
32519304Speter	}								\
32619304Speter}
32719304Speter	/*
32819304Speter	 * Indirect global options to global space.  Specifically, set up
32919304Speter	 * terminal, lines, columns first, they're used by other options.
33019304Speter	 * Note, don't set the flags until we've set up the indirection.
33119304Speter	 */
33219304Speter	if (o_set(sp, O_TERM, 0, NULL, GO_TERM))
33319304Speter		goto err;
33419304Speter	F_SET(&sp->opts[O_TERM], OPT_GLOBAL);
33519304Speter	if (o_set(sp, O_LINES, 0, NULL, GO_LINES))
33619304Speter		goto err;
33719304Speter	F_SET(&sp->opts[O_LINES], OPT_GLOBAL);
33819304Speter	if (o_set(sp, O_COLUMNS, 0, NULL, GO_COLUMNS))
33919304Speter		goto err;
34019304Speter	F_SET(&sp->opts[O_COLUMNS], OPT_GLOBAL);
34119304Speter	if (o_set(sp, O_SECURE, 0, NULL, GO_SECURE))
34219304Speter		goto err;
34319304Speter	F_SET(&sp->opts[O_SECURE], OPT_GLOBAL);
34419304Speter
34519304Speter	/* Initialize string values. */
346254225Speter	(void)SPRINTF(b2, SIZE(b2),
347254225Speter	    L("cdpath=%s"), (s = getenv("CDPATH")) == NULL ? ":" : s);
348254225Speter	OI(O_CDPATH, b2);
349254225Speter	OI(O_CEDIT, L("cedit=\033"));
35019304Speter
35119304Speter	/*
35219304Speter	 * !!!
35319304Speter	 * Vi historically stored temporary files in /var/tmp.  We store them
35419304Speter	 * in /tmp by default, hoping it's a memory based file system.  There
35519304Speter	 * are two ways to change this -- the user can set either the directory
35619304Speter	 * option or the TMPDIR environmental variable.
35719304Speter	 */
358254225Speter	(void)SPRINTF(b2, SIZE(b2),
359254225Speter	    L("directory=%s"), (s = getenv("TMPDIR")) == NULL ? _PATH_TMP : s);
360254225Speter	OI(O_TMPDIR, b2);
361254225Speter	OI(O_ESCAPETIME, L("escapetime=6"));
362254225Speter	OI(O_FILEC, L("filec=\t"));
363254225Speter	OI(O_KEYTIME, L("keytime=6"));
364254225Speter	OI(O_MATCHCHARS, L("matchchars=()[]{}"));
365254225Speter	OI(O_MATCHTIME, L("matchtime=7"));
366254225Speter	(void)SPRINTF(b2, SIZE(b2), L("msgcat=%s"), _PATH_MSGCAT);
367254225Speter	OI(O_MSGCAT, b2);
368254225Speter	OI(O_REPORT, L("report=5"));
369254225Speter	OI(O_PARAGRAPHS, L("paragraphs=IPLPPPQPP LIpplpipbp"));
370254225Speter	(void)SPRINTF(b2, SIZE(b2), L("path=%s"), "");
371254225Speter	OI(O_PATH, b2);
372254225Speter	(void)SPRINTF(b2, SIZE(b2), L("recdir=%s"), _PATH_PRESERVE);
373254225Speter	OI(O_RECDIR, b2);
374254225Speter	OI(O_SECTIONS, L("sections=NHSHH HUnhsh"));
375254225Speter	(void)SPRINTF(b2, SIZE(b2),
376254225Speter	    L("shell=%s"), (s = getenv("SHELL")) == NULL ? _PATH_BSHELL : s);
377254225Speter	OI(O_SHELL, b2);
378254225Speter	OI(O_SHELLMETA, L("shellmeta=~{[*?$`'\"\\"));
379254225Speter	OI(O_SHIFTWIDTH, L("shiftwidth=8"));
380254225Speter	OI(O_SIDESCROLL, L("sidescroll=16"));
381254225Speter	OI(O_TABSTOP, L("tabstop=8"));
382254225Speter	(void)SPRINTF(b2, SIZE(b2), L("tags=%s"), _PATH_TAGS);
383254225Speter	OI(O_TAGS, b2);
38419304Speter
38519304Speter	/*
38619304Speter	 * XXX
38719304Speter	 * Initialize O_SCROLL here, after term; initializing term should
38819304Speter	 * have created a LINES/COLUMNS value.
38919304Speter	 */
39019304Speter	if ((v = (O_VAL(sp, O_LINES) - 1) / 2) == 0)
39119304Speter		v = 1;
392254225Speter	(void)SPRINTF(b2, SIZE(b2), L("scroll=%ld"), v);
393254225Speter	OI(O_SCROLL, b2);
39419304Speter
39519304Speter	/*
39619304Speter	 * The default window option values are:
39719304Speter	 *		8 if baud rate <=  600
39819304Speter	 *	       16 if baud rate <= 1200
39919304Speter	 *	LINES - 1 if baud rate  > 1200
40019304Speter	 *
40119304Speter	 * Note, the windows option code will correct any too-large value
40219304Speter	 * or when the O_LINES value is 1.
40319304Speter	 */
40419304Speter	if (sp->gp->scr_baud(sp, &v))
40519304Speter		return (1);
40619304Speter	if (v <= 600)
40719304Speter		v = 8;
40819304Speter	else if (v <= 1200)
40919304Speter		v = 16;
410254225Speter	else if ((v = O_VAL(sp, O_LINES) - 1) == 0)
411254225Speter		v = 1;
41219304Speter
413254225Speter	(void)SPRINTF(b2, SIZE(b2), L("window=%lu"), v);
414254225Speter	OI(O_WINDOW, b2);
415254225Speter
41619304Speter	/*
41719304Speter	 * Set boolean default values, and copy all settings into the default
41819304Speter	 * information.  OS_NOFREE is set, we're copying, not replacing.
41919304Speter	 */
420254225Speter	for (op = optlist, cnt = 0; op->name != NULL; ++op, ++cnt) {
421254225Speter		if (F_ISSET(op, OPT_GLOBAL))
422254225Speter			continue;
42319304Speter		switch (op->type) {
42419304Speter		case OPT_0BOOL:
42519304Speter			break;
42619304Speter		case OPT_1BOOL:
42719304Speter			O_SET(sp, cnt);
42819304Speter			O_D_SET(sp, cnt);
42919304Speter			break;
43019304Speter		case OPT_NUM:
43119304Speter			o_set(sp, cnt, OS_DEF, NULL, O_VAL(sp, cnt));
43219304Speter			break;
43319304Speter		case OPT_STR:
43419304Speter			if (O_STR(sp, cnt) != NULL && o_set(sp, cnt,
43519304Speter			    OS_DEF | OS_NOFREE | OS_STRDUP, O_STR(sp, cnt), 0))
43619304Speter				goto err;
43719304Speter			break;
43819304Speter		default:
43919304Speter			abort();
44019304Speter		}
441254225Speter	}
44219304Speter
44319304Speter	/*
44419304Speter	 * !!!
44519304Speter	 * Some options can be initialized by the command name or the
44619304Speter	 * command-line arguments.  They don't set the default values,
44719304Speter	 * it's historic practice.
44819304Speter	 */
44919304Speter	for (; *oargs != -1; ++oargs)
45019304Speter		OI(*oargs, optlist[*oargs].name);
451254225Speter#undef OI
45219304Speter	return (0);
45319304Speter
454254225Spetererr:	msgq_wstr(sp, M_ERR, optlist[optindx].name,
455254225Speter	    "031|Unable to set default %s option");
45619304Speter	return (1);
45719304Speter}
45819304Speter
45919304Speter/*
46019304Speter * opts_set --
46119304Speter *	Change the values of one or more options.
46219304Speter *
46319304Speter * PUBLIC: int opts_set __P((SCR *, ARGS *[], char *));
46419304Speter */
46519304Speterint
466254225Speteropts_set(
467254225Speter	SCR *sp,
468254225Speter	ARGS *argv[],
469254225Speter	char *usage)
47019304Speter{
47119304Speter	enum optdisp disp;
47219304Speter	enum nresult nret;
47319304Speter	OPTLIST const *op;
47419304Speter	OPTION *spo;
475254225Speter	u_long isset, turnoff, value;
47619304Speter	int ch, equals, nf, nf2, offset, qmark, rval;
477254225Speter	CHAR_T *endp, *name, *p, *sep;
478254225Speter	char *p2, *t2;
479254225Speter	char *np;
480254225Speter	size_t nlen;
48119304Speter
48219304Speter	disp = NO_DISPLAY;
48319304Speter	for (rval = 0; argv[0]->len != 0; ++argv) {
48419304Speter		/*
48519304Speter		 * The historic vi dumped the options for each occurrence of
48619304Speter		 * "all" in the set list.  Puhleeze.
48719304Speter		 */
488254225Speter		if (!STRCMP(argv[0]->bp, L("all"))) {
48919304Speter			disp = ALL_DISPLAY;
49019304Speter			continue;
49119304Speter		}
49219304Speter
49319304Speter		/* Find equals sign or question mark. */
49419304Speter		for (sep = NULL, equals = qmark = 0,
49519304Speter		    p = name = argv[0]->bp; (ch = *p) != '\0'; ++p)
49619304Speter			if (ch == '=' || ch == '?') {
49719304Speter				if (p == name) {
49819304Speter					if (usage != NULL)
49919304Speter						msgq(sp, M_ERR,
50019304Speter						    "032|Usage: %s", usage);
50119304Speter					return (1);
50219304Speter				}
50319304Speter				sep = p;
50419304Speter				if (ch == '=')
50519304Speter					equals = 1;
50619304Speter				else
50719304Speter					qmark = 1;
50819304Speter				break;
50919304Speter			}
51019304Speter
51119304Speter		turnoff = 0;
51219304Speter		op = NULL;
51319304Speter		if (sep != NULL)
51419304Speter			*sep++ = '\0';
51519304Speter
51619304Speter		/* Search for the name, then name without any leading "no". */
51719304Speter		if ((op = opts_search(name)) == NULL &&
51819304Speter		    name[0] == 'n' && name[1] == 'o') {
51919304Speter			turnoff = 1;
52019304Speter			name += 2;
52119304Speter			op = opts_search(name);
52219304Speter		}
52319304Speter		if (op == NULL) {
52419304Speter			opts_nomatch(sp, name);
52519304Speter			rval = 1;
52619304Speter			continue;
52719304Speter		}
52819304Speter
52919304Speter		/* Find current option values. */
53019304Speter		offset = op - optlist;
53119304Speter		spo = sp->opts + offset;
53219304Speter
53319304Speter		/*
53419304Speter		 * !!!
53519304Speter		 * Historically, the question mark could be a separate
53619304Speter		 * argument.
53719304Speter		 */
53819304Speter		if (!equals && !qmark &&
53919304Speter		    argv[1]->len == 1 && argv[1]->bp[0] == '?') {
54019304Speter			++argv;
54119304Speter			qmark = 1;
54219304Speter		}
54319304Speter
54419304Speter		/* Set name, value. */
54519304Speter		switch (op->type) {
54619304Speter		case OPT_0BOOL:
54719304Speter		case OPT_1BOOL:
54819304Speter			/* Some options may not be reset. */
54919304Speter			if (F_ISSET(op, OPT_NOUNSET) && turnoff) {
550254225Speter				msgq_wstr(sp, M_ERR, name,
55119304Speter			    "291|set: the %s option may not be turned off");
55219304Speter				rval = 1;
55319304Speter				break;
55419304Speter			}
55519304Speter
55619304Speter			/* Some options may not be set. */
55719304Speter			if (F_ISSET(op, OPT_NOSET) && !turnoff) {
558254225Speter				msgq_wstr(sp, M_ERR, name,
55919304Speter			    "313|set: the %s option may never be turned on");
56019304Speter				rval = 1;
56119304Speter				break;
56219304Speter			}
56319304Speter
56419304Speter			if (equals) {
565254225Speter				msgq_wstr(sp, M_ERR, name,
56619304Speter			    "034|set: [no]%s option doesn't take a value");
56719304Speter				rval = 1;
56819304Speter				break;
56919304Speter			}
57019304Speter			if (qmark) {
57119304Speter				if (!disp)
57219304Speter					disp = SELECT_DISPLAY;
57319304Speter				F_SET(spo, OPT_SELECTED);
57419304Speter				break;
57519304Speter			}
57619304Speter
57719304Speter			/*
57819304Speter			 * Do nothing if the value is unchanged, the underlying
57919304Speter			 * functions can be expensive.
58019304Speter			 */
581254225Speter			isset = !turnoff;
58219304Speter			if (!F_ISSET(op, OPT_ALWAYS))
583254225Speter				if (isset) {
584254225Speter					if (O_ISSET(sp, offset))
585254225Speter						break;
586254225Speter				} else
58719304Speter					if (!O_ISSET(sp, offset))
58819304Speter						break;
58919304Speter
59019304Speter			/* Report to subsystems. */
591254225Speter			if ((op->func != NULL &&
592254225Speter			    op->func(sp, spo, NULL, &isset)) ||
593254225Speter			    ex_optchange(sp, offset, NULL, &isset) ||
594254225Speter			    v_optchange(sp, offset, NULL, &isset) ||
595254225Speter			    sp->gp->scr_optchange(sp, offset, NULL, &isset)) {
59619304Speter				rval = 1;
59719304Speter				break;
59819304Speter			}
59919304Speter
60019304Speter			/* Set the value. */
601254225Speter			if (isset)
602254225Speter				O_SET(sp, offset);
603254225Speter			else
60419304Speter				O_CLR(sp, offset);
60519304Speter			break;
60619304Speter		case OPT_NUM:
60719304Speter			if (turnoff) {
608254225Speter				msgq_wstr(sp, M_ERR, name,
60919304Speter				    "035|set: %s option isn't a boolean");
61019304Speter				rval = 1;
61119304Speter				break;
61219304Speter			}
61319304Speter			if (qmark || !equals) {
61419304Speter				if (!disp)
61519304Speter					disp = SELECT_DISPLAY;
61619304Speter				F_SET(spo, OPT_SELECTED);
61719304Speter				break;
61819304Speter			}
61919304Speter
620254225Speter			if (!ISDIGIT(sep[0]))
62119304Speter				goto badnum;
62219304Speter			if ((nret =
62319304Speter			    nget_uslong(&value, sep, &endp, 10)) != NUM_OK) {
624254225Speter				INT2CHAR(sp, name, STRLEN(name) + 1,
625254225Speter					     np, nlen);
626254225Speter				p2 = msg_print(sp, np, &nf);
627254225Speter				INT2CHAR(sp, sep, STRLEN(sep) + 1,
628254225Speter					     np, nlen);
629254225Speter				t2 = msg_print(sp, np, &nf2);
63019304Speter				switch (nret) {
63119304Speter				case NUM_ERR:
63219304Speter					msgq(sp, M_SYSERR,
633254225Speter					    "036|set: %s option: %s", p2, t2);
63419304Speter					break;
63519304Speter				case NUM_OVER:
63619304Speter					msgq(sp, M_ERR,
637254225Speter			    "037|set: %s option: %s: value overflow", p2, t2);
63819304Speter					break;
63919304Speter				case NUM_OK:
64019304Speter				case NUM_UNDER:
64119304Speter					abort();
64219304Speter				}
64319304Speter				if (nf)
644254225Speter					FREE_SPACE(sp, p2, 0);
64519304Speter				if (nf2)
646254225Speter					FREE_SPACE(sp, t2, 0);
64719304Speter				rval = 1;
64819304Speter				break;
64919304Speter			}
650254225Speter			if (*endp && !cmdskip(*endp)) {
651254225Speterbadnum:				INT2CHAR(sp, name, STRLEN(name) + 1,
652254225Speter					     np, nlen);
653254225Speter				p2 = msg_print(sp, np, &nf);
654254225Speter				INT2CHAR(sp, sep, STRLEN(sep) + 1,
655254225Speter					     np, nlen);
656254225Speter				t2 = msg_print(sp, np, &nf2);
65719304Speter				msgq(sp, M_ERR,
658254225Speter		    "038|set: %s option: %s is an illegal number", p2, t2);
65919304Speter				if (nf)
660254225Speter					FREE_SPACE(sp, p2, 0);
66119304Speter				if (nf2)
662254225Speter					FREE_SPACE(sp, t2, 0);
66319304Speter				rval = 1;
66419304Speter				break;
66519304Speter			}
66619304Speter
66719304Speter			/* Some options may never be set to zero. */
66819304Speter			if (F_ISSET(op, OPT_NOZERO) && value == 0) {
669254225Speter				msgq_wstr(sp, M_ERR, name,
67019304Speter			    "314|set: the %s option may never be set to 0");
67119304Speter				rval = 1;
67219304Speter				break;
67319304Speter			}
67419304Speter
67519304Speter			/*
67619304Speter			 * Do nothing if the value is unchanged, the underlying
67719304Speter			 * functions can be expensive.
67819304Speter			 */
67919304Speter			if (!F_ISSET(op, OPT_ALWAYS) &&
68019304Speter			    O_VAL(sp, offset) == value)
68119304Speter				break;
68219304Speter
68319304Speter			/* Report to subsystems. */
684254225Speter			INT2CHAR(sp, sep, STRLEN(sep) + 1, np, nlen);
685254225Speter			if ((op->func != NULL &&
686254225Speter			    op->func(sp, spo, np, &value)) ||
687254225Speter			    ex_optchange(sp, offset, np, &value) ||
688254225Speter			    v_optchange(sp, offset, np, &value) ||
689254225Speter			    sp->gp->scr_optchange(sp, offset, np, &value)) {
69019304Speter				rval = 1;
69119304Speter				break;
69219304Speter			}
69319304Speter
69419304Speter			/* Set the value. */
69519304Speter			if (o_set(sp, offset, 0, NULL, value))
69619304Speter				rval = 1;
69719304Speter			break;
69819304Speter		case OPT_STR:
69919304Speter			if (turnoff) {
700254225Speter				msgq_wstr(sp, M_ERR, name,
70119304Speter				    "039|set: %s option isn't a boolean");
70219304Speter				rval = 1;
70319304Speter				break;
70419304Speter			}
70519304Speter			if (qmark || !equals) {
70619304Speter				if (!disp)
70719304Speter					disp = SELECT_DISPLAY;
70819304Speter				F_SET(spo, OPT_SELECTED);
70919304Speter				break;
71019304Speter			}
71119304Speter
712254225Speter			/* Check for strings that must have even length. */
713254225Speter			if (F_ISSET(op, OPT_PAIRS) && STRLEN(sep) & 1) {
714254225Speter				msgq_wstr(sp, M_ERR, name,
715254225Speter				    "047|The %s option must be in two character groups");
716254225Speter				rval = 1;
717254225Speter				break;
718254225Speter			}
719254225Speter
72019304Speter			/*
72119304Speter			 * Do nothing if the value is unchanged, the underlying
72219304Speter			 * functions can be expensive.
72319304Speter			 */
724254225Speter			INT2CHAR(sp, sep, STRLEN(sep) + 1, np, nlen);
72519304Speter			if (!F_ISSET(op, OPT_ALWAYS) &&
72619304Speter			    O_STR(sp, offset) != NULL &&
727254225Speter			    !strcmp(O_STR(sp, offset), np))
72819304Speter				break;
72919304Speter
73019304Speter			/* Report to subsystems. */
731254225Speter			if ((op->func != NULL &&
732254225Speter			    op->func(sp, spo, np, NULL)) ||
733254225Speter			    ex_optchange(sp, offset, np, NULL) ||
734254225Speter			    v_optchange(sp, offset, np, NULL) ||
735254225Speter			    sp->gp->scr_optchange(sp, offset, np, NULL)) {
73619304Speter				rval = 1;
73719304Speter				break;
73819304Speter			}
73919304Speter
74019304Speter			/* Set the value. */
741254225Speter			if (o_set(sp, offset, OS_STRDUP, np, 0))
74219304Speter				rval = 1;
74319304Speter			break;
74419304Speter		default:
74519304Speter			abort();
74619304Speter		}
74719304Speter	}
74819304Speter	if (disp != NO_DISPLAY)
74919304Speter		opts_dump(sp, disp);
75019304Speter	return (rval);
75119304Speter}
75219304Speter
75319304Speter/*
75419304Speter * o_set --
75519304Speter *	Set an option's value.
75619304Speter *
75719304Speter * PUBLIC: int o_set __P((SCR *, int, u_int, char *, u_long));
75819304Speter */
75919304Speterint
760254225Spetero_set(
761254225Speter	SCR *sp,
762254225Speter	int opt,
763254225Speter	u_int flags,
764254225Speter	char *str,
765254225Speter	u_long val)
76619304Speter{
76719304Speter	OPTION *op;
76819304Speter
76919304Speter	/* Set a pointer to the options area. */
77019304Speter	op = F_ISSET(&sp->opts[opt], OPT_GLOBAL) ?
77119304Speter	    &sp->gp->opts[sp->opts[opt].o_cur.val] : &sp->opts[opt];
77219304Speter
77319304Speter	/* Copy the string, if requested. */
77419304Speter	if (LF_ISSET(OS_STRDUP) && (str = strdup(str)) == NULL) {
77519304Speter		msgq(sp, M_SYSERR, NULL);
77619304Speter		return (1);
77719304Speter	}
77819304Speter
77919304Speter	/* Free the previous string, if requested, and set the value. */
78019304Speter	if LF_ISSET(OS_DEF)
78119304Speter		if (LF_ISSET(OS_STR | OS_STRDUP)) {
78219304Speter			if (!LF_ISSET(OS_NOFREE) && op->o_def.str != NULL)
78319304Speter				free(op->o_def.str);
78419304Speter			op->o_def.str = str;
78519304Speter		} else
78619304Speter			op->o_def.val = val;
78719304Speter	else
78819304Speter		if (LF_ISSET(OS_STR | OS_STRDUP)) {
78919304Speter			if (!LF_ISSET(OS_NOFREE) && op->o_cur.str != NULL)
79019304Speter				free(op->o_cur.str);
79119304Speter			op->o_cur.str = str;
79219304Speter		} else
79319304Speter			op->o_cur.val = val;
79419304Speter	return (0);
79519304Speter}
79619304Speter
79719304Speter/*
79819304Speter * opts_empty --
79919304Speter *	Return 1 if the string option is invalid, 0 if it's OK.
80019304Speter *
80119304Speter * PUBLIC: int opts_empty __P((SCR *, int, int));
80219304Speter */
80319304Speterint
804254225Speteropts_empty(
805254225Speter	SCR *sp,
806254225Speter	int off,
807254225Speter	int silent)
80819304Speter{
80919304Speter	char *p;
81019304Speter
81119304Speter	if ((p = O_STR(sp, off)) == NULL || p[0] == '\0') {
81219304Speter		if (!silent)
813254225Speter			msgq_wstr(sp, M_ERR, optlist[off].name,
81419304Speter			    "305|No %s edit option specified");
81519304Speter		return (1);
81619304Speter	}
81719304Speter	return (0);
81819304Speter}
81919304Speter
82019304Speter/*
82119304Speter * opts_dump --
82219304Speter *	List the current values of selected options.
82319304Speter *
82419304Speter * PUBLIC: void opts_dump __P((SCR *, enum optdisp));
82519304Speter */
82619304Spetervoid
827254225Speteropts_dump(
828254225Speter	SCR *sp,
829254225Speter	enum optdisp type)
83019304Speter{
83119304Speter	OPTLIST const *op;
83219304Speter	int base, b_num, cnt, col, colwidth, curlen, s_num;
83319304Speter	int numcols, numrows, row;
83419304Speter	int b_op[O_OPTIONCOUNT], s_op[O_OPTIONCOUNT];
83519304Speter	char nbuf[20];
83619304Speter
83719304Speter	/*
83819304Speter	 * Options are output in two groups -- those that fit in a column and
83919304Speter	 * those that don't.  Output is done on 6 character "tab" boundaries
84019304Speter	 * for no particular reason.  (Since we don't output tab characters,
84119304Speter	 * we can ignore the terminal's tab settings.)  Ignore the user's tab
84219304Speter	 * setting because we have no idea how reasonable it is.
84319304Speter	 *
84419304Speter	 * Find a column width we can live with, testing from 10 columns to 1.
84519304Speter	 */
84619304Speter	for (numcols = 10; numcols > 1; --numcols) {
84719304Speter		colwidth = sp->cols / numcols & ~(STANDARD_TAB - 1);
84819304Speter		if (colwidth >= 10) {
84919304Speter			colwidth =
85019304Speter			    (colwidth + STANDARD_TAB) & ~(STANDARD_TAB - 1);
85119304Speter			numcols = sp->cols / colwidth;
85219304Speter			break;
85319304Speter		}
85419304Speter		colwidth = 0;
85519304Speter	}
85619304Speter
85719304Speter	/*
85819304Speter	 * Get the set of options to list, entering them into
85919304Speter	 * the column list or the overflow list.
86019304Speter	 */
86119304Speter	for (b_num = s_num = 0, op = optlist; op->name != NULL; ++op) {
86219304Speter		cnt = op - optlist;
86319304Speter
86419304Speter		/* If OPT_NDISP set, it's never displayed. */
86519304Speter		if (F_ISSET(op, OPT_NDISP))
86619304Speter			continue;
86719304Speter
86819304Speter		switch (type) {
86919304Speter		case ALL_DISPLAY:		/* Display all. */
87019304Speter			break;
87119304Speter		case CHANGED_DISPLAY:		/* Display changed. */
87219304Speter			/* If OPT_ADISP set, it's always "changed". */
87319304Speter			if (F_ISSET(op, OPT_ADISP))
87419304Speter				break;
87519304Speter			switch (op->type) {
87619304Speter			case OPT_0BOOL:
87719304Speter			case OPT_1BOOL:
87819304Speter			case OPT_NUM:
87919304Speter				if (O_VAL(sp, cnt) == O_D_VAL(sp, cnt))
88019304Speter					continue;
88119304Speter				break;
88219304Speter			case OPT_STR:
88319304Speter				if (O_STR(sp, cnt) == O_D_STR(sp, cnt) ||
884254225Speter				    (O_D_STR(sp, cnt) != NULL &&
885254225Speter				    !strcmp(O_STR(sp, cnt), O_D_STR(sp, cnt))))
88619304Speter					continue;
88719304Speter				break;
88819304Speter			}
88919304Speter			break;
89019304Speter		case SELECT_DISPLAY:		/* Display selected. */
89119304Speter			if (!F_ISSET(&sp->opts[cnt], OPT_SELECTED))
89219304Speter				continue;
89319304Speter			break;
89419304Speter		default:
89519304Speter		case NO_DISPLAY:
89619304Speter			abort();
89719304Speter		}
89819304Speter		F_CLR(&sp->opts[cnt], OPT_SELECTED);
89919304Speter
900254225Speter		curlen = STRLEN(op->name);
90119304Speter		switch (op->type) {
90219304Speter		case OPT_0BOOL:
90319304Speter		case OPT_1BOOL:
90419304Speter			if (!O_ISSET(sp, cnt))
90519304Speter				curlen += 2;
90619304Speter			break;
90719304Speter		case OPT_NUM:
90819304Speter			(void)snprintf(nbuf,
90919304Speter			    sizeof(nbuf), "%ld", O_VAL(sp, cnt));
91019304Speter			curlen += strlen(nbuf);
91119304Speter			break;
91219304Speter		case OPT_STR:
91319304Speter			if (O_STR(sp, cnt) != NULL)
91419304Speter				curlen += strlen(O_STR(sp, cnt));
91519304Speter			curlen += 3;
91619304Speter			break;
91719304Speter		}
91819304Speter		/* Offset by 2 so there's a gap. */
91919304Speter		if (curlen <= colwidth - 2)
92019304Speter			s_op[s_num++] = cnt;
92119304Speter		else
92219304Speter			b_op[b_num++] = cnt;
92319304Speter	}
92419304Speter
92519304Speter	if (s_num > 0) {
92619304Speter		/* Figure out the number of rows. */
92719304Speter		if (s_num > numcols) {
92819304Speter			numrows = s_num / numcols;
92919304Speter			if (s_num % numcols)
93019304Speter				++numrows;
93119304Speter		} else
93219304Speter			numrows = 1;
93319304Speter
93419304Speter		/* Display the options in sorted order. */
93519304Speter		for (row = 0; row < numrows;) {
93619304Speter			for (base = row, col = 0; col < numcols; ++col) {
93719304Speter				cnt = opts_print(sp, &optlist[s_op[base]]);
93819304Speter				if ((base += numrows) >= s_num)
93919304Speter					break;
94019304Speter				(void)ex_printf(sp, "%*s",
94119304Speter				    (int)(colwidth - cnt), "");
94219304Speter			}
94319304Speter			if (++row < numrows || b_num)
94419304Speter				(void)ex_puts(sp, "\n");
94519304Speter		}
94619304Speter	}
94719304Speter
94819304Speter	for (row = 0; row < b_num;) {
94919304Speter		(void)opts_print(sp, &optlist[b_op[row]]);
95019304Speter		if (++row < b_num)
95119304Speter			(void)ex_puts(sp, "\n");
95219304Speter	}
95319304Speter	(void)ex_puts(sp, "\n");
95419304Speter}
95519304Speter
95619304Speter/*
95719304Speter * opts_print --
95819304Speter *	Print out an option.
95919304Speter */
96019304Speterstatic int
961254225Speteropts_print(
962254225Speter	SCR *sp,
963254225Speter	OPTLIST const *op)
96419304Speter{
96519304Speter	int curlen, offset;
96619304Speter
96719304Speter	curlen = 0;
96819304Speter	offset = op - optlist;
96919304Speter	switch (op->type) {
97019304Speter	case OPT_0BOOL:
97119304Speter	case OPT_1BOOL:
97219304Speter		curlen += ex_printf(sp,
973254225Speter		    "%s"WS, O_ISSET(sp, offset) ? "" : "no", op->name);
97419304Speter		break;
97519304Speter	case OPT_NUM:
976254225Speter		curlen += ex_printf(sp, WS"=%ld", op->name, O_VAL(sp, offset));
97719304Speter		break;
97819304Speter	case OPT_STR:
979254225Speter		curlen += ex_printf(sp, WS"=\"%s\"", op->name,
98019304Speter		    O_STR(sp, offset) == NULL ? "" : O_STR(sp, offset));
98119304Speter		break;
98219304Speter	}
98319304Speter	return (curlen);
98419304Speter}
98519304Speter
98619304Speter/*
98719304Speter * opts_save --
98819304Speter *	Write the current configuration to a file.
98919304Speter *
99019304Speter * PUBLIC: int opts_save __P((SCR *, FILE *));
99119304Speter */
99219304Speterint
993254225Speteropts_save(
994254225Speter	SCR *sp,
995254225Speter	FILE *fp)
99619304Speter{
99719304Speter	OPTLIST const *op;
998254225Speter	CHAR_T ch, *p;
999254225Speter	char nch, *np;
1000254225Speter	int cnt;
100119304Speter
100219304Speter	for (op = optlist; op->name != NULL; ++op) {
100319304Speter		if (F_ISSET(op, OPT_NOSAVE))
100419304Speter			continue;
100519304Speter		cnt = op - optlist;
100619304Speter		switch (op->type) {
100719304Speter		case OPT_0BOOL:
100819304Speter		case OPT_1BOOL:
100919304Speter			if (O_ISSET(sp, cnt))
1010254225Speter				(void)fprintf(fp, "set "WS"\n", op->name);
101119304Speter			else
1012254225Speter				(void)fprintf(fp, "set no"WS"\n", op->name);
101319304Speter			break;
101419304Speter		case OPT_NUM:
101519304Speter			(void)fprintf(fp,
1016254225Speter			    "set "WS"=%-3ld\n", op->name, O_VAL(sp, cnt));
101719304Speter			break;
101819304Speter		case OPT_STR:
101919304Speter			if (O_STR(sp, cnt) == NULL)
102019304Speter				break;
102119304Speter			(void)fprintf(fp, "set ");
102219304Speter			for (p = op->name; (ch = *p) != '\0'; ++p) {
1023254225Speter				if (cmdskip(ch) || ch == '\\')
102419304Speter					(void)putc('\\', fp);
1025254225Speter				fprintf(fp, WC, ch);
102619304Speter			}
102719304Speter			(void)putc('=', fp);
1028254225Speter			for (np = O_STR(sp, cnt); (nch = *np) != '\0'; ++np) {
1029254225Speter				if (cmdskip(nch) || nch == '\\')
103019304Speter					(void)putc('\\', fp);
1031254225Speter				(void)putc(nch, fp);
103219304Speter			}
103319304Speter			(void)putc('\n', fp);
103419304Speter			break;
103519304Speter		}
103619304Speter		if (ferror(fp)) {
103719304Speter			msgq(sp, M_SYSERR, NULL);
103819304Speter			return (1);
103919304Speter		}
104019304Speter	}
104119304Speter	return (0);
104219304Speter}
104319304Speter
104419304Speter/*
104519304Speter * opts_search --
104619304Speter *	Search for an option.
104719304Speter *
1048254225Speter * PUBLIC: OPTLIST const *opts_search __P((CHAR_T *));
104919304Speter */
105019304SpeterOPTLIST const *
1051254225Speteropts_search(CHAR_T *name)
105219304Speter{
105319304Speter	OPTLIST const *op, *found;
105419304Speter	OABBREV atmp, *ap;
105519304Speter	OPTLIST otmp;
105619304Speter	size_t len;
105719304Speter
105819304Speter	/* Check list of abbreviations. */
105919304Speter	atmp.name = name;
106019304Speter	if ((ap = bsearch(&atmp, abbrev, sizeof(abbrev) / sizeof(OABBREV) - 1,
106119304Speter	    sizeof(OABBREV), opts_abbcmp)) != NULL)
106219304Speter		return (optlist + ap->offset);
106319304Speter
106419304Speter	/* Check list of options. */
106519304Speter	otmp.name = name;
106619304Speter	if ((op = bsearch(&otmp, optlist, sizeof(optlist) / sizeof(OPTLIST) - 1,
106719304Speter	    sizeof(OPTLIST), opts_cmp)) != NULL)
106819304Speter		return (op);
106919304Speter
107019304Speter	/*
107119304Speter	 * Check to see if the name is the prefix of one (and only one)
107219304Speter	 * option.  If so, return the option.
107319304Speter	 */
1074254225Speter	len = STRLEN(name);
107519304Speter	for (found = NULL, op = optlist; op->name != NULL; ++op) {
107619304Speter		if (op->name[0] < name[0])
107719304Speter			continue;
107819304Speter		if (op->name[0] > name[0])
107919304Speter			break;
1080254225Speter		if (!MEMCMP(op->name, name, len)) {
108119304Speter			if (found != NULL)
108219304Speter				return (NULL);
108319304Speter			found = op;
108419304Speter		}
108519304Speter	}
108619304Speter	return (found);
108719304Speter}
108819304Speter
108919304Speter/*
109019304Speter * opts_nomatch --
109119304Speter *	Standard nomatch error message for options.
109219304Speter *
1093254225Speter * PUBLIC: void opts_nomatch __P((SCR *, CHAR_T *));
109419304Speter */
109519304Spetervoid
1096254225Speteropts_nomatch(
1097254225Speter	SCR *sp,
1098254225Speter	CHAR_T *name)
109919304Speter{
1100254225Speter	msgq_wstr(sp, M_ERR, name,
110119304Speter	    "033|set: no %s option: 'set all' gives all option values");
110219304Speter}
110319304Speter
110419304Speterstatic int
1105254225Speteropts_abbcmp(
1106254225Speter        const void *a,
1107254225Speter		const void *b)
110819304Speter{
1109254225Speter        return(STRCMP(((OABBREV *)a)->name, ((OABBREV *)b)->name));
111019304Speter}
111119304Speter
111219304Speterstatic int
1113254225Speteropts_cmp(
1114254225Speter        const void *a,
1115254225Speter		const void *b)
111619304Speter{
1117254225Speter        return(STRCMP(((OPTLIST *)a)->name, ((OPTLIST *)b)->name));
111819304Speter}
111919304Speter
112019304Speter/*
112119304Speter * opts_copy --
112219304Speter *	Copy a screen's OPTION array.
112319304Speter *
112419304Speter * PUBLIC: int opts_copy __P((SCR *, SCR *));
112519304Speter */
112619304Speterint
1127254225Speteropts_copy(
1128254225Speter	SCR *orig,
1129254225Speter	SCR *sp)
113019304Speter{
113119304Speter	int cnt, rval;
113219304Speter
113319304Speter	/* Copy most everything without change. */
113419304Speter	memcpy(sp->opts, orig->opts, sizeof(orig->opts));
113519304Speter
113619304Speter	/* Copy the string edit options. */
113719304Speter	for (cnt = rval = 0; cnt < O_OPTIONCOUNT; ++cnt) {
113819304Speter		if (optlist[cnt].type != OPT_STR ||
1139254225Speter		    F_ISSET(&sp->opts[cnt], OPT_GLOBAL))
114019304Speter			continue;
114119304Speter		/*
114219304Speter		 * If never set, or already failed, NULL out the entries --
114319304Speter		 * have to continue after failure, otherwise would have two
114419304Speter		 * screens referencing the same memory.
114519304Speter		 */
114619304Speter		if (rval || O_STR(sp, cnt) == NULL) {
114719304Speter			o_set(sp, cnt, OS_NOFREE | OS_STR, NULL, 0);
114819304Speter			o_set(sp, cnt, OS_DEF | OS_NOFREE | OS_STR, NULL, 0);
114919304Speter			continue;
115019304Speter		}
115119304Speter
115219304Speter		/* Copy the current string. */
115319304Speter		if (o_set(sp, cnt, OS_NOFREE | OS_STRDUP, O_STR(sp, cnt), 0)) {
115419304Speter			o_set(sp, cnt, OS_DEF | OS_NOFREE | OS_STR, NULL, 0);
115519304Speter			goto nomem;
115619304Speter		}
115719304Speter
115819304Speter		/* Copy the default string. */
115919304Speter		if (O_D_STR(sp, cnt) != NULL && o_set(sp, cnt,
116019304Speter		    OS_DEF | OS_NOFREE | OS_STRDUP, O_D_STR(sp, cnt), 0)) {
116119304Speternomem:			msgq(orig, M_SYSERR, NULL);
116219304Speter			rval = 1;
116319304Speter		}
116419304Speter	}
116519304Speter	return (rval);
116619304Speter}
116719304Speter
116819304Speter/*
116919304Speter * opts_free --
117019304Speter *	Free all option strings
117119304Speter *
117219304Speter * PUBLIC: void opts_free __P((SCR *));
117319304Speter */
117419304Spetervoid
1175254225Speteropts_free(SCR *sp)
117619304Speter{
117719304Speter	int cnt;
117819304Speter
117919304Speter	for (cnt = 0; cnt < O_OPTIONCOUNT; ++cnt) {
118019304Speter		if (optlist[cnt].type != OPT_STR ||
1181254225Speter		    F_ISSET(&sp->opts[cnt], OPT_GLOBAL))
118219304Speter			continue;
118319304Speter		if (O_STR(sp, cnt) != NULL)
118419304Speter			free(O_STR(sp, cnt));
118519304Speter		if (O_D_STR(sp, cnt) != NULL)
118619304Speter			free(O_D_STR(sp, cnt));
118719304Speter	}
118819304Speter}
1189