NetBSD/usr.bin/elvis/opts.c

814 lines
17 KiB
C

/* opts.c */
/* Author:
* Steve Kirkendall
* 14407 SW Teal Blvd. #C
* Beaverton, OR 97005
* kirkenda@cs.pdx.edu
*/
/* This file contains the code that manages the run-time options -- The
* values that can be modified via the "set" command.
*/
#include "config.h"
#include "vi.h"
#include "ctype.h"
#ifndef NULL
#define NULL (char *)0
#endif
/* maximum width to permit for strings, including ="" */
#define MAXWIDTH 20
/* These are the default values of all options */
char o_autoindent[1] = {FALSE};
char o_autoprint[1] = {TRUE};
char o_autotab[1] = {TRUE};
char o_autowrite[1] = {FALSE};
char o_columns[3] = {80, 32, ~0};
char o_directory[30] = TMPDIR;
char o_edcompatible[1] = {FALSE};
char o_equalprg[80] = {"fmt"};
char o_errorbells[1] = {TRUE};
char o_exrefresh[1] = {TRUE};
char o_ignorecase[1] = {FALSE};
char o_keytime[3] = {2, 0, 50};
char o_keywordprg[80] = {KEYWORDPRG};
char o_lines[3] = {25, 2, 96};
char o_list[1] = {FALSE};
char o_number[1] = {FALSE};
char o_readonly[1] = {FALSE};
char o_remap[1] = {TRUE};
char o_report[3] = {5, 1, 127};
char o_scroll[3] = {12, 1, 127};
char o_shell[60] = SHELL;
char o_shiftwidth[3] = {8, 1, ~0};
char o_sidescroll[3] = {8, 1, 40};
char o_sync[1] = {NEEDSYNC};
char o_tabstop[3] = {8, 1, 40};
char o_term[30] = "?";
char o_flash[1] = {TRUE};
char o_warn[1] = {TRUE};
char o_wrapscan[1] = {TRUE};
#ifndef CRUNCH
char o_beautify[1] = {FALSE};
char o_exrc[1] = {FALSE};
char o_mesg[1] = {TRUE};
char o_more[1] = {TRUE};
char o_nearscroll[3] = {15, 0, ~0};
char o_novice[1] = {FALSE};
char o_prompt[1] = {TRUE};
char o_taglength[3] = {0, 0, 30};
char o_tags[256] = {"tags"};
char o_terse[1] = {FALSE};
char o_window[3] = {0, 1, 24};
char o_wrapmargin[3] = {0, 0, ~0};
char o_writeany[1] = {FALSE};
#endif
#ifndef NO_ERRLIST
char o_cc[30] = {CC_COMMAND};
char o_make[30] = {MAKE_COMMAND};
#endif
#ifndef NO_CHARATTR
char o_charattr[1] = {FALSE};
#endif
#ifndef NO_DIGRAPH
char o_digraph[1] = {FALSE};
char o_flipcase[80]
# ifdef CS_IBMPC
= {"\207\200\201\232\202\220\204\216\206\217\221\222\224\231\244\245"}
# endif
# ifdef CS_LATIN1
/* initialized by initopts() */
# endif
;
#endif
#ifndef NO_SENTENCE
char o_hideformat[1] = {FALSE};
#endif
#ifndef NO_EXTENSIONS
char o_inputmode[1] = {FALSE};
char o_ruler[1] = {FALSE};
#endif
#ifndef NO_MAGIC
char o_magic[1] = {TRUE};
#endif
#ifndef NO_MODELINES
char o_modelines[1] = {FALSE};
#endif
#ifndef NO_SENTENCE
char o_paragraphs[30] = "PPppIPLPQP";
char o_sections[30] = "NHSHSSSEse";
#endif
#if MSDOS
char o_pcbios[1] = {TRUE};
#endif
#ifndef NO_SHOWMATCH
char o_showmatch[1] = {FALSE};
#endif
#ifndef NO_SHOWMODE
char o_smd[1] = {FALSE};
#endif
#ifndef NO_TAGSTACK
char o_tagstack[1] = {TRUE};
#endif
/* The following describes the names & types of all options */
#define BOOL 0
#define NUM 1
#define STR 2
#define SET 0x01 /* this option has had its value altered */
#define CANSET 0x02 /* this option can be set at any time */
#define RCSET 0x06 /* this option can be set in a .exrc file only */
#define NOSAVE 0x0a /* this option should never be saved by mkexrc */
#define WSET 0x20 /* is this the "window" size option? */
#define MR 0x40 /* does this option affect the way text is displayed? */
struct
{
char *name; /* name of an option */
char *nm; /* short name of an option */
char type; /* type of an option */
char flags; /* boolean: has this option been set? */
char *value; /* value */
}
opts[] =
{
/* name type flags value */
{ "autoindent", "ai", BOOL, CANSET, o_autoindent },
{ "autoprint", "ap", BOOL, CANSET, o_autoprint },
{ "autotab", "at", BOOL, CANSET, o_autotab },
{ "autowrite", "aw", BOOL, CANSET, o_autowrite },
#ifndef CRUNCH
{ "beautify", "bf", BOOL, CANSET, o_beautify },
#endif
#ifndef NO_ERRLIST
{ "cc", "cc", STR, CANSET, o_cc },
#endif
#ifndef NO_CHARATTR
{ "charattr", "ca", BOOL, CANSET|MR, o_charattr },
#endif
{ "columns", "co", NUM, SET|NOSAVE|MR, o_columns },
#ifndef NO_DIGRAPH
{ "digraph", "dig", BOOL, CANSET, o_digraph },
#endif
{ "directory", "dir", STR, RCSET, o_directory },
{ "edcompatible","ed", BOOL, CANSET, o_edcompatible },
{ "equalprg", "ep", STR, CANSET, o_equalprg },
{ "errorbells", "eb", BOOL, CANSET, o_errorbells },
#ifndef CRUNCH
{ "exrc", "exrc", BOOL, CANSET, o_exrc },
#endif
{ "exrefresh", "er", BOOL, CANSET, o_exrefresh },
{ "flash", "vbell",BOOL, CANSET, o_flash },
#ifndef NO_DIGRAPH
{ "flipcase", "fc", STR, CANSET, o_flipcase },
#endif
#ifndef NO_SENTENCE
{ "hideformat", "hf", BOOL, CANSET|MR, o_hideformat },
#endif
{ "ignorecase", "ic", BOOL, CANSET, o_ignorecase },
#ifndef NO_EXTENSIONS
{ "inputmode", "im", BOOL, CANSET, o_inputmode },
#endif
{ "keytime", "kt", NUM, CANSET, o_keytime },
{ "keywordprg", "kp", STR, CANSET, o_keywordprg },
{ "lines", "ls", NUM, SET|NOSAVE|MR, o_lines },
{ "list", "li", BOOL, CANSET|MR, o_list },
#ifndef NO_MAGIC
{ "magic", "ma", BOOL, CANSET, o_magic },
#endif
#ifndef NO_ERRLIST
{ "make", "mk", STR, CANSET, o_make },
#endif
#ifndef CRUNCH
{ "mesg", "me", BOOL, CANSET, o_mesg },
#endif
#ifndef NO_MODELINES
{ "modelines", "ml", BOOL, CANSET, o_modelines },
#endif
#ifndef CRUNCH
{ "more", "mo", BOOL, CANSET, o_more },
{ "nearscroll", "ns", NUM, CANSET, o_nearscroll },
{ "novice", "nov", BOOL, CANSET, o_novice },
#endif
{ "number", "nu", BOOL, CANSET|MR, o_number },
#ifndef NO_SENTENCE
{ "paragraphs", "para", STR, CANSET, o_paragraphs },
#endif
#if MSDOS
{ "pcbios", "pc", BOOL, SET|NOSAVE, o_pcbios },
#endif
#ifndef CRUNCH
{ "prompt", "pr", BOOL, CANSET, o_prompt },
#endif
{ "readonly", "ro", BOOL, CANSET, o_readonly },
{ "remap", "remap",BOOL, CANSET, o_remap },
{ "report", "re", NUM, CANSET, o_report },
#ifndef NO_EXTENSIONS
{ "ruler", "ru", BOOL, CANSET, o_ruler },
#endif
{ "scroll", "sc", NUM, CANSET, o_scroll },
#ifndef NO_SENTENCE
{ "sections", "sect", STR, CANSET, o_sections },
#endif
{ "shell", "sh", STR, CANSET, o_shell },
#ifndef NO_SHOWMATCH
{ "showmatch", "sm", BOOL, CANSET, o_showmatch },
#endif
#ifndef NO_SHOWMODE
{ "showmode", "smd", BOOL, CANSET, o_smd },
#endif
{ "shiftwidth", "sw", NUM, CANSET, o_shiftwidth },
{ "sidescroll", "ss", NUM, CANSET, o_sidescroll },
{ "sync", "sy", BOOL, CANSET, o_sync },
{ "tabstop", "ts", NUM, CANSET|MR, o_tabstop },
#ifndef CRUNCH
{ "taglength", "tl", NUM, CANSET, o_taglength },
{ "tags", "tag", STR, CANSET, o_tags },
#endif
#ifndef NO_TAGSTACK
{ "tagstack", "tgs", BOOL, CANSET, o_tagstack },
#endif
{ "term", "te", STR, SET, o_term },
#ifndef CRUNCH
{ "terse", "tr", BOOL, CANSET, o_terse },
{ "timeout", "to", BOOL, CANSET, o_keytime },
#endif
#ifndef CRUNCH
{ "window", "wi", NUM, CANSET|MR|WSET, o_window },
{ "wrapmargin", "wm", NUM, CANSET, o_wrapmargin },
#endif
{ "wrapscan", "ws", BOOL, CANSET, o_wrapscan },
#ifndef CRUNCH
{ "writeany", "wr", BOOL, CANSET, o_writeany },
#endif
{ NULL, NULL, 0, CANSET, NULL }
};
/* This function initializes certain options from environment variables, etc. */
void initopts()
{
char *val;
int i;
/* set some stuff from environment variables */
#if MSDOS
if (val = getenv("COMSPEC")) /* yes, ASSIGNMENT! */
#else
if (val = getenv("SHELL")) /* yes, ASSIGNMENT! */
#endif
{
strcpy(o_shell, val);
}
strcpy(o_term, termtype);
#if MSDOS
if (strcmp(termtype, "pcbios"))
{
o_pcbios[0] = FALSE;
}
else
{
o_pcbios[0] = TRUE;
}
#endif
#if AMIGA || MSDOS || TOS
if ((val = getenv("TMP")) /* yes, ASSIGNMENT! */
|| (val = getenv("TEMP")))
strcpy(o_directory, val);
#endif
#ifndef CRUNCH
if ((val = getenv("LINES")) && atoi(val) > 4) /* yes, ASSIGNMENT! */
{
LINES = atoi(val);
}
if ((val = getenv("COLUMNS")) && atoi(val) > 30) /* yes, ASSIGNMENT! */
{
COLS = atoi(val);
}
#endif
*o_lines = LINES;
*o_columns = COLS;
*o_scroll = LINES / 2 - 1;
#ifndef CRUNCH
if (o_window[0] == 0)
{
o_window[0] = o_window[2] = *o_lines;
}
*o_nearscroll = *o_lines;
#endif
/* disable the flash option if we don't know how to do a flash */
if (!has_VB)
{
for (i = 0; opts[i].value != o_flash; i++)
{
}
opts[i].flags &= ~CANSET;
*o_flash = FALSE;
}
#ifndef NO_DIGRAPH
# ifdef CS_LATIN1
for (i = 0, val = o_flipcase; i < 32; i++)
{
/* leave out the multiply/divide symbols */
if (i == 23)
continue;
/* add lower/uppercase pair */
*val++ = i + 0xe0;
*val++ = i + 0xc0;
}
*val = '\0';
# endif /* CS_LATIN1 */
/* initialize the ctype package */
_ct_init(o_flipcase);
#else
_ct_init("");
#endif /* not NO_DIGRAPH */
}
/* This function lists the current values of all options */
void dumpopts(all)
int all; /* boolean: dump all options, or just set ones? */
{
#ifndef NO_OPTCOLS
int i, j, k;
char nbuf[4]; /* used for converting numbers to ASCII */
int widths[5]; /* width of each column, including gap */
int ncols; /* number of columns */
int nrows; /* number of options per column */
int nset; /* number of options to be output */
int width; /* width of a particular option */
int todump[60]; /* indicies of options to be dumped */
/* step 1: count the number of set options */
for (nset = i = 0; opts[i].name; i++)
{
if (all || (opts[i].flags & SET))
{
todump[nset++] = i;
}
}
/* step two: try to use as many columns as possible */
for (ncols = (nset > 5 ? 5 : nset); ncols > 1; ncols--)
{
/* how many would go in this column? */
nrows = (nset + ncols - 1) / ncols;
/* figure out the width of each column */
for (i = 0; i < ncols; i++)
{
widths[i] = 0;
for (j = 0, k = nrows * i; j < nrows && k < nset; j++, k++)
{
/* figure out the width of a particular option */
switch (opts[todump[k]].type)
{
case BOOL:
if (!*opts[todump[k]].value)
width = 2;
else
width = 0;
break;
case STR:
width = 3 + strlen(opts[todump[k]].value);
if (width > MAXWIDTH)
width = MAXWIDTH;
break;
case NUM:
width = 4;
break;
}
width += strlen(opts[todump[k]].name);
/* if this is the widest so far, widen col */
if (width > widths[i])
{
widths[i] = width;
}
}
}
/* if the total width is narrow enough, then use it */
for (width = -2, i = 0; i < ncols; i++)
{
width += widths[i] + 2;
}
if (width < COLS - 1)
{
break;
}
}
/* step 3: output the columns */
nrows = (nset + ncols - 1) / ncols;
for (i = 0; i < nrows; i++)
{
for (j = 0; j < ncols; j++)
{
/* if we hit the end of the options, quit */
k = i + j * nrows;
if (k >= nset)
{
break;
}
/* output this option's value */
width = 0;
switch (opts[todump[k]].type)
{
case BOOL:
if (!*opts[todump[k]].value)
{
qaddch('n');
qaddch('o');
width = 2;
}
qaddstr(opts[todump[k]].name);
width += strlen(opts[todump[k]].name);
break;
case NUM:
sprintf(nbuf, "%-3d", UCHAR(*opts[todump[k]].value));
qaddstr(opts[todump[k]].name);
qaddch('=');
qaddstr(nbuf);
width = 4 + strlen(opts[todump[k]].name);
break;
case STR:
qaddstr(opts[todump[k]].name);
qaddch('=');
qaddch('"');
strcpy(tmpblk.c, opts[todump[k]].value);
width = 3 + strlen(tmpblk.c);
if (width > MAXWIDTH)
{
width = MAXWIDTH;
strcpy(tmpblk.c + MAXWIDTH - 6, "...");
}
qaddstr(tmpblk.c);
qaddch('"');
width += strlen(opts[todump[k]].name);
break;
}
/* pad the field to the correct size */
if (k + nrows <= nset)
{
while (width < widths[j] + 2)
{
qaddch(' ');
width++;
}
}
}
addch('\n');
exrefresh();
}
#else
int i;
int col;
char nbuf[4];
for (i = col = 0; opts[i].name; i++)
{
/* if not set and not all, ignore this option */
if (!all && !(opts[i].flags & SET))
{
continue;
}
/* align this option in one of the columns */
if (col > 52)
{
addch('\n');
col = 0;
}
else if (col > 26)
{
while (col < 52)
{
qaddch(' ');
col++;
}
}
else if (col > 0)
{
while (col < 26)
{
qaddch(' ');
col++;
}
}
switch (opts[i].type)
{
case BOOL:
if (!*opts[i].value)
{
qaddch('n');
qaddch('o');
col += 2;
}
qaddstr(opts[i].name);
col += strlen(opts[i].name);
break;
case NUM:
sprintf(nbuf, "%-3d", UCHAR(*opts[i].value));
qaddstr(opts[i].name);
qaddch('=');
qaddstr(nbuf);
col += 4 + strlen(opts[i].name);
break;
case STR:
qaddstr(opts[i].name);
qaddch('=');
qaddch('"');
qaddstr(opts[i].value);
qaddch('"');
col += 3 + strlen(opts[i].name) + strlen(opts[i].value);
break;
}
exrefresh();
}
if (col > 0)
{
addch('\n');
exrefresh();
}
#endif
}
#ifndef NO_MKEXRC
/* This function saves the current configuration of options to a file */
void saveopts(fd)
int fd; /* file descriptor to write to */
{
int i;
char buf[256], *pos;
/* write each set options */
for (i = 0; opts[i].name; i++)
{
/* if unset or unsettable, ignore this option */
if ((opts[i].flags & (SET|CANSET|NOSAVE)) != (SET|CANSET))
{
continue;
}
strcpy(buf, "set ");
pos = &buf[4];
switch (opts[i].type)
{
case BOOL:
if (!*opts[i].value)
{
*pos++='n';
*pos++='o';
}
strcpy(pos, opts[i].name);
strcat(pos, "\n");
break;
case NUM:
sprintf(pos, "%s=%-3d\n", opts[i].name, *opts[i].value & 0xff);
break;
case STR:
sprintf(pos, "%s=\"%s\"\n", opts[i].name, opts[i].value);
break;
}
twrite(fd, buf, (unsigned)strlen(buf));
}
}
#endif
/* This function changes the values of one or more options. */
void setopts(assignments)
char *assignments; /* a string containing option assignments */
{
char *name; /* name of variable in assignments */
char *value; /* value of the variable */
char *scan; /* used for moving through strings */
char *build; /* used for copying chars from "scan" */
char *prefix; /* pointer to "neg" or "no" at front of a boolean */
int quote; /* boolean: inside '"' quotes? */
int i, j;
#ifndef CRUNCH
/* reset the upper limit of "window" option to lines-1 */
*o_window = *o_lines - 1;
#endif
/* for each assignment... */
for (name = assignments; *name; )
{
/* skip whitespace */
if (*name == ' ' || *name == '\t')
{
name++;
continue;
}
/* after the name, find the value (if any) */
for (scan = name; isalnum(*scan); scan++)
{
}
if (*scan == '=')
{
*scan++ = '\0';
value = build = scan;
for (quote = FALSE; *scan && (quote || !isspace(*scan)); scan++)
{
if (*scan == '"')
{
quote = !quote;
}
else if (*scan == '\\' && scan[1])
{
*build++ = *++scan;
}
else
{
*build++ = *scan;
}
}
if (*scan)
scan++;
*build = '\0';
}
else /* no "=" so it is probably boolean... */
{
if (*scan)
{
*scan++ = '\0';
}
value = NULL;
prefix = name;
#ifndef CRUNCH
if (!strcmp(name, "novice"))
/* don't check for a "no" prefix */;
else
#endif
if (prefix[0] == 'n' && prefix[1] == 'o')
name += 2;
else if (prefix[0] == 'n' && prefix[1] == 'e' && prefix[2] == 'g')
name += 3;
}
/* find the variable */
for (i = 0;
opts[i].name && strcmp(opts[i].name, name) && strcmp(opts[i].nm, name);
i++)
{
}
/* change the variable */
if (!opts[i].name)
{
/* only complain about unknown options if we're editing
* a file; i.e., if we're not executing the .exrc now.
*/
if (tmpfd >= 0)
msg("invalid option name \"%s\"", name);
}
else if ((opts[i].flags & CANSET) != CANSET)
{
msg("option \"%s\" can't be altered", name);
}
else if ((opts[i].flags & RCSET) != CANSET && nlines >= 1L)
{
msg("option \"%s\" can only be set in a %s file", name, EXRC);
}
else if (value)
{
switch (opts[i].type)
{
case BOOL:
msg("option \"[no]%s\" is boolean", name);
break;
case NUM:
j = atoi(value);
if (j == 0 && *value != '0')
{
msg("option \"%s\" must have a numeric value", name);
}
else if (j < opts[i].value[1] || j > (opts[i].value[2] & 0xff))
{
msg("option \"%s\" must have a value between %d and %d",
name, opts[i].value[1], opts[i].value[2] & 0xff);
}
else
{
*opts[i].value = atoi(value);
opts[i].flags |= SET;
}
break;
case STR:
strcpy(opts[i].value, value);
opts[i].flags |= SET;
break;
}
if (opts[i].flags & MR)
{
redraw(MARK_UNSET, FALSE);
}
#ifndef CRUNCH
if (opts[i].flags & WSET)
{
wset = TRUE;
}
#endif
}
else /* valid option, no value */
{
if (opts[i].type == BOOL)
{
if (prefix == name)
*opts[i].value = TRUE;
else if (prefix[1] == 'o')
*opts[i].value = FALSE;
else
*opts[i].value = !*opts[i].value;
opts[i].flags |= SET;
if (opts[i].flags & MR)
{
redraw(MARK_UNSET, FALSE);
}
}
else
{
msg("option \"%s\" must be given a value", name);
}
}
/* move on to the next option */
name = scan;
}
/* special processing ... */
#ifndef CRUNCH
/* if "novice" is set, then ":set report=1 showmode nomagic" */
if (*o_novice)
{
*o_report = 1;
# ifndef NO_SHOWMODE
*o_smd = TRUE;
# endif
# ifndef NO_MAGIC
*o_magic = FALSE;
# endif
}
#endif
/* if "readonly" then set the READONLY flag for this file */
if (*o_readonly)
{
setflag(file, READONLY);
}
#ifndef NO_DIGRAPH
/* re-initialize the ctype package */
_ct_init(o_flipcase);
#endif /* not NO_DIGRAPH */
/* copy o_lines and o_columns into LINES and COLS */
LINES = (*o_lines & 255);
COLS = (*o_columns & 255);
}