/*
 *				c s e t . c
 */

/*)LIBRARY
*/

#ifdef	DOCUMENTATION

title	cset	Make a character set from a string
index		Make a character set from a string
index		Make a temporary character set from a string
index		Define global parameters for csets

synopsis

	 #ifdef vms
	 #include "c:cset.h"
	 #else
	 #include <cset.h>
	 #endif

	 CSET *cset(s)
	 char s[];

	 CSET *cset_t(s)
	 char s[];

	 extern int csmask;

	 extern int cssize;

	 extern int csunique;

description

	Both cset() and cset_t() make a character set containing exactly the
	characters in the string passed, and return a pointer to it, or NULL
	if they couldn't allocate space.  The difference between the two is
	that cset_t() does not reserve the space it has used - the next cset
	created will re-use the space used for the set created at this call.
	Hence, cset_t() should be used to create temporary cset's that can be
	discarded automatically.

	WARNING:  Do NOT use cset_t() to construct an argument to a function,
	such as csjoin(), that can allocate a cset.  Should that function
	have to make an allocation, it will clobber the temporary cset that
	you passed as an argument, with unpredictabel results.  cset_t() is
	meant for use in functions like span() which only read csets.  Note,
	however, that the cscomp() never creates a new cset, so calls like
	span(cscomp(cset_t("aeiou")) are safe.

	The trailing null on s will not be a member of the cset created.

	Defined within this module are three global parameters that can be
	set to modify the actions of the cset functions.

	cssize defines the size of a cset, i.e., the maximum number of
	distinct characters that can be members.  (The members have
	numbers from 0 to cssize-1.)  By default, cssize is 256 to support
	full 8-bit ASCII.

	Do not change cssize once you have created a cset, or unpredictable
	results will occur.

	csmask is a mask applied to characters before they are used as indices
	in the data arrays.  It is defined because without such masking the
	sign extension of 8-bit ASCII characters when they are made into
	int's - when passed to functions, for example - would cause problems.
	By default, csmask is 0377, which keeps only the low byte.  You might
	want to change it, probably to ~0, if you set cssize greater than 256;
	in this case, the string-oriented routines - like cset() - may not
	work properly if strings with characters whose ASCII codes are greater
	than 127 are passed.

	Note:  csmask is only used where necessary to avoid sign-extension
	problems; it should not be viewed as a general-purpose "hook".
	Values of csmask other than 0377 or ~0 will yield unpredictable
	results.

	csunique, when TRUE, forces all cset operators, EXCEPT cscomp(), to
	return unique csets, rather than attempting to optimize (on both
	space and speed) by sharing existing data.  This is mainly useful
	if you intend to use csets as general sets (with cswith() and
	csless()).  By default, csunique is FALSE.

	WARNING:  cscomp() is NOT sensitive to the value of csunique.  You
	must use the real function, _cscomp(), which does test csunique.
	You might want to consider re-defining the macro form to call the
	function in this case.

	Note:  csunique has no effect on cset_t(), which always returns
	a "temporary" cset.

bugs

author

	Jerry Leichter

#endif

/*
)EDITLEVEL=18
 * Edit history
 * 0.0 12-Jul-82 JSL	Invention
 */

#ifdef vms
#include "c:cset.h"
#else
#include <cset.h>
#endif

#define NULL 0

int cssize	= 256;			/* Size of character set table	*/
int csmask	= 0377;			/* Character mask		*/
int csunique	= 0;			/* No forced unique copies	*/

extern char *malloc();

static char *_table;			/* Table to allocate from	*/
static char _mask = 0;			/* Next mask to use		*/
static CSET *_cs = NULL;		/* A CSET stored for use	*/

static CSET *
_cset(s,keep)
char *s;
int keep;
{	register int i;
	register CSET *cs;
	register char mask;

	if (_mask == 0)				/* Used up last table	*/
	{	_table = malloc(cssize);	/* Get a new one	*/
		if (_table == NULL)
			return(NULL);		/* No room, sorry	*/
		_mask = 1;			/* From the top...	*/
	}

	if ((cs = _cs) != NULL)			/* Use saved one?	*/
		_cs = NULL;			/* Free it up		*/
	else
	{	cs = (CSET *)malloc(sizeof(CSET));
						/* Must get a new one	*/
		if (cs == NULL)
			return(NULL);		/* No room, sorry	*/
	}

	cs->_fill_ = 0;
	cs->mask = mask = _mask;
	cs->table = _table;
	for (i = 0; i < cssize; )		/* Clear the table	*/
		cs->table[i++] &= ~mask;

	while (*s)
		cs->table[(unsigned)*s++] |= mask;
						/* Turn on our bits	*/
						/*  (must avoid sign	*/
						/*   extension)		*/
	if (keep)
		_mask <<= 1;			/* Next bit		*/
	else
		_cs = cs;			/* Save for later	*/
						/*  (reuse mask bit)	*/
	return(cs);
}

CSET *
cset(s)
char s[];
{	return(_cset(s,1));	}

CSET *
cset_t(s)
char s[];
{	return(_cset(s,0));	}
