diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-02-23 21:10:47 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-02-23 21:10:47 +0000 |
| commit | 0dec6de38b721b4faae5567f8b4643df1055a175 (patch) | |
| tree | 80dca3727327907efc3e9d6d6261eecc248fa72e /shell | |
| parent | cc5715184bf91fa5a6bb7d4423e6c7aed71a98ef (diff) | |
| download | busybox-w32-0dec6de38b721b4faae5567f8b4643df1055a175.tar.gz busybox-w32-0dec6de38b721b4faae5567f8b4643df1055a175.tar.bz2 busybox-w32-0dec6de38b721b4faae5567f8b4643df1055a175.zip | |
ash: cleanup part 2.7
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/ash.c | 962 |
1 files changed, 471 insertions, 491 deletions
diff --git a/shell/ash.c b/shell/ash.c index 5c9060cad..1de4cb50b 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
| @@ -1593,6 +1593,21 @@ struct shparam { | |||
| 1593 | 1593 | ||
| 1594 | static struct shparam shellparam; /* $@ current positional parameters */ | 1594 | static struct shparam shellparam; /* $@ current positional parameters */ |
| 1595 | 1595 | ||
| 1596 | /* | ||
| 1597 | * Free the list of positional parameters. | ||
| 1598 | */ | ||
| 1599 | static void | ||
| 1600 | freeparam(volatile struct shparam *param) | ||
| 1601 | { | ||
| 1602 | char **ap; | ||
| 1603 | |||
| 1604 | if (param->malloc) { | ||
| 1605 | for (ap = param->p; *ap; ap++) | ||
| 1606 | free(*ap); | ||
| 1607 | free(param->p); | ||
| 1608 | } | ||
| 1609 | } | ||
| 1610 | |||
| 1596 | #if ENABLE_ASH_GETOPTS | 1611 | #if ENABLE_ASH_GETOPTS |
| 1597 | static void | 1612 | static void |
| 1598 | getoptsreset(const char *value) | 1613 | getoptsreset(const char *value) |
| @@ -2871,29 +2886,6 @@ static const char syntax_index_table[258] = { | |||
| 2871 | #endif /* USE_SIT_FUNCTION */ | 2886 | #endif /* USE_SIT_FUNCTION */ |
| 2872 | 2887 | ||
| 2873 | 2888 | ||
| 2874 | /* options.h */ | ||
| 2875 | |||
| 2876 | static void optschanged(void); | ||
| 2877 | static void setparam(char **); | ||
| 2878 | static void freeparam(volatile struct shparam *); | ||
| 2879 | static int shiftcmd(int, char **); | ||
| 2880 | static int setcmd(int, char **); | ||
| 2881 | static int nextopt(const char *); | ||
| 2882 | |||
| 2883 | |||
| 2884 | /* redir.h */ | ||
| 2885 | |||
| 2886 | /* flags passed to redirect */ | ||
| 2887 | #define REDIR_PUSH 01 /* save previous values of file descriptors */ | ||
| 2888 | #define REDIR_SAVEFD2 03 /* set preverrout */ | ||
| 2889 | |||
| 2890 | static void redirect(union node *, int); | ||
| 2891 | static void popredir(int); | ||
| 2892 | static void clearredir(int); | ||
| 2893 | static int copyfd(int, int); | ||
| 2894 | static int redirectsafe(union node *, int); | ||
| 2895 | |||
| 2896 | |||
| 2897 | /* ============ Alias handling */ | 2889 | /* ============ Alias handling */ |
| 2898 | 2890 | ||
| 2899 | #if ENABLE_ASH_ALIAS | 2891 | #if ENABLE_ASH_ALIAS |
| @@ -4622,6 +4614,357 @@ stoppedjobs(void) | |||
| 4622 | } | 4614 | } |
| 4623 | 4615 | ||
| 4624 | 4616 | ||
| 4617 | /* ============ redir.c | ||
| 4618 | * | ||
| 4619 | * Code for dealing with input/output redirection. | ||
| 4620 | */ | ||
| 4621 | |||
| 4622 | #define EMPTY -2 /* marks an unused slot in redirtab */ | ||
| 4623 | #ifndef PIPE_BUF | ||
| 4624 | # define PIPESIZE 4096 /* amount of buffering in a pipe */ | ||
| 4625 | #else | ||
| 4626 | # define PIPESIZE PIPE_BUF | ||
| 4627 | #endif | ||
| 4628 | |||
| 4629 | /* | ||
| 4630 | * Open a file in noclobber mode. | ||
| 4631 | * The code was copied from bash. | ||
| 4632 | */ | ||
| 4633 | static int | ||
| 4634 | noclobberopen(const char *fname) | ||
| 4635 | { | ||
| 4636 | int r, fd; | ||
| 4637 | struct stat finfo, finfo2; | ||
| 4638 | |||
| 4639 | /* | ||
| 4640 | * If the file exists and is a regular file, return an error | ||
| 4641 | * immediately. | ||
| 4642 | */ | ||
| 4643 | r = stat(fname, &finfo); | ||
| 4644 | if (r == 0 && S_ISREG(finfo.st_mode)) { | ||
| 4645 | errno = EEXIST; | ||
| 4646 | return -1; | ||
| 4647 | } | ||
| 4648 | |||
| 4649 | /* | ||
| 4650 | * If the file was not present (r != 0), make sure we open it | ||
| 4651 | * exclusively so that if it is created before we open it, our open | ||
| 4652 | * will fail. Make sure that we do not truncate an existing file. | ||
| 4653 | * Note that we don't turn on O_EXCL unless the stat failed -- if the | ||
| 4654 | * file was not a regular file, we leave O_EXCL off. | ||
| 4655 | */ | ||
| 4656 | if (r != 0) | ||
| 4657 | return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666); | ||
| 4658 | fd = open(fname, O_WRONLY|O_CREAT, 0666); | ||
| 4659 | |||
| 4660 | /* If the open failed, return the file descriptor right away. */ | ||
| 4661 | if (fd < 0) | ||
| 4662 | return fd; | ||
| 4663 | |||
| 4664 | /* | ||
| 4665 | * OK, the open succeeded, but the file may have been changed from a | ||
| 4666 | * non-regular file to a regular file between the stat and the open. | ||
| 4667 | * We are assuming that the O_EXCL open handles the case where FILENAME | ||
| 4668 | * did not exist and is symlinked to an existing file between the stat | ||
| 4669 | * and open. | ||
| 4670 | */ | ||
| 4671 | |||
| 4672 | /* | ||
| 4673 | * If we can open it and fstat the file descriptor, and neither check | ||
| 4674 | * revealed that it was a regular file, and the file has not been | ||
| 4675 | * replaced, return the file descriptor. | ||
| 4676 | */ | ||
| 4677 | if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) | ||
| 4678 | && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino) | ||
| 4679 | return fd; | ||
| 4680 | |||
| 4681 | /* The file has been replaced. badness. */ | ||
| 4682 | close(fd); | ||
| 4683 | errno = EEXIST; | ||
| 4684 | return -1; | ||
| 4685 | } | ||
| 4686 | |||
| 4687 | /* | ||
| 4688 | * Handle here documents. Normally we fork off a process to write the | ||
| 4689 | * data to a pipe. If the document is short, we can stuff the data in | ||
| 4690 | * the pipe without forking. | ||
| 4691 | */ | ||
| 4692 | /* openhere needs this forward reference */ | ||
| 4693 | static void expandhere(union node *arg, int fd); | ||
| 4694 | static int | ||
| 4695 | openhere(union node *redir) | ||
| 4696 | { | ||
| 4697 | int pip[2]; | ||
| 4698 | size_t len = 0; | ||
| 4699 | |||
| 4700 | if (pipe(pip) < 0) | ||
| 4701 | ash_msg_and_raise_error("Pipe call failed"); | ||
| 4702 | if (redir->type == NHERE) { | ||
| 4703 | len = strlen(redir->nhere.doc->narg.text); | ||
| 4704 | if (len <= PIPESIZE) { | ||
| 4705 | full_write(pip[1], redir->nhere.doc->narg.text, len); | ||
| 4706 | goto out; | ||
| 4707 | } | ||
| 4708 | } | ||
| 4709 | if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { | ||
| 4710 | close(pip[0]); | ||
| 4711 | signal(SIGINT, SIG_IGN); | ||
| 4712 | signal(SIGQUIT, SIG_IGN); | ||
| 4713 | signal(SIGHUP, SIG_IGN); | ||
| 4714 | #ifdef SIGTSTP | ||
| 4715 | signal(SIGTSTP, SIG_IGN); | ||
| 4716 | #endif | ||
| 4717 | signal(SIGPIPE, SIG_DFL); | ||
| 4718 | if (redir->type == NHERE) | ||
| 4719 | full_write(pip[1], redir->nhere.doc->narg.text, len); | ||
| 4720 | else | ||
| 4721 | expandhere(redir->nhere.doc, pip[1]); | ||
| 4722 | _exit(0); | ||
| 4723 | } | ||
| 4724 | out: | ||
| 4725 | close(pip[1]); | ||
| 4726 | return pip[0]; | ||
| 4727 | } | ||
| 4728 | |||
| 4729 | static int | ||
| 4730 | openredirect(union node *redir) | ||
| 4731 | { | ||
| 4732 | char *fname; | ||
| 4733 | int f; | ||
| 4734 | |||
| 4735 | switch (redir->nfile.type) { | ||
| 4736 | case NFROM: | ||
| 4737 | fname = redir->nfile.expfname; | ||
| 4738 | f = open(fname, O_RDONLY); | ||
| 4739 | if (f < 0) | ||
| 4740 | goto eopen; | ||
| 4741 | break; | ||
| 4742 | case NFROMTO: | ||
| 4743 | fname = redir->nfile.expfname; | ||
| 4744 | f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666); | ||
| 4745 | if (f < 0) | ||
| 4746 | goto ecreate; | ||
| 4747 | break; | ||
| 4748 | case NTO: | ||
| 4749 | /* Take care of noclobber mode. */ | ||
| 4750 | if (Cflag) { | ||
| 4751 | fname = redir->nfile.expfname; | ||
| 4752 | f = noclobberopen(fname); | ||
| 4753 | if (f < 0) | ||
| 4754 | goto ecreate; | ||
| 4755 | break; | ||
| 4756 | } | ||
| 4757 | /* FALLTHROUGH */ | ||
| 4758 | case NCLOBBER: | ||
| 4759 | fname = redir->nfile.expfname; | ||
| 4760 | f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666); | ||
| 4761 | if (f < 0) | ||
| 4762 | goto ecreate; | ||
| 4763 | break; | ||
| 4764 | case NAPPEND: | ||
| 4765 | fname = redir->nfile.expfname; | ||
| 4766 | f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666); | ||
| 4767 | if (f < 0) | ||
| 4768 | goto ecreate; | ||
| 4769 | break; | ||
| 4770 | default: | ||
| 4771 | #if DEBUG | ||
| 4772 | abort(); | ||
| 4773 | #endif | ||
| 4774 | /* Fall through to eliminate warning. */ | ||
| 4775 | case NTOFD: | ||
| 4776 | case NFROMFD: | ||
| 4777 | f = -1; | ||
| 4778 | break; | ||
| 4779 | case NHERE: | ||
| 4780 | case NXHERE: | ||
| 4781 | f = openhere(redir); | ||
| 4782 | break; | ||
| 4783 | } | ||
| 4784 | |||
| 4785 | return f; | ||
| 4786 | ecreate: | ||
| 4787 | ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "Directory nonexistent")); | ||
| 4788 | eopen: | ||
| 4789 | ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "No such file")); | ||
| 4790 | } | ||
| 4791 | |||
| 4792 | /* | ||
| 4793 | * Copy a file descriptor to be >= to. Returns -1 | ||
| 4794 | * if the source file descriptor is closed, EMPTY if there are no unused | ||
| 4795 | * file descriptors left. | ||
| 4796 | */ | ||
| 4797 | static int | ||
| 4798 | copyfd(int from, int to) | ||
| 4799 | { | ||
| 4800 | int newfd; | ||
| 4801 | |||
| 4802 | newfd = fcntl(from, F_DUPFD, to); | ||
| 4803 | if (newfd < 0) { | ||
| 4804 | if (errno == EMFILE) | ||
| 4805 | return EMPTY; | ||
| 4806 | ash_msg_and_raise_error("%d: %m", from); | ||
| 4807 | } | ||
| 4808 | return newfd; | ||
| 4809 | } | ||
| 4810 | |||
| 4811 | static void | ||
| 4812 | dupredirect(union node *redir, int f) | ||
| 4813 | { | ||
| 4814 | int fd = redir->nfile.fd; | ||
| 4815 | |||
| 4816 | if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) { | ||
| 4817 | if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ | ||
| 4818 | copyfd(redir->ndup.dupfd, fd); | ||
| 4819 | } | ||
| 4820 | return; | ||
| 4821 | } | ||
| 4822 | |||
| 4823 | if (f != fd) { | ||
| 4824 | copyfd(f, fd); | ||
| 4825 | close(f); | ||
| 4826 | } | ||
| 4827 | } | ||
| 4828 | |||
| 4829 | /* | ||
| 4830 | * Process a list of redirection commands. If the REDIR_PUSH flag is set, | ||
| 4831 | * old file descriptors are stashed away so that the redirection can be | ||
| 4832 | * undone by calling popredir. If the REDIR_BACKQ flag is set, then the | ||
| 4833 | * standard output, and the standard error if it becomes a duplicate of | ||
| 4834 | * stdout, is saved in memory. | ||
| 4835 | */ | ||
| 4836 | /* flags passed to redirect */ | ||
| 4837 | #define REDIR_PUSH 01 /* save previous values of file descriptors */ | ||
| 4838 | #define REDIR_SAVEFD2 03 /* set preverrout */ | ||
| 4839 | static void | ||
| 4840 | redirect(union node *redir, int flags) | ||
| 4841 | { | ||
| 4842 | union node *n; | ||
| 4843 | struct redirtab *sv; | ||
| 4844 | int i; | ||
| 4845 | int fd; | ||
| 4846 | int newfd; | ||
| 4847 | int *p; | ||
| 4848 | nullredirs++; | ||
| 4849 | if (!redir) { | ||
| 4850 | return; | ||
| 4851 | } | ||
| 4852 | sv = NULL; | ||
| 4853 | INT_OFF; | ||
| 4854 | if (flags & REDIR_PUSH) { | ||
| 4855 | struct redirtab *q; | ||
| 4856 | q = ckmalloc(sizeof(struct redirtab)); | ||
| 4857 | q->next = redirlist; | ||
| 4858 | redirlist = q; | ||
| 4859 | q->nullredirs = nullredirs - 1; | ||
| 4860 | for (i = 0; i < 10; i++) | ||
| 4861 | q->renamed[i] = EMPTY; | ||
| 4862 | nullredirs = 0; | ||
| 4863 | sv = q; | ||
| 4864 | } | ||
| 4865 | n = redir; | ||
| 4866 | do { | ||
| 4867 | fd = n->nfile.fd; | ||
| 4868 | if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) | ||
| 4869 | && n->ndup.dupfd == fd) | ||
| 4870 | continue; /* redirect from/to same file descriptor */ | ||
| 4871 | |||
| 4872 | newfd = openredirect(n); | ||
| 4873 | if (fd == newfd) | ||
| 4874 | continue; | ||
| 4875 | if (sv && *(p = &sv->renamed[fd]) == EMPTY) { | ||
| 4876 | i = fcntl(fd, F_DUPFD, 10); | ||
| 4877 | |||
| 4878 | if (i == -1) { | ||
| 4879 | i = errno; | ||
| 4880 | if (i != EBADF) { | ||
| 4881 | close(newfd); | ||
| 4882 | errno = i; | ||
| 4883 | ash_msg_and_raise_error("%d: %m", fd); | ||
| 4884 | /* NOTREACHED */ | ||
| 4885 | } | ||
| 4886 | } else { | ||
| 4887 | *p = i; | ||
| 4888 | close(fd); | ||
| 4889 | } | ||
| 4890 | } else { | ||
| 4891 | close(fd); | ||
| 4892 | } | ||
| 4893 | dupredirect(n, newfd); | ||
| 4894 | } while ((n = n->nfile.next)); | ||
| 4895 | INT_ON; | ||
| 4896 | if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0) | ||
| 4897 | preverrout_fd = sv->renamed[2]; | ||
| 4898 | } | ||
| 4899 | |||
| 4900 | /* | ||
| 4901 | * Undo the effects of the last redirection. | ||
| 4902 | */ | ||
| 4903 | static void | ||
| 4904 | popredir(int drop) | ||
| 4905 | { | ||
| 4906 | struct redirtab *rp; | ||
| 4907 | int i; | ||
| 4908 | |||
| 4909 | if (--nullredirs >= 0) | ||
| 4910 | return; | ||
| 4911 | INT_OFF; | ||
| 4912 | rp = redirlist; | ||
| 4913 | for (i = 0; i < 10; i++) { | ||
| 4914 | if (rp->renamed[i] != EMPTY) { | ||
| 4915 | if (!drop) { | ||
| 4916 | close(i); | ||
| 4917 | copyfd(rp->renamed[i], i); | ||
| 4918 | } | ||
| 4919 | close(rp->renamed[i]); | ||
| 4920 | } | ||
| 4921 | } | ||
| 4922 | redirlist = rp->next; | ||
| 4923 | nullredirs = rp->nullredirs; | ||
| 4924 | free(rp); | ||
| 4925 | INT_ON; | ||
| 4926 | } | ||
| 4927 | |||
| 4928 | /* | ||
| 4929 | * Undo all redirections. Called on error or interrupt. | ||
| 4930 | */ | ||
| 4931 | |||
| 4932 | /* | ||
| 4933 | * Discard all saved file descriptors. | ||
| 4934 | */ | ||
| 4935 | static void | ||
| 4936 | clearredir(int drop) | ||
| 4937 | { | ||
| 4938 | for (;;) { | ||
| 4939 | nullredirs = 0; | ||
| 4940 | if (!redirlist) | ||
| 4941 | break; | ||
| 4942 | popredir(drop); | ||
| 4943 | } | ||
| 4944 | } | ||
| 4945 | |||
| 4946 | static int | ||
| 4947 | redirectsafe(union node *redir, int flags) | ||
| 4948 | { | ||
| 4949 | int err; | ||
| 4950 | volatile int saveint; | ||
| 4951 | struct jmploc *volatile savehandler = exception_handler; | ||
| 4952 | struct jmploc jmploc; | ||
| 4953 | |||
| 4954 | SAVE_INT(saveint); | ||
| 4955 | err = setjmp(jmploc.loc) * 2; | ||
| 4956 | if (!err) { | ||
| 4957 | exception_handler = &jmploc; | ||
| 4958 | redirect(redir, flags); | ||
| 4959 | } | ||
| 4960 | exception_handler = savehandler; | ||
| 4961 | if (err && exception != EXERROR) | ||
| 4962 | longjmp(exception_handler->loc, 1); | ||
| 4963 | RESTORE_INT(saveint); | ||
| 4964 | return err; | ||
| 4965 | } | ||
| 4966 | |||
| 4967 | |||
| 4625 | /* ============ Routines to expand arguments to commands | 4968 | /* ============ Routines to expand arguments to commands |
| 4626 | * | 4969 | * |
| 4627 | * We have to deal with backquotes, shell variables, and file metacharacters. | 4970 | * We have to deal with backquotes, shell variables, and file metacharacters. |
| @@ -7475,6 +7818,58 @@ evalpipe(union node *n, int flags) | |||
| 7475 | INT_ON; | 7818 | INT_ON; |
| 7476 | } | 7819 | } |
| 7477 | 7820 | ||
| 7821 | /* | ||
| 7822 | * Controls whether the shell is interactive or not. | ||
| 7823 | */ | ||
| 7824 | static void | ||
| 7825 | setinteractive(int on) | ||
| 7826 | { | ||
| 7827 | static int is_interactive; | ||
| 7828 | |||
| 7829 | if (++on == is_interactive) | ||
| 7830 | return; | ||
| 7831 | is_interactive = on; | ||
| 7832 | setsignal(SIGINT); | ||
| 7833 | setsignal(SIGQUIT); | ||
| 7834 | setsignal(SIGTERM); | ||
| 7835 | #if !ENABLE_FEATURE_SH_EXTRA_QUIET | ||
| 7836 | if (is_interactive > 1) { | ||
| 7837 | /* Looks like they want an interactive shell */ | ||
| 7838 | static smallint do_banner; | ||
| 7839 | |||
| 7840 | if (!do_banner) { | ||
| 7841 | out1fmt( | ||
| 7842 | "\n\n" | ||
| 7843 | "%s Built-in shell (ash)\n" | ||
| 7844 | "Enter 'help' for a list of built-in commands." | ||
| 7845 | "\n\n", | ||
| 7846 | BB_BANNER); | ||
| 7847 | do_banner = 1; | ||
| 7848 | } | ||
| 7849 | } | ||
| 7850 | #endif | ||
| 7851 | } | ||
| 7852 | |||
| 7853 | #if ENABLE_FEATURE_EDITING_VI | ||
| 7854 | #define setvimode(on) do { \ | ||
| 7855 | if (on) line_input_state->flags |= VI_MODE; \ | ||
| 7856 | else line_input_state->flags &= ~VI_MODE; \ | ||
| 7857 | } while (0) | ||
| 7858 | #else | ||
| 7859 | #define setvimode(on) viflag = 0 /* forcibly keep the option off */ | ||
| 7860 | #endif | ||
| 7861 | |||
| 7862 | static void | ||
| 7863 | optschanged(void) | ||
| 7864 | { | ||
| 7865 | #if DEBUG | ||
| 7866 | opentrace(); | ||
| 7867 | #endif | ||
| 7868 | setinteractive(iflag); | ||
| 7869 | setjobctl(mflag); | ||
| 7870 | setvimode(viflag); | ||
| 7871 | } | ||
| 7872 | |||
| 7478 | static struct localvar *localvars; | 7873 | static struct localvar *localvars; |
| 7479 | 7874 | ||
| 7480 | /* | 7875 | /* |
| @@ -7894,7 +8289,7 @@ evalcommand(union node *cmd, int flags) | |||
| 7894 | 8289 | ||
| 7895 | preverrout_fd = 2; | 8290 | preverrout_fd = 2; |
| 7896 | expredir(cmd->ncmd.redirect); | 8291 | expredir(cmd->ncmd.redirect); |
| 7897 | status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH|REDIR_SAVEFD2); | 8292 | status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2); |
| 7898 | 8293 | ||
| 7899 | path = vpath.text; | 8294 | path = vpath.text; |
| 7900 | for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) { | 8295 | for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) { |
| @@ -8678,88 +9073,35 @@ changemail(const char *val) | |||
| 8678 | /* ============ ??? */ | 9073 | /* ============ ??? */ |
| 8679 | 9074 | ||
| 8680 | /* | 9075 | /* |
| 8681 | * Take commands from a file. To be compatible we should do a path | 9076 | * Set the shell parameters. |
| 8682 | * search for the file, which is necessary to find sub-commands. | ||
| 8683 | */ | ||
| 8684 | static char * | ||
| 8685 | find_dot_file(char *name) | ||
| 8686 | { | ||
| 8687 | char *fullname; | ||
| 8688 | const char *path = pathval(); | ||
| 8689 | struct stat statb; | ||
| 8690 | |||
| 8691 | /* don't try this for absolute or relative paths */ | ||
| 8692 | if (strchr(name, '/')) | ||
| 8693 | return name; | ||
| 8694 | |||
| 8695 | while ((fullname = padvance(&path, name)) != NULL) { | ||
| 8696 | if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { | ||
| 8697 | /* | ||
| 8698 | * Don't bother freeing here, since it will | ||
| 8699 | * be freed by the caller. | ||
| 8700 | */ | ||
| 8701 | return fullname; | ||
| 8702 | } | ||
| 8703 | stunalloc(fullname); | ||
| 8704 | } | ||
| 8705 | |||
| 8706 | /* not found in the PATH */ | ||
| 8707 | ash_msg_and_raise_error("%s: not found", name); | ||
| 8708 | /* NOTREACHED */ | ||
| 8709 | } | ||
| 8710 | |||
| 8711 | /* | ||
| 8712 | * Controls whether the shell is interactive or not. | ||
| 8713 | */ | 9077 | */ |
| 8714 | static void | 9078 | static void |
| 8715 | setinteractive(int on) | 9079 | setparam(char **argv) |
| 8716 | { | 9080 | { |
| 8717 | static int is_interactive; | 9081 | char **newparam; |
| 8718 | 9082 | char **ap; | |
| 8719 | if (++on == is_interactive) | 9083 | int nparam; |
| 8720 | return; | ||
| 8721 | is_interactive = on; | ||
| 8722 | setsignal(SIGINT); | ||
| 8723 | setsignal(SIGQUIT); | ||
| 8724 | setsignal(SIGTERM); | ||
| 8725 | #if !ENABLE_FEATURE_SH_EXTRA_QUIET | ||
| 8726 | if (is_interactive > 1) { | ||
| 8727 | /* Looks like they want an interactive shell */ | ||
| 8728 | static smallint do_banner; | ||
| 8729 | 9084 | ||
| 8730 | if (!do_banner) { | 9085 | for (nparam = 0; argv[nparam]; nparam++); |
| 8731 | out1fmt( | 9086 | ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap)); |
| 8732 | "\n\n" | 9087 | while (*argv) { |
| 8733 | "%s Built-in shell (ash)\n" | 9088 | *ap++ = ckstrdup(*argv++); |
| 8734 | "Enter 'help' for a list of built-in commands." | ||
| 8735 | "\n\n", | ||
| 8736 | BB_BANNER); | ||
| 8737 | do_banner = 1; | ||
| 8738 | } | ||
| 8739 | } | 9089 | } |
| 9090 | *ap = NULL; | ||
| 9091 | freeparam(&shellparam); | ||
| 9092 | shellparam.malloc = 1; | ||
| 9093 | shellparam.nparam = nparam; | ||
| 9094 | shellparam.p = newparam; | ||
| 9095 | #if ENABLE_ASH_GETOPTS | ||
| 9096 | shellparam.optind = 1; | ||
| 9097 | shellparam.optoff = -1; | ||
| 8740 | #endif | 9098 | #endif |
| 8741 | } | 9099 | } |
| 8742 | 9100 | ||
| 8743 | #if ENABLE_FEATURE_EDITING_VI | 9101 | /* |
| 8744 | #define setvimode(on) do { \ | 9102 | * Process shell options. The global variable argptr contains a pointer |
| 8745 | if (on) line_input_state->flags |= VI_MODE; \ | 9103 | * to the argument list; we advance it past the options. |
| 8746 | else line_input_state->flags &= ~VI_MODE; \ | 9104 | */ |
| 8747 | } while (0) | ||
| 8748 | #else | ||
| 8749 | #define setvimode(on) viflag = 0 /* forcibly keep the option off */ | ||
| 8750 | #endif | ||
| 8751 | |||
| 8752 | static void | ||
| 8753 | optschanged(void) | ||
| 8754 | { | ||
| 8755 | #if DEBUG | ||
| 8756 | opentrace(); | ||
| 8757 | #endif | ||
| 8758 | setinteractive(iflag); | ||
| 8759 | setjobctl(mflag); | ||
| 8760 | setvimode(viflag); | ||
| 8761 | } | ||
| 8762 | |||
| 8763 | static void | 9105 | static void |
| 8764 | minus_o(char *name, int val) | 9106 | minus_o(char *name, int val) |
| 8765 | { | 9107 | { |
| @@ -8779,7 +9121,6 @@ minus_o(char *name, int val) | |||
| 8779 | out1fmt("%-16s%s\n", optnames(i), | 9121 | out1fmt("%-16s%s\n", optnames(i), |
| 8780 | optlist[i] ? "on" : "off"); | 9122 | optlist[i] ? "on" : "off"); |
| 8781 | } | 9123 | } |
| 8782 | |||
| 8783 | static void | 9124 | static void |
| 8784 | setoption(int flag, int val) | 9125 | setoption(int flag, int val) |
| 8785 | { | 9126 | { |
| @@ -8794,11 +9135,6 @@ setoption(int flag, int val) | |||
| 8794 | ash_msg_and_raise_error("Illegal option -%c", flag); | 9135 | ash_msg_and_raise_error("Illegal option -%c", flag); |
| 8795 | /* NOTREACHED */ | 9136 | /* NOTREACHED */ |
| 8796 | } | 9137 | } |
| 8797 | |||
| 8798 | /* | ||
| 8799 | * Process shell options. The global variable argptr contains a pointer | ||
| 8800 | * to the argument list; we advance it past the options. | ||
| 8801 | */ | ||
| 8802 | static void | 9138 | static void |
| 8803 | options(int cmdline) | 9139 | options(int cmdline) |
| 8804 | { | 9140 | { |
| @@ -8849,47 +9185,6 @@ options(int cmdline) | |||
| 8849 | } | 9185 | } |
| 8850 | 9186 | ||
| 8851 | /* | 9187 | /* |
| 8852 | * Set the shell parameters. | ||
| 8853 | */ | ||
| 8854 | static void | ||
| 8855 | setparam(char **argv) | ||
| 8856 | { | ||
| 8857 | char **newparam; | ||
| 8858 | char **ap; | ||
| 8859 | int nparam; | ||
| 8860 | |||
| 8861 | for (nparam = 0; argv[nparam]; nparam++); | ||
| 8862 | ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap)); | ||
| 8863 | while (*argv) { | ||
| 8864 | *ap++ = ckstrdup(*argv++); | ||
| 8865 | } | ||
| 8866 | *ap = NULL; | ||
| 8867 | freeparam(&shellparam); | ||
| 8868 | shellparam.malloc = 1; | ||
| 8869 | shellparam.nparam = nparam; | ||
| 8870 | shellparam.p = newparam; | ||
| 8871 | #if ENABLE_ASH_GETOPTS | ||
| 8872 | shellparam.optind = 1; | ||
| 8873 | shellparam.optoff = -1; | ||
| 8874 | #endif | ||
| 8875 | } | ||
| 8876 | |||
| 8877 | /* | ||
| 8878 | * Free the list of positional parameters. | ||
| 8879 | */ | ||
| 8880 | static void | ||
| 8881 | freeparam(volatile struct shparam *param) | ||
| 8882 | { | ||
| 8883 | char **ap; | ||
| 8884 | |||
| 8885 | if (param->malloc) { | ||
| 8886 | for (ap = param->p; *ap; ap++) | ||
| 8887 | free(*ap); | ||
| 8888 | free(param->p); | ||
| 8889 | } | ||
| 8890 | } | ||
| 8891 | |||
| 8892 | /* | ||
| 8893 | * The shift builtin command. | 9188 | * The shift builtin command. |
| 8894 | */ | 9189 | */ |
| 8895 | static int | 9190 | static int |
| @@ -10720,6 +11015,37 @@ cmdloop(int top) | |||
| 10720 | return 0; | 11015 | return 0; |
| 10721 | } | 11016 | } |
| 10722 | 11017 | ||
| 11018 | /* | ||
| 11019 | * Take commands from a file. To be compatible we should do a path | ||
| 11020 | * search for the file, which is necessary to find sub-commands. | ||
| 11021 | */ | ||
| 11022 | static char * | ||
| 11023 | find_dot_file(char *name) | ||
| 11024 | { | ||
| 11025 | char *fullname; | ||
| 11026 | const char *path = pathval(); | ||
| 11027 | struct stat statb; | ||
| 11028 | |||
| 11029 | /* don't try this for absolute or relative paths */ | ||
| 11030 | if (strchr(name, '/')) | ||
| 11031 | return name; | ||
| 11032 | |||
| 11033 | while ((fullname = padvance(&path, name)) != NULL) { | ||
| 11034 | if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { | ||
| 11035 | /* | ||
| 11036 | * Don't bother freeing here, since it will | ||
| 11037 | * be freed by the caller. | ||
| 11038 | */ | ||
| 11039 | return fullname; | ||
| 11040 | } | ||
| 11041 | stunalloc(fullname); | ||
| 11042 | } | ||
| 11043 | |||
| 11044 | /* not found in the PATH */ | ||
| 11045 | ash_msg_and_raise_error("%s: not found", name); | ||
| 11046 | /* NOTREACHED */ | ||
| 11047 | } | ||
| 11048 | |||
| 10723 | static int | 11049 | static int |
| 10724 | dotcmd(int argc, char **argv) | 11050 | dotcmd(int argc, char **argv) |
| 10725 | { | 11051 | { |
| @@ -10982,352 +11308,6 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
| 10982 | } | 11308 | } |
| 10983 | 11309 | ||
| 10984 | 11310 | ||
| 10985 | /* ============ redir.c | ||
| 10986 | * | ||
| 10987 | * Code for dealing with input/output redirection. | ||
| 10988 | */ | ||
| 10989 | |||
| 10990 | #define EMPTY -2 /* marks an unused slot in redirtab */ | ||
| 10991 | #ifndef PIPE_BUF | ||
| 10992 | # define PIPESIZE 4096 /* amount of buffering in a pipe */ | ||
| 10993 | #else | ||
| 10994 | # define PIPESIZE PIPE_BUF | ||
| 10995 | #endif | ||
| 10996 | |||
| 10997 | /* | ||
| 10998 | * Open a file in noclobber mode. | ||
| 10999 | * The code was copied from bash. | ||
| 11000 | */ | ||
| 11001 | static int | ||
| 11002 | noclobberopen(const char *fname) | ||
| 11003 | { | ||
| 11004 | int r, fd; | ||
| 11005 | struct stat finfo, finfo2; | ||
| 11006 | |||
| 11007 | /* | ||
| 11008 | * If the file exists and is a regular file, return an error | ||
| 11009 | * immediately. | ||
| 11010 | */ | ||
| 11011 | r = stat(fname, &finfo); | ||
| 11012 | if (r == 0 && S_ISREG(finfo.st_mode)) { | ||
| 11013 | errno = EEXIST; | ||
| 11014 | return -1; | ||
| 11015 | } | ||
| 11016 | |||
| 11017 | /* | ||
| 11018 | * If the file was not present (r != 0), make sure we open it | ||
| 11019 | * exclusively so that if it is created before we open it, our open | ||
| 11020 | * will fail. Make sure that we do not truncate an existing file. | ||
| 11021 | * Note that we don't turn on O_EXCL unless the stat failed -- if the | ||
| 11022 | * file was not a regular file, we leave O_EXCL off. | ||
| 11023 | */ | ||
| 11024 | if (r != 0) | ||
| 11025 | return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666); | ||
| 11026 | fd = open(fname, O_WRONLY|O_CREAT, 0666); | ||
| 11027 | |||
| 11028 | /* If the open failed, return the file descriptor right away. */ | ||
| 11029 | if (fd < 0) | ||
| 11030 | return fd; | ||
| 11031 | |||
| 11032 | /* | ||
| 11033 | * OK, the open succeeded, but the file may have been changed from a | ||
| 11034 | * non-regular file to a regular file between the stat and the open. | ||
| 11035 | * We are assuming that the O_EXCL open handles the case where FILENAME | ||
| 11036 | * did not exist and is symlinked to an existing file between the stat | ||
| 11037 | * and open. | ||
| 11038 | */ | ||
| 11039 | |||
| 11040 | /* | ||
| 11041 | * If we can open it and fstat the file descriptor, and neither check | ||
| 11042 | * revealed that it was a regular file, and the file has not been | ||
| 11043 | * replaced, return the file descriptor. | ||
| 11044 | */ | ||
| 11045 | if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) | ||
| 11046 | && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino) | ||
| 11047 | return fd; | ||
| 11048 | |||
| 11049 | /* The file has been replaced. badness. */ | ||
| 11050 | close(fd); | ||
| 11051 | errno = EEXIST; | ||
| 11052 | return -1; | ||
| 11053 | } | ||
| 11054 | |||
| 11055 | /* | ||
| 11056 | * Handle here documents. Normally we fork off a process to write the | ||
| 11057 | * data to a pipe. If the document is short, we can stuff the data in | ||
| 11058 | * the pipe without forking. | ||
| 11059 | */ | ||
| 11060 | static int | ||
| 11061 | openhere(union node *redir) | ||
| 11062 | { | ||
| 11063 | int pip[2]; | ||
| 11064 | size_t len = 0; | ||
| 11065 | |||
| 11066 | if (pipe(pip) < 0) | ||
| 11067 | ash_msg_and_raise_error("Pipe call failed"); | ||
| 11068 | if (redir->type == NHERE) { | ||
| 11069 | len = strlen(redir->nhere.doc->narg.text); | ||
| 11070 | if (len <= PIPESIZE) { | ||
| 11071 | full_write(pip[1], redir->nhere.doc->narg.text, len); | ||
| 11072 | goto out; | ||
| 11073 | } | ||
| 11074 | } | ||
| 11075 | if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { | ||
| 11076 | close(pip[0]); | ||
| 11077 | signal(SIGINT, SIG_IGN); | ||
| 11078 | signal(SIGQUIT, SIG_IGN); | ||
| 11079 | signal(SIGHUP, SIG_IGN); | ||
| 11080 | #ifdef SIGTSTP | ||
| 11081 | signal(SIGTSTP, SIG_IGN); | ||
| 11082 | #endif | ||
| 11083 | signal(SIGPIPE, SIG_DFL); | ||
| 11084 | if (redir->type == NHERE) | ||
| 11085 | full_write(pip[1], redir->nhere.doc->narg.text, len); | ||
| 11086 | else | ||
| 11087 | expandhere(redir->nhere.doc, pip[1]); | ||
| 11088 | _exit(0); | ||
| 11089 | } | ||
| 11090 | out: | ||
| 11091 | close(pip[1]); | ||
| 11092 | return pip[0]; | ||
| 11093 | } | ||
| 11094 | |||
| 11095 | static int | ||
| 11096 | openredirect(union node *redir) | ||
| 11097 | { | ||
| 11098 | char *fname; | ||
| 11099 | int f; | ||
| 11100 | |||
| 11101 | switch (redir->nfile.type) { | ||
| 11102 | case NFROM: | ||
| 11103 | fname = redir->nfile.expfname; | ||
| 11104 | f = open(fname, O_RDONLY); | ||
| 11105 | if (f < 0) | ||
| 11106 | goto eopen; | ||
| 11107 | break; | ||
| 11108 | case NFROMTO: | ||
| 11109 | fname = redir->nfile.expfname; | ||
| 11110 | f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666); | ||
| 11111 | if (f < 0) | ||
| 11112 | goto ecreate; | ||
| 11113 | break; | ||
| 11114 | case NTO: | ||
| 11115 | /* Take care of noclobber mode. */ | ||
| 11116 | if (Cflag) { | ||
| 11117 | fname = redir->nfile.expfname; | ||
| 11118 | f = noclobberopen(fname); | ||
| 11119 | if (f < 0) | ||
| 11120 | goto ecreate; | ||
| 11121 | break; | ||
| 11122 | } | ||
| 11123 | /* FALLTHROUGH */ | ||
| 11124 | case NCLOBBER: | ||
| 11125 | fname = redir->nfile.expfname; | ||
| 11126 | f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666); | ||
| 11127 | if (f < 0) | ||
| 11128 | goto ecreate; | ||
| 11129 | break; | ||
| 11130 | case NAPPEND: | ||
| 11131 | fname = redir->nfile.expfname; | ||
| 11132 | f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666); | ||
| 11133 | if (f < 0) | ||
| 11134 | goto ecreate; | ||
| 11135 | break; | ||
| 11136 | default: | ||
| 11137 | #if DEBUG | ||
| 11138 | abort(); | ||
| 11139 | #endif | ||
| 11140 | /* Fall through to eliminate warning. */ | ||
| 11141 | case NTOFD: | ||
| 11142 | case NFROMFD: | ||
| 11143 | f = -1; | ||
| 11144 | break; | ||
| 11145 | case NHERE: | ||
| 11146 | case NXHERE: | ||
| 11147 | f = openhere(redir); | ||
| 11148 | break; | ||
| 11149 | } | ||
| 11150 | |||
| 11151 | return f; | ||
| 11152 | ecreate: | ||
| 11153 | ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "Directory nonexistent")); | ||
| 11154 | eopen: | ||
| 11155 | ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "No such file")); | ||
| 11156 | } | ||
| 11157 | |||
| 11158 | static void | ||
| 11159 | dupredirect(union node *redir, int f) | ||
| 11160 | { | ||
| 11161 | int fd = redir->nfile.fd; | ||
| 11162 | |||
| 11163 | if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) { | ||
| 11164 | if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ | ||
| 11165 | copyfd(redir->ndup.dupfd, fd); | ||
| 11166 | } | ||
| 11167 | return; | ||
| 11168 | } | ||
| 11169 | |||
| 11170 | if (f != fd) { | ||
| 11171 | copyfd(f, fd); | ||
| 11172 | close(f); | ||
| 11173 | } | ||
| 11174 | } | ||
| 11175 | |||
| 11176 | /* | ||
| 11177 | * Process a list of redirection commands. If the REDIR_PUSH flag is set, | ||
| 11178 | * old file descriptors are stashed away so that the redirection can be | ||
| 11179 | * undone by calling popredir. If the REDIR_BACKQ flag is set, then the | ||
| 11180 | * standard output, and the standard error if it becomes a duplicate of | ||
| 11181 | * stdout, is saved in memory. | ||
| 11182 | */ | ||
| 11183 | static void | ||
| 11184 | redirect(union node *redir, int flags) | ||
| 11185 | { | ||
| 11186 | union node *n; | ||
| 11187 | struct redirtab *sv; | ||
| 11188 | int i; | ||
| 11189 | int fd; | ||
| 11190 | int newfd; | ||
| 11191 | int *p; | ||
| 11192 | nullredirs++; | ||
| 11193 | if (!redir) { | ||
| 11194 | return; | ||
| 11195 | } | ||
| 11196 | sv = NULL; | ||
| 11197 | INT_OFF; | ||
| 11198 | if (flags & REDIR_PUSH) { | ||
| 11199 | struct redirtab *q; | ||
| 11200 | q = ckmalloc(sizeof(struct redirtab)); | ||
| 11201 | q->next = redirlist; | ||
| 11202 | redirlist = q; | ||
| 11203 | q->nullredirs = nullredirs - 1; | ||
| 11204 | for (i = 0; i < 10; i++) | ||
| 11205 | q->renamed[i] = EMPTY; | ||
| 11206 | nullredirs = 0; | ||
| 11207 | sv = q; | ||
| 11208 | } | ||
| 11209 | n = redir; | ||
| 11210 | do { | ||
| 11211 | fd = n->nfile.fd; | ||
| 11212 | if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) | ||
| 11213 | && n->ndup.dupfd == fd) | ||
| 11214 | continue; /* redirect from/to same file descriptor */ | ||
| 11215 | |||
| 11216 | newfd = openredirect(n); | ||
| 11217 | if (fd == newfd) | ||
| 11218 | continue; | ||
| 11219 | if (sv && *(p = &sv->renamed[fd]) == EMPTY) { | ||
| 11220 | i = fcntl(fd, F_DUPFD, 10); | ||
| 11221 | |||
| 11222 | if (i == -1) { | ||
| 11223 | i = errno; | ||
| 11224 | if (i != EBADF) { | ||
| 11225 | close(newfd); | ||
| 11226 | errno = i; | ||
| 11227 | ash_msg_and_raise_error("%d: %m", fd); | ||
| 11228 | /* NOTREACHED */ | ||
| 11229 | } | ||
| 11230 | } else { | ||
| 11231 | *p = i; | ||
| 11232 | close(fd); | ||
| 11233 | } | ||
| 11234 | } else { | ||
| 11235 | close(fd); | ||
| 11236 | } | ||
| 11237 | dupredirect(n, newfd); | ||
| 11238 | } while ((n = n->nfile.next)); | ||
| 11239 | INT_ON; | ||
| 11240 | if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0) | ||
| 11241 | preverrout_fd = sv->renamed[2]; | ||
| 11242 | } | ||
| 11243 | |||
| 11244 | /* | ||
| 11245 | * Undo the effects of the last redirection. | ||
| 11246 | */ | ||
| 11247 | static void | ||
| 11248 | popredir(int drop) | ||
| 11249 | { | ||
| 11250 | struct redirtab *rp; | ||
| 11251 | int i; | ||
| 11252 | |||
| 11253 | if (--nullredirs >= 0) | ||
| 11254 | return; | ||
| 11255 | INT_OFF; | ||
| 11256 | rp = redirlist; | ||
| 11257 | for (i = 0; i < 10; i++) { | ||
| 11258 | if (rp->renamed[i] != EMPTY) { | ||
| 11259 | if (!drop) { | ||
| 11260 | close(i); | ||
| 11261 | copyfd(rp->renamed[i], i); | ||
| 11262 | } | ||
| 11263 | close(rp->renamed[i]); | ||
| 11264 | } | ||
| 11265 | } | ||
| 11266 | redirlist = rp->next; | ||
| 11267 | nullredirs = rp->nullredirs; | ||
| 11268 | free(rp); | ||
| 11269 | INT_ON; | ||
| 11270 | } | ||
| 11271 | |||
| 11272 | /* | ||
| 11273 | * Undo all redirections. Called on error or interrupt. | ||
| 11274 | */ | ||
| 11275 | |||
| 11276 | /* | ||
| 11277 | * Discard all saved file descriptors. | ||
| 11278 | */ | ||
| 11279 | static void | ||
| 11280 | clearredir(int drop) | ||
| 11281 | { | ||
| 11282 | for (;;) { | ||
| 11283 | nullredirs = 0; | ||
| 11284 | if (!redirlist) | ||
| 11285 | break; | ||
| 11286 | popredir(drop); | ||
| 11287 | } | ||
| 11288 | } | ||
| 11289 | |||
| 11290 | /* | ||
| 11291 | * Copy a file descriptor to be >= to. Returns -1 | ||
| 11292 | * if the source file descriptor is closed, EMPTY if there are no unused | ||
| 11293 | * file descriptors left. | ||
| 11294 | */ | ||
| 11295 | static int | ||
| 11296 | copyfd(int from, int to) | ||
| 11297 | { | ||
| 11298 | int newfd; | ||
| 11299 | |||
| 11300 | newfd = fcntl(from, F_DUPFD, to); | ||
| 11301 | if (newfd < 0) { | ||
| 11302 | if (errno == EMFILE) | ||
| 11303 | return EMPTY; | ||
| 11304 | ash_msg_and_raise_error("%d: %m", from); | ||
| 11305 | } | ||
| 11306 | return newfd; | ||
| 11307 | } | ||
| 11308 | |||
| 11309 | static int | ||
| 11310 | redirectsafe(union node *redir, int flags) | ||
| 11311 | { | ||
| 11312 | int err; | ||
| 11313 | volatile int saveint; | ||
| 11314 | struct jmploc *volatile savehandler = exception_handler; | ||
| 11315 | struct jmploc jmploc; | ||
| 11316 | |||
| 11317 | SAVE_INT(saveint); | ||
| 11318 | err = setjmp(jmploc.loc) * 2; | ||
| 11319 | if (!err) { | ||
| 11320 | exception_handler = &jmploc; | ||
| 11321 | redirect(redir, flags); | ||
| 11322 | } | ||
| 11323 | exception_handler = savehandler; | ||
| 11324 | if (err && exception != EXERROR) | ||
| 11325 | longjmp(exception_handler->loc, 1); | ||
| 11326 | RESTORE_INT(saveint); | ||
| 11327 | return err; | ||
| 11328 | } | ||
| 11329 | |||
| 11330 | |||
| 11331 | /* ============ trap.c */ | 11311 | /* ============ trap.c */ |
| 11332 | 11312 | ||
| 11333 | /* | 11313 | /* |
