This is options.c in view mode; [Download] [Up]
/* options.c */ /* Copyright 1995 by Steve Kirkendall */ char id_options[] = "$Id: options.c,v 2.31 1996/08/09 20:13:45 steve Exp $"; /* This file contains functions which manipulate options. * * If compiled with -DTRY, it will include a main() function which can be * used to test these functions. */ #include "elvis.h" #ifndef OPT_MAXCOLS # define OPT_MAXCOLS 7 #endif /* This data type is used to record a collection of options that were added * via a call to optinsert(). */ typedef struct domain_s { struct domain_s *next; /* next domain in a linked list */ char *name; /* domain name */ int nopts; /* number of options in this domain */ OPTDESC *desc; /* descriptions */ OPTVAL *val; /* option values */ } OPTDOMAIN; /* This data type is used to collect the names & values of options which are * supposed to be output. */ typedef struct optout_s { struct optout_s *next; /* another option to be output */ OPTDOMAIN *dom; /* domain of option to be output */ int idx; /* index of option to be output */ int width; /* width of name+value */ } OPTOUT; #if USE_PROTOTYPES static BOOLEAN optshow(char *name); static void optoutput(BOOLEAN domain, BOOLEAN all, BOOLEAN set, CHAR *outbuf, size_t outsize); #endif /* head of the list of current option domains */ static OPTDOMAIN *head; /* Check a number's validity. If valid & different, then set val and return * 1; if valid & same, then return 0; else give error message and return -1. */ int optisnumber(desc, val, newval) OPTDESC *desc; /* description of the option */ OPTVAL *val; /* value of the option */ CHAR *newval;/* value the option should have (as a string) */ { long min, max, value; /* convert value to binary */ if (!calcnumber(newval)) { msg(MSG_ERROR, "[s]$1 requires a numeric value", desc->longname); return -1; } value = CHAR2long(newval); /* compare against range string */ if (desc->limit) { sscanf(desc->limit, "%ld:%ld", &min, &max); if (value < min || value > max) { msg(MSG_ERROR, "[sdd]$1 must be between $2 and $3", desc->longname, min, max); return -1; } } /* same value as before? */ if (val->value.number == value) { return 0; } /* store the value */ val->value.number = value; return 1; } /* Check a strings validity (all are valid) and set the option. Return 1 * if different, or 0 if same. */ int optisstring(desc, val, newval) OPTDESC *desc; /* description of the option */ OPTVAL *val; /* value of the option */ CHAR *newval;/* value the option should have (as a string) */ { /* if value is the same, do nothing */ if (val->value.string && !CHARcmp(val->value.string, newval)) { return 0; } /* free the old string, if necessary */ if (val->value.string && (val->flags & OPT_FREE)) { safefree(val->value.string); } /* store a copy of the new string */ val->value.string = CHARkdup(newval); val->flags |= OPT_FREE; return 1; } /* Check a string's validity against a space-delimited list of legal values. * If valid & different, then set val to the string's first character, and * return 1; if valid & same, then return 0; else give error message and * return -1. Note that each acceptable valid string must begin with a * unique character for this to work. */ int optisoneof(desc, val, newval) OPTDESC *desc; /* description of the option */ OPTVAL *val; /* value of the option */ CHAR *newval;/* value the option should have (as a string) */ { int len; char *scan; assert(desc->limit != NULL); /* compute the length of newval */ len = CHARlen(newval); if (len <= 0) { goto NoMatch; } /* compare against each legal value */ for (scan = desc->limit; scan - 1 != NULL; scan = strchr(scan, ' ') + 1) { /* does it match this value? */ if (!CHARncmp(newval, toCHAR(scan), (size_t)len)) { /* yes! Either save it & return 1, or just return 0 */ if ((char)*newval != val->value.character) { val->value.character = (char)*newval; return 1; } return 0; } } /* no match. Give an error message and exit */ NoMatch: msg(MSG_ERROR, "[ss]$1 must be one of {$2}", desc->longname, desc->limit); return -1; } /* convert a "number" value to a string */ CHAR *optnstring(desc, val) OPTDESC *desc; /* description of the option */ OPTVAL *val; /* value of the option */ { static char buf[30]; /* convert the value to a string */ sprintf(buf, "%ld", val->value.number); return toCHAR(buf); } /* convert a "string" value to a string. For NULL strings, return "". */ CHAR *optsstring(desc, val) OPTDESC *desc; /* description of the option */ OPTVAL *val; /* value of the option */ { if (val->value.string) return val->value.string; else return toCHAR(""); } /* convert a "one of" value to a string */ CHAR *opt1string(desc, val) OPTDESC *desc; /* description of the option */ OPTVAL *val; /* value of the option */ { static CHAR buf[30], *build; char *scan; /* locate the current value in the limit string */ scan = desc->limit; assert(scan != NULL); while (*scan != val->value.character) { scan = strchr(scan, ' '); assert(scan != NULL); scan++; } /* copy the value to buf, and terminate it with a NUL */ for (build = buf; *scan && *scan != ' '; ) { *build++ = *scan++; } *build = '\0'; return buf; } /* Delete the options whose values are stored starting at val. */ void optdelete(val) OPTVAL val[]; /* array of values to delete */ { OPTDOMAIN *scan, *lag; assert(head != (OPTDOMAIN *)0); /* locate the domain in the list */ for (scan = head, lag = (OPTDOMAIN *)0; scan->val != val; lag = scan, scan = scan->next) { assert(scan->next != (OPTDOMAIN *)0); } /* remove the domain from the list */ if (lag) { lag->next = scan->next; } else { head = scan->next; } /* free the domain structure */ safefree(scan); } /* Add options to the list known to :set. desc is an array of * option descriptions, as described below. val is a parallel * array where the option values are stored. nopts is the number * of options being added. * * Descriptions and values are stored separately to support the * situation multiple items such as buffers must have their own * values for the same options. */ void optinsert(domain, nopts, desc, val) char *domain; /* name of this set of options */ int nopts; /* number of options being inserted */ OPTDESC desc[]; /* descriptions of options */ OPTVAL val[]; /* values of options */ { OPTDOMAIN *newp; /* create a new domain structure */ newp = (OPTDOMAIN *)safekept(1, sizeof(OPTDOMAIN)); assert(newp != (OPTDOMAIN *)0); newp->name = domain, newp->nopts = nopts; newp->desc = desc; newp->val = val; /* insert it at the start of the list, so its values take precedence * over any other options that may have been declared with the same * name. */ newp->next = head; head = newp; } /* This function calls safefree() on any values which have the OPT_FREE * flag set. This is handy when you intend to free a struct which contains * some option values. */ void optfree(nopts, vals) int nopts; /* number of options */ OPTVAL *vals; /* values of options */ { int i; for (i = 0; i < nopts; i++) { if (vals[i].flags & OPT_FREE) { safefree(vals[i].value.string); } } } /* This function sets the "show" flag for a given option. Returns True if * successful, or False if the option doesn't exist. */ static BOOLEAN optshow(name) char *name; /* name of an option that should be shown */ { OPTDOMAIN *dom; BOOLEAN ret = False; int i; /* for each domain of options... */ for (dom = head; dom; dom = dom->next) { /* for each option in that domain... */ for (i = 0; i < dom->nopts; i++) { /* if this is the one we're looking for... */ if (!strcmp(dom->desc[i].longname, name) || !strcmp(dom->desc[i].shortname, name) || !strcmp(dom->name, name)) { dom->val[i].flags |= OPT_SHOW; ret = True; } } } /* complain if unknown */ if (!ret) { msg(MSG_ERROR, "[s]bad option name $1", name); } return ret; } /* This function collects option names & values, sorts them, puts them into * columns, and outputs them. Parameters are: * domain - include domain names as part of option name? * all - output all options? * set - output all options which have been set? * (If neither "all" nor "set" then only options which were * touched by a previous optshow() are output.) */ static void optoutput(domain, all, set, outbuf, outsize) BOOLEAN domain; /* if True, include domain names */ BOOLEAN all; /* if True, output all non-hidden options */ BOOLEAN set; /* if True, output all changed options */ CHAR *outbuf; /* where to place the values */ size_t outsize; /* size of outbuf */ { OPTOUT *out; /* list of options to be output */ OPTOUT *scan, *lag; /* used for scanning through "out" list */ OPTOUT *newp; /* a new OPTOUT to be inserted into list */ OPTDOMAIN *dom; /* used for scanning through domains */ int i, j, k; /* used for scanning through opts in a domain */ int cmp; /* results of comparison */ int nshown; /* number of options to show */ int maxwidth; /* width of widest item */ int ncols, nrows; /* number of columns, and items per column */ struct { OPTOUT *opt; /* first option in a column */ int width; /* width of the column */ } colinfo[OPT_MAXCOLS]; /* start with an empty list */ out = (OPTOUT *)0; nshown = 0; maxwidth = 0; *outbuf = '\0'; /* For each domain... */ for (dom = head; dom; dom = dom->next) { /* For each option in the domain... */ for (i = 0; i < dom->nopts; dom->val[i++].flags &= ~OPT_SHOW) { /* Skip if we aren't supposed to output this option */ if (all ? ((dom->val[i].flags & OPT_HIDE) != 0 && !set) : !(dom->val[i].flags & (set ? OPT_SET : OPT_SHOW))) { continue; } /* See where this should be inserted into the output * list. Beware of duplicates; keep only first of each. */ for (scan = out, lag = (OPTOUT *)0, cmp = 1; scan && (cmp = strcmp(scan->dom->desc[scan->idx].longname, dom->desc[i].longname)) < 0; lag = scan, scan = scan->next) { } if (cmp == 0) { continue; } /* create a new OPTOUT structure for this option */ newp = (OPTOUT *)safealloc(1, sizeof(OPTOUT)); newp->dom = dom; newp->idx = i; newp->width = strlen(dom->desc[i].longname); if (domain) /* including domain name? */ { newp->width += strlen(dom->name) + 1; } if (dom->desc[i].isvalid) /* non-boolean? */ { newp->width += 1; if (dom->desc[i].asstring) newp->width += CHARlen((*dom->desc[i].asstring)(&dom->desc[i], &dom->val[i])); } else if (!dom->val[i].value.boolean) { newp->width += 2; } /* is this the widest value so far? */ if (newp->width > maxwidth) { maxwidth = newp->width; } /* insert the new OPTOUT into the list */ if (lag) { newp->next = lag->next; lag->next = newp; } else { newp->next = out; out = newp; } nshown++; } } /* if nothing to show, then exit */ if (nshown == 0) { return; } /* try to use as many columns as possible */ for (ncols = (nshown > OPT_MAXCOLS ? OPT_MAXCOLS : nshown); ; ncols--) { /* how many options would go in each column? */ nrows = (nshown + ncols - 1) / ncols; /* figure out the width of each column */ for (scan = out, i = 0; i < ncols; i++) { colinfo[i].opt = scan; colinfo[i].width = 0; for (j = 0; j < nrows && scan; j++, scan = scan->next) { /* if this is the widest so far, widen col */ if (scan->width > colinfo[i].width) { colinfo[i].width = scan->width; } } colinfo[i].width += 2; } /* if the total width is narrow enough, then use it */ for (j = -2, i = 0; i < ncols; i++) { j += colinfo[i].width; } if (ncols == 1 || j < (windefault ? o_columns(windefault) : 80) - 1) { break; } } /* if the list is too large to fit in the output buffer, then just * return an empty string. */ if ((size_t)j * (size_t)nrows >= outsize) { /* free the list */ for (scan = out; scan; scan = lag) { lag = scan->next; safefree(scan); } CHARncpy(outbuf, toCHAR("too big!\n"), outsize); return; } /* show 'em */ for (i = 0; i < nrows; i++) { for (j = 0; j < ncols && colinfo[j].opt; j++) { /* include the domain name, if we're supposed to */ scan = colinfo[j].opt; if (domain) { (void)CHARcat(outbuf, scan->dom->name); (void)CHARcat(outbuf, "."); } /* booleans are special... */ if (!scan->dom->desc[scan->idx].isvalid) { if (!scan->dom->val[scan->idx].value.boolean) { (void)CHARcat(outbuf, "no"); } (void)CHARcat(outbuf, scan->dom->desc[scan->idx].longname); } else { (void)CHARcat(outbuf, scan->dom->desc[scan->idx].longname); (void)CHARcat(outbuf, toCHAR("=")); (void)CHARcat(outbuf, (*scan->dom->desc[scan->idx].asstring)(&scan->dom->desc[scan->idx], &scan->dom->val[scan->idx])); } /* pad to max width, except at end of column */ if (j + 1 == ncols || !colinfo[j + 1].opt) { (void)CHARcat(outbuf, "\n"); } else { for (k = colinfo[j].width - scan->width; k > 0; k--) { (void)CHARcat(outbuf, " "); } } /* next time, use the next option */ colinfo[j].opt = colinfo[j].opt->next; } } /* free the list */ for (scan = out; scan; scan = lag) { lag = scan->next; safefree(scan); } } /* This function returns the value of an option, as a nul-terminated string. * For booleans, it returns "true" or "false". For invalid option names, it * returns a NULL pointer. */ CHAR *optgetstr(name) CHAR *name; /* NUL-terminated name */ { OPTDOMAIN *dom; /* used for scanning through domains */ int i; /* used for scanning through opts in a domain */ /* For each domain... */ for (dom = head; dom; dom = dom->next) { /* For each option in the domain... */ for (i = 0; i < dom->nopts; i++) { /* skip options with the wrong name */ if (strcmp(dom->desc[i].longname, tochar8(name)) && strcmp(dom->desc[i].shortname, tochar8(name))) { continue; } /* convert it */ if (dom->desc[i].isvalid) /* non-boolean? */ { if (dom->desc[i].asstring) { return (CHAR *)(*dom->desc[i].asstring)(&dom->desc[i], &dom->val[i]); } return (CHAR *)""; } else if (dom->val[i].value.boolean) { return (CHAR *)"true"; } else { return (CHAR *)"false"; } } } /* if we get here, then we didn't find the option */ return (CHAR *)0; } /* This function assigns a new value to an option. For booleans, it expects * "true" or "false". For invalid option names or inappropriate values it * outputs an error message and returns False. If the value is NULL it * returns False without issueing an error message, on the assumption that * whatever caused the NULL pointer already issued a message. */ BOOLEAN optputstr(name, value) CHAR *name; /* NUL-terminated name */ CHAR *value; /* NUL-terminated value */ { OPTDOMAIN *dom; /* used for scanning through domains */ int i; /* used for scanning through opts in a domain */ BOOLEAN ret; /* return code */ /* For each domain... */ for (dom = head; dom; dom = dom->next) { /* For each option in the domain... */ for (i = 0; i < dom->nopts; i++) { /* skip options with the wrong name */ if (strcmp(dom->desc[i].longname, tochar8(name)) && strcmp(dom->desc[i].shortname, tochar8(name))) { continue; } /* if the option is locked, then fail */ if (dom->val[i].flags & OPT_LOCK) { msg(MSG_ERROR, "[S]$1 is locked", name); return False; } /* convert it */ ret = True; if (dom->desc[i].isvalid) /* non-boolean? */ { /* if the value is valid & different and we need to * call a store function, then call it. */ if ((*dom->desc[i].isvalid)(&dom->desc[i], &dom->val[i], value) == 1 && dom->desc[i].store) { ret = (BOOLEAN)((*dom->desc[i].store)(&dom->desc[i], &dom->val[i], value) >= 0); } } else { dom->val[i].value.boolean = calctrue(value); } /* set the "set" flag */ dom->val[i].flags |= OPT_SET; /* if the "redraw" flag is set, then force redraw */ if ((dom->val[i].flags & OPT_REDRAW) != 0 && windefault) { windefault->di->logic = DRAW_CHANGED; } return ret; } } /* if we get here, then we didn't find the option */ msg(MSG_ERROR, "[S]bad option name $1", name); return False; } /* This function parses the arguments to a ":set" command. Returns True if * successful. For errors, it issues an error message via msg() and returns * False. If any options are to be output, their values will be stored in * a null-terminated string in outbuf. */ BOOLEAN optset(bang, args, outbuf, outsize) BOOLEAN bang; /* if True, any options displayed will include domain */ CHAR *args; /* arguments of ":set" command */ CHAR *outbuf; /* buffer for storing output string */ size_t outsize; /* size of outbuf */ { CHAR *name; /* name of option in args */ 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 */ BOOLEAN quote; /* boolean: inside '"' quotes? */ OPTDOMAIN *dom; /* used for scanning through domains list */ BOOLEAN ret; /* return code */ WINDOW w; BOOLEAN b; int i; /* be optimistic. Begin by assuming this will succeed. */ ret = True; /* initialize "prefix" just to avoid a compiler warning */ prefix = NULL; /* if no arguments, list values of any set values */ if (!*args) { optoutput(bang, False, True, outbuf, outsize); return True; } /* if "all", list values of all options */ if (!CHARcmp(args, toCHAR("all"))) { optoutput(bang, True, False, outbuf, outsize); return True; } if (!CHARcmp(args, toCHAR("everything"))) { optoutput(bang, True, True, outbuf, outsize); return True; } /* for each assignment... */ for (name = args; *name; name = scan) { /* skip whitespace */ while (*name == ' ' || *name == '\t') { name++; } /* 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 = (BOOLEAN)!quote; } else if (*scan == '\\' && scan[1] && !isalnum(scan[1])) { *build++ = *++scan; } else { *build++ = *scan; } } if (*scan) scan++; *build = '\0'; } else if (*scan == '?') { /* mark the option for showing */ *scan++ = '\0'; ret &= optshow(tochar8(name)); continue; } else /* no "=" or "?" */ { if (*scan) { *scan++ = '\0'; } value = NULL; prefix = name; if (!CHARcmp(name, toCHAR("novice")) || !CHARcmp(name, toCHAR("nonascii"))) /* don't check for a "no" prefix */; else if (prefix[0] == 'n' && prefix[1] == 'o') name += 2; else if (prefix[0] == 'n' && prefix[1] == 'e' && prefix[2] == 'g') name += 3; } /* find the option */ for (dom = head; dom; dom = dom->next) { /* check each option in this domain */ for (i = 0; i < dom->nopts; i++) { if (!CHARcmp(name, toCHAR(dom->desc[i].longname)) || !CHARcmp(name, toCHAR(dom->desc[i].shortname))) { goto BreakBreak; } } } BreakBreak: /* if not found, complain */ if (!dom) { msg(MSG_ERROR, "[S]bad option name $1", name); ret = False; continue; } /* if non-boolean & we got no value, then assume '?' */ if (dom->desc[i].isvalid && !value) { if (prefix == name) { optshow(tochar8(name)); } else { msg(MSG_ERROR, "[S]$1 is not a boolean option", name); ret = False; } continue; } /* if option is locked, then complain */ if (dom->val[i].flags & OPT_LOCK) { msg(MSG_ERROR, "[S]$1 is locked", name); name = scan; ret = False; continue; } /* if boolean & we got a value, then complain */ if (!dom->desc[i].isvalid && value) { msg(MSG_ERROR, "[S]$1 is a boolean option", name); name = scan; ret = False; continue; } /* if boolean, set it */ if (!dom->desc[i].isvalid) { /* set the value */ if (prefix == name) b = True; else if (prefix[0] == 'n' && prefix[1] == 'o') b = False; else /* "neg" */ b = (BOOLEAN)!dom->val[i].value.boolean; /* if there's a store function, then call it */ if (dom->desc[i].store) ret &= (BOOLEAN)((*dom->desc[i].store)(&dom->desc[i], &dom->val[i], toCHAR(b ? "true" : "false")) >= 0); else dom->val[i].value.boolean = b; } else /* non-boolean with a value */ { /* if the value is valid & different and we need to * call a store function, then call it. */ if ((*dom->desc[i].isvalid)(&dom->desc[i], &dom->val[i], value) == 1 && dom->desc[i].store) { ret &= (BOOLEAN)((*dom->desc[i].store)(&dom->desc[i], &dom->val[i], value) >= 0); } } /* set the "set" flag */ if (!bang) { dom->val[i].flags |= OPT_SET; } /* If the "redraw" flag is set, then force redrawing (or at * least regeneration) of all windows. */ if (dom->val[i].flags & OPT_REDRAW) { for (w = winofbuf(NULL, NULL); w; w = winofbuf(w, NULL)) { if (w->di->logic == DRAW_NORMAL) w->di->logic = DRAW_CHANGED; } } } /* show any options which we're supposed to show */ optoutput(bang, False, False, outbuf, outsize); return ret; } /* This function returns the full name of an option, given a possibly- * abbreviated string. If the string is not the name of an option, it * returns NULL. */ CHAR *optname(name) CHAR *name; { OPTDOMAIN *dom; int i; /* for each domain of options... */ for (dom = head; dom; dom = dom->next) { /* for each option in that domain... */ for (i = 0; i < dom->nopts; i++) { /* if this is the one we're looking for... */ if (!strcmp(dom->desc[i].longname, tochar8(name)) || !strcmp(dom->desc[i].shortname, tochar8(name))) { /* return the long name */ return toCHAR(dom->desc[i].longname); } } } return NULL; } /* This function saves the values of some options. It only does this for * options whose values have been changed, and which are in the "global", * "buf", "win", "syntax", or "lp" domains. */ void optsave(custom) BUFFER custom; /* where to stuff the "set" commands */ { MARKBUF m; OPTDOMAIN *dom; int i, j; CHAR *str; char *tmp; /* for each domain of options... */ for (dom = head; dom; dom = dom->next) { /* ignore if not "global", "buf", "win", "lp", or "syntax" */ if (strcmp(dom->name, "global") && strcmp(dom->name, "buf") && strcmp(dom->name, "win") && strcmp(dom->name, "lp") && strcmp(dom->name, "syntax")) { continue; } /* for each option in that domain... */ for (i = 0; i < dom->nopts; i++) { /* if its value has been set... */ if ((dom->val[i].flags & (OPT_SET|OPT_LOCK|OPT_UNSAFE)) == OPT_SET) { /* then add it to the custom buffer */ if (dom->desc[i].asstring) { str = (*dom->desc[i].asstring)(&dom->desc[i], &dom->val[i]); tmp = safealloc(7 + strlen(dom->desc[i].longname) + 2 * CHARlen(str), sizeof(char)); strcpy(tmp, "set "); strcat(tmp, dom->desc[i].longname); strcat(tmp, "="); for (j = strlen(tmp); *str; ) { if (*str == ' ' || *str == '\t' || *str == '|' || *str == '\\') { tmp[j++] = '\\'; } tmp[j++] = (char)*str++; } tmp[j++] = '\n'; tmp[j] = '\0'; } else { tmp = safealloc(8 + strlen(dom->desc[i].longname), sizeof(char)); sprintf(tmp, "set %s%s\n", dom->val[i].value.boolean ? "" : "no", dom->desc[i].longname); } bufreplace(marktmp(m, custom, o_bufchars(custom)), &m, toCHAR(tmp), (long)strlen(tmp)); safefree((void *)tmp); } } } }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.