1 /* prim-io.c -- input/output and redirection primitives ($Revision: 1.2 $) */
2 
3 #include "es.h"
4 #include "gc.h"
5 #include "prim.h"
6 
7 static const char *caller;
8 
getnumber(const char * s)9 static int getnumber(const char *s) {
10 	char *end;
11 	int result = strtol(s, &end, 0);
12 
13 	if (*end != '\0' || result < 0)
14 		fail(caller, "bad number: %s", s);
15 	return result;
16 }
17 
redir(List * (* rop)(int * fd,List * list),List * list,int evalflags)18 static List *redir(List *(*rop)(int *fd, List *list), List *list, int evalflags) {
19 	int destfd, srcfd;
20 	volatile int inparent = (evalflags & eval_inchild) == 0;
21 	volatile int ticket = UNREGISTERED;
22 
23 	assert(list != NULL);
24 	Ref(List *, lp, list);
25 	destfd = getnumber(getstr(lp->term));
26 	lp = (*rop)(&srcfd, lp->next);
27 
28 	ExceptionHandler
29 		ticket = (srcfd == -1)
30 			   ? defer_close(inparent, destfd)
31 			   : defer_mvfd(inparent, srcfd, destfd);
32 		lp = eval(lp, NULL, evalflags);
33 		undefer(ticket);
34 	CatchException (e)
35 		undefer(ticket);
36 		throw(e);
37 	EndExceptionHandler
38 
39 	RefReturn(lp);
40 }
41 
42 #define	REDIR(name)	static List *CONCAT(redir_,name)(int *srcfdp, List *list)
43 
argcount(const char * s)44 static noreturn argcount(const char *s) {
45 	fail(caller, "argument count: usage: %s", s);
46 }
47 
REDIR(openfile)48 REDIR(openfile) {
49 	int i, fd;
50 	char *mode, *name;
51 	OpenKind kind;
52 	static const struct {
53 		const char *name;
54 		OpenKind kind;
55 	} modes[] = {
56 		{ "r",	oOpen },
57 		{ "w",	oCreate },
58 		{ "a",	oAppend },
59 		{ "r+",	oReadWrite },
60 		{ "w+",	oReadCreate },
61 		{ "a+",	oReadAppend },
62 		{ NULL, 0 }
63 	};
64 
65 	assert(length(list) == 3);
66 	Ref(List *, lp, list);
67 
68 	mode = getstr(lp->term);
69 	lp = lp->next;
70 	for (i = 0;; i++) {
71 		if (modes[i].name == NULL)
72 			fail("$&openfile", "bad %%openfile mode: %s", mode);
73 		if (streq(mode, modes[i].name)) {
74 			kind = modes[i].kind;
75 			break;
76 		}
77 	}
78 
79 	name = getstr(lp->term);
80 	lp = lp->next;
81 	fd = eopen(name, kind);
82 	if (fd == -1)
83 		fail("$&openfile", "%s: %s", name, esstrerror(errno));
84 	*srcfdp = fd;
85 	RefReturn(lp);
86 }
87 
PRIM(openfile)88 PRIM(openfile) {
89 	List *lp;
90 	caller = "$&openfile";
91 	if (length(list) != 4)
92 		argcount("%openfile mode fd file cmd");
93 	/* transpose the first two elements */
94 	lp = list->next;
95 	list->next = lp->next;
96 	lp->next = list;
97 	return redir(redir_openfile, lp, evalflags);
98 }
99 
REDIR(dup)100 REDIR(dup) {
101 	int fd;
102 	assert(length(list) == 2);
103 	Ref(List *, lp, list);
104 	fd = dup(fdmap(getnumber(getstr(lp->term))));
105 	if (fd == -1)
106 		fail("$&dup", "dup: %s", esstrerror(errno));
107 	*srcfdp = fd;
108 	lp = lp->next;
109 	RefReturn(lp);
110 }
111 
PRIM(dup)112 PRIM(dup) {
113 	caller = "$&dup";
114 	if (length(list) != 3)
115 		argcount("%dup newfd oldfd cmd");
116 	return redir(redir_dup, list, evalflags);
117 }
118 
REDIR(close)119 REDIR(close) {
120 	*srcfdp = -1;
121 	return list;
122 }
123 
PRIM(close)124 PRIM(close) {
125 	caller = "$&close";
126 	if (length(list) != 2)
127 		argcount("%close fd cmd");
128 	return redir(redir_close, list, evalflags);
129 }
130 
131 /* pipefork -- create a pipe and fork */
pipefork(int p[2],int * extra)132 static int pipefork(int p[2], int *extra) {
133 	volatile int pid = 0;
134 
135 	if (pipe(p) == -1)
136 		fail(caller, "pipe: %s", esstrerror(errno));
137 
138 	registerfd(&p[0], FALSE);
139 	registerfd(&p[1], FALSE);
140 	if (extra != NULL)
141 		registerfd(extra, FALSE);
142 
143 	ExceptionHandler
144 		pid = efork(TRUE, FALSE);
145 	CatchExceptionIf (pid != 0, e)
146 		unregisterfd(&p[0]);
147 		unregisterfd(&p[1]);
148 		if (extra != NULL)
149 			unregisterfd(extra);
150 		throw(e);
151 	EndExceptionHandler;
152 
153 	unregisterfd(&p[0]);
154 	unregisterfd(&p[1]);
155 	if (extra != NULL)
156 		unregisterfd(extra);
157 	return pid;
158 }
159 
REDIR(here)160 REDIR(here) {
161 	int pid, p[2];
162 	List *doc, *tail, **tailp;
163 
164 	assert(list != NULL);
165 	for (tailp = &list; (tail = *tailp)->next != NULL; tailp = &tail->next)
166 		;
167 	doc = (list == tail) ? NULL : list;
168 	*tailp = NULL;
169 
170 	if ((pid = pipefork(p, NULL)) == 0) {		/* child that writes to pipe */
171 		close(p[0]);
172 		fprint(p[1], "%L", doc, "");
173 		exit(0);
174 	}
175 
176 	close(p[1]);
177 	*srcfdp = p[0];
178 	return tail;
179 }
180 
PRIM(here)181 PRIM(here) {
182 	caller = "$&here";
183 	if (length(list) < 2)
184 		argcount("%here fd [word ...] cmd");
185 	return redir(redir_here, list, evalflags);
186 }
187 
PRIM(pipe)188 PRIM(pipe) {
189 	int n, infd, inpipe;
190 	static int *pids = NULL, pidmax = 0;
191 
192 	caller = "$&pipe";
193 	n = length(list);
194 	if ((n % 3) != 1)
195 		fail("$&pipe", "usage: pipe cmd [ outfd infd cmd ] ...");
196 	n = (n + 2) / 3;
197 	if (n > pidmax) {
198 		pids = erealloc(pids, n * sizeof *pids);
199 		pidmax = n;
200 	}
201 	n = 0;
202 
203 	infd = inpipe = -1;
204 
205 	for (;; list = list->next) {
206 		int p[2], pid;
207 
208 		pid = (list->next == NULL) ? efork(TRUE, FALSE) : pipefork(p, &inpipe);
209 
210 		if (pid == 0) {		/* child */
211 			if (inpipe != -1) {
212 				assert(infd != -1);
213 				releasefd(infd);
214 				mvfd(inpipe, infd);
215 			}
216 			if (list->next != NULL) {
217 				int fd = getnumber(getstr(list->next->term));
218 				releasefd(fd);
219 				mvfd(p[1], fd);
220 				close(p[0]);
221 			}
222 			exit(exitstatus(eval1(list->term, evalflags | eval_inchild)));
223 		}
224 		pids[n++] = pid;
225 		close(inpipe);
226 		if (list->next == NULL)
227 			break;
228 		list = list->next->next;
229 		infd = getnumber(getstr(list->term));
230 		inpipe = p[0];
231 		close(p[1]);
232 	}
233 
234 	Ref(List *, result, NULL);
235 	do {
236 		Term *t;
237 		int status = ewaitfor(pids[--n]);
238 		printstatus(0, status);
239 		t = mkstr(mkstatus(status));
240 		result = mklist(t, result);
241 	} while (0 < n);
242 	if (evalflags & eval_inchild)
243 		exit(exitstatus(result));
244 	RefReturn(result);
245 }
246 
247 #if HAVE_DEV_FD
PRIM(readfrom)248 PRIM(readfrom) {
249 	int pid, p[2], status;
250 	Push push;
251 
252 	caller = "$&readfrom";
253 	if (length(list) != 3)
254 		argcount("%readfrom var input cmd");
255 	Ref(List *, lp, list);
256 	Ref(char *, var, getstr(lp->term));
257 	lp = lp->next;
258 	Ref(Term *, input, lp->term);
259 	lp = lp->next;
260 	Ref(Term *, cmd, lp->term);
261 
262 	if ((pid = pipefork(p, NULL)) == 0) {
263 		close(p[0]);
264 		mvfd(p[1], 1);
265 		exit(exitstatus(eval1(input, evalflags &~ eval_inchild)));
266 	}
267 
268 	close(p[1]);
269 	lp = mklist(mkstr(str(DEVFD_PATH, p[0])), NULL);
270 	varpush(&push, var, lp);
271 
272 	ExceptionHandler
273 		lp = eval1(cmd, evalflags);
274 	CatchException (e)
275 		close(p[0]);
276 		ewaitfor(pid);
277 		throw(e);
278 	EndExceptionHandler
279 
280 	close(p[0]);
281 	status = ewaitfor(pid);
282 	printstatus(0, status);
283 	varpop(&push);
284 	RefEnd3(cmd, input, var);
285 	RefReturn(lp);
286 }
287 
PRIM(writeto)288 PRIM(writeto) {
289 	int pid, p[2], status;
290 	Push push;
291 	Handler h;
292 
293 	caller = "$&writeto";
294 	if (length(list) != 3)
295 		argcount("%writeto var output cmd");
296 	Ref(List *, lp, list);
297 	Ref(char *, var, getstr(lp->term));
298 	lp = lp->next;
299 	Ref(Term *, output, lp->term);
300 	lp = lp->next;
301 	Ref(Term *, cmd, lp->term);
302 
303 	if ((pid = pipefork(p, NULL)) == 0) {
304 		close(p[1]);
305 		mvfd(p[0], 0);
306 		exit(exitstatus(eval1(output, evalflags &~ eval_inchild)));
307 	}
308 
309 	close(p[0]);
310 	lp = mklist(mkstr(str(DEVFD_PATH, p[1])), NULL);
311 	varpush(&push, var, lp);
312 
313 	ExceptionHandler
314 		lp = eval1(cmd, evalflags);
315 	CatchException (e)
316 		close(p[1]);
317 		ewaitfor(pid);
318 		throw(e);
319 	EndExceptionHandler
320 
321 	pophandler(&h);
322 	close(p[1]);
323 	status = ewaitfor(pid);
324 	printstatus(0, status);
325 	varpop(&push);
326 	RefEnd3(cmd, output, var);
327 	RefReturn(lp);
328 }
329 #endif
330 
331 #define	BUFSIZE	4096
332 
bqinput(const char * sep,int fd)333 static List *bqinput(const char *sep, int fd) {
334 	long n;
335 	char in[BUFSIZE];
336 	startsplit(sep, TRUE);
337 
338 restart:
339 	while ((n = eread(fd, in, sizeof in)) > 0)
340 		splitstring(in, n, FALSE);
341 	SIGCHK();
342 	if (n == -1) {
343 		if (errno == EINTR)
344 			goto restart;
345 		close(fd);
346 		fail("$&backquote", "backquote read: %s", esstrerror(errno));
347 	}
348 	return endsplit();
349 }
350 
PRIM(backquote)351 PRIM(backquote) {
352 	int pid, p[2], status;
353 
354 	caller = "$&backquote";
355 	if (list == NULL)
356 		fail(caller, "usage: backquote separator command [args ...]");
357 
358 	Ref(List *, lp, list);
359 	Ref(char *, sep, getstr(lp->term));
360 	lp = lp->next;
361 
362 	if ((pid = pipefork(p, NULL)) == 0) {
363 		mvfd(p[1], 1);
364 		close(p[0]);
365 		exit(exitstatus(eval(lp, NULL, evalflags | eval_inchild)));
366 	}
367 
368 	close(p[1]);
369 	gcdisable();
370 	lp = bqinput(sep, p[0]);
371 	close(p[0]);
372 	status = ewaitfor(pid);
373 	printstatus(0, status);
374 	lp = mklist(mkstr(mkstatus(status)), lp);
375 	gcenable();
376 	list = lp;
377 	RefEnd2(sep, lp);
378 	SIGCHK();
379 	return list;
380 }
381 
PRIM(newfd)382 PRIM(newfd) {
383 	if (list != NULL)
384 		fail("$&newfd", "usage: $&newfd");
385 	return mklist(mkstr(str("%d", newfd())), NULL);
386 }
387 
388 /* read1 -- read one byte */
read1(int fd)389 static int read1(int fd) {
390 	int nread;
391 	unsigned char buf;
392 	do {
393 		nread = eread(fd, (char *) &buf, 1);
394 		SIGCHK();
395 	} while (nread == -1 && errno == EINTR);
396 	if (nread == -1)
397 		fail("$&read", esstrerror(errno));
398 	return nread == 0 ? EOF : buf;
399 }
400 
PRIM(read)401 PRIM(read) {
402 	int c;
403 	int fd = fdmap(0);
404 
405 	static Buffer *buffer = NULL;
406 	if (buffer != NULL)
407 		freebuffer(buffer);
408 	buffer = openbuffer(0);
409 
410 	while ((c = read1(fd)) != EOF && c != '\n')
411 		buffer = bufputc(buffer, c);
412 
413 	if (c == EOF && buffer->current == 0) {
414 		freebuffer(buffer);
415 		buffer = NULL;
416 		return NULL;
417 	} else {
418 		List *result = mklist(mkstr(sealcountedbuffer(buffer)), NULL);
419 		buffer = NULL;
420 		return result;
421 	}
422 }
423 
initprims_io(Dict * primdict)424 extern Dict *initprims_io(Dict *primdict) {
425 	X(openfile);
426 	X(close);
427 	X(dup);
428 	X(pipe);
429 	X(backquote);
430 	X(newfd);
431 	X(here);
432 #if HAVE_DEV_FD
433 	X(readfrom);
434 	X(writeto);
435 #endif
436 	X(read);
437 	return primdict;
438 }
439