ftp.nice.ch/pub/next/unix/editor/elvis-2.0.N.bs.tar.gz#/elvis-2.0.N.bs/options.c

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.