This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / usr.bin / vi / options.c
CommitLineData
1e64b3ba 1/*-
304585c1 2 * Copyright (c) 1991, 1993, 1994
1e64b3ba
JH
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
304585c1 35static char sccsid[] = "@(#)options.c 8.52 (Berkeley) 3/24/94";
1e64b3ba
JH
36#endif /* not lint */
37
38#include <sys/types.h>
304585c1 39#include <queue.h>
1e64b3ba 40#include <sys/stat.h>
304585c1 41#include <sys/time.h>
1e64b3ba 42
304585c1 43#include <bitstring.h>
1e64b3ba 44#include <ctype.h>
1e64b3ba 45#include <errno.h>
304585c1
AM
46#include <limits.h>
47#include <signal.h>
48#include <stdio.h>
1e64b3ba
JH
49#include <stdlib.h>
50#include <string.h>
304585c1 51#include <termios.h>
1e64b3ba
JH
52#include <unistd.h>
53
304585c1
AM
54#include <db.h>
55#include <regex.h>
56#include <pathnames.h>
57
1e64b3ba
JH
58#include "vi.h"
59#include "excmd.h"
1e64b3ba
JH
60
61static int opts_abbcmp __P((const void *, const void *));
62static int opts_cmp __P((const void *, const void *));
63static OPTLIST const *opts_prefix __P((char *));
304585c1 64static int opts_print __P((SCR *, OPTLIST const *));
1e64b3ba
JH
65
66/*
67 * O'Reilly noted options and abbreviations are from "Learning the VI Editor",
68 * Fifth Edition, May 1992. There's no way of knowing what systems they are
69 * actually from.
70 *
71 * HPUX noted options and abbreviations are from "The Ultimate Guide to the
72 * VI and EX Text Editors", 1990.
73 */
74static OPTLIST const optlist[] = {
75/* O_ALTWERASE 4.4BSD */
76 {"altwerase", f_altwerase, OPT_0BOOL, 0},
77/* O_AUTOINDENT 4BSD */
78 {"autoindent", NULL, OPT_0BOOL, 0},
79/* O_AUTOPRINT 4BSD */
80 {"autoprint", NULL, OPT_1BOOL, 0},
81/* O_AUTOWRITE 4BSD */
82 {"autowrite", NULL, OPT_0BOOL, 0},
83/* O_BEAUTIFY 4BSD */
84 {"beautify", NULL, OPT_0BOOL, 0},
304585c1
AM
85/* O_CDPATH 4.4BSD */
86 {"cdpath", f_cdpath, OPT_STR, 0},
1e64b3ba
JH
87/* O_COLUMNS 4.4BSD */
88 {"columns", f_columns, OPT_NUM, OPT_NOSAVE},
89/* O_COMMENT 4.4BSD */
90 {"comment", NULL, OPT_0BOOL, 0},
91/* O_DIGRAPH XXX: Elvis */
92 {"digraph", NULL, OPT_0BOOL, 0},
93/* O_DIRECTORY 4BSD */
94 {"directory", NULL, OPT_STR, 0},
95/* O_EDCOMPATIBLE 4BSD */
96 {"edcompatible",NULL, OPT_0BOOL, 0},
97/* O_ERRORBELLS 4BSD */
98 {"errorbells", NULL, OPT_0BOOL, 0},
99/* O_EXRC System V (undocumented) */
100 {"exrc", NULL, OPT_0BOOL, 0},
101/* O_EXTENDED 4.4BSD */
102 {"extended", NULL, OPT_0BOOL, 0},
103/* O_FLASH HPUX */
104 {"flash", NULL, OPT_1BOOL, 0},
105/* O_HARDTABS 4BSD */
106 {"hardtabs", NULL, OPT_NUM, 0},
107/* O_IGNORECASE 4BSD */
108 {"ignorecase", NULL, OPT_0BOOL, 0},
109/* O_KEYTIME 4.4BSD */
110 {"keytime", f_keytime, OPT_NUM, 0},
111/* O_LEFTRIGHT 4.4BSD */
112 {"leftright", f_leftright, OPT_0BOOL, 0},
113/* O_LINES 4.4BSD */
114 {"lines", f_lines, OPT_NUM, OPT_NOSAVE},
115/* O_LISP 4BSD */
304585c1
AM
116/*
117 * XXX
118 * When the lisp option is implemented, delete
119 * the OPT_NOSAVE flag, so that :mkexrc dumps it.
120 */
121 {"lisp", f_lisp, OPT_0BOOL, OPT_NOSAVE},
1e64b3ba
JH
122/* O_LIST 4BSD */
123 {"list", f_list, OPT_0BOOL, 0},
124/* O_MAGIC 4BSD */
125 {"magic", NULL, OPT_1BOOL, 0},
126/* O_MATCHTIME 4.4BSD */
127 {"matchtime", f_matchtime, OPT_NUM, 0},
128/* O_MESG 4BSD */
129 {"mesg", f_mesg, OPT_1BOOL, 0},
130/* O_MODELINE 4BSD */
131 {"modeline", f_modeline, OPT_0BOOL, 0},
132/* O_NUMBER 4BSD */
133 {"number", f_number, OPT_0BOOL, 0},
134/* O_OPEN 4BSD */
135 {"open", NULL, OPT_1BOOL, 0},
136/* O_OPTIMIZE 4BSD */
304585c1 137 {"optimize", NULL, OPT_1BOOL, 0},
1e64b3ba
JH
138/* O_PARAGRAPHS 4BSD */
139 {"paragraphs", f_paragraph, OPT_STR, 0},
140/* O_PROMPT 4BSD */
141 {"prompt", NULL, OPT_1BOOL, 0},
142/* O_READONLY 4BSD (undocumented) */
143 {"readonly", f_readonly, OPT_0BOOL, 0},
144/* O_RECDIR 4.4BSD */
145 {"recdir", NULL, OPT_STR, 0},
146/* O_REDRAW 4BSD */
147 {"redraw", NULL, OPT_0BOOL, 0},
148/* O_REMAP 4BSD */
149 {"remap", NULL, OPT_1BOOL, 0},
304585c1
AM
150/* O_REMAPMAX 4.4BSD */
151 {"remapmax", NULL, OPT_1BOOL, 0},
1e64b3ba
JH
152/* O_REPORT 4BSD */
153 {"report", NULL, OPT_NUM, OPT_NOSTR},
154/* O_RULER 4.4BSD */
155 {"ruler", f_ruler, OPT_0BOOL, 0},
156/* O_SCROLL 4BSD */
157 {"scroll", NULL, OPT_NUM, 0},
158/* O_SECTIONS 4BSD */
159 {"sections", f_section, OPT_STR, 0},
160/* O_SHELL 4BSD */
161 {"shell", NULL, OPT_STR, 0},
162/* O_SHIFTWIDTH 4BSD */
163 {"shiftwidth", f_shiftwidth, OPT_NUM, 0},
164/* O_SHOWDIRTY 4.4BSD */
165 {"showdirty", NULL, OPT_0BOOL, 0},
166/* O_SHOWMATCH 4BSD */
167 {"showmatch", NULL, OPT_0BOOL, 0},
168/* O_SHOWMODE 4.4BSD */
169 {"showmode", NULL, OPT_0BOOL, 0},
170/* O_SIDESCROLL 4.4BSD */
171 {"sidescroll", f_sidescroll, OPT_NUM, 0},
172/* O_SLOWOPEN 4BSD */
173 {"slowopen", NULL, OPT_0BOOL, 0},
174/* O_SOURCEANY 4BSD (undocumented) */
175 {"sourceany", f_sourceany, OPT_0BOOL, 0},
176/* O_TABSTOP 4BSD */
177 {"tabstop", f_tabstop, OPT_NUM, 0},
178/* O_TAGLENGTH 4BSD */
179 {"taglength", NULL, OPT_NUM, OPT_NOSTR},
180/* O_TAGS 4BSD */
181 {"tags", f_tags, OPT_STR, 0},
182/* O_TERM 4BSD */
183 {"term", f_term, OPT_STR, OPT_NOSAVE},
184/* O_TERSE 4BSD */
185 {"terse", NULL, OPT_0BOOL, 0},
186/* O_TIMEOUT 4BSD (undocumented) */
187 {"timeout", NULL, OPT_1BOOL, 0},
188/* O_TTYWERASE 4.4BSD */
189 {"ttywerase", f_ttywerase, OPT_0BOOL, 0},
190/* O_VERBOSE 4.4BSD */
191 {"verbose", NULL, OPT_0BOOL, 0},
192/* O_W1200 4BSD */
304585c1 193 {"w1200", f_w1200, OPT_NUM, OPT_NEVER|OPT_NOSAVE},
1e64b3ba 194/* O_W300 4BSD */
304585c1 195 {"w300", f_w300, OPT_NUM, OPT_NEVER|OPT_NOSAVE},
1e64b3ba 196/* O_W9600 4BSD */
304585c1 197 {"w9600", f_w9600, OPT_NUM, OPT_NEVER|OPT_NOSAVE},
1e64b3ba
JH
198/* O_WARN 4BSD */
199 {"warn", NULL, OPT_1BOOL, 0},
200/* O_WINDOW 4BSD */
201 {"window", f_window, OPT_NUM, 0},
202/* O_WRAPMARGIN 4BSD */
203 {"wrapmargin", f_wrapmargin, OPT_NUM, OPT_NOSTR},
204/* O_WRAPSCAN 4BSD */
205 {"wrapscan", NULL, OPT_1BOOL, 0},
206/* O_WRITEANY 4BSD */
207 {"writeany", NULL, OPT_0BOOL, 0},
208 {NULL},
209};
210
211typedef struct abbrev {
212 char *name;
213 int offset;
214} OABBREV;
215
216static OABBREV const abbrev[] = {
217 {"ai", O_AUTOINDENT}, /* 4BSD */
218 {"ap", O_AUTOPRINT}, /* 4BSD */
219 {"aw", O_AUTOWRITE}, /* 4BSD */
220 {"bf", O_BEAUTIFY}, /* 4BSD */
221 {"co", O_COLUMNS}, /* 4.4BSD */
222 {"dir", O_DIRECTORY}, /* 4BSD */
223 {"eb", O_ERRORBELLS}, /* 4BSD */
224 {"ed", O_EDCOMPATIBLE}, /* 4BSD (undocumented) */
225 {"ex", O_EXRC}, /* System V (undocumented) */
226 {"ht", O_HARDTABS}, /* 4BSD */
227 {"ic", O_IGNORECASE}, /* 4BSD */
228 {"li", O_LINES}, /* 4.4BSD */
229 {"modelines", O_MODELINE}, /* HPUX */
230 {"nu", O_NUMBER}, /* 4BSD */
231 {"opt", O_OPTIMIZE}, /* 4BSD */
232 {"para", O_PARAGRAPHS}, /* 4BSD */
233 {"re", O_REDRAW}, /* O'Reilly */
234 {"ro", O_READONLY}, /* 4BSD (undocumented) */
235 {"scr", O_SCROLL}, /* 4BSD (undocumented) */
236 {"sect", O_SECTIONS}, /* O'Reilly */
237 {"sh", O_SHELL}, /* 4BSD */
238 {"slow", O_SLOWOPEN}, /* 4BSD */
239 {"sm", O_SHOWMATCH}, /* 4BSD */
240 {"sw", O_SHIFTWIDTH}, /* 4BSD */
241 {"tag", O_TAGS}, /* 4BSD (undocumented) */
242 {"tl", O_TAGLENGTH}, /* 4BSD */
243 {"to", O_TIMEOUT}, /* 4BSD (undocumented) */
244 {"ts", O_TABSTOP}, /* 4BSD */
245 {"tty", O_TERM}, /* 4BSD (undocumented) */
246 {"ttytype", O_TERM}, /* 4BSD (undocumented) */
247 {"w", O_WINDOW}, /* O'Reilly */
248 {"wa", O_WRITEANY}, /* 4BSD */
249 {"wi", O_WINDOW}, /* 4BSD (undocumented) */
250 {"wm", O_WRAPMARGIN}, /* 4BSD */
251 {"ws", O_WRAPSCAN}, /* 4BSD */
252 {NULL},
253};
254
255/*
256 * opts_init --
257 * Initialize some of the options. Since the user isn't really
258 * "setting" these variables, don't set their OPT_SET bits.
259 */
260int
261opts_init(sp)
262 SCR *sp;
263{
264 ARGS *argv[2], a, b;
265 OPTLIST const *op;
266 u_long v;
267 int cnt;
268 char *s, b1[1024];
269
270 a.bp = b1;
271 a.len = 0;
272 b.bp = NULL;
273 b.len = 0;
274 argv[0] = &a;
275 argv[1] = &b;
276
277#define SET_DEF(opt, str) { \
278 if (str != b1) /* GCC puts strings in text-space. */ \
279 (void)strcpy(b1, str); \
280 a.len = strlen(b1); \
281 if (opts_set(sp, argv)) { \
282 msgq(sp, M_ERR, \
283 "Unable to set default %s option", optlist[opt]); \
284 return (1); \
285 } \
286 F_CLR(&sp->opts[opt], OPT_SET); \
287}
288 /* Set default values. */
289 for (op = optlist, cnt = 0; op->name != NULL; ++op, ++cnt)
290 if (op->type == OPT_0BOOL)
291 O_CLR(sp, cnt);
292 else if (op->type == OPT_1BOOL)
293 O_SET(sp, cnt);
304585c1
AM
294
295 (void)snprintf(b1, sizeof(b1), "cdpath=%s",
296 (s = getenv("CDPATH")) == NULL ? ":" : s);
297 SET_DEF(O_CDPATH, b1);
298
1e64b3ba
JH
299 /*
300 * !!!
301 * Vi historically stored temporary files in /var/tmp. We store them
302 * in /tmp by default, hoping it's a memory based file system. There
303 * are two ways to change this -- the user can set either the directory
304 * option or the TMPDIR environmental variable.
305 */
306 (void)snprintf(b1, sizeof(b1), "directory=%s",
307 (s = getenv("TMPDIR")) == NULL ? _PATH_TMP : s);
308 SET_DEF(O_DIRECTORY, b1);
309 SET_DEF(O_KEYTIME, "keytime=6");
310 SET_DEF(O_MATCHTIME, "matchtime=7");
311 SET_DEF(O_REPORT, "report=5");
312 SET_DEF(O_PARAGRAPHS, "paragraphs=IPLPPPQPP LIpplpipbp");
313 (void)snprintf(b1, sizeof(b1), "recdir=%s", _PATH_PRESERVE);
314 SET_DEF(O_RECDIR, b1);
315 (void)snprintf(b1, sizeof(b1), "scroll=%ld", O_VAL(sp, O_LINES) / 2);
316 SET_DEF(O_SCROLL, b1);
317 SET_DEF(O_SECTIONS, "sections=NHSHH HUnhsh");
304585c1
AM
318 (void)snprintf(b1, sizeof(b1), "shell=%s",
319 (s = getenv("SHELL")) == NULL ? _PATH_BSHELL : s);
1e64b3ba
JH
320 SET_DEF(O_SHELL, b1);
321 SET_DEF(O_SHIFTWIDTH, "shiftwidth=8");
322 SET_DEF(O_SIDESCROLL, "sidescroll=16");
323 SET_DEF(O_TABSTOP, "tabstop=8");
324 (void)snprintf(b1, sizeof(b1), "tags=%s", _PATH_TAGS);
325 SET_DEF(O_TAGS, b1);
304585c1
AM
326 (void)snprintf(b1, sizeof(b1), "term=%s",
327 (s = getenv("TERM")) == NULL ? "unknown" : s);
1e64b3ba
JH
328 SET_DEF(O_TERM, b1);
329
330 /*
331 * The default window option values are:
332 * 8 if baud rate <= 600
333 * 16 if baud rate <= 1200
334 * LINES - 1 if baud rate > 1200
335 */
336 v = baud_from_bval(sp);
337 if (v <= 600)
338 v = 8;
339 else if (v <= 1200)
340 v = 16;
341 else
342 v = O_VAL(sp, O_LINES) - 1;
343 (void)snprintf(b1, sizeof(b1), "window=%lu", v);
344 SET_DEF(O_WINDOW, b1);
345
346 SET_DEF(O_WRAPMARGIN, "wrapmargin=0");
347
348 /*
349 * By default, the historic vi always displayed information
350 * about two options, redraw and term. Term seems sufficient.
351 */
352 F_SET(&sp->opts[O_TERM], OPT_SET);
353 return (0);
354}
355
356/*
357 * opts_set --
358 * Change the values of one or more options.
359 */
360int
361opts_set(sp, argv)
362 SCR *sp;
363 ARGS *argv[];
364{
365 enum optdisp disp;
366 OABBREV atmp, *ap;
367 OPTLIST const *op;
368 OPTLIST otmp;
369 OPTION *spo;
370 u_long value, turnoff;
371 int ch, offset, rval;
372 char *endp, *equals, *name, *p;
304585c1 373
1e64b3ba
JH
374 disp = NO_DISPLAY;
375 for (rval = 0; (*argv)->len != 0; ++argv) {
376 /*
377 * The historic vi dumped the options for each occurrence of
378 * "all" in the set list. Puhleeze.
379 */
380 if (!strcmp(argv[0]->bp, "all")) {
381 disp = ALL_DISPLAY;
382 continue;
383 }
304585c1 384
1e64b3ba 385 /* Find equals sign or end of set, skipping backquoted chars. */
304585c1
AM
386 for (equals = NULL,
387 p = name = argv[0]->bp; (ch = *p) != '\0'; ++p)
388 switch (ch) {
1e64b3ba
JH
389 case '=':
390 equals = p;
391 break;
392 case '\\':
393 /* Historic vi just used the backslash. */
394 if (p[1] == '\0')
395 break;
396 ++p;
397 break;
398 }
399
400 turnoff = 0;
401 op = NULL;
402 if (equals)
403 *equals++ = '\0';
404
405 /* Check list of abbreviations. */
406 atmp.name = name;
407 if ((ap = bsearch(&atmp, abbrev,
408 sizeof(abbrev) / sizeof(OABBREV) - 1,
409 sizeof(OABBREV), opts_abbcmp)) != NULL) {
410 op = optlist + ap->offset;
411 goto found;
412 }
413
414 /* Check list of options. */
415 otmp.name = name;
416 if ((op = bsearch(&otmp, optlist,
417 sizeof(optlist) / sizeof(OPTLIST) - 1,
418 sizeof(OPTLIST), opts_cmp)) != NULL)
419 goto found;
420
421 /* Try the name without any leading "no". */
422 if (name[0] == 'n' && name[1] == 'o') {
423 turnoff = 1;
424 name += 2;
425 } else
426 goto prefix;
427
428 /* Check list of abbreviations. */
429 atmp.name = name;
430 if ((ap = bsearch(&atmp, abbrev,
431 sizeof(abbrev) / sizeof(OABBREV) - 1,
432 sizeof(OABBREV), opts_abbcmp)) != NULL) {
433 op = optlist + ap->offset;
434 goto found;
435 }
436
437 /* Check list of options. */
438 otmp.name = name;
439 if ((op = bsearch(&otmp, optlist,
440 sizeof(optlist) / sizeof(OPTLIST) - 1,
441 sizeof(OPTLIST), opts_cmp)) != NULL)
442 goto found;
443
444 /* Check for prefix match. */
445prefix: op = opts_prefix(name);
446
447found: if (op == NULL) {
448 msgq(sp, M_ERR,
449 "no %s option: 'set all' gives all option values",
450 name);
451 continue;
452 }
453
454 /* Find current option values. */
455 offset = op - optlist;
456 spo = sp->opts + offset;
457
458 /* Set name, value. */
459 switch (op->type) {
460 case OPT_0BOOL:
461 case OPT_1BOOL:
462 if (equals) {
463 msgq(sp, M_ERR,
464 "set: [no]%s option doesn't take a value",
465 name);
466 break;
467 }
468 if (op->func != NULL) {
469 if (op->func(sp, spo, NULL, turnoff)) {
470 rval = 1;
471 break;
472 }
473 } else if (turnoff)
474 O_CLR(sp, offset);
475 else
476 O_SET(sp, offset);
477 goto change;
478 case OPT_NUM:
479 /*
480 * !!!
481 * Extension to historic vi. If the OPT_NOSTR flag is
482 * set, a numeric option may be turned off by using a
483 * "no" prefix, e.g. "nowrapmargin". (We assume that
484 * setting the value to 0 turns a numeric option off.)
485 */
486 if (turnoff) {
487 if (F_ISSET(op, OPT_NOSTR)) {
488 value = 0;
489 goto nostr;
490 }
491 msgq(sp, M_ERR,
492 "set: %s option isn't a boolean", name);
493 break;
494 }
495 if (!equals) {
496 if (!disp)
497 disp = SELECT_DISPLAY;
498 F_SET(spo, OPT_SELECTED);
499 break;
500 }
501 value = strtol(equals, &endp, 10);
502 if (*endp && !isblank(*endp)) {
503 msgq(sp, M_ERR,
504 "set %s: illegal number %s", name, equals);
505 break;
506 }
507nostr: if (op->func != NULL) {
508 if (op->func(sp, spo, equals, value)) {
509 rval = 1;
510 break;
511 }
512 } else
513 O_VAL(sp, offset) = value;
514 goto change;
515 case OPT_STR:
516 if (turnoff) {
517 msgq(sp, M_ERR,
518 "set: %s option isn't a boolean", name);
519 break;
520 }
521 if (!equals) {
522 if (!disp)
523 disp = SELECT_DISPLAY;
524 F_SET(spo, OPT_SELECTED);
525 break;
526 }
527 if (op->func != NULL) {
528 if (op->func(sp, spo, equals, (u_long)0)) {
529 rval = 1;
530 break;
531 }
532 } else {
533 if (F_ISSET(&sp->opts[offset], OPT_ALLOCATED))
534 free(O_STR(sp, offset));
535 if ((O_STR(sp, offset) =
536 strdup(equals)) == NULL) {
537 msgq(sp, M_SYSERR, NULL);
538 rval = 1;
539 break;
540 } else
541 F_SET(&sp->opts[offset], OPT_ALLOCATED);
542 }
543change: if (sp->s_optchange != NULL)
544 (void)sp->s_optchange(sp, offset);
545 F_SET(&sp->opts[offset], OPT_SET);
546 break;
547 default:
548 abort();
549 }
550 }
304585c1 551 if (disp != NO_DISPLAY)
1e64b3ba
JH
552 opts_dump(sp, disp);
553 return (rval);
554}
555
556/*
557 * opts_dump --
558 * List the current values of selected options.
559 */
560void
561opts_dump(sp, type)
562 SCR *sp;
563 enum optdisp type;
564{
565 OPTLIST const *op;
566 int base, b_num, cnt, col, colwidth, curlen, s_num;
567 int numcols, numrows, row;
568 int b_op[O_OPTIONCOUNT], s_op[O_OPTIONCOUNT];
569 char nbuf[20];
570
304585c1
AM
571 /*
572 * XXX
573 * It's possible to get here by putting "set option" in the
574 * .exrc file. I can't think of a clean way to layer this,
575 * or a reasonable check to make, so we block it here.
576 */
577 if (sp->stdfp == NULL) {
578 msgq(sp, M_ERR,
579 "Option display requires that the screen be initialized.");
580 return;
581 }
582
1e64b3ba
JH
583 /*
584 * Options are output in two groups -- those that fit in a column and
585 * those that don't. Output is done on 6 character "tab" boundaries
586 * for no particular reason. (Since we don't output tab characters,
587 * we can ignore the terminal's tab settings.) Ignore the user's tab
588 * setting because we have no idea how reasonable it is.
304585c1
AM
589 *
590 * Find a column width we can live with.
1e64b3ba 591 */
1e64b3ba 592 for (cnt = 6; cnt > 1; --cnt) {
304585c1 593 colwidth = (sp->cols - 1) / cnt & ~(STANDARD_TAB - 1);
1e64b3ba 594 if (colwidth >= 10) {
304585c1
AM
595 colwidth =
596 (colwidth + STANDARD_TAB) & ~(STANDARD_TAB - 1);
1e64b3ba
JH
597 break;
598 }
599 colwidth = 0;
600 }
601
304585c1
AM
602 /*
603 * Get the set of options to list, entering them into
604 * the column list or the overflow list.
1e64b3ba 605 */
304585c1 606 for (b_num = s_num = 0, op = optlist; op->name != NULL; ++op) {
1e64b3ba
JH
607 cnt = op - optlist;
608
609 /* If OPT_NEVER set, it's never displayed. */
610 if (F_ISSET(op, OPT_NEVER))
611 continue;
612
613 switch (type) {
614 case ALL_DISPLAY: /* Display all. */
615 break;
616 case CHANGED_DISPLAY: /* Display changed. */
617 if (!F_ISSET(&sp->opts[cnt], OPT_SET))
618 continue;
619 break;
620 case SELECT_DISPLAY: /* Display selected. */
621 if (!F_ISSET(&sp->opts[cnt], OPT_SELECTED))
622 continue;
623 break;
624 default:
625 case NO_DISPLAY:
626 abort();
627 /* NOTREACHED */
628 }
629 F_CLR(&sp->opts[cnt], OPT_SELECTED);
630
631 curlen = strlen(op->name);
632 switch (op->type) {
633 case OPT_0BOOL:
634 case OPT_1BOOL:
635 if (!O_ISSET(sp, cnt))
636 curlen += 2;
637 break;
638 case OPT_NUM:
639 (void)snprintf(nbuf,
640 sizeof(nbuf), "%ld", O_VAL(sp, cnt));
641 curlen += strlen(nbuf);
642 break;
643 case OPT_STR:
644 curlen += strlen(O_STR(sp, cnt)) + 3;
645 break;
646 }
647 /* Offset by two so there's a gap. */
648 if (curlen < colwidth - 2)
649 s_op[s_num++] = cnt;
650 else
651 b_op[b_num++] = cnt;
652 }
653
c0763269 654 if (s_num > 0) {
304585c1
AM
655 /* Figure out the number of columns. */
656 numcols = (sp->cols - 1) / colwidth;
657 if (s_num > numcols) {
658 numrows = s_num / numcols;
659 if (s_num % numcols)
660 ++numrows;
661 } else
662 numrows = 1;
663
664 /* Display the options in sorted order. */
c0763269
AS
665 for (row = 0; row < numrows;) {
666 for (base = row, col = 0; col < numcols; ++col) {
304585c1 667 cnt = opts_print(sp, &optlist[s_op[base]]);
c0763269
AS
668 if ((base += numrows) >= s_num)
669 break;
670 (void)ex_printf(EXCOOKIE,
671 "%*s", (int)(colwidth - cnt), "");
672 }
673 if (++row < numrows || b_num)
674 (void)ex_printf(EXCOOKIE, "\n");
1e64b3ba 675 }
1e64b3ba
JH
676 }
677
678 for (row = 0; row < b_num;) {
304585c1 679 (void)opts_print(sp, &optlist[b_op[row]]);
1e64b3ba
JH
680 if (++row < b_num)
681 (void)ex_printf(EXCOOKIE, "\n");
682 }
683 (void)ex_printf(EXCOOKIE, "\n");
684}
685
686/*
687 * opts_print --
688 * Print out an option.
689 */
690static int
304585c1 691opts_print(sp, op)
1e64b3ba
JH
692 SCR *sp;
693 OPTLIST const *op;
1e64b3ba
JH
694{
695 int curlen, offset;
696
697 curlen = 0;
698 offset = op - optlist;
699 switch (op->type) {
700 case OPT_0BOOL:
701 case OPT_1BOOL:
702 curlen += ex_printf(EXCOOKIE,
703 "%s%s", O_ISSET(sp, offset) ? "" : "no", op->name);
704 break;
705 case OPT_NUM:
706 curlen += ex_printf(EXCOOKIE,
707 "%s=%ld", op->name, O_VAL(sp, offset));
708 break;
709 case OPT_STR:
710 curlen += ex_printf(EXCOOKIE,
711 "%s=\"%s\"", op->name, O_STR(sp, offset));
712 break;
713 }
714 return (curlen);
715}
716
717/*
718 * opts_save --
719 * Write the current configuration to a file.
720 */
721int
722opts_save(sp, fp)
723 SCR *sp;
724 FILE *fp;
725{
1e64b3ba
JH
726 OPTLIST const *op;
727 int ch, cnt;
728 char *p;
729
304585c1 730 for (op = optlist; op->name != NULL; ++op) {
1e64b3ba
JH
731 if (F_ISSET(op, OPT_NOSAVE))
732 continue;
733 cnt = op - optlist;
734 switch (op->type) {
735 case OPT_0BOOL:
736 case OPT_1BOOL:
737 if (O_ISSET(sp, cnt))
738 (void)fprintf(fp, "set %s\n", op->name);
739 else
740 (void)fprintf(fp, "set no%s\n", op->name);
741 break;
742 case OPT_NUM:
743 (void)fprintf(fp,
744 "set %s=%-3d\n", op->name, O_VAL(sp, cnt));
745 break;
746 case OPT_STR:
747 (void)fprintf(fp, "set ");
748 for (p = op->name; (ch = *p) != '\0'; ++p) {
749 if (isblank(ch))
750 (void)putc('\\', fp);
751 (void)putc(ch, fp);
752 }
753 (void)putc('=', fp);
754 for (p = O_STR(sp, cnt); (ch = *p) != '\0'; ++p) {
755 if (isblank(ch))
756 (void)putc('\\', fp);
757 (void)putc(ch, fp);
758 }
759 (void)putc('\n', fp);
760 break;
761 }
762 if (ferror(fp)) {
763 msgq(sp, M_ERR, "I/O error: %s", strerror(errno));
764 return (1);
765 }
766 }
767 return (0);
768}
769
770/*
771 * opts_prefix --
772 * Check to see if the name is the prefix of one (and only one)
773 * option. If so, return the option.
774 */
775static OPTLIST const *
776opts_prefix(name)
777 char *name;
778{
779 OPTLIST const *op, *save_op;
780 size_t len;
781
782 save_op = NULL;
783 len = strlen(name);
784 for (op = optlist; op->name != NULL; ++op) {
785 if (op->name[0] < name[0])
786 continue;
787 if (op->name[0] > name[0])
788 break;
789 if (!memcmp(op->name, name, len)) {
790 if (save_op != NULL)
791 return (NULL);
792 save_op = op;
793 }
794 }
795 return (save_op);
796}
797
798static int
799opts_abbcmp(a, b)
800 const void *a, *b;
801{
802 return(strcmp(((OABBREV *)a)->name, ((OABBREV *)b)->name));
803}
804
805static int
806opts_cmp(a, b)
807 const void *a, *b;
808{
809 return(strcmp(((OPTLIST *)a)->name, ((OPTLIST *)b)->name));
810}
811
812/*
813 * opts_free --
814 * Free all option strings
815 */
816void
817opts_free(sp)
818 SCR *sp;
819{
820 int cnt;
821 char *p;
822
823 for (cnt = 0; cnt < O_OPTIONCOUNT; ++cnt)
824 if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED)) {
825 p = O_STR(sp, cnt);
826 FREE(p, strlen(p) + 1);
827 }
828}
829
830/*
831 * opts_copy --
832 * Copy a screen's OPTION array.
833 */
834int
835opts_copy(orig, sp)
836 SCR *orig, *sp;
837{
838 OPTION *op;
839 int cnt;
840
841 /* Copy most everything without change. */
842 memmove(sp->opts, orig->opts, sizeof(orig->opts));
843
844 /*
845 * Allocate copies of the strings -- keep trying to reallocate
846 * after ENOMEM failure, otherwise end up with more than one
847 * screen referencing the original memory.
848 */
849 for (op = sp->opts, cnt = 0; cnt < O_OPTIONCOUNT; ++cnt, ++op)
850 if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED) &&
851 (O_STR(sp, cnt) = strdup(O_STR(sp, cnt))) == NULL) {
852 msgq(orig, M_SYSERR, NULL);
853 return (1);
854 }
855 return (0);
856}