[net.lang.c] An example of creeping featurism

keith@reed.UUCP (Packard) (11/16/84)

I couldn't resist, a friend of mine needed a program to wait
for a specified process, berkeley kernels let you do this
by using kill, just kill a process with sig 0, it returns the
error status without actually doing anything.  In any case,
as a first cut the program looked like:

#include	<errno.h>

extern int	errno;

main (argc, argv) char **argv;
{
	register int	pid;

	pid = atoi (argv[1]);
	while (kill (pid, 0) != -1 || errno != ESRCH)
		sleep (1);
}

Just another example of a quick hack.  Well, it got a bit out of
hand, the final version ended up a bit different (oh, I write
compilers for a living by the way):


	keith packard
	...!tektronix!reed!keith
	or
	...!tektronix!tekmdp!keithp
	
/*
 *	await.c
 *
 *	wait for several processes to terminate
 *	accepts an expression using the following grammar:
 *
 * expr	:	expr -a expr	wait for both expressions to become true
 *	|	expr -o expr	wait for either expression to become true
 *	|	-n expr		wait for expr to become false (why not?)
 *	|	pid		wait for process pid to exit
 *	|	( expr )	used for grouping
 *	;
 *
 *	optional characters:
 *
 *	open close and {} are the same as ()
 *	not, ! or ~ are the same as -n
 *	and and && are the same as -a
 *	or and || are the same as -o
 *
 *	example:
 *	wait either for process 2 and process 3 to terminate or
 *	for process 4 to terminate:
 *
 *	await 2 and 3 or 4
 *
 *	is this silly or what?
 */

# include <errno.h>
# include <stdio.h>
# define	END	1
# define	OR	2
# define	AND	3
# define	NOT	4
# define	OP	5
# define	CP	6
# define	PID	7

extern errno;

struct expr {
	struct expr	*left;
	struct expr	*right;
	int		value;
	int		op;
};

struct expr	*parse(),*e(), *e_e(), *t(), *e_t(), *f(), *allocExpr();

struct expr	*expr;

main(argc,argv)
char **argv;
{
	expr = parse(argv+1);
	for (;;) {
		if (test(expr))
			exit (0);
		sleep (1);
	}
}

test (expr)
register struct expr	*expr;
{
	switch (expr->op) {
	case PID:
		if (expr->value == -1)
			return 1;
		if (kill (expr->value, 0) == -1) {
			if (errno != ESRCH) {
				char buf[14];
				perror (itoa (expr->value, buf));
				exit (1);
			}
			expr->value = -1;
			return 1;
		}
		return 0;
	case OR:
		if (test (expr->left))
			return 1;
		else
			return test (expr->right);
	case AND:
		if (!test (expr->left))
			return 0;
		else
			return test (expr->right);
	case NOT:
		return test (expr->left) == 0;
	}
}

int	token, lvalue;

/*
 *	grammar
 *
 *	p	:	e END
 *	e	:	t e_e
 *	e_e	:	OR e e_e
 *		|	nil
 *	t	:	f e_t
 *	e_t	:	AND e_t
 *		|	nil
 *	f	:	PID
 *		|	NOT f
 *		|	OP e CP
 *
 */

struct expr *
parse (a)
char **a;
{
	init (a);
	token = lex();
	return e();
}

struct expr *
e ()
{
	register struct expr	*et;
	switch (token) {
	case NOT:
	case OP:
	case PID:
		et = t();
		et = e_e(et);
		break;
	default:
		error();
	}
	return et;
}

struct expr *
e_e (left)
struct expr *left;
{
	register struct expr	*et;
	switch (token) {
	case OR:
		et = allocExpr ();
		et->left = left;
		et->op = OR;
		token = lex();
		et->right = e();
		et = e_e(et);
		break;
	case CP:
	case END:
		et = left;
		break;
	default:
		error ();
	}
	return et;
}

struct expr *
t()
{
	register struct expr	*et;
	switch (token) {
	case NOT:
	case OP:
	case PID:
		et = f();
		et = e_t(et);
		break;
	default:
		error ();
	}
	return et;
}

struct expr *
e_t(left)
struct expr *left;
{
	register struct expr *et;
	switch (token) {
	case AND:
		et = allocExpr ();
		et->op = AND;
		et->left = left;
		token = lex();
		et->right = t();
		et = e_t(et);
		break;
	case CP:
	case OR:
	case END:
		et = left;
		break;
	default:
		error ();
	}
	return et;
}

struct expr *
f()
{
	register struct expr *et;
	switch (token) {
	case PID:
		et = allocExpr ();
		et->op = PID;
		et->value = lvalue;
		token = lex();
		break;
	case NOT:
		et = allocExpr ();
		et->op = NOT;
		token = lex();
		et->left = f();
		break;
	case OP:
		token = lex();
		et = e();
		switch (token) {
		case CP:
			token = lex();
			break;
		default:
			error ();
		}
		break;
	default:
		error ();
	}
	return et;
}

char	**buf;

init(a)
char **a;
{
	buf = a;
}

char *
getw ()
{
	register char	*c, *d;

	do {
		d = c = *buf;
		if (!*buf)
			return 0;
		for (;;) {
			if (!*c) {
				++buf;
				break;
			} else if (*c == ' ' || *c == '\t') {
				*c = '\0';
				while (*++c == ' ' || *c == '\t')
					;
				*buf = c;
				break;
			}
			++c;
		}
	} while (!*d);
	return d;
}

struct token {
	char	*string;
	int		value;
} tokens [] = {
	"or",	OR,
	"-o",	OR,
	"||",	OR,
	"and",	AND,
	"-a",	AND,
	"&&",	AND,
	"not",	NOT,
	"-n",	NOT,
	"!",	NOT,
	"~",	NOT,
	"open",	OP,
	"(",	OP,
	"{",	OP,
	"close",CP,
	")",	CP,
	"}",	CP,
	0,		0,
};

lex ()
{
	register int	i;
	register char	*s;
	register struct token	*t;

	for (;;) {
		if (!(s = getw()))
			return END;
		for (t = tokens;t->string;++t) {
			if (!strcmp (t->string, s))
				return t->value;
		}
		if ('0' <= *s && *s <= '9') {
			i = 0;
			while ('0' <= *s && *s <= '9')
				i = i * 10 + *s++ - '0';
			if (!*s) {
				lvalue = i;
				return PID;
			}
		}
		fprintf (stderr, "lex error on token: %s\n", s);
	}
}

error ()
{
	fprintf (stderr, "syntax error on %s\n", buf[-1]);
	exit (-1);
}

struct expr *
allocExpr ()
{
	char	*malloc ();
	return (struct expr *) malloc (sizeof (struct expr));
}

gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (11/18/84)

> ... a friend of mine needed a program to wait
> for a specified process, berkeley kernels let you do this
> by using kill, just kill a process with sig 0, it returns the
> error status without actually doing anything.  ...

All UNIXes I have ever used (from 6th Ed. on) support this.
It is even documented in the manual these days.