source: mainline/uspace/app/ash/redir.c@ 8ccd2ea

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8ccd2ea was c28a023, checked in by Josef Cejka <malyzelenyhnus@…>, 17 years ago

Initial commit of ash shell.
It cannot be compiled yet.

  • Property mode set to 100644
File size: 10.8 KB
Line 
1/* $NetBSD: redir.c,v 1.22 2000/05/22 10:18:47 elric Exp $ */
2
3/*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39#include <sys/cdefs.h>
40#ifndef lint
41#if 0
42static char sccsid[] = "@(#)redir.c 8.2 (Berkeley) 5/4/95";
43#else
44__RCSID("$NetBSD: redir.c,v 1.22 2000/05/22 10:18:47 elric Exp $");
45#endif
46#endif /* not lint */
47
48#include <sys/stat.h>
49#include <sys/types.h>
50#include <sys/param.h> /* PIPE_BUF */
51#include <signal.h>
52#include <string.h>
53#include <fcntl.h>
54#include <errno.h>
55#include <unistd.h>
56#include <stdlib.h>
57
58/*
59 * Code for dealing with input/output redirection.
60 */
61
62#include "shell.h"
63#include "nodes.h"
64#include "jobs.h"
65#include "expand.h"
66#include "redir.h"
67#include "output.h"
68#include "memalloc.h"
69#include "error.h"
70#include "options.h"
71
72
73#define EMPTY -2 /* marks an unused slot in redirtab */
74#ifndef PIPE_BUF
75# define PIPESIZE 4096 /* amount of buffering in a pipe */
76#else
77# define PIPESIZE PIPE_BUF
78#endif
79
80
81MKINIT
82struct redirtab {
83 struct redirtab *next;
84 short renamed[10];
85};
86
87
88MKINIT struct redirtab *redirlist;
89
90/*
91 * We keep track of whether or not fd0 has been redirected. This is for
92 * background commands, where we want to redirect fd0 to /dev/null only
93 * if it hasn't already been redirected.
94*/
95int fd0_redirected = 0;
96
97/*
98 * We also keep track of where fd2 goes.
99 */
100int fd2 = 2;
101
102STATIC int openredirect (union node *);
103STATIC void dupredirect (union node *, int, char[10 ]);
104STATIC int openhere (union node *);
105STATIC int noclobberopen (const char *);
106
107
108/*
109 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
110 * old file descriptors are stashed away so that the redirection can be
111 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
112 * standard output, and the standard error if it becomes a duplicate of
113 * stdout, is saved in memory.
114 */
115
116void
117redirect(redir, flags)
118 union node *redir;
119 int flags;
120 {
121 union node *n;
122 struct redirtab *sv = NULL;
123 int i;
124 int fd;
125 int newfd;
126 int try;
127 char memory[10]; /* file descriptors to write to memory */
128
129 for (i = 10 ; --i >= 0 ; )
130 memory[i] = 0;
131 memory[1] = flags & REDIR_BACKQ;
132 if (flags & REDIR_PUSH) {
133 sv = ckmalloc(sizeof (struct redirtab));
134 for (i = 0 ; i < 10 ; i++)
135 sv->renamed[i] = EMPTY;
136 sv->next = redirlist;
137 redirlist = sv;
138 }
139 for (n = redir ; n ; n = n->nfile.next) {
140 fd = n->nfile.fd;
141 try = 0;
142 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
143 n->ndup.dupfd == fd)
144 continue; /* redirect from/to same file descriptor */
145
146 INTOFF;
147 newfd = openredirect(n);
148 if (((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) ||
149 (fd == fd2)) {
150 if (newfd == fd) {
151 try++;
152 } else if ((i = fcntl(fd, F_DUPFD, 10)) == -1) {
153 switch (errno) {
154 case EBADF:
155 if (!try) {
156 dupredirect(n, newfd, memory);
157 try++;
158 break;
159 }
160 /* FALLTHROUGH*/
161 default:
162 if (newfd >= 0) {
163 close(newfd);
164 }
165 INTON;
166 error("%d: %s", fd, strerror(errno));
167 /* NOTREACHED */
168 }
169 }
170 if (!try) {
171 close(fd);
172 if (flags & REDIR_PUSH) {
173 sv->renamed[fd] = i;
174 }
175 if (fd == fd2) {
176 fd2 = i;
177 }
178 }
179 } else if (fd != newfd) {
180 close(fd);
181 }
182 if (fd == 0)
183 fd0_redirected++;
184 if (!try)
185 dupredirect(n, newfd, memory);
186 INTON;
187 }
188 if (memory[1])
189 out1 = &memout;
190 if (memory[2])
191 out2 = &memout;
192}
193
194
195STATIC int
196openredirect(redir)
197 union node *redir;
198 {
199 char *fname;
200 int f;
201
202 switch (redir->nfile.type) {
203 case NFROM:
204 fname = redir->nfile.expfname;
205 if ((f = open(fname, O_RDONLY)) < 0)
206 goto eopen;
207 break;
208 case NFROMTO:
209 fname = redir->nfile.expfname;
210 if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
211 goto ecreate;
212 break;
213 case NTO:
214 /* Take care of noclobber mode. */
215 if (Cflag) {
216 fname = redir->nfile.expfname;
217 if ((f = noclobberopen(fname)) < 0)
218 goto ecreate;
219 break;
220 }
221 case NTOOV:
222 fname = redir->nfile.expfname;
223#ifdef O_CREAT
224 if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
225 goto ecreate;
226#else
227 if ((f = creat(fname, 0666)) < 0)
228 goto ecreate;
229#endif
230 break;
231 case NAPPEND:
232 fname = redir->nfile.expfname;
233#ifdef O_APPEND
234 if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
235 goto ecreate;
236#else
237 if ((f = open(fname, O_WRONLY)) < 0
238 && (f = creat(fname, 0666)) < 0)
239 goto ecreate;
240 lseek(f, (off_t)0, 2);
241#endif
242 break;
243 case NTOFD:
244 case NFROMFD:
245 f = -1;
246 break;
247 case NHERE:
248 case NXHERE:
249 f = openhere(redir);
250 break;
251 default:
252 abort();
253 }
254
255 return f;
256ecreate:
257 error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
258eopen:
259 error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
260}
261
262
263STATIC void
264dupredirect(redir, f, memory)
265 union node *redir;
266 int f;
267 char memory[10];
268 {
269 int fd = redir->nfile.fd;
270
271 memory[fd] = 0;
272 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
273 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
274 if (memory[redir->ndup.dupfd])
275 memory[fd] = 1;
276 else
277 copyfd(redir->ndup.dupfd, fd);
278 }
279 return;
280 }
281
282 if (f != fd) {
283 copyfd(f, fd);
284 close(f);
285 }
286 return;
287}
288
289
290/*
291 * Handle here documents. Normally we fork off a process to write the
292 * data to a pipe. If the document is short, we can stuff the data in
293 * the pipe without forking.
294 */
295
296STATIC int
297openhere(redir)
298 union node *redir;
299 {
300 int pip[2];
301 int len = 0;
302
303 if (pipe(pip) < 0)
304 error("Pipe call failed");
305 if (redir->type == NHERE) {
306 len = strlen(redir->nhere.doc->narg.text);
307 if (len <= PIPESIZE) {
308 xwrite(pip[1], redir->nhere.doc->narg.text, len);
309 goto out;
310 }
311 }
312 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
313 close(pip[0]);
314 signal(SIGINT, SIG_IGN);
315 signal(SIGQUIT, SIG_IGN);
316 signal(SIGHUP, SIG_IGN);
317#ifdef SIGTSTP
318 signal(SIGTSTP, SIG_IGN);
319#endif
320 signal(SIGPIPE, SIG_DFL);
321 if (redir->type == NHERE)
322 xwrite(pip[1], redir->nhere.doc->narg.text, len);
323 else
324 expandhere(redir->nhere.doc, pip[1]);
325 _exit(0);
326 }
327out:
328 close(pip[1]);
329 return pip[0];
330}
331
332
333
334/*
335 * Undo the effects of the last redirection.
336 */
337
338void
339popredir() {
340 struct redirtab *rp = redirlist;
341 int i;
342
343 INTOFF;
344 for (i = 0 ; i < 10 ; i++) {
345 if (rp->renamed[i] != EMPTY) {
346 if (i == 0)
347 fd0_redirected--;
348 close(i);
349 if (rp->renamed[i] >= 0) {
350 copyfd(rp->renamed[i], i);
351 close(rp->renamed[i]);
352 }
353 if (rp->renamed[i] == fd2) {
354 fd2 = i;
355 }
356 }
357 }
358 redirlist = rp->next;
359 ckfree(rp);
360 INTON;
361}
362
363/*
364 * Undo all redirections. Called on error or interrupt.
365 */
366
367#ifdef mkinit
368
369INCLUDE "redir.h"
370
371RESET {
372 while (redirlist)
373 popredir();
374}
375
376SHELLPROC {
377 clearredir();
378}
379
380#endif
381
382/* Return true if fd 0 has already been redirected at least once. */
383int
384fd0_redirected_p () {
385 return fd0_redirected != 0;
386}
387
388/*
389 * Discard all saved file descriptors.
390 */
391
392void
393clearredir() {
394 struct redirtab *rp;
395 int i;
396
397 for (rp = redirlist ; rp ; rp = rp->next) {
398 for (i = 0 ; i < 10 ; i++) {
399 if (rp->renamed[i] >= 0) {
400 close(rp->renamed[i]);
401 if (rp->renamed[i] == fd2) {
402 fd2 = -1;
403 }
404 }
405 rp->renamed[i] = EMPTY;
406 }
407 }
408}
409
410
411
412/*
413 * Copy a file descriptor to be >= to. Returns -1
414 * if the source file descriptor is closed, EMPTY if there are no unused
415 * file descriptors left.
416 */
417
418int
419copyfd(from, to)
420 int from;
421 int to;
422{
423 int newfd;
424
425 newfd = fcntl(from, F_DUPFD, to);
426 if (newfd < 0) {
427 if (errno == EMFILE)
428 return EMPTY;
429 else
430 error("%d: %s", from, strerror(errno));
431 }
432 return newfd;
433}
434
435/*
436 * Open a file in noclobber mode.
437 * The code was copied from bash.
438 */
439int
440noclobberopen(fname)
441 const char *fname;
442{
443 int r, fd;
444 struct stat finfo, finfo2;
445
446 /*
447 * If the file exists and is a regular file, return an error
448 * immediately.
449 */
450 r = stat(fname, &finfo);
451 if (r == 0 && S_ISREG(finfo.st_mode)) {
452 errno = EEXIST;
453 return -1;
454 }
455
456 /*
457 * If the file was not present (r != 0), make sure we open it
458 * exclusively so that if it is created before we open it, our open
459 * will fail. Make sure that we do not truncate an existing file.
460 * Note that we don't turn on O_EXCL unless the stat failed -- if the
461 * file was not a regular file, we leave O_EXCL off.
462 */
463 if (r != 0)
464 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
465 fd = open(fname, O_WRONLY|O_CREAT, 0666);
466
467 /* If the open failed, return the file descriptor right away. */
468 if (fd < 0)
469 return fd;
470
471 /*
472 * OK, the open succeeded, but the file may have been changed from a
473 * non-regular file to a regular file between the stat and the open.
474 * We are assuming that the O_EXCL open handles the case where FILENAME
475 * did not exist and is symlinked to an existing file between the stat
476 * and open.
477 */
478
479 /*
480 * If we can open it and fstat the file descriptor, and neither check
481 * revealed that it was a regular file, and the file has not been
482 * replaced, return the file descriptor.
483 */
484 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) &&
485 finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
486 return fd;
487
488 /* The file has been replaced. badness. */
489 close(fd);
490 errno = EEXIST;
491 return -1;
492}
Note: See TracBrowser for help on using the repository browser.