V8/usr/src/cmd/monk/cmd/monk/envir.c

Compare this file to the similar file:
Show the results in this format:

#include	<stdio.h>
#include	<ctype.h>
#include	"search.h"
#include	"warn.h"
#include	"rd.h"

struct environment *
begin_envir(oldenv, hashkey, close_delim, textwrite)
struct environment	*oldenv;
char	*hashkey;
int	close_delim;
short	textwrite;
{
	struct definition	*def;
	struct environment	*env;

	def = get_envir(hashkey, close_delim, textwrite);
	if (def == (struct definition *) 0)
		return(oldenv);
	env = do_envir(oldenv, def, TOP_LEVEL, close_delim, textwrite);
	return(env);
}

struct definition *
get_envir(hashkey, close_delim, textwrite)
char	*hashkey;
int	close_delim;
short	textwrite;
{
	ENTRY	*entry;
	char	c;

	/* hashentry contains appropriate definition list */
	if ((entry = hashfind(DEF_TABLE, hashkey)) == (ENTRY *) NULL) {
		if (textwrite == USER_TEXT) {
/* ZZ shouldn't all colon environments be called with textwrite == MONK_DB? */
			if (*hashkey != COLON)
				warn_user(PR_FILENAME | PR_LINENUMBER,
			"%s is not defined: use ``\\|'' if not a monk command\n",
								hashkey);
			textputc(BEGIN_PRIMITIVE, USER_TEXT);
			textputs(hashkey, USER_TEXT);
			if ((c = open_match(close_delim)) != (char) FALSE)
				textputc(c, USER_TEXT);
		}
		return((struct definition *) 0);
	}
	return((struct definition *) entry->data);
}

struct environment *
do_envir(oldenv, def, level, close_delim, textwrite)
struct environment	*oldenv;
struct definition	*def;
int	level, close_delim;
short	textwrite;
{
	struct environment	*newenv;

	newenv = mk_envir(oldenv, def, level, close_delim);
	dodef(newenv, def->begin_def, textwrite);
	return(newenv);
}

struct environment *
mk_envir(oldenv, def, level, close_delim)
struct environment	*oldenv;
struct definition	*def;
int	level, close_delim;
{
	struct environment	*newenv;
	struct file_info	*fi;

	newenv = (struct environment *) mymalloc(sizeof(struct environment));
	newenv->def = def;
	newenv->state_list = (struct state *) 0;
	newenv->envname = def->name;
	if (level == TOP_LEVEL) {
		newenv->lastinstance = def->instance;
		def->instance = newenv;
	} else
		newenv->lastinstance = (struct environment *) SUB_LEVEL;
	newenv->previous = oldenv;
	fi = save_file_info();
	newenv->filename = fi->file_name;
	newenv->linenumber = fi->line_number;
	newenv->how_to_end = close_delim;
	return(newenv);
}

/* assoc_envir:
	execute any subenvironments called within the top-level environment
 */

struct environment *
assoc_envir(userfile, env, def, close_delim, endwhen, textwrite)
FILE	*userfile;
struct environment	*env;
struct definition	*def;
int	close_delim, endwhen;
short	textwrite;
{
	struct definition	*subd;
	struct value	*v;
	char	c;

	if ((subd = def->sub_def) == (struct definition *) 0)
		return(env);
	while ((v = read_values(userfile, TRUE)) != (struct value *) 0) {
		env = do_subenv(env, def, v, close_delim, endwhen, textwrite);
		if ((c = read_nonblank(userfile)) == close_delim) {
			myungetc(c, userfile);
			break;
		}
	}
	return(env);
}

struct environment *
do_subenv(env, def, v, close_delim, endwhen, textwrite)
struct environment	*env;
struct definition	*def;
struct value	*v;
int	close_delim, endwhen;
short	textwrite;
{
	struct definition	*d, *subd;

	if ((d = match_assoc(def->sub_def, v)) != (struct definition *) 0) {
		/* if there is a match (and there are no
			expanders) then can pass as definition values */
		d->values = v;
		env = do_envir(env, d, SUB_LEVEL, close_delim, textwrite);
		if (endwhen == END_NOW)
			env = end_envir(env, SUB_LEVEL);
	} else {
		warn_db(PR_FILENAME | PR_LINENUMBER,
				"The %s command does not provide ``%s''\n",
				def->name, str_avalue(v, FALSE));
		textputs(str_avalue(v, FALSE), USER_TEXT);
	}
	return(env);
}

struct definition *
match_assoc(d, values)
struct definition	*d;
struct value	*values;
{
	struct value	*matchthese;
	short	nvalues;

	for (nvalues = 0, matchthese = values; values != (struct value *) 0;
							values = values->next)
		++nvalues;
	if (d != (struct definition *) 0)
		do {
			if (match_arg(matchthese, d->values, nvalues) == TRUE)
				return(d);
		} while ((d = d->sub_def) != (struct definition *) 0);
	return((struct definition *) 0);
}

/* looking for good ideas for trying to recover from author errors.
	this attempts to handle without adding problems the case where
	one environment is not ended, or two adjacent ones are switched.
	On a mismatch, it will pop two environments if the previous one
	matches the key given.
	which cases will be aggravated? */

env_health(env, key)
struct environment	*env;
char	*key;
{
}

/* newstates loops through def_elements:
		writing troff strings to output file
		adding absolute value troff strings to state stack
	 */

void
dodef(newenv, define, textwrite)
struct environment	*newenv;
register struct def_element	*define;
short	textwrite;
{
	register struct statestack	*statestack;
	ENTRY	*grabit;
	struct state	*newstate, *tempstate;
	struct value	*definevalues;
	register struct def_element	*el;
	struct cond_def_el	*cdl;
	char	*modtext, *p;

	if (define == (struct def_element *) 0)
		return;
	newstate = (struct state *) 0;
	definevalues = newenv->def->values;
	textwrite = (textwrite == USER_TEXT) ? MONK_DB : textwrite;
	do {
		while (define->cdl != (struct cond_def_el *) 0) {
			cdl = define->cdl;
			if (iscondtrue(cdl->attribute,cdl->value,cdl->type))
				break;
			else
				define = cdl->next_on_fail;
		}
#ifdef MONK_SAVE
zzz if monksave,remember
save = name (then if save != 0, call read utext til close_delim)
close_delim, end_envir
#endif
		if (define->troff == (char *) 0) {
			/* should not spew error messages if internal def */
			use_define(newenv, define, textwrite);
			continue;
		}
		if (textwrite != NO_WRITE) {
			if (definevalues == (struct value *) 0
						&& define->special == FALSE)
				textputs(define->troff, textwrite);
			else {
				modtext = replace_args(define->troff,
					define->special, definevalues, newenv);
				textputs(modtext, textwrite);
				if (modtext != define->troff)
					myfree(modtext, 44);
			}
		}
		if ((grabit = hashfind(STACK_TABLE, define->attribute))
							== (ENTRY *) NULL)
			continue;
		/* next is next to undo - need to undo the first states last */
		if (newstate == (struct state *) 0) {
			newstate = (struct state *)
						mymalloc(sizeof(struct state));
			newstate->next = (struct state *) 0;
		} else {
			tempstate = (struct state *)
						mymalloc(sizeof(struct state));
			tempstate->next = newstate;
			newstate = tempstate;
		}
		newstate->attribute = define->attribute;
	/* attribute values are not replaced with define_level values */
		newstate->value = define->value;
		newstate->allocated = FALSE;
		abstroff(newstate);
	/* previous == previous instance of this stacking attribute */
		statestack = (struct statestack *) grabit->data;
		if (statestack->first = (struct state *) 0) {
			newstate->previous = (struct state *) 0;
			statestack->first = newstate;
		} else
			newstate->previous = statestack->last;
		statestack->last = newstate;
	} while ((define = define->next) != (struct def_element *) 0);
	newenv->state_list = newstate;
}


use_define(env, define, textwrite)
struct environment	*env;
register struct def_element	*define;
short	textwrite;
{
	struct environment	*temp_env, *old_env;

	temp_env = begin_envir(env, define->attribute, (char) 0, textwrite);
	if (temp_env != env) {
		old_env=end_envir(temp_env, TOP_LEVEL);
		if (env != old_env)
			warn_me(PR_FILENAME | PR_LINENUMBER,
				"PANIC: temp_env changed ongoing env stack\n");
	}
}

void
abstroff(state)
register struct state	*state;
{
	struct value	*oldv, *v, *newv;
	int	arg, n;
	char	*p;
	char	temp[SAFESIZ];

	if ((v = state->value) == (struct value *) 0)
		return;
	do {
		if (mindex(v->value, "+-") == 0)
			continue;
		state->allocated = TRUE;
	} while ((v = v->next) != (struct value *) 0);
	if (state->allocated == FALSE)
		return;
	oldv = state->value;
	do {
		newv = (struct value *) mymalloc(sizeof(struct value));
		if (state->value == oldv)	
			state->value = newv;
		else
			v->next = newv;
		v = newv;
		/* now could (*oldv->value == '+' || *old->value == '-') */
		if ((n = mindex(oldv->value, "+-")) != 0) {
			arg = atoi(oldv->value);
			if (state->previous != (struct state *) 0) 	
				arg += atoi(state->previous->value->value);
			p = temp;
			sprintf(p, "%d", arg);
		} else
			p = oldv->value;	
		/* always allocate the string; faciliates later free */
		v->value = mymalloc(strlen(p)+1);
		strcpy(v->value, p);
	} while ((oldv = oldv->next) != (struct value *) 0);
	v->next = (struct value *) 0;
}

/* end_envir:
	restore previous environment
	generate end_def troff
 */

struct environment *
end_envir(env, level)
struct environment	*env;
int	level;
{
	struct environment	*newenv;

	if (level == TOP_LEVEL)
		/* pop top_environment and any associated sub_envir*/
		do {
			newenv = do_end_envir(env);
			if (newenv == env)
				break;
			env = newenv;
		} while (env->lastinstance == (struct environment *) SUB_LEVEL);
	else
		env = do_end_envir(env);
	return(env);
}

struct environment *
do_end_envir(env)
struct environment	*env;
{
	ENTRY	*grabit;
	register struct state	*s;
	struct state	*ps;
	struct statestack	*states;
	register struct definition	*d;
	register struct def_element	*define;
	char	*troff_string;

	for (s = env->state_list; s != (struct state *) 0; s = s->next) {
		/* skip if there is another instance of this attribute in the
			list to restore: avoids mulitple restores of font */
		if (isanother(s) == TRUE)
			continue;
		/* restore Previous state */
		grabit = hashfind(STACK_TABLE, s->attribute);
		if (grabit == (ENTRY *) NULL) {
			warn_me(PR_FILENAME | PR_LINENUMBER,
				"End_envir: %s not in stack!\n", s->attribute );
			continue;
		}
		states = (struct statestack *) grabit->data;
		if ((ps = s->previous) == (struct state *) 0) {
			/* no previous to restore */
			states->first = states->last = (struct state *) 0;
			continue;
		}
		states->last = ps;
		troff_string = trans_state(ps);
		textputs(troff_string, MONK_DB);
	}
	d = (struct definition *) env->def;
	define = d->end_def;
	if (define != (struct def_element *) 0)
		do {
			if (define->troff == (char *) 0)
				use_define(env, define, MONK_DB);
			else
				if (d->values == (struct value *) 0
						&& define->special == FALSE)
					textputs(define->troff, MONK_DB);
				else {
					troff_string =
						replace_args(define->troff,
						define->special, d->values,env);
					textputs(troff_string, MONK_DB);
					if (troff_string != define->troff)
						myfree(troff_string, 44);
				}
		} while ((define = define->next) != (struct def_element *) 0);
	/* previous NULL if end invoked before any other command, incl make */
	if (env->previous == (struct environment *) 0)
		return(env);
	if (env->lastinstance != (struct environment *) SUB_LEVEL)
		env->def->instance = env->lastinstance;
	free_env(env);
	return(env->previous);
}

isanother(s)
struct state	*s;
{
	struct state	*s2;

	for (s2 = s->next; s2 != (struct state *) 0; s2 = s2->next)
		if (strcmp(s->attribute, s2->attribute) == 0)
			return(TRUE);
	return(FALSE);
}

/* now use this one stack for both environment structures and state change info */

struct environment *
init_envir()
{
	struct environment	*env;

	/* run constructed initialize */
	checkifmissing();
	init_statestack();
	env = begin_envir((struct environment *)0,INIT_STACK,(char) 0,NO_WRITE);
	env = begin_envir(env, INIT_TROFF, (char) 0, MONK_DB);
	env = begin_envir(env, MONK_TEXT, (char) 0, MONK_DB);
	return(env);
}

end_allenvir(env)
struct environment	*env;
{
	if (env == (struct environment *) 0)
		return;
	for(;;) {
		if (*env->def->name != COLON)
			warn_user(0, "No end for %s begun at line %d\n",
					env->def->name, env->linenumber);
		if (env->previous == (struct environment *) 0) {
			end_envir(env, TOP_LEVEL);
			return;
		}
		env = end_envir(env, TOP_LEVEL);
	}
}

/* build hash table of stacking variables from attribute hash table */
init_statestack()
{
	struct table_info	*gethashtbl(), *t;
	ENTRY	*p, *pe;
	struct attribute_info	*ai;

	t = gethashtbl(ATT_TABLE);
	for (p = t->start, pe = p + t->length; p < pe; ++p)
		if (p->key != NULL) {
			ai = (struct attribute_info *) p->data;
			if (ai->stacking == TRUE)
				add_state(p->key);
		}
}

add_state(attribute)
char	*attribute;
{
	ENTRY	*stack_att;
	struct statestack	*s;

	if (hashfind(STACK_TABLE, attribute) == (ENTRY *) NULL) {
		stack_att = (ENTRY *) mymalloc(sizeof(ENTRY));
		stack_att->key = attribute;
		stack_att->data = mymalloc(sizeof(struct statestack));
		s = (struct statestack *) stack_att->data;
		s->first = (struct state *) 0;
		s->last = (struct state *) 0;
		hashenter(STACK_TABLE, stack_att);
	}
}