| #include <u.h> | 
 | #include <libc.h> | 
 | #include <bio.h> | 
 | #include "plot.h" | 
 | #include <draw.h> | 
 | #include <event.h> | 
 | #include <ctype.h> | 
 |  | 
 | void	define(char*); | 
 | void	call(char*); | 
 | void	include(char*); | 
 | int	process(Biobuf*); | 
 | int	server(void); | 
 |  | 
 | enum{ | 
 | 	ARC, | 
 | 	BOX, | 
 | 	CALL, | 
 | 	CFILL, | 
 | 	CIRC, | 
 | 	CLOSEPL, | 
 | 	COLOR, | 
 | 	CSPLINE, | 
 | 	DEFINE, | 
 | 	DISK, | 
 | 	DSPLINE, | 
 | 	ERASE, | 
 | 	FILL, | 
 | 	FRAME, | 
 | 	FSPLINE, | 
 | 	GRADE, | 
 | 	IDLE, | 
 | 	INCLUDE, | 
 | 	LINE, | 
 | 	LSPLINE, | 
 | 	MOVE, | 
 | 	OPENPL, | 
 | 	PARABOLA, | 
 | 	PEN, | 
 | 	PAUSE, | 
 | 	POINT, | 
 | 	POLY, | 
 | 	RANGE, | 
 | 	RESTORE, | 
 | 	RMOVE, | 
 | 	RVEC, | 
 | 	SAVE, | 
 | 	SBOX, | 
 | 	SPLINE, | 
 | 	TEXT, | 
 | 	VEC, | 
 | 	LAST | 
 | }; | 
 |  | 
 | struct pcall { | 
 | 	char	*cc; | 
 | 	int	numc; | 
 | } plots[] = { | 
 | 	/*ARC*/ 		"a", 	1, | 
 | 	/*BOX*/ 		"bo", 	2, | 
 | 	/*CALL*/		"ca",	2, | 
 | 	/*CFILL*/ 	"cf", 	2, | 
 | 	/*CIRC*/ 		"ci", 	2, | 
 | 	/*CLOSEPL*/ 	"cl", 	2, | 
 | 	/*COLOR*/ 	"co", 	2, | 
 | 	/*CSPLINE*/	"cs",	2, | 
 | 	/*DEFINE*/	"de",	2, | 
 | 	/*DISK*/		"di",	2, | 
 | 	/*DSPLINE*/	"ds",	2, | 
 | 	/*ERASE*/ 	"e", 	1, | 
 | 	/*FILL*/ 		"fi", 	2, | 
 | 	/*FRAME*/ 	"fr", 	2, | 
 | 	/*FSPLINE*/	"fs",	2, | 
 | 	/*GRADE*/ 	"g", 	1, | 
 | 	/*IDLE*/ 		"id", 	2, | 
 | 	/*INCLUDE*/	"in",	2, | 
 | 	/*LINE*/ 		"li", 	2, | 
 | 	/*LSPLINE*/	"ls",	2, | 
 | 	/*MOVE*/ 		"m", 	1, | 
 | 	/*OPENPL*/ 	"o", 	1, | 
 | 	/*PARABOLA*/ 	"par", 	3, | 
 | 	/*PEN*/ 		"pe", 	2, | 
 | 	/*PAUSE*/ 	"pau", 	3, | 
 | 	/*POINT*/ 	"poi", 	3, | 
 | 	/*POLY*/ 		"pol", 	3, | 
 | 	/*RANGE*/ 	"ra", 	2, | 
 | 	/*RESTORE*/ 	"re", 	2, | 
 | 	/*RMOVE*/ 	"rm", 	2, | 
 | 	/*RVEC*/ 		"rv", 	2, | 
 | 	/*SAVE*/ 		"sa", 	2, | 
 | 	/*SBOX*/ 		"sb", 	2, | 
 | 	/*SPLINE*/ 	"sp", 	2, | 
 | 	/*TEXT*/ 		"t", 	1, | 
 | 	/*VEC*/ 		"v", 	1, | 
 | 	/*LAST*/	 	0, 	0 | 
 | }; | 
 |  | 
 | struct pcall *pplots;		/* last command read */ | 
 |  | 
 | #define MAXL 16 | 
 | struct fcall { | 
 | 	char *name; | 
 | 	char *stash; | 
 | } flibr[MAXL];			/* define strings */ | 
 |  | 
 | struct fcall *fptr = flibr; | 
 |  | 
 | #define	NFSTACK	50 | 
 | struct fstack{ | 
 | 	int peekc; | 
 | 	int lineno; | 
 | 	char *corebuf; | 
 | 	Biobuf *fd; | 
 | 	double scale; | 
 | }fstack[NFSTACK];		/* stack of open input files & defines */ | 
 | struct fstack *fsp=fstack; | 
 |  | 
 | #define	NARGSTR	8192 | 
 | char argstr[NARGSTR+1];		/* string arguments */ | 
 |  | 
 | #define	NX	8192 | 
 | double x[NX];			/* numeric arguments */ | 
 |  | 
 | #define	NPTS	256 | 
 | int cnt[NPTS];			/* control-polygon vertex counts */ | 
 | double *pts[NPTS];		/* control-polygon vertex pointers */ | 
 |  | 
 | void eresized(int new){ | 
 | 	if(new && getwindow(display, Refnone) < 0){ | 
 | 		fprint(2, "Can't reattach to window: %r\n"); | 
 | 		exits("resize"); | 
 | 	} | 
 | } | 
 | char *items[]={ | 
 | 	"exit", | 
 | 	0 | 
 | }; | 
 | Menu menu={items}; | 
 | void | 
 | main(int arc, char *arv[]){ | 
 | 	char *ap; | 
 | 	Biobuf *bp; | 
 | 	int fd; | 
 | 	int i; | 
 | 	int dflag; | 
 | 	char *oflag; | 
 | 	Mouse m; | 
 | 	bp = 0; | 
 | 	fd = dup(0, -1);		/* because openpl will close 0! */ | 
 | 	dflag=0; | 
 | 	oflag=""; | 
 | 	winsize = "512x512"; | 
 | 	for(i=1;i!=arc;i++) if(arv[i][0]=='-') switch(arv[i][1]){ | 
 | 	case 'd': dflag=1; break; | 
 | 	case 'o': oflag=arv[i]+2; break; | 
 | 	case 's': fd=server(); break; | 
 | 	} | 
 | 	openpl(oflag); | 
 | 	if(dflag) doublebuffer(); | 
 | 	for (; arc > 1; arc--, arv++) { | 
 | 		if (arv[1][0] == '-') { | 
 | 			ap = arv[1]; | 
 | 			ap++; | 
 | 			switch (*ap) { | 
 | 			default: | 
 | 				fprint(2, "%s not allowed as argument\n", ap); | 
 | 				exits("usage"); | 
 | 			case 'T': break; | 
 | 			case 'D': break; | 
 | 			case 'd': break; | 
 | 			case 'o': break; | 
 | 			case 's': break; | 
 | 			case 'e': erase(); break; | 
 | 			case 'C': closepl(); break; | 
 | 			case 'w': ppause(); break; | 
 | 			case 'c': color(ap+1); break; | 
 | 			case 'f': cfill(ap+1); break; | 
 | 			case 'p': pen(ap+1); break; | 
 | 			case 'g': grade(atof(ap+1)); break; | 
 | 			case 'W': winsize = ap+1; break; | 
 | 			} | 
 | 		} | 
 | 		else if ((bp = Bopen(arv[1], OREAD)) == 0) { | 
 | 			perror(arv[1]); | 
 | 			fprint(2, "Cannot find file %s\n", arv[1]); | 
 | 		} | 
 | 		else if(process(bp)) Bterm(fsp->fd); | 
 | 		else break; | 
 | 	} | 
 | 	if (bp == 0){ | 
 | 		bp = malloc(sizeof *bp); | 
 | 		Binit(bp, fd, OREAD); | 
 | 		process(bp); | 
 | 	} | 
 | 	closepl(); | 
 | 	for(;;){ | 
 | 		m=emouse(); | 
 | 		if(m.buttons&4 && emenuhit(3, &m, &menu)==0) exits(0); | 
 | 	} | 
 | } | 
 | int nextc(void){ | 
 | 	int c; | 
 | 	Rune r; | 
 | 	for(;;){ | 
 | 		if(fsp->peekc!=Beof){ | 
 | 			c=fsp->peekc; | 
 | 			fsp->peekc=Beof; | 
 | 			return c; | 
 | 		} | 
 | 		if(fsp->fd) | 
 | 			c=Bgetrune(fsp->fd); | 
 | 		else if(*fsp->corebuf){ | 
 | 			fsp->corebuf+=chartorune(&r, fsp->corebuf); | 
 | 			c=r; | 
 | 		}else | 
 | 			c=Beof; | 
 | 		if(c!=Beof || fsp==fstack) break; | 
 | 		if(fsp->fd) Bterm(fsp->fd); | 
 | 		--fsp; | 
 | 	} | 
 | 	if(c=='\n') fsp->lineno++; | 
 | 	return c; | 
 | } | 
 | /* | 
 |  * Read a string into argstr -- ignores leading spaces | 
 |  * and an optional leading quote-mark | 
 |  */ | 
 | void | 
 | strarg(void){ | 
 | 	int c; | 
 | 	Rune r; | 
 | 	int quote=0; | 
 | 	char *s=argstr; | 
 | 	do | 
 | 		c=nextc(); | 
 | 	while(c==' ' || c=='\t'); | 
 | 	if(c=='\'' || c=='"'){ | 
 | 		quote=c; | 
 | 		c=nextc(); | 
 | 	} | 
 | 	r = 0; | 
 | 	while(c!='\n' && c!=Beof){ | 
 | 		r=c; | 
 | 		s+=runetochar(s, &r); | 
 | 		c=nextc(); | 
 | 	} | 
 | 	if(quote && s!=argstr && r==quote) --s; | 
 | 	*s='\0'; | 
 | } | 
 | /* | 
 |  * Read a floating point number into argstr | 
 |  */ | 
 | int | 
 | numstring(void){ | 
 | 	int ndp=0; | 
 | 	int ndig=0; | 
 | 	char *s=argstr; | 
 | 	int c=nextc(); | 
 | 	if(c=='+' || c=='-'){ | 
 | 		*s++=c; | 
 | 		c=nextc(); | 
 | 	} | 
 | 	while(isdigit(c) || c=='.'){ | 
 | 		if(s!=&argstr[NARGSTR]) *s++=c; | 
 | 		if(c=='.') ndp++; | 
 | 		else ndig++; | 
 | 		c=nextc(); | 
 | 	} | 
 | 	if(ndp>1 || ndig==0){ | 
 | 		fsp->peekc=c; | 
 | 		return 0; | 
 | 	} | 
 | 	if(c=='e' || c=='E'){ | 
 | 		if(s!=&argstr[NARGSTR]) *s++=c; | 
 | 		c=nextc(); | 
 | 		if(c=='+' || c=='-'){ | 
 | 			if(s!=&argstr[NARGSTR]) *s++=c; | 
 | 			c=nextc(); | 
 | 		} | 
 | 		if(!isdigit(c)){ | 
 | 			fsp->peekc=c; | 
 | 			return 0; | 
 | 		} | 
 | 		while(isdigit(c)){ | 
 | 			if(s!=&argstr[NARGSTR]) *s++=c; | 
 | 			c=nextc(); | 
 | 		} | 
 | 	} | 
 | 	fsp->peekc=c; | 
 | 	*s='\0'; | 
 | 	return 1; | 
 | } | 
 | /* | 
 |  * Read n numeric arguments, storing them in | 
 |  * x[0], ..., x[n-1] | 
 |  */ | 
 | void | 
 | numargs(int n){ | 
 | 	int i, c; | 
 | 	for(i=0;i!=n;i++){ | 
 | 		do{ | 
 | 			c=nextc(); | 
 | 		}while(strchr(" \t\n", c) || c!='.' && c!='+' && c!='-' && ispunct(c)); | 
 | 		fsp->peekc=c; | 
 | 		if(!numstring()){ | 
 | 			fprint(2, "line %d: number expected\n", fsp->lineno); | 
 | 			exits("input error"); | 
 | 		} | 
 | 		x[i]=atof(argstr)*fsp->scale; | 
 | 	} | 
 | } | 
 | /* | 
 |  * Read a list of lists of control vertices, storing points in x[.], | 
 |  * pointers in pts[.] and counts in cnt[.] | 
 |  */ | 
 | void | 
 | polyarg(void){ | 
 | 	int nleft, l, r, c; | 
 | 	double **ptsp=pts, *xp=x; | 
 | 	int *cntp=cnt; | 
 | 	do{ | 
 | 		c=nextc(); | 
 | 	}while(c==' ' || c=='\t'); | 
 | 	if(c=='{'){ | 
 | 		l='{'; | 
 | 		r='}'; | 
 | 	} | 
 | 	else{ | 
 | 		l=r='\n'; | 
 | 		fsp->peekc=c; | 
 | 	} | 
 | 	nleft=1; | 
 | 	*cntp=0; | 
 | 	*ptsp=xp; | 
 | 	for(;;){ | 
 | 		c=nextc(); | 
 | 		if(c==r){ | 
 | 			if(*cntp){ | 
 | 				if(*cntp&1){ | 
 | 					fprint(2, "line %d: phase error\n", | 
 | 						fsp->lineno); | 
 | 					exits("bad input"); | 
 | 				} | 
 | 				*cntp/=2; | 
 | 				if(ptsp==&pts[NPTS]){ | 
 | 					fprint(2, "line %d: out of polygons\n", | 
 | 						fsp->lineno); | 
 | 					exits("exceeded limit"); | 
 | 				} | 
 | 				*++ptsp=xp; | 
 | 				*++cntp=0; | 
 | 			} | 
 | 			if(--nleft==0) return; | 
 | 		} | 
 | 		else switch(c){ | 
 | 		case Beof:  return; | 
 | 		case ' ':  break; | 
 | 		case '\t': break; | 
 | 		case '\n': break; | 
 | 		case '.': case '+': case '-': | 
 | 		case '0': case '1': case '2': case '3': case '4': | 
 | 		case '5': case '6': case '7': case '8': case '9': | 
 | 			fsp->peekc=c; | 
 | 			if(!numstring()){ | 
 | 				fprint(2, "line %d: expected number\n", fsp->lineno); | 
 | 				exits("bad input"); | 
 | 			} | 
 | 			if(xp==&x[NX]){ | 
 | 				fprint(2, "line %d: out of space\n", fsp->lineno); | 
 | 				exits("exceeded limit"); | 
 | 			} | 
 | 			*xp++=atof(argstr); | 
 | 			++*cntp; | 
 | 			break; | 
 | 		default: | 
 | 			if(c==l) nleft++; | 
 | 			else if(!ispunct(c)){ | 
 | 				fsp->peekc=c; | 
 | 				return; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | int | 
 | process(Biobuf *fd){ | 
 | 	char *s; | 
 | 	int c; | 
 | 	fsp=fstack; | 
 | 	fsp->fd=fd; | 
 | 	fsp->corebuf=0; | 
 | 	fsp->peekc=Beof; | 
 | 	fsp->lineno=1; | 
 | 	fsp->scale=1.; | 
 | 	for(;;){ | 
 | 		do | 
 | 			c=nextc(); | 
 | 		while(c==' ' || c=='\t'); | 
 | 		if(c==':'){ | 
 | 			do | 
 | 				c=nextc(); | 
 | 			while(c!='\n' && c!=Beof); | 
 | 			if(c==Beof) break; | 
 | 			continue; | 
 | 		} | 
 | 		while(c=='.'){ | 
 | 			c=nextc(); | 
 | 			if(isdigit(c)){ | 
 | 				if(fsp->fd) Bungetc(fsp->fd); | 
 | 				else --fsp->corebuf; | 
 | 				c='.'; | 
 | 				break; | 
 | 			} | 
 | 		} | 
 | 		if(c==Beof) break; | 
 | 		if(c=='\n') continue; | 
 | 		if(isalpha(c)){ | 
 | 			s=argstr; | 
 | 			do{ | 
 | 				if(isupper(c)) c=tolower(c); | 
 | 				if(s!=&argstr[NARGSTR]) *s++=c; | 
 | 				c=nextc(); | 
 | 			}while(isalpha(c)); | 
 | 			fsp->peekc=c; | 
 | 			*s='\0'; | 
 | 			for(pplots=plots;pplots->cc;pplots++) | 
 | 				if(strncmp(argstr, pplots->cc, pplots->numc)==0) | 
 | 					break; | 
 | 			if(pplots->cc==0){ | 
 | 				fprint(2, "line %d, %s unknown\n", fsp->lineno, | 
 | 					argstr); | 
 | 				exits("bad command"); | 
 | 			} | 
 | 		} | 
 | 		else{ | 
 | 			fsp->peekc=c; | 
 | 		} | 
 | 		if(!pplots){ | 
 | 			fprint(2, "line %d, no command!\n", fsp->lineno); | 
 | 			exits("no command"); | 
 | 		} | 
 | 		switch(pplots-plots){ | 
 | 		case ARC:	numargs(7); rarc(x[0],x[1],x[2],x[3],x[4],x[5],x[6]); break; | 
 | 		case BOX:	numargs(4); box(x[0], x[1], x[2], x[3]); break; | 
 | 		case CALL:	strarg();   call(argstr); pplots=0; break; | 
 | 		case CFILL:	strarg();   cfill(argstr); pplots=0; break; | 
 | 		case CIRC:	numargs(3); circ(x[0], x[1], x[2]); break; | 
 | 		case CLOSEPL:	strarg();   closepl(); pplots=0; break; | 
 | 		case COLOR:	strarg();   color(argstr); pplots=0; break; | 
 | 		case CSPLINE:	polyarg();  splin(4, cnt, pts); break; | 
 | 		case DEFINE:	strarg();   define(argstr); pplots=0; break; | 
 | 		case DISK:	numargs(3); plotdisc(x[0], x[1], x[2]); break; | 
 | 		case DSPLINE:	polyarg();  splin(3, cnt, pts); break; | 
 | 		case ERASE:	strarg();   erase(); pplots=0; break; | 
 | 		case FILL:	polyarg();  fill(cnt, pts); break; | 
 | 		case FRAME:	numargs(4); frame(x[0], x[1], x[2], x[3]); break; | 
 | 		case FSPLINE:	polyarg();  splin(1, cnt, pts); break; | 
 | 		case GRADE:	numargs(1); grade(x[0]); break; | 
 | 		case IDLE:	strarg();   idle(); pplots=0; break; | 
 | 		case INCLUDE:	strarg();   include(argstr); pplots=0; break; | 
 | 		case LINE:	numargs(4); plotline(x[0], x[1], x[2], x[3]); break; | 
 | 		case LSPLINE:	polyarg();  splin(2, cnt, pts); break; | 
 | 		case MOVE:	numargs(2); move(x[0], x[1]); break; | 
 | 		case OPENPL:	strarg();   openpl(argstr); pplots=0; break; | 
 | 		case PARABOLA:	numargs(6); parabola(x[0],x[1],x[2],x[3],x[4],x[5]); break; | 
 | 		case PAUSE:	strarg();   ppause(); pplots=0; break; | 
 | 		case PEN:	strarg();   pen(argstr); pplots=0; break; | 
 | 		case POINT:	numargs(2); dpoint(x[0], x[1]); break; | 
 | 		case POLY:	polyarg();  plotpoly(cnt, pts); break; | 
 | 		case RANGE:	numargs(4); range(x[0], x[1], x[2], x[3]); break; | 
 | 		case RESTORE:	strarg();   restore(); pplots=0; break; | 
 | 		case RMOVE:	numargs(2); rmove(x[0], x[1]); break; | 
 | 		case RVEC:	numargs(2); rvec(x[0], x[1]); break; | 
 | 		case SAVE:	strarg();   save(); pplots=0; break; | 
 | 		case SBOX:	numargs(4); sbox(x[0], x[1], x[2], x[3]); break; | 
 | 		case SPLINE:	polyarg();  splin(0, cnt, pts); break; | 
 | 		case TEXT:	strarg();   text(argstr); pplots=0; break; | 
 | 		case VEC:	numargs(2); vec(x[0], x[1]); break; | 
 | 		default: | 
 | 			fprint(2, "plot: missing case %ld\n", pplots-plots); | 
 | 			exits("internal error"); | 
 | 		} | 
 | 	} | 
 | 	return 1; | 
 | } | 
 | char *names = 0; | 
 | char *enames = 0; | 
 | char *bstash = 0; | 
 | char *estash = 0; | 
 | unsigned size = 1024; | 
 | char *nstash = 0; | 
 | void define(char *a){ | 
 | 	char	*ap; | 
 | 	short	i, j; | 
 | 	int curly = 0; | 
 | 	ap = a; | 
 | 	while(isalpha((uchar)*ap))ap++; | 
 | 	if(ap == a){ | 
 | 		fprint(2,"no name with define\n"); | 
 | 		exits("define"); | 
 | 	} | 
 | 	i = ap - a; | 
 | 	if(names+i+1 > enames){ | 
 | 		names = malloc((unsigned)512); | 
 | 		enames = names + 512; | 
 | 	} | 
 | 	fptr->name = names; | 
 | 	strncpy(names, a,i); | 
 | 	names += i; | 
 | 	*names++ = '\0'; | 
 | 	if(!bstash){ | 
 | 		bstash = nstash = malloc(size); | 
 | 		estash = bstash + size; | 
 | 	} | 
 | 	fptr->stash = nstash; | 
 | 	while(*ap != '{') | 
 | 		if(*ap == '\n'){ | 
 | 			if((ap=Brdline(fsp->fd, '\n'))==0){ | 
 | 				fprint(2,"unexpected end of file\n"); | 
 | 				exits("eof"); | 
 | 			} | 
 | 		} | 
 | 		else ap++; | 
 | 	while((j=Bgetc(fsp->fd))!= Beof){ | 
 | 		if(j == '{')curly++; | 
 | 		else if(j == '}'){ | 
 | 			if(curly == 0)break; | 
 | 			else curly--; | 
 | 		} | 
 | 		*nstash++ = j; | 
 | 		if(nstash == estash){ | 
 | 			free(bstash); | 
 | 			size += 1024; | 
 | 			bstash = realloc(bstash,size); | 
 | 			estash = bstash+size; | 
 | 		} | 
 | 	} | 
 | 	*nstash++ = '\0'; | 
 | 	if(fptr++ >= &flibr[MAXL]){ | 
 | 		fprint(2,"Too many objects\n"); | 
 | 		exits("too many objects"); | 
 | 	} | 
 | } | 
 | void call(char *a){ | 
 | 	char *ap; | 
 | 	struct fcall *f; | 
 | 	char sav; | 
 | 	double SC; | 
 | 	ap = a; | 
 | 	while(isalpha((uchar)*ap))ap++; | 
 | 	sav = *ap; | 
 | 	*ap = '\0'; | 
 | 	for(f=flibr;f<fptr;f++){ | 
 | 		if (!(strcmp(a, f->name))) | 
 | 			break; | 
 | 	} | 
 | 	if(f == fptr){ | 
 | 		fprint(2, "object %s not defined\n",a); | 
 | 		exits("undefined"); | 
 | 	} | 
 | 	*ap = sav; | 
 | 	while (isspace((uchar)*ap) || *ap == ',')  | 
 | 		ap++; | 
 | 	if (*ap != '\0') | 
 | 		SC = atof(ap); | 
 | 	else SC = 1.; | 
 | 	if(++fsp==&fstack[NFSTACK]){ | 
 | 		fprint(2, "input stack overflow\n"); | 
 | 		exits("blew stack"); | 
 | 	} | 
 | 	fsp->peekc=Beof; | 
 | 	fsp->lineno=1; | 
 | 	fsp->corebuf=f->stash; | 
 | 	fsp->fd=0; | 
 | 	fsp->scale=fsp[-1].scale*SC; | 
 | } | 
 | void include(char *a){ | 
 | 	Biobuf *fd; | 
 | 	fd=Bopen(a, OREAD); | 
 | 	if(fd==0){ | 
 | 		perror(a); | 
 | 		exits("can't include"); | 
 | 	} | 
 | 	if(++fsp==&fstack[NFSTACK]){ | 
 | 		fprint(2, "input stack overflow\n"); | 
 | 		exits("blew stack"); | 
 | 	} | 
 | 	fsp->peekc=Beof; | 
 | 	fsp->lineno=1; | 
 | 	fsp->corebuf=0; | 
 | 	fsp->fd=fd; | 
 | } | 
 | /* | 
 |  * Doesn't work.  Why? | 
 |  */ | 
 | int server(void){ | 
 | 	int fd, p[2]; | 
 | 	char buf[32]; | 
 | 	pipe(p); | 
 | 	fd = create("/srv/plot", 1, 0666); | 
 | 	sprint(buf, "%d", p[1]); | 
 | 	write(fd, buf, strlen(buf)); | 
 | 	close(fd); | 
 | 	close(p[1]); | 
 | 	return p[0]; | 
 | } |