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.