[comp.unix.questions] argv ==> stdin, got it

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!"