update, move recovery information into docs
[nvi.git] / common / options.c
blob7ff728c6c5c5defd1cb1e0042a672d2e7d4d89e4
1 /*-
2 * Copyright (c) 1991, 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
6 */
8 #ifndef lint
9 static char sccsid[] = "$Id: options.c,v 8.32 1993/12/20 18:16:37 bostic Exp $ (Berkeley) $Date: 1993/12/20 18:16:37 $";
10 #endif /* not lint */
12 #include <sys/types.h>
13 #include <sys/stat.h>
15 #include <ctype.h>
16 #include <curses.h>
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
22 #include "vi.h"
23 #include "excmd.h"
24 #include "pathnames.h"
26 static int opts_abbcmp __P((const void *, const void *));
27 static int opts_cmp __P((const void *, const void *));
28 static OPTLIST const *opts_prefix __P((char *));
29 static int opts_print __P((SCR *, OPTLIST const *, OPTION *));
32 * O'Reilly noted options and abbreviations are from "Learning the VI Editor",
33 * Fifth Edition, May 1992. There's no way of knowing what systems they are
34 * actually from.
36 * HPUX noted options and abbreviations are from "The Ultimate Guide to the
37 * VI and EX Text Editors", 1990.
39 static OPTLIST const optlist[] = {
40 /* O_ALTWERASE 4.4BSD */
41 {"altwerase", f_altwerase, OPT_0BOOL, 0},
42 /* O_AUTOINDENT 4BSD */
43 {"autoindent", NULL, OPT_0BOOL, 0},
44 /* O_AUTOPRINT 4BSD */
45 {"autoprint", NULL, OPT_1BOOL, 0},
46 /* O_AUTOWRITE 4BSD */
47 {"autowrite", NULL, OPT_0BOOL, 0},
48 /* O_BEAUTIFY 4BSD */
49 {"beautify", NULL, OPT_0BOOL, 0},
50 /* O_COLUMNS 4.4BSD */
51 {"columns", f_columns, OPT_NUM, OPT_NOSAVE},
52 /* O_COMMENT 4.4BSD */
53 {"comment", NULL, OPT_0BOOL, 0},
54 /* O_DIGRAPH XXX: Elvis */
55 {"digraph", NULL, OPT_0BOOL, 0},
56 /* O_DIRECTORY 4BSD */
57 {"directory", NULL, OPT_STR, OPT_NOSAVE},
58 /* O_EDCOMPATIBLE 4BSD */
59 {"edcompatible",NULL, OPT_0BOOL, 0},
60 /* O_ERRORBELLS 4BSD */
61 {"errorbells", NULL, OPT_0BOOL, 0},
62 /* O_EXRC System V (undocumented) */
63 {"exrc", NULL, OPT_0BOOL, 0},
64 /* O_EXTENDED 4.4BSD */
65 {"extended", NULL, OPT_0BOOL, 0},
66 /* O_FLASH HPUX */
67 {"flash", NULL, OPT_1BOOL, 0},
68 /* O_HARDTABS 4BSD */
69 {"hardtabs", NULL, OPT_NUM, 0},
70 /* O_IGNORECASE 4BSD */
71 {"ignorecase", NULL, OPT_0BOOL, 0},
72 /* O_KEYTIME 4.4BSD */
73 {"keytime", f_keytime, OPT_NUM, 0},
74 /* O_LEFTRIGHT 4.4BSD */
75 {"leftright", f_leftright, OPT_0BOOL, 0},
76 /* O_LINES 4.4BSD */
77 {"lines", f_lines, OPT_NUM, OPT_NOSAVE},
78 /* O_LISP 4BSD */
79 {"lisp", f_lisp, OPT_0BOOL, 0},
80 /* O_LIST 4BSD */
81 {"list", f_list, OPT_0BOOL, 0},
82 /* O_MAGIC 4BSD */
83 {"magic", NULL, OPT_1BOOL, 0},
84 /* O_MATCHTIME 4.4BSD */
85 {"matchtime", f_matchtime, OPT_NUM, 0},
86 /* O_MESG 4BSD */
87 {"mesg", f_mesg, OPT_1BOOL, 0},
88 /* O_MODELINE 4BSD */
89 {"modeline", f_modeline, OPT_0BOOL, 0},
90 /* O_NUMBER 4BSD */
91 {"number", f_number, OPT_0BOOL, 0},
92 /* O_NUNDO 4.4BSD */
93 {"nundo", NULL, OPT_0BOOL, 0},
94 /* O_OPEN 4BSD */
95 {"open", NULL, OPT_1BOOL, 0},
96 /* O_OPTIMIZE 4BSD */
97 {"optimize", f_optimize, OPT_1BOOL, 0},
98 /* O_PARAGRAPHS 4BSD */
99 {"paragraphs", f_paragraph, OPT_STR, 0},
100 /* O_PROMPT 4BSD */
101 {"prompt", NULL, OPT_1BOOL, 0},
102 /* O_READONLY 4BSD (undocumented) */
103 {"readonly", f_readonly, OPT_0BOOL, 0},
104 /* O_REDRAW 4BSD */
105 {"redraw", NULL, OPT_0BOOL, 0},
106 /* O_REMAP 4BSD */
107 {"remap", NULL, OPT_1BOOL, 0},
108 /* O_REPORT 4BSD */
109 {"report", NULL, OPT_NUM, OPT_NOSTR},
110 /* O_RULER 4.4BSD */
111 {"ruler", f_ruler, OPT_0BOOL, 0},
112 /* O_SCROLL 4BSD */
113 {"scroll", NULL, OPT_NUM, 0},
114 /* O_SECTIONS 4BSD */
115 {"sections", f_section, OPT_STR, 0},
116 /* O_SHELL 4BSD */
117 {"shell", NULL, OPT_STR, 0},
118 /* O_SHIFTWIDTH 4BSD */
119 {"shiftwidth", f_shiftwidth, OPT_NUM, 0},
120 /* O_SHOWDIRTY 4.4BSD */
121 {"showdirty", NULL, OPT_0BOOL, 0},
122 /* O_SHOWMATCH 4BSD */
123 {"showmatch", NULL, OPT_0BOOL, 0},
124 /* O_SHOWMODE 4.4BSD */
125 {"showmode", NULL, OPT_0BOOL, 0},
126 /* O_SIDESCROLL 4.4BSD */
127 {"sidescroll", f_sidescroll, OPT_NUM, 0},
128 /* O_SLOWOPEN 4BSD */
129 {"slowopen", NULL, OPT_0BOOL, 0},
130 /* O_SOURCEANY 4BSD (undocumented) */
131 {"sourceany", f_sourceany, OPT_0BOOL, 0},
132 /* O_TABSTOP 4BSD */
133 {"tabstop", f_tabstop, OPT_NUM, 0},
134 /* O_TAGLENGTH 4BSD */
135 {"taglength", NULL, OPT_NUM, OPT_NOSTR},
136 /* O_TAGS 4BSD */
137 {"tags", f_tags, OPT_STR, 0},
138 /* O_TERM 4BSD */
139 {"term", f_term, OPT_STR, OPT_NOSAVE},
140 /* O_TERSE 4BSD */
141 {"terse", NULL, OPT_0BOOL, 0},
142 /* O_TIMEOUT 4BSD (undocumented) */
143 {"timeout", NULL, OPT_1BOOL, 0},
144 /* O_TTYWERASE 4.4BSD */
145 {"ttywerase", f_ttywerase, OPT_0BOOL, 0},
146 /* O_VERBOSE 4.4BSD */
147 {"verbose", NULL, OPT_0BOOL, 0},
148 /* O_W1200 4BSD */
149 {"w1200", f_w1200, OPT_NUM, OPT_NEVER},
150 /* O_W300 4BSD */
151 {"w300", f_w300, OPT_NUM, OPT_NEVER},
152 /* O_W9600 4BSD */
153 {"w9600", f_w9600, OPT_NUM, OPT_NEVER},
154 /* O_WARN 4BSD */
155 {"warn", NULL, OPT_1BOOL, 0},
156 /* O_WINDOW 4BSD */
157 {"window", f_window, OPT_NUM, 0},
158 /* O_WRAPMARGIN 4BSD */
159 {"wrapmargin", f_wrapmargin, OPT_NUM, OPT_NOSTR},
160 /* O_WRAPSCAN 4BSD */
161 {"wrapscan", NULL, OPT_1BOOL, 0},
162 /* O_WRITEANY 4BSD */
163 {"writeany", NULL, OPT_0BOOL, 0},
164 {NULL},
167 typedef struct abbrev {
168 char *name;
169 int offset;
170 } OABBREV;
172 static OABBREV const abbrev[] = {
173 {"ai", O_AUTOINDENT}, /* 4BSD */
174 {"ap", O_AUTOPRINT}, /* 4BSD */
175 {"aw", O_AUTOWRITE}, /* 4BSD */
176 {"bf", O_BEAUTIFY}, /* 4BSD */
177 {"co", O_COLUMNS}, /* 4.4BSD */
178 {"dir", O_DIRECTORY}, /* 4BSD */
179 {"eb", O_ERRORBELLS}, /* 4BSD */
180 {"ed", O_EDCOMPATIBLE}, /* 4BSD (undocumented) */
181 {"ex", O_EXRC}, /* System V (undocumented) */
182 {"ht", O_HARDTABS}, /* 4BSD */
183 {"ic", O_IGNORECASE}, /* 4BSD */
184 {"li", O_LINES}, /* 4.4BSD */
185 {"modelines", O_MODELINE}, /* HPUX */
186 {"nu", O_NUMBER}, /* 4BSD */
187 {"opt", O_OPTIMIZE}, /* 4BSD */
188 {"para", O_PARAGRAPHS}, /* 4BSD */
189 {"re", O_REDRAW}, /* O'Reilly */
190 {"ro", O_READONLY}, /* 4BSD (undocumented) */
191 {"scr", O_SCROLL}, /* 4BSD (undocumented) */
192 {"sect", O_SECTIONS}, /* O'Reilly */
193 {"sh", O_SHELL}, /* 4BSD */
194 {"slow", O_SLOWOPEN}, /* 4BSD */
195 {"sm", O_SHOWMATCH}, /* 4BSD */
196 {"sw", O_SHIFTWIDTH}, /* 4BSD */
197 {"tag", O_TAGS}, /* 4BSD (undocumented) */
198 {"tl", O_TAGLENGTH}, /* 4BSD */
199 {"to", O_TIMEOUT}, /* 4BSD (undocumented) */
200 {"ts", O_TABSTOP}, /* 4BSD */
201 {"tty", O_TERM}, /* 4BSD (undocumented) */
202 {"ttytype", O_TERM}, /* 4BSD (undocumented) */
203 {"w", O_WINDOW}, /* O'Reilly */
204 {"wa", O_WRITEANY}, /* 4BSD */
205 {"wi", O_WINDOW}, /* 4BSD (undocumented) */
206 {"wm", O_WRAPMARGIN}, /* 4BSD */
207 {"ws", O_WRAPSCAN}, /* 4BSD */
208 {NULL},
212 * opts_init --
213 * Initialize some of the options. Since the user isn't really
214 * "setting" these variables, don't set their OPT_SET bits.
217 opts_init(sp)
218 SCR *sp;
220 ARGS *argv[2], a, b;
221 OPTLIST const *op;
222 u_long v;
223 int cnt;
224 char *s, b1[1024];
226 a.bp = b1;
227 a.len = 0;
228 b.bp = NULL;
229 b.len = 0;
230 argv[0] = &a;
231 argv[1] = &b;
233 #define SET_DEF(opt, str) { \
234 if (str != b1) /* GCC puts strings in text-space. */ \
235 (void)strcpy(b1, str); \
236 a.len = strlen(b1); \
237 if (opts_set(sp, argv)) { \
238 msgq(sp, M_ERR, \
239 "Unable to set default %s option", optlist[opt]); \
240 return (1); \
242 F_CLR(&sp->opts[opt], OPT_SET); \
244 /* Set default values. */
245 for (op = optlist, cnt = 0; op->name != NULL; ++op, ++cnt)
246 if (op->type == OPT_0BOOL)
247 O_CLR(sp, cnt);
248 else if (op->type == OPT_1BOOL)
249 O_SET(sp, cnt);
251 (void)snprintf(b1, sizeof(b1), "directory=%s",
252 (s = getenv("TMPDIR")) == NULL ? _PATH_PRESERVE : s);
253 SET_DEF(O_DIRECTORY, b1);
254 SET_DEF(O_KEYTIME, "keytime=6");
255 SET_DEF(O_MATCHTIME, "matchtime=7");
256 SET_DEF(O_REPORT, "report=5");
257 SET_DEF(O_PARAGRAPHS, "paragraphs=IPLPPPQPP LIpplpipbp");
258 (void)snprintf(b1, sizeof(b1), "scroll=%ld", O_VAL(sp, O_LINES) / 2);
259 SET_DEF(O_SCROLL, b1);
260 SET_DEF(O_SECTIONS, "sections=NHSHH HUnhsh");
261 (void)snprintf(b1, sizeof(b1),
262 "shell=%s", (s = getenv("SHELL")) == NULL ? _PATH_BSHELL : s);
263 SET_DEF(O_SHELL, b1);
264 SET_DEF(O_SHIFTWIDTH, "shiftwidth=8");
265 SET_DEF(O_SIDESCROLL, "sidescroll=16");
266 SET_DEF(O_TABSTOP, "tabstop=8");
267 (void)snprintf(b1, sizeof(b1), "tags=%s", _PATH_TAGS);
268 SET_DEF(O_TAGS, b1);
269 (void)snprintf(b1, sizeof(b1),
270 "term=%s", (s = getenv("TERM")) == NULL ? "unknown" : s);
271 SET_DEF(O_TERM, b1);
274 * The default window option values are:
275 * 8 if baud rate <= 600
276 * 16 if baud rate <= 1200
277 * LINES - 1 if baud rate > 1200
279 v = baud_from_bval(sp);
280 if (v <= 600)
281 v = 8;
282 else if (v <= 1200)
283 v = 16;
284 else
285 v = O_VAL(sp, O_LINES) - 1;
286 (void)snprintf(b1, sizeof(b1), "window=%lu", v);
287 SET_DEF(O_WINDOW, b1);
289 SET_DEF(O_WRAPMARGIN, "wrapmargin=0");
292 * By default, the historic vi always displayed information
293 * about two options, redraw and term. Term seems sufficient.
295 F_SET(&sp->opts[O_TERM], OPT_SET);
296 return (0);
300 * opts_set --
301 * Change the values of one or more options.
304 opts_set(sp, argv)
305 SCR *sp;
306 ARGS *argv[];
308 enum optdisp disp;
309 OABBREV atmp, *ap;
310 OPTLIST const *op;
311 OPTLIST otmp;
312 OPTION *spo;
313 u_long value, turnoff;
314 int ch, offset, rval;
315 char *endp, *equals, *name, *p;
317 disp = NO_DISPLAY;
318 for (rval = 0; (*argv)->len != 0; ++argv) {
320 * The historic vi dumped the options for each occurrence of
321 * "all" in the set list. Puhleeze.
323 if (!strcmp(argv[0]->bp, "all")) {
324 disp = ALL_DISPLAY;
325 continue;
328 /* Find equals sign or end of set, skipping backquoted chars. */
329 for (p = name = argv[0]->bp, equals = NULL; ch = *p; ++p)
330 switch(ch) {
331 case '=':
332 equals = p;
333 break;
334 case '\\':
335 /* Historic vi just used the backslash. */
336 if (p[1] == '\0')
337 break;
338 ++p;
339 break;
342 turnoff = 0;
343 op = NULL;
344 if (equals)
345 *equals++ = '\0';
347 /* Check list of abbreviations. */
348 atmp.name = name;
349 if ((ap = bsearch(&atmp, abbrev,
350 sizeof(abbrev) / sizeof(OABBREV) - 1,
351 sizeof(OABBREV), opts_abbcmp)) != NULL) {
352 op = optlist + ap->offset;
353 goto found;
356 /* Check list of options. */
357 otmp.name = name;
358 if ((op = bsearch(&otmp, optlist,
359 sizeof(optlist) / sizeof(OPTLIST) - 1,
360 sizeof(OPTLIST), opts_cmp)) != NULL)
361 goto found;
363 /* Try the name without any leading "no". */
364 if (name[0] == 'n' && name[1] == 'o') {
365 turnoff = 1;
366 name += 2;
367 } else
368 goto prefix;
370 /* Check list of abbreviations. */
371 atmp.name = name;
372 if ((ap = bsearch(&atmp, abbrev,
373 sizeof(abbrev) / sizeof(OABBREV) - 1,
374 sizeof(OABBREV), opts_abbcmp)) != NULL) {
375 op = optlist + ap->offset;
376 goto found;
379 /* Check list of options. */
380 otmp.name = name;
381 if ((op = bsearch(&otmp, optlist,
382 sizeof(optlist) / sizeof(OPTLIST) - 1,
383 sizeof(OPTLIST), opts_cmp)) != NULL)
384 goto found;
386 /* Check for prefix match. */
387 prefix: op = opts_prefix(name);
389 found: if (op == NULL) {
390 msgq(sp, M_ERR,
391 "no %s option: 'set all' gives all option values",
392 name);
393 continue;
396 /* Find current option values. */
397 offset = op - optlist;
398 spo = sp->opts + offset;
400 /* Set name, value. */
401 switch (op->type) {
402 case OPT_0BOOL:
403 case OPT_1BOOL:
404 if (equals) {
405 msgq(sp, M_ERR,
406 "set: [no]%s option doesn't take a value",
407 name);
408 break;
410 if (op->func != NULL) {
411 if (op->func(sp, spo, NULL, turnoff)) {
412 rval = 1;
413 break;
415 } else if (turnoff)
416 O_CLR(sp, offset);
417 else
418 O_SET(sp, offset);
419 goto change;
420 case OPT_NUM:
422 * !!!
423 * Extension to historic vi. If the OPT_NOSTR flag is
424 * set, a numeric option may be turned off by using a
425 * "no" prefix, e.g. "nowrapmargin". (We assume that
426 * setting the value to 0 turns a numeric option off.)
428 if (turnoff) {
429 if (F_ISSET(op, OPT_NOSTR)) {
430 value = 0;
431 goto nostr;
433 msgq(sp, M_ERR,
434 "set: %s option isn't a boolean", name);
435 break;
437 if (!equals) {
438 if (!disp)
439 disp = SELECT_DISPLAY;
440 F_SET(spo, OPT_SELECTED);
441 break;
443 value = strtol(equals, &endp, 10);
444 if (*endp && !isblank(*endp)) {
445 msgq(sp, M_ERR,
446 "set %s: illegal number %s", name, equals);
447 break;
449 nostr: if (op->func != NULL) {
450 if (op->func(sp, spo, equals, value)) {
451 rval = 1;
452 break;
454 } else
455 O_VAL(sp, offset) = value;
456 goto change;
457 case OPT_STR:
458 if (turnoff) {
459 msgq(sp, M_ERR,
460 "set: %s option isn't a boolean", name);
461 break;
463 if (!equals) {
464 if (!disp)
465 disp = SELECT_DISPLAY;
466 F_SET(spo, OPT_SELECTED);
467 break;
469 if (op->func != NULL) {
470 if (op->func(sp, spo, equals, (u_long)0)) {
471 rval = 1;
472 break;
474 } else {
475 if (F_ISSET(&sp->opts[offset], OPT_ALLOCATED))
476 free(O_STR(sp, offset));
477 if ((O_STR(sp, offset) =
478 strdup(equals)) == NULL) {
479 msgq(sp, M_SYSERR, NULL);
480 rval = 1;
481 break;
482 } else
483 F_SET(&sp->opts[offset], OPT_ALLOCATED);
485 change: if (sp->s_optchange != NULL)
486 (void)sp->s_optchange(sp, offset);
487 F_SET(&sp->opts[offset], OPT_SET);
488 break;
489 default:
490 abort();
493 if (disp)
494 opts_dump(sp, disp);
495 return (rval);
499 * opts_dump --
500 * List the current values of selected options.
502 void
503 opts_dump(sp, type)
504 SCR *sp;
505 enum optdisp type;
507 OPTLIST const *op;
508 int base, b_num, chcnt, cnt, col, colwidth, curlen, endcol, s_num;
509 int numcols, numrows, row, tablen, termwidth;
510 int b_op[O_OPTIONCOUNT], s_op[O_OPTIONCOUNT];
511 char nbuf[20];
514 * Options are output in two groups -- those that fit at least two to
515 * a line and those that don't. We do output on tab boundaries for no
516 * particular reason. First get the set of options to list, keeping
517 * track of the length of each. No error checking, because we know
518 * that O_TERM was set so at least one option has the OPT_SET bit on.
519 * Termwidth is the tab stop before half of the line in the first loop,
520 * and the full line length later on.
522 colwidth = -1;
523 tablen = O_VAL(sp, O_TABSTOP);
524 termwidth = (sp->cols - 1) / 2 & ~(tablen - 1);
525 for (b_num = s_num = 0, op = optlist; op->name; ++op) {
526 cnt = op - optlist;
528 /* If OPT_NEVER set, it's never displayed. */
529 if (F_ISSET(op, OPT_NEVER))
530 continue;
532 switch (type) {
533 case ALL_DISPLAY: /* Display all. */
534 break;
535 case CHANGED_DISPLAY: /* Display changed. */
536 if (!F_ISSET(&sp->opts[cnt], OPT_SET))
537 continue;
538 break;
539 case SELECT_DISPLAY: /* Display selected. */
540 if (!F_ISSET(&sp->opts[cnt], OPT_SELECTED))
541 continue;
542 break;
543 default:
544 case NO_DISPLAY:
545 abort();
546 /* NOTREACHED */
548 F_CLR(&sp->opts[cnt], OPT_SELECTED);
550 curlen = strlen(op->name);
551 switch (op->type) {
552 case OPT_0BOOL:
553 case OPT_1BOOL:
554 if (!O_ISSET(sp, cnt))
555 curlen += 2;
556 break;
557 case OPT_NUM:
558 (void)snprintf(nbuf,
559 sizeof(nbuf), "%ld", O_VAL(sp, cnt));
560 curlen += strlen(nbuf);
561 break;
562 case OPT_STR:
563 curlen += strlen(O_STR(sp, cnt)) + 3;
564 break;
566 if (curlen < termwidth) {
567 if (colwidth < curlen)
568 colwidth = curlen;
569 s_op[s_num++] = cnt;
570 } else
571 b_op[b_num++] = cnt;
574 colwidth = (colwidth + tablen) & ~(tablen - 1);
575 termwidth = sp->cols - 1;
576 numcols = termwidth / colwidth;
577 if (s_num > numcols) {
578 numrows = s_num / numcols;
579 if (s_num % numcols)
580 ++numrows;
581 } else
582 numrows = 1;
584 for (row = 0; row < numrows;) {
585 endcol = colwidth;
586 for (base = row, chcnt = col = 0; col < numcols; ++col) {
587 chcnt += opts_print(sp,
588 &optlist[s_op[base]], &sp->opts[s_op[base]]);
589 if ((base += numrows) >= s_num)
590 break;
591 while ((cnt =
592 (chcnt + tablen & ~(tablen - 1))) <= endcol) {
593 (void)ex_printf(EXCOOKIE, "\t");
594 chcnt = cnt;
596 endcol += colwidth;
598 if (++row < numrows || b_num)
599 (void)ex_printf(EXCOOKIE, "\n");
602 for (row = 0; row < b_num;) {
603 (void)opts_print(sp, &optlist[b_op[row]], &sp->opts[b_op[row]]);
604 if (++row < b_num)
605 (void)ex_printf(EXCOOKIE, "\n");
607 (void)ex_printf(EXCOOKIE, "\n");
611 * opts_print --
612 * Print out an option.
614 static int
615 opts_print(sp, op, spo)
616 SCR *sp;
617 OPTLIST const *op;
618 OPTION *spo;
620 int curlen, offset;
622 curlen = 0;
623 offset = op - optlist;
624 switch (op->type) {
625 case OPT_0BOOL:
626 case OPT_1BOOL:
627 curlen += ex_printf(EXCOOKIE,
628 "%s%s", O_ISSET(sp, offset) ? "" : "no", op->name);
629 break;
630 case OPT_NUM:
631 curlen += ex_printf(EXCOOKIE,
632 "%s=%ld", op->name, O_VAL(sp, offset));
633 break;
634 case OPT_STR:
635 curlen += ex_printf(EXCOOKIE,
636 "%s=\"%s\"", op->name, O_STR(sp, offset));
637 break;
639 return (curlen);
643 * opts_save --
644 * Write the current configuration to a file.
647 opts_save(sp, fp)
648 SCR *sp;
649 FILE *fp;
651 OPTION *spo;
652 OPTLIST const *op;
653 int ch, cnt;
654 char *p;
656 for (spo = sp->opts, op = optlist; op->name; ++op) {
657 if (F_ISSET(op, OPT_NOSAVE))
658 continue;
659 cnt = op - optlist;
660 switch (op->type) {
661 case OPT_0BOOL:
662 case OPT_1BOOL:
663 if (O_ISSET(sp, cnt))
664 (void)fprintf(fp, "set %s\n", op->name);
665 else
666 (void)fprintf(fp, "set no%s\n", op->name);
667 break;
668 case OPT_NUM:
669 (void)fprintf(fp,
670 "set %s=%-3d\n", op->name, O_VAL(sp, cnt));
671 break;
672 case OPT_STR:
673 (void)fprintf(fp, "set ");
674 for (p = op->name; (ch = *p) != '\0'; ++p) {
675 if (isblank(ch))
676 (void)putc('\\', fp);
677 (void)putc(ch, fp);
679 (void)putc('=', fp);
680 for (p = O_STR(sp, cnt); (ch = *p) != '\0'; ++p) {
681 if (isblank(ch))
682 (void)putc('\\', fp);
683 (void)putc(ch, fp);
685 (void)putc('\n', fp);
686 break;
688 if (ferror(fp)) {
689 msgq(sp, M_ERR, "I/O error: %s", strerror(errno));
690 return (1);
693 return (0);
697 * opts_prefix --
698 * Check to see if the name is the prefix of one (and only one)
699 * option. If so, return the option.
701 static OPTLIST const *
702 opts_prefix(name)
703 char *name;
705 OPTLIST const *op, *save_op;
706 size_t len;
708 save_op = NULL;
709 len = strlen(name);
710 for (op = optlist; op->name != NULL; ++op) {
711 if (op->name[0] < name[0])
712 continue;
713 if (op->name[0] > name[0])
714 break;
715 if (!memcmp(op->name, name, len)) {
716 if (save_op != NULL)
717 return (NULL);
718 save_op = op;
721 return (save_op);
724 static int
725 opts_abbcmp(a, b)
726 const void *a, *b;
728 return(strcmp(((OABBREV *)a)->name, ((OABBREV *)b)->name));
731 static int
732 opts_cmp(a, b)
733 const void *a, *b;
735 return(strcmp(((OPTLIST *)a)->name, ((OPTLIST *)b)->name));
739 * opts_free --
740 * Free all option strings
742 void
743 opts_free(sp)
744 SCR *sp;
746 int cnt;
747 char *p;
749 for (cnt = 0; cnt < O_OPTIONCOUNT; ++cnt)
750 if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED)) {
751 p = O_STR(sp, cnt);
752 FREE(p, strlen(p) + 1);
757 * opts_copy --
758 * Copy a screen's OPTION array.
761 opts_copy(orig, sp)
762 SCR *orig, *sp;
764 OPTION *op;
765 int cnt;
767 /* Copy most everything without change. */
768 memmove(sp->opts, orig->opts, sizeof(orig->opts));
771 * Allocate copies of the strings -- keep trying to reallocate
772 * after ENOMEM failure, otherwise end up with more than one
773 * screen referencing the original memory.
775 for (op = sp->opts, cnt = 0; cnt < O_OPTIONCOUNT; ++cnt, ++op)
776 if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED) &&
777 (O_STR(sp, cnt) = strdup(O_STR(sp, cnt))) == NULL) {
778 msgq(orig, M_SYSERR, NULL);
779 return (1);
781 return (0);