#ifdef COMMENTS

>From upjomon!hamilton Sun Nov 22 02:21 JST 1987 remote from upshowa
	Here is something you might like.  I got playing around
with Steve Falco's fib program, and decided that I wanted to try
the other examples in Peter Langston's article.  What I've got
is a program called bigfib (sorry, I couldn't resist), which takes
a rules file that contains the grammar.  It then reads stdin and
writes stdout, just like fib.  Therefore, the rules file:

a b
b (a)[b]

would generate the FIB grammar in the paper.

To make doing successive generations a little easier, the program
takes an option -g numb, where numb is the number of generations
to run the grammar for.  The way this does this is to fork and
exec enough copies of bigfib to make a pipeline of the desired
size, so obviously it won't work with really big examples, but
the 20 or so that you can get seems to be plenty big enough in
most cases.

The program is not heavily tested, and in particular, it will
take nearly any garbage for a rules file (the space between left
hand and right hand sides is not strictly needed).  This can be
interesting, however.  Try /etc/passwd.  It will use the first
left hand side as the "axiom" and print it out when the input is
empty, so it will work like fib.  In fact, it should provide
equivalent (modulo placing of newlines) output to fib when the
same rules are used.

#endif

/*
 *
 * bigfib takes the ideas of Peter Langston and Steve Falco to
 * make a general program for generating strings of an 0L grammar.
 *
 * bigfib [ -g numb ] rulefile
 *
 * bigfib takes stdin, rewrites it with the rules in rulefile and
 * prints the output on stdout.  If argument -g is supplied with a
 * numeric value, bigfib will repeat the process for the number of
 * generations requested.  This is done by forking copies of bigfib,
 * so the number of generations is limited by the number of processes
 * that can be spawned by a user.
 *
 * If input is empty, the first rule's left hand side is generated.
 *
 * The rules in rulefile are in the following form, one rule to
 * a line:
 *
 * c string
 *
 * where c is a single 8-bit character and string is a character string.
 * c and string are separated by one or more spaces or tabs.  String is
 * taken to begin with the first non space character and end before the
 * '\n'.  c may be any 8-bit value except space, \0, and \n, and string
 * may contain any value except \n and \0.
 *
 * Dick Hamilton  attunix!upshowa!hamilton
 */
#include <stdio.h>
#define	MAXRULE	64		/* maximum size of a rule	*/
unsigned char	*Rules[256];	/* one for each possible 8 bit value	*/
unsigned char	Axiom;
int	Debug;
extern char	*optarg;
extern int	optind;

main(argc,argv)
int	argc;
char	*argv[];
{
	int	c;
	int	count = 0;
	int	lines = 0;
	int	gens=1;	/* generations of the grammar	*/
	FILE	*rfp;	/* rules file file pointer	*/

	while ((c = getopt(argc, argv, "g:d")) != EOF)
		switch (c) {
		case 'g':
			if ((gens = atoi(optarg)) < 1) {
				fprintf(stderr,"%s: bad arg to -g '%s'\n",
					argv[0], optarg);
				exit(2);
			}
			break;
		case 'd':
			Debug++;
			break;
		case '?':
			fprintf(stderr, "Usage: %s [ -g numb ] rulefile\n",
				argv[0]);
			exit(2);
		}

	if (optind >= argc || argc < 1) {
		fprintf(stderr, "Usage: %s [ -g numb ] rulefile\n",
			argv[0]);
		exit(2);
	}

	if ((rfp = fopen(argv[optind], "r")) == NULL) {
		fprintf(stderr, "%s: can't open rulefile '%s'\n",
			argv[0], argv[optind]);
		exit(2);
	}

	if (readrules(rfp))
		exit(2);

	if (gens > 1 && forkme(gens))
		exit(2);

	/* now it's time to go to work	*/
        while((c = getchar()) != EOF) {
		if (Debug > 1)
			fprintf(stderr,"%d: %c\n",getpid(),c);

		if (Rules[c] == NULL) {
			printf("%c",c);
			count++;
		}
		else {
			count += strlen(Rules[c]);
			printf("%s",Rules[c]);
		}
		/* an attempt to keep lines to a reasonable size */
		if ((count / 10) > lines) {
			lines = count/10;
			printf("\n");
		}
	}
	if (!count)
		printf("%c",Axiom);

	printf("\n");
	exit(0);
}
/*
 * readrules takes a file pointer to a rules file and
 * reads the rules into an array accessed by a one byte
 * key.
 *
 */
readrules(rfp)
FILE	*rfp;
{
	unsigned char	*rule;
	char		input[MAXRULE];
	char		*malloc();
	unsigned char	key;
	int		first=1;
	int		badinput = 0;

	/* fake things out so that \n is ignored */
	Rules['\n'] = (unsigned char *) malloc(2);
	Rules['\n'][0] = '\0';

	while (fgets(input, MAXRULE, rfp) != NULL) {
		if (!(rule = (unsigned char *) malloc(strlen(input+1)))) {
			fprintf(stderr,"malloc failed\n");
			return(-1);
		}
		if (sscanf(input,"%1s %s\n",&key,rule) != 2) {
			fprintf(stderr,"Badly formed rule ignored: %s", input);
			continue;
		}
		Rules[key] = rule;
		if (Debug)
			fprintf(stderr,"Rule: %c %s\n",key,Rules[key]);
		if (first) {
			Axiom = key;
			first = 0;
		}
	}
	if (first) { /* no good rules	*/
		fprintf(stderr,"No good rules found\n");
		return(-1);
	}
	return(0);
}
/*
 * forkme forks itself and sets up the piping for gens repetitions
 * of bigfib.  Each parent returns, and each child until the last
 * forks a new child.
 */
forkme(gens)
int	gens;
{
	int	fd[2];

	while (gens > 1) {
		if (pipe(fd)) {
			fprintf(stderr,"pipe call failed\n");
			return(-1);
		}
		switch (fork()) {
		case 0:		/* child */
			close(1);
		 	dup(fd[1]);
			close(fd[0]);
			close(fd[1]);
			break;	/* go back for more	*/
		default:	/* parent	*/
			close(0);
			dup(fd[0]);
			close(fd[0]);
			close(fd[1]);
			return(0);
		case -1:	/* error	*/
			fprintf(stderr,"fork call failed\n");
			return(-1);
		}
		gens--;
	}
	return(0);
}


