mcvoy@rsch.WISC.EDU (Lawrence W. McVoy) (11/21/86)
This works very well. My previous fears about speed were unfounded. Now, here's a question: what happen if I used vfork() instead? I thought that the parent sleeps until the child dies, but what if the child blocks? Like on I/O? Would that work even better? # include <stdio.h> # define child_stdin output[1] yyerror(s) char* s; { fprintf(stderr, "%s\n"); } /* * code to fork a child and have control of the child's stdin/out * from usenet. Works. Fast, too. The idea is that the command line * is fed to the stdin of the child. This is so that you don't have * to f*ck with the stupid code in y.tab.c or lex.yy.c. It should work * for anything that wants stdin. */ main(argc, argv) char** argv; { int output[2]; int i; pipe(output); /* parent writes 1, child reads 0 */ if (fork() == 0) { /* child */ close(0); dup(output[0]); return yyparse(); } else { /* parent */ close(output[0]); /* write only */ for (i=1; i<argc; i++) write(child_stdin, argv[i], strlen(argv[i])); write(child_stdin, "\n", strlen("\n")); close(child_stdin); wait(0); /* ASSUME: child doesn't fork */ } } -- Larry McVoy mcvoy@rsch.wisc.edu, {seismo, topaz, harvard, ihnp4, etc}!uwvax!mcvoy "They're coming soon! Quad-stated guru-gates!"
chris@mimsy.UUCP (Chris Torek) (11/21/86)
In article <2976@rsch.WISC.EDU> mcvoy@rsch.WISC.EDU (Lawrence W. McVoy) writes: >/* > * code to fork a child and have control of the child's stdin/out > * from usenet. Works. Fast, too. The idea is that the command line > * is fed to the stdin of the child. This is so that you don't have > * to f*ck with the stupid code in y.tab.c or lex.yy.c. It should work > * for anything that wants stdin. > */ So what is so hard about making lex read argv rather than stdin? Here is a trivial parser that accepts only `foo bar;'. Note that the semicolon must be quoted to protect it from the shell. : Run this shell script with "sh" not "csh" PATH=/bin:/usr/bin:/usr/ucb:/etc:$PATH export PATH all=FALSE if [ x$1 = x-a ]; then all=TRUE fi echo Extracting Makefile sed 's/^X//' <<'//go.sysin dd *' >Makefile a.out: main.o y.o cc main.o y.o y.o: y.c l.c clean: rm -f a.out core *.o l.c y.c //go.sysin dd * if [ `wc -c < Makefile` != 84 ]; then made=FALSE echo error transmitting Makefile -- echo length should be 84, not `wc -c < Makefile` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 Makefile echo -n ' '; ls -ld Makefile fi echo Extracting l.l sed 's/^X//' <<'//go.sysin dd *' >l.l %{ #undef input #undef unput %} %% foo { return (FOO); } bar { return (BAR); } [ \t\n] ; X. { return (yytext[0]); } %% //go.sysin dd * if [ `wc -c < l.l` != 123 ]; then made=FALSE echo error transmitting l.l -- echo length should be 123, not `wc -c < l.l` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 l.l echo -n ' '; ls -ld l.l fi echo Extracting main.c sed 's/^X//' <<'//go.sysin dd *' >main.c int argc; char **argv; char unbuf[512]; int unc; main(ac, av) int ac; char **av; { argc = ac - 1; argv = av + 1; exit(yyparse()); } yyerror() { write(2, "syntax error\n", 13); exit(1); } unput(c) int c; { unbuf[unc++] = c; } input() { if (unc) return (unbuf[--unc]); if (argc <= 0) return (0); if (**argv == 0) { argc--; argv++; return (' '); } return (*(*argv)++); } //go.sysin dd * if [ `wc -c < main.c` != 400 ]; then made=FALSE echo error transmitting main.c -- echo length should be 400, not `wc -c < main.c` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 main.c echo -n ' '; ls -ld main.c fi echo Extracting y.y sed 's/^X//' <<'//go.sysin dd *' >y.y %token FOO BAR %% prog: FOO BAR ';' ; %% #include "l.c" yywrap() { return (1); } //go.sysin dd * if [ `wc -c < y.y` != 84 ]; then made=FALSE echo error transmitting y.y -- echo length should be 84, not `wc -c < y.y` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 y.y echo -n ' '; ls -ld y.y fi -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) UUCP: seismo!mimsy!chris ARPA/CSNet: chris@mimsy.umd.edu
mcvoy@uwvax.UUCP (11/22/86)
In article <4379@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes: >In article <2976@rsch.WISC.EDU> mcvoy@rsch.WISC.EDU (Lawrence W. McVoy) writes: >>/* >> * code to fork a child and have control of the child's stdin/out >> * from usenet. Works. Fast, too. The idea is that the command line >> * is fed to the stdin of the child. This is so that you don't have >> * to f*ck with the stupid code in y.tab.c or lex.yy.c. It should work ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >> * for anything that wants stdin. >> */ > >So what is so hard about making lex read argv rather than stdin? >Here is a trivial parser that accepts only `foo bar;'. Note that >the semicolon must be quoted to protect it from the shell. [ about 100 lines of code deleted, showing how to make lex take input from argv ] Well, I tried something like that. It works fine with lex, but not with yacc. Yacc must actually diddle various input buffers and just redefining the input and unput routines was not enough to make it fly. Now, I know the hackers out there (such as chris) are going to say, "look, it's easy. All you have to do is be intimately familiar with the way lex & yacc interact. And it's only about 20 lines of code spread over lex.yy.c, y.tab.c, and /usr/src/usr.lib/libl/somefile." Well, a better solution has been suggested. Just use a pipe and send the command line into the pipe. As someone pointed out, for a cl you don't even have to fork, the pipe is big enough to buffer it up. Here's the code, and thanks for all the help... # include <stdio.h> # define write_end in[1] # define read_end in[0] /* * code to stuff the command line into a pipe and then call a function that * expects input from stdin. * suggested by - aweinste@diamond.bbn.com * Bugs - the cl had better be smaller tthan the pipe or this code * will hang (blocked on the write). */ main(argc, argv) char** argv; { int in[2]; int i; /* set it up so that stdin is the read side of pipe (in[1]) */ pipe(in); close(0); dup(read_end); /* stdin <-- pipe */ close(read_end); /* don't need this (now extra) fd */ /* stuff the cl into the write side of the pipe */ for (i=1; i<argc; i++) write(write_end, argv[i], strlen(argv[i])); write(write_end, "\n", strlen("\n")); close(write_end); /* call the routine that wants input from stdin */ return yyparse(); } yyerror(s) char* s; { fprintf(stderr, "%s\n"); } -- Larry McVoy mcvoy@rsch.wisc.edu, {seismo, topaz, harvard, ihnp4, etc}!uwvax!mcvoy "They're coming soon! Quad-stated guru-gates!"