[net.sources] variable handling functions

perlman (06/10/82)

.TH VARPAK 3CSL "January 20, 1982"
.SH NAME
varpak \- string variable handling routines
.SH SYNOPSIS
.nf
#include <varpak.h>
extern char varchar, escchar;
extern VAR *stdvar;

vsetvar (variables, name, value) VAR *variables; char *name, *value;
setvar (name, value) char *name, *value;

vlsetvar (variables, line) VAR *variables; char *line;
lsetvar (line) char *line;

vreadvar (variables, filename) VAR *variables; char *filename;
readvar (file) char *file;

vusetvar (variables) VAR *variables;
usetvar ();

char *vgetvar (variables, name) VAR *variables; char *name;
char *getvar (name) char *name;

vtruevar (variables, name) VAR *variables; char *name;
truevar (name) char *name;

char *vsubvar (variables, string) VAR *variables; char *string;
char *subvar (string) char *string;

vtravar (variables, func) VAR *variables; extern func ();
travar (func) extern func ();
func (variable) VAR *variable;

vwritevar (variables, file) VAR *variables; char *file;
writevar (file) char *file;
.fi
.br
.SH DESCRIPTION
These functions provide a general purpose set of tools for
developing a user interface in which options are set with variables.
All the functions use the VAR datatype defined in <varpak.h>
and set and retrieve the values of variables set by name.
A variable name is any uninterrupted string of alphanumeric
characters, including the underscore.
A variable's value can be any string of characters
including variables names preceded by the variable accessing character,
the above mentioned "varchar," whose default value is the dollar sign, '$'.
If the variable accessing character is to be part of a variable's value,
it should be preceded by the variable escape character,
the above mentioned "escchar," whose default value is the backslash, '\\'.
The escape character is also useful to include leading blank space in a
variable's value.
Usually, it is necessary to add an escape character before the
escape character, a bad feature of such characters.

For each function,
there is a simpler version defined as a macro in <varpak.h>.
Using the macros saves the programmer from having to
learn about and use the VAR datatype.
These simpler macros assume the standard VAR, \fIstdvar\fR,
which is hidden from the programmer, is to be used.

\fIVsetvar\fR sets the variable \fIname\fR to \fIvalue\fR.
If \fIvalue\fR contains any variables,
identified by preceding their names with the variable
accessing character, which by default is the dollar sign, '$',
then \fIvgetvar\fR substitutes their values.
If these variables have no values,
a null string is substituted in their place.
\fIVsetvar\fR is used by all the functions setting variables
and so determines how all the other functions work.
If the name of the variable being set is "readvar"
then \fIvsetvar\fR calls \fIvreadvar\fR to read from the file
named by the value argument to \fIvsetvar\fR.
If the value is NULL,
then the file read from is the standard input,
and the user is allowed to set variables interactively.
If the name of the variable being set is "writevar"
then \fIvsetvar\fR calls \fIvwritevar\fR to write
all the variable so far set to the file named by
the value argument to \fIvsetvar\fR.
If the value is NULL, then the file written to
is the standard output.

\fIVlsetvar\fI sets variables with a single argument,
a line of the form:
.ce
NAME = VALUE
which gets interpreted and passed off to \fIvsetvar\fR
as:
.ce
vsetvar (variables, "NAME", "VALUE");
Leading blank space around an optional variable accessing character is ignored,
as is any blank space around the optional equal sign.

\fIVreadvar\fR reads variables from a file.
The lines of the file are read by \fIvlsetvar\fR,
which, in turn, are interpreted by \fIvsetvar\fR.

\fIVusetvar\fR interacts with users at their terminal,
which is assumed to have the right tty status
(ECHO + COOKED modes, see \fIstty(2)\fR).
Users are prompted for names and values
and are given the option to see the values of variables
so far set, and abort if that is desired.
While setting variables interactively,
users can read from files of variables by setting
the pseudo variable called "readvar"
or write to a file by setting the pseudo variable called writevar.

\fIVgetvar\fR is the basic function for getting the values of
variables.  It returns a char pointer to the value of its
named variable argument if it exists,
otherwise, it returns a NULL pointer.

\fIVtruevar\fR is useful for determining whether a Boolean variable
is true or false.  If the variable named is unset, \fIvtruevar\fR
returns 0 (false), and if it is set, but has no value,
then it returns 1 (true).
For all the follwoing values, \fIvtruevar\fR returns 1,
and except for previous notes, 0.
.ce
true yes t y 1

\fIVwritevar\fR writes all the set variables' names and values
to the named file, and to the standard output if the named file is NULL.
It can be called from within a variable setting file, read by \fIvreadvar\fR,
or from within the interactive variable setting function, \fIvusetvar\fR,
by setting the pseudo variable, "writevar."
\fIVwritevar\fR uses the function \fIvtravar\fR to traverse the
variables of its VAR pointer argument.
\fIVtravar\fR can be used to apply some function to each
entry in a set of variables.

\fIVsubvar\fR does variable substitution, sometimes called interpolation.
\fIVsubvar\fR copies its string argument verbatim except where
it sees the variable accessing character, the so-called "varchar"
(by default, the dollar sign, '$'),
mentioned above, in which case it looks after the $ and
substitutes the value of the variable of the name it finds.
This substitution can be avoided by preceding the $
by the variable escape character, the so-called "varchar
(default value is the backslash, '\\').
.SH EXAMPLE
.PP
The following main program will read in variables from its first argument,
then let the user print and set them interactively,
and finally print them out to the second argument.
.nf
.in 1.5i
#include <varpak.h>
main (argc, argv) char **argv;
	{
	readvar (argv[1]);
	usetvar ();
	writevar (argv[2]);
	}
.in 0
.fi
.SH DIAGNOSTICS
.PP
The functions tend to do their work silently,
returning zero or NULL when nothing can be found.
.SH SEE\ ALSO
.PP
The Interface Arsenal:  Software Tools for User Interface Development

Varpak Reference Guide.
.SH AUTHOR
Gary Perlman
=M=A=K=E=F=I=L=E=====================================================
varpak: varpak.o
	cc -c varpak.o
	echo "nothin' to it"
=V=A=R=P=A=K=.=H=====================================================
#include <stdio.h>
#include <ctype.h>

extern	char	escchar;
extern	char	varchar;

typedef	struct varpair
	{
	char	*name, *value;
	struct	varpair *left, *right;
	} VAR;
extern	VAR *stdvar;

char	*vsubvar ();		/* variables, string */
int	vsetvar ();		/* variables, name, value */
int	vusetvar ();		/* variables */
int	vlsetvar ();		/* variables, line */
VAR	*newvar ();		/* name, value */
int	vreadvar ();		/* variables, file */
int	vtruevar ();		/* variables, name */
char	*vgetvar ();		/* variables, name */
int	vwritevar ();		/* variables, file */
extern	vfreevar ();		/* variables */
extern	vtravar ();		/* func (VAR *) */

#define subvar(s)	vsubvar(stdvar,s)
#define setvar(n,v)	vsetvar(stdvar,n,v)
#define	freevar()	vfreevar(stdvar);
#define usetvar()	vusetvar(stdvar)
#define lsetvar(line)	vlsetvar(stdvar,line)
#define readvar(f)	vreadvar(stdvar,f)
#define truevar(n)	vtruevar(stdvar,n)
#define getvar(n)	vgetvar(stdvar,n)
#define writevar(f)	vwritevar(stdvar,f)
#define travar(f)	vtravar(stdvar,f)
=V=A=R=P=A=K=.=C===================================================
#include <stdio.h>
#include <ctype.h>

char	escchar = '\\';
char	varchar = '$';

typedef	struct varpair
	{
	char	*name, *value;
	struct	varpair *left, *right;
	} VAR;
VAR	initstdvar, *stdvar = &initstdvar;

char	*vsubvar ();		/* variables, string */
int	vsetvar ();		/* variables, name, value */
int	vusetvar ();		/* variables */
int	vlsetvar ();		/* variables, line */
VAR	*newvar ();		/* name, value */
int	vreadvar ();		/* variables, file */
int	vtruevar ();		/* variables, name */
char	*vgetvar ();		/* variables, name */
extern	vwritevar ();		/* variables */
char	*fgetline ();		/* s, ioptr */
extern	vfreevar ();		/* variables */

#define	skipspace(s)	while(isspace(*s))s++;
#define	isvarname(c)	(isalnum(c)||c=='_')
#define copy(s) (strcpy(malloc((unsigned)strlen(s)+1),s))
char *strcpy (), *malloc ();

VAR *
newvar (name, value)
char *name, *value;
	{
	VAR	*newvar = (VAR *) calloc (1, sizeof (VAR));
	if (newvar)
		{
		if (name)  newvar->name  = copy (name);
		if (value) newvar->value = copy (value);
		}
	return (newvar);
	}

vfreevar (variables)
VAR *variables;
	{
	if (variables == NULL) return;
	if (variables->name) free (variables->name);
	if (variables->value) free (variables->value);
	vfreevar (variables->left);
	vfreevar (variables->right);
	free (variables);
	}

vsetvar (variables, name, value)
VAR *variables; char *name, *value;
	{
	int	cmp;
	if (variables == NULL || name == NULL || *name == NULL) return (0);
	value = vsubvar (variables, value);
	if (vtruevar (variables, "DEBUG"))
		printf ("vsetvar: '%s'='%s'\n", name, value);
	if (!strcmp (name, "readvar"))
		return (vreadvar (variables, value));
	if (!strcmp (name, "writevar"))
		return (vwritevar (variables, value));
	if (variables->name == NULL)
		{
		variables->name  = copy (name);
		variables->value = copy (value);
		return (variables->name && variables->value);
		}
	while (variables)
	    if ((cmp = strcmp (name, variables->name)) > 0)
		if (variables->right == NULL)
		    return ((variables->right = newvar (name, value)) != NULL);
		else variables = variables->right;
	    else if (cmp < 0)
		if (variables->left == NULL)
		    return ((variables->left = newvar (name, value)) != NULL);
		else variables = variables->left;
	    else /* match */
		{
		free (variables->value);
		return ((variables->value = copy (value)) != NULL);
		}
	}

vlsetvar (variables, lineptr)
VAR *variables; char *lineptr;
	{
	char	namebuf[BUFSIZ], *nameptr;
	char	*ptr;
	if (!variables || !lineptr) return (0);
	if (vtruevar (variables, "DEBUG")) printf ("vlsetvar: '%s'\n", lineptr);
	skipspace (lineptr);
	if (*lineptr == varchar)
		{
		lineptr++;
		skipspace (lineptr);
		}
	if (!*lineptr) return (1);
	nameptr = namebuf; *nameptr = NULL;
	while (isvarname (*lineptr)) *nameptr++ = *lineptr++;
	*nameptr = NULL;
	skipspace (lineptr);
	if (*lineptr == '=') lineptr++;
	skipspace (lineptr);
	ptr = lineptr;
	while (*ptr) ptr++;
	if (ptr > lineptr && (*(ptr-1) == '\n')) *(ptr-1) = NULL;
	return (vsetvar (variables, namebuf, lineptr));
	}

char *
fgetline (s, ioptr)
register char *s; FILE *ioptr;
	{
	char	*base = s;
	*s = NULL;
	while ((*s = fgetc (ioptr)) != EOF)
		if (*s == escchar) *s++ = fgetc (ioptr);
		else if (*s == '\n') break;
		else s++;
	if (*s == EOF && s == base) return (NULL);
	*s = NULL;
	return (base);
	}

vreadvar (variables, filename)
VAR *variables; char *filename;
	{
	char	line[BUFSIZ];
	FILE	*ioptr, *fopen ();
	if (variables == NULL) return (0);
	if (vtruevar (variables, "DEBUG"))
		printf ("vreadvar: '%s'\n", filename);
	if (filename == NULL || *filename == NULL)
		{
		while (vusetvar (variables));
		return (1);
		}
	if ((ioptr = fopen (filename, "r")) == NULL) return (0);
	while (fgetline (line, ioptr))
	    if (vlsetvar (variables, line) == 0)
		{
		fclose (ioptr);
		return (0);
		}
	fclose (ioptr);
	return (1);
	}

char *
vgetvar (variables, varname)
VAR *variables; char *varname;
	{
	int	cmp;
	while (variables)
		if ((cmp = strcmp (varname, variables->name)) > 0)
			variables = variables->right;
		else if (cmp < 0)
			variables = variables->left;
		else	return (variables->value);
	return (NULL);
	}

truth (s)
char *s;
	{
	if (!s) return (0);
	if (*s == NULL) return (1);
	if (strlen (s) == 1)
		switch (*s)
			{
			case '1':
			case 'y':
			case 't': return (1);
			}
	return (!strcmp (s, "true") || !strcmp (s, "yes"));
	}

vtruevar (variables, varname)
VAR *variables;  char *varname;
	{
	char	*val;
	if (!varname || !variables) return (0);
	if ((val = vgetvar (variables, varname)) == NULL) return (0);
	return (truth (val));
	}

char *
vsubvar (variables, s)
VAR *variables; char *s;
	{
	static	char vsubvar[BUFSIZ];
	char	*interptr = vsubvar;
	char	varname[BUFSIZ], *ptr;
	extern	char varchar, escchar;
	*vsubvar = NULL;
	if (!variables) return (s);
	while (*s)
		if (*s == varchar)
			{
			s++;
			ptr = varname;
			while (isvarname (*s))
				*ptr++ = *s++;
			*ptr = NULL;
			if (ptr = vgetvar (variables, varname))
			    {
			    strcpy (interptr, ptr);
			    while (*interptr) interptr++;
			    }
			}
		else if (*s == escchar)
			{
			s++;
			*interptr++ = *s++;
			}
		else	*interptr++ = *s++;
	*interptr = NULL;
	return (vsubvar);
	}

vtravar (variables, func)
VAR *variables;
int (*func)();
	{
	if (variables == NULL) return;
	vtravar (variables->left, func);
	(*func) (variables);
	vtravar (variables->right, func);
	}

FILE *pvarioptr;
vwritevar (variables, filename)
VAR *variables;
char *filename;
	{
	extern	vprintvar ();
	if (variables == NULL) return (0);
	if (filename == NULL || *filename == NULL) pvarioptr = stdout;
	else if ((pvarioptr = fopen (filename, "w")) == NULL) return (0);
	vtravar (variables, vprintvar);
	if (pvarioptr != stdout) fclose (pvarioptr);
	}
vprintvar (variables)
VAR *variables;
	{
	fprintf (pvarioptr, "%15s = ", variables->name);
	fprintf (pvarioptr, "%s\n",
		*variables->value ? variables->value : "(null)");
	}

vusetvar (variables)
VAR *variables;
	{
	char	name[BUFSIZ], value[BUFSIZ];
	char	*vptr;
	char	*noeffect = "(RETURN to abort)";
	if (variables == NULL) return (0);
	printf ("enter variable name (RETURN for list): ");
	gets (name);
	if (*name == NULL)
		{
		vwritevar (variables, NULL);
		printf ("enter variable name, %s: ", noeffect);
		gets (name);
		if (*name == NULL) return (0);
		}
	if (vptr = vgetvar (variables, name))
		{
		printf ("%c%s = %s\n", varchar, name, vptr);
		printf ("enter new value %s: ", noeffect);
		}
	else	printf ("enter value %s: ", noeffect);
	gets (value);
	if (*value == NULL) return (0);
	if (vsetvar (variables, name, value) == 0) return (0);
	printf ("%c%s = %s\n", varchar, name, vgetvar (variables, name));
	return (1);
	}