/*
 *			R S E N T
 *
 * Generates random text messages.  From James Gimpel,
 * "Algorithms in SNOBOL4".  Calling sequence:
 *
 *	rsent(text, rules, outfun, fildes)
 *	char		*text;		-- generate from this text
 *	RS_RULE		*rules;		-- compiled rule structure
 *	int		(*outfun)();	-- output function
 *	FILE		*fildes;	-- file descriptor for outfun()
 *
 * The text line is copied to the output file, with commands expanded
 * using the rule vector.   The rules are built and tested by rsmake.c.
 *
 * outfun() is defined:
 *
 *	outfun(words, fildes)
 *	char		*words;		-- a string to output
 *	FILE		*fildes;	-- the file to output to
 *
 * Note:
 *	outfun(NULL, fildes)		-- flush partial by writing
 *					-- a newline to fildes.
 */

#include <stdio.h>
#define	EOS		'\0'
#define	EOL		'\n'
#define	FALSE		0
#define	TRUE		1

typedef struct rule {
	char	*r_name;		/* Rule name			*/
	int	r_weightsum;		/* Sum of all weights		*/
	char	**r_term;		/* Rule terms			*/
} RS_RULE;

rsent(text, rules, outfun, fildes)
char		*text;			/* generate from this text	*/
RS_RULE		*rules;			/* compiled rule structure	*/
int		(*outfun)();		/* output function		*/
FILE		*fildes;		/* file descriptor for outfun()	*/
/*
 * Generate text from the indicated pattern - recursive.
 */
{
	register char	*start;		/* Start of current stuff	*/
	register char	*end;		/* End of current stuff		*/
	register char	*tp;		/* Random text pointer		*/
	RS_RULE		*rp;		/* Rule pointer			*/
	short		weight;		/* Weight for randomness	*/
	char		**termp;	/* Rule term pointer		*/
	char		*workp;		/* Temp buffer pointer		*/
	extern char	*malloc();	/* Buffer allocator		*/
#ifndef	nomacarg
/*
 * Decus does this with a subroutine.
 */
#define irand(max)	(((((short) rand()) & 32767)*max)/32768)
#endif

	for (start = text; *start != EOS;) {
	    /*
	     * Look for <rule> in input string.
	     */
	    end = (start == text) ? start : start + 1;
	    while (*end != EOS && *end != '<')
		end++;
	    /*
	     * Output start .. end-1 (even if null string)
	     */
	    if (end > start && (workp = malloc((end - start) + 1)) != NULL) {
	        for (tp = workp; start < end;) {
		    *tp++ = *start++;
		}
		*tp = EOS;
		(*outfun)(workp, fildes);
		free(workp);
	    }
	    if (*end++ == EOS)
		break;				/* All done		*/
	    /*
	     * (else scan stopped at '<'.  Output the <rule>
	     */
	    for ((rp = rules); (tp = rp->r_name) != NULL; rp++) {
		/*
		 * Look for the <rule> in the rule name table
		 */
		for (start = end; *tp != EOS && *start == *tp;) {
		    start++;
		    tp++;
		}
		if (*tp == EOS && *start == '>') {	/* Found a rule	*/
		    weight = irand(rp->r_weightsum);
		    for (termp = rp->r_term; *termp != NULL; *termp++) {
			if ((weight -= (termp[0][0] & 0377)) < 0)
			    break;
		    }
		    if (termp == NULL) {
			(*outfun)("\n(bug, weight messup in \"", fildes);
			(*outfun)(rp->r_name, fildes);
			(*outfun)("\".)\n", fildes);
		    }
		    else
			rsent(&termp[0][1], rules, outfun, fildes);
		    break;		/* Exit rule search 	*/
		}
	    }
	    if (tp == NULL) {
		/*
		 * No match for this rule.  It was a real '<'.
		 */
		(*outfun)("<", fildes);
		start = end;			/* start at "foo>"	*/
	    }
	    else
		start++;			/* start after '>'	*/
	}
}
