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