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