diff options
author | Eric Andersen <andersen@codepoet.org> | 2000-07-17 19:14:41 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2000-07-17 19:14:41 +0000 |
commit | a1d187a8a8a75cf36a45c8d6e0de86964eb2056f (patch) | |
tree | 94705b86b9a570bb1a97513bd8ec3e2e9f8466e5 /sh.c | |
parent | bc0aed79a8c7bb24c32a21533893fdad660b7292 (diff) | |
download | busybox-w32-a1d187a8a8a75cf36a45c8d6e0de86964eb2056f.tar.gz busybox-w32-a1d187a8a8a75cf36a45c8d6e0de86964eb2056f.tar.bz2 busybox-w32-a1d187a8a8a75cf36a45c8d6e0de86964eb2056f.zip |
Backtick support to infinite (memory limited) levels of nesting is
now implemented... So now busybox shell can do cool stuff like:
/home/andersen/CVS/busybox # echo foo `wc README` bar
foo 71 422 2951 README bar
I love writing cool new features.... Muhahahaha... (I think this is
leaking a little bit of memory every time it expands a backtick process,
so I still needs to do a bit of cleanup...)
-Erik
Diffstat (limited to 'sh.c')
-rw-r--r-- | sh.c | 115 |
1 files changed, 85 insertions, 30 deletions
@@ -25,6 +25,11 @@ | |||
25 | * | 25 | * |
26 | */ | 26 | */ |
27 | 27 | ||
28 | |||
29 | #define BB_FEATURE_SH_BACKTICKS | ||
30 | |||
31 | |||
32 | |||
28 | #include "internal.h" | 33 | #include "internal.h" |
29 | #include <stdio.h> | 34 | #include <stdio.h> |
30 | #include <stdlib.h> | 35 | #include <stdlib.h> |
@@ -108,7 +113,7 @@ static void checkJobs(struct jobSet *jobList); | |||
108 | static int getCommand(FILE * source, char *command); | 113 | static int getCommand(FILE * source, char *command); |
109 | static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg); | 114 | static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg); |
110 | static int setupRedirections(struct childProgram *prog); | 115 | static int setupRedirections(struct childProgram *prog); |
111 | static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg); | 116 | static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]); |
112 | static int busy_loop(FILE * input); | 117 | static int busy_loop(FILE * input); |
113 | 118 | ||
114 | 119 | ||
@@ -420,6 +425,10 @@ static void checkJobs(struct jobSet *jobList) | |||
420 | break; | 425 | break; |
421 | } | 426 | } |
422 | 427 | ||
428 | /* This happens on backticked commands */ | ||
429 | if(job==NULL) | ||
430 | return; | ||
431 | |||
423 | if (WIFEXITED(status) || WIFSIGNALED(status)) { | 432 | if (WIFEXITED(status) || WIFSIGNALED(status)) { |
424 | /* child exited */ | 433 | /* child exited */ |
425 | job->runningProgs--; | 434 | job->runningProgs--; |
@@ -697,7 +706,7 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi | |||
697 | if (*prog->argv[argc]) | 706 | if (*prog->argv[argc]) |
698 | argc++; | 707 | argc++; |
699 | if (!argc) { | 708 | if (!argc) { |
700 | errorMsg("empty command in pipe1\n"); | 709 | errorMsg("empty command in pipe\n"); |
701 | freeJob(job); | 710 | freeJob(job); |
702 | job->numProgs=0; | 711 | job->numProgs=0; |
703 | return 1; | 712 | return 1; |
@@ -723,7 +732,7 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi | |||
723 | src++; | 732 | src++; |
724 | 733 | ||
725 | if (!*src) { | 734 | if (!*src) { |
726 | errorMsg("empty command in pipe2\n"); | 735 | errorMsg("empty command in pipe\n"); |
727 | freeJob(job); | 736 | freeJob(job); |
728 | job->numProgs=0; | 737 | job->numProgs=0; |
729 | return 1; | 738 | return 1; |
@@ -749,12 +758,16 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi | |||
749 | if (*src == '*' || *src == '[' || *src == ']' | 758 | if (*src == '*' || *src == '[' || *src == ']' |
750 | || *src == '?') *buf++ = '\\'; | 759 | || *src == '?') *buf++ = '\\'; |
751 | /* fallthrough */ | 760 | /* fallthrough */ |
761 | #ifdef BB_FEATURE_SH_BACKTICKS | ||
752 | case '`': | 762 | case '`': |
753 | /* Exec a backtick-ed command */ | 763 | /* Exec a backtick-ed command */ |
754 | { | 764 | { |
755 | char* newcmd=NULL; | 765 | char* charptr1=NULL, *charptr2; |
756 | char* ptr=NULL; | 766 | char* ptr=NULL; |
757 | struct job newJob; | 767 | struct job *newJob; |
768 | struct jobSet njobList = { NULL, NULL }; | ||
769 | int pipefd[2]; | ||
770 | int size; | ||
758 | 771 | ||
759 | ptr=strchr(++src, '`'); | 772 | ptr=strchr(++src, '`'); |
760 | if (ptr==NULL) { | 773 | if (ptr==NULL) { |
@@ -763,19 +776,56 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi | |||
763 | return 1; | 776 | return 1; |
764 | } | 777 | } |
765 | 778 | ||
766 | newcmd = xmalloc(1+ptr-src); | 779 | /* Make a copy of any stuff left over in the command |
767 | snprintf(newcmd, 1+ptr-src, src); | 780 | * line after the second backtick */ |
768 | 781 | charptr2 = xmalloc(strlen(ptr)+1); | |
769 | if (!parseCommand(&newcmd, &newJob, jobList, isBg) && | 782 | memcpy(charptr2, ptr+1, strlen(ptr)); |
770 | newJob.numProgs) { | 783 | |
771 | runCommand(&newJob, jobList, *isBg); | 784 | /* Make some space to hold just the backticked command */ |
785 | charptr1 = xmalloc(1+ptr-src); | ||
786 | snprintf(charptr1, 1+ptr-src, src); | ||
787 | newJob = xmalloc(sizeof(struct job)); | ||
788 | /* Now parse and run the backticked command */ | ||
789 | if (!parseCommand(&charptr1, newJob, &njobList, isBg) | ||
790 | && newJob->numProgs) { | ||
791 | pipe(pipefd); | ||
792 | runCommand(newJob, &njobList, 0, pipefd); | ||
772 | } | 793 | } |
773 | 794 | checkJobs(jobList); | |
774 | /* Clip out the the backticked command from the string */ | 795 | free(charptr1); |
775 | memmove(--src, ptr, strlen(ptr)+1); | 796 | |
776 | free(newcmd); | 797 | /* Copy the output from the backtick-ed command into the |
798 | * command line, making extra room as needed */ | ||
799 | --src; | ||
800 | charptr1 = xmalloc(BUFSIZ); | ||
801 | while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) { | ||
802 | int newSize=src - *commandPtr + size + 1 + strlen(charptr2); | ||
803 | if (newSize > BUFSIZ) { | ||
804 | *commandPtr=realloc(*commandPtr, src - *commandPtr + | ||
805 | size + 1 + strlen(charptr2)); | ||
806 | } | ||
807 | memcpy(src, charptr1, size); | ||
808 | src+=size; | ||
809 | } | ||
810 | free(charptr1); | ||
811 | close(pipefd[0]); | ||
812 | if (*(src-1)=='\n') | ||
813 | --src; | ||
814 | |||
815 | /* Now paste into the *commandPtr all the stuff | ||
816 | * leftover after the second backtick */ | ||
817 | memcpy(src, charptr2, strlen(charptr2)); | ||
818 | fprintf(stderr,"*commandPtr='%s'\n", *commandPtr); | ||
819 | free(charptr2); | ||
820 | |||
821 | |||
822 | /* Now recursively call parseCommand to deal with the new | ||
823 | * and improved version of the command line with the backtick | ||
824 | * results expanded in place... */ | ||
825 | return(parseCommand(commandPtr, job, jobList, isBg)); | ||
777 | } | 826 | } |
778 | break; | 827 | break; |
828 | #endif // BB_FEATURE_SH_BACKTICKS | ||
779 | default: | 829 | default: |
780 | *buf++ = *src; | 830 | *buf++ = *src; |
781 | } | 831 | } |
@@ -810,25 +860,23 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi | |||
810 | } | 860 | } |
811 | 861 | ||
812 | 862 | ||
813 | static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg) | 863 | static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]) |
814 | { | 864 | { |
815 | struct job *job; | 865 | struct job *job; |
866 | int nextin=0, nextout, stdoutfd=fileno(stdout); | ||
816 | int i; | 867 | int i; |
817 | int nextin, nextout; | ||
818 | int pipefds[2]; /* pipefd[0] is for reading */ | 868 | int pipefds[2]; /* pipefd[0] is for reading */ |
819 | struct builtInCommand *x; | 869 | struct builtInCommand *x; |
820 | #ifdef BB_FEATURE_SH_STANDALONE_SHELL | 870 | #ifdef BB_FEATURE_SH_STANDALONE_SHELL |
821 | const struct BB_applet *a = applets; | 871 | const struct BB_applet *a = applets; |
822 | #endif | 872 | #endif |
823 | 873 | ||
824 | |||
825 | nextin = 0, nextout = 1; | ||
826 | for (i = 0; i < newJob->numProgs; i++) { | 874 | for (i = 0; i < newJob->numProgs; i++) { |
827 | if ((i + 1) < newJob->numProgs) { | 875 | if ((i + 1) < newJob->numProgs) { |
828 | pipe(pipefds); | 876 | pipe(pipefds); |
829 | nextout = pipefds[1]; | 877 | nextout = pipefds[1]; |
830 | } else { | 878 | } else { |
831 | nextout = 1; | 879 | nextout = stdoutfd; |
832 | } | 880 | } |
833 | 881 | ||
834 | /* Check if the command matches any non-forking builtins */ | 882 | /* Check if the command matches any non-forking builtins */ |
@@ -841,16 +889,17 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg) | |||
841 | if (!(newJob->progs[i].pid = fork())) { | 889 | if (!(newJob->progs[i].pid = fork())) { |
842 | signal(SIGTTOU, SIG_DFL); | 890 | signal(SIGTTOU, SIG_DFL); |
843 | 891 | ||
844 | if (nextin != 0) { | 892 | if (outPipe[1]!=-1) { |
845 | dup2(nextin, 0); | 893 | close(outPipe[0]); |
846 | close(nextin); | 894 | nextout = stdoutfd = outPipe[1]; |
847 | } | ||
848 | |||
849 | if (nextout != 1) { | ||
850 | dup2(nextout, 1); | 895 | dup2(nextout, 1); |
896 | dup2(nextout, 2); | ||
851 | close(nextout); | 897 | close(nextout); |
852 | } | 898 | } |
853 | 899 | ||
900 | //dup2(nextin, 0); | ||
901 | //close(nextin); | ||
902 | |||
854 | /* explicit redirections override pipes */ | 903 | /* explicit redirections override pipes */ |
855 | setupRedirections(newJob->progs + i); | 904 | setupRedirections(newJob->progs + i); |
856 | 905 | ||
@@ -862,8 +911,8 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg) | |||
862 | } | 911 | } |
863 | #ifdef BB_FEATURE_SH_STANDALONE_SHELL | 912 | #ifdef BB_FEATURE_SH_STANDALONE_SHELL |
864 | /* Check if the command matches any busybox internal commands here */ | 913 | /* Check if the command matches any busybox internal commands here */ |
865 | /* TODO: Add matching when paths are appended (i.e. 'cat' currently | 914 | /* TODO: Add matching on commands with paths appended (i.e. 'cat' |
866 | * works, but '/bin/cat' doesn't ) */ | 915 | * currently works, but '/bin/cat' doesn't ) */ |
867 | while (a->name != 0) { | 916 | while (a->name != 0) { |
868 | if (strcmp(newJob->progs[i].argv[0], a->name) == 0) { | 917 | if (strcmp(newJob->progs[i].argv[0], a->name) == 0) { |
869 | int argc; | 918 | int argc; |
@@ -879,6 +928,9 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg) | |||
879 | fatalError("%s: %s\n", newJob->progs[i].argv[0], | 928 | fatalError("%s: %s\n", newJob->progs[i].argv[0], |
880 | strerror(errno)); | 929 | strerror(errno)); |
881 | } | 930 | } |
931 | if (outPipe[1]!=-1) { | ||
932 | close(outPipe[1]); | ||
933 | } | ||
882 | 934 | ||
883 | /* put our child in the process group whose leader is the | 935 | /* put our child in the process group whose leader is the |
884 | first process in this pipe */ | 936 | first process in this pipe */ |
@@ -886,7 +938,7 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg) | |||
886 | 938 | ||
887 | if (nextin != 0) | 939 | if (nextin != 0) |
888 | close(nextin); | 940 | close(nextin); |
889 | if (nextout != 1) | 941 | if (nextout != stdoutfd) |
890 | close(nextout); | 942 | close(nextout); |
891 | 943 | ||
892 | /* If there isn't another process, nextin is garbage | 944 | /* If there isn't another process, nextin is garbage |
@@ -1007,9 +1059,12 @@ static int busy_loop(FILE * input) | |||
1007 | 1059 | ||
1008 | if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) && | 1060 | if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) && |
1009 | newJob.numProgs) { | 1061 | newJob.numProgs) { |
1010 | runCommand(&newJob, &jobList, inBg); | 1062 | int pipefds[2] = {-1,-1}; |
1063 | runCommand(&newJob, &jobList, inBg, pipefds); | ||
1011 | } else { | 1064 | } else { |
1012 | nextCommand=NULL; | 1065 | nextCommand=NULL; |
1066 | free(command); | ||
1067 | command = (char *) calloc(BUFSIZ, sizeof(char)); | ||
1013 | } | 1068 | } |
1014 | } else { | 1069 | } else { |
1015 | /* a job is running in the foreground; wait for it */ | 1070 | /* a job is running in the foreground; wait for it */ |