/* 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 extern char *getenv(); /* 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, 255}; 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] = {1, 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, 255}; 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_novice[1] = {FALSE}; char o_prompt[1] = {TRUE}; char o_taglength[3] = {0, 0, 30}; char o_terse[1] = {FALSE}; char o_window[3] = {0, 1, 24}; char o_wrapmargin[3] = {0, 0, 255}; 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 /* 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 }, { "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 }, #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; } #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) { 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); }