diff options
author | Eric Andersen <andersen@codepoet.org> | 2001-05-07 17:59:25 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2001-05-07 17:59:25 +0000 |
commit | 83a2ae2184f5a77adfc4c0207aee5d195991a1ab (patch) | |
tree | 1d7aef0d43ccb92fd704280dc621cc6f33d5a4ef | |
parent | 7e1273edf7a75f2797ea5c0d95d33ff1056f21d8 (diff) | |
download | busybox-w32-83a2ae2184f5a77adfc4c0207aee5d195991a1ab.tar.gz busybox-w32-83a2ae2184f5a77adfc4c0207aee5d195991a1ab.tar.bz2 busybox-w32-83a2ae2184f5a77adfc4c0207aee5d195991a1ab.zip |
More hush updates from Larry:
Update some comments. Generate partial placeholders for the missing
builtins. Write builtin_umask. Properly treat exec without arguments
as a means to open/close files within the running script. Implement
"4<&-" that encodes for file descriptor closure.
-rw-r--r-- | hush.c | 73 | ||||
-rw-r--r-- | shell/hush.c | 73 |
2 files changed, 108 insertions, 38 deletions
@@ -45,24 +45,25 @@ | |||
45 | * fancy forms of Parameter Expansion | 45 | * fancy forms of Parameter Expansion |
46 | * Arithmetic Expansion | 46 | * Arithmetic Expansion |
47 | * <(list) and >(list) Process Substitution | 47 | * <(list) and >(list) Process Substitution |
48 | * reserved words: case, esac, function | 48 | * reserved words: case, esac, select, function |
49 | * Here Documents ( << word ) | 49 | * Here Documents ( << word ) |
50 | * Functions | 50 | * Functions |
51 | * Major bugs: | 51 | * Major bugs: |
52 | * job handling woefully incomplete and buggy | 52 | * job handling woefully incomplete and buggy |
53 | * reserved word execution woefully incomplete and buggy | 53 | * reserved word execution woefully incomplete and buggy |
54 | * to-do: | 54 | * to-do: |
55 | * port selected bugfixes from post-0.49 busybox lash | 55 | * port selected bugfixes from post-0.49 busybox lash - done? |
56 | * finish implementing reserved words | 56 | * finish implementing reserved words: for, while, until, do, done |
57 | * change { and } from special chars to reserved words | ||
58 | * builtins: break, continue, eval, return, set, trap, ulimit | ||
59 | * test magic exec | ||
57 | * handle children going into background | 60 | * handle children going into background |
58 | * clean up recognition of null pipes | 61 | * clean up recognition of null pipes |
59 | * have builtin_exec set flag to avoid restore_redirects | 62 | * have builtin_exec set flag to avoid restore_redirects |
60 | * figure out if "echo foo}" is fixable | ||
61 | * check setting of global_argc and global_argv | 63 | * check setting of global_argc and global_argv |
62 | * control-C handling, probably with longjmp | 64 | * control-C handling, probably with longjmp |
63 | * VAR=value prefix for simple commands | 65 | * VAR=value prefix for simple commands |
64 | * follow IFS rules more precisely, including update semantics | 66 | * follow IFS rules more precisely, including update semantics |
65 | * write builtin_eval, builtin_ulimit, builtin_umask | ||
66 | * figure out what to do with backslash-newline | 67 | * figure out what to do with backslash-newline |
67 | * explain why we use signal instead of sigaction | 68 | * explain why we use signal instead of sigaction |
68 | * propagate syntax errors, die on resource errors? | 69 | * propagate syntax errors, die on resource errors? |
@@ -97,6 +98,7 @@ | |||
97 | #include <fcntl.h> | 98 | #include <fcntl.h> |
98 | #include <getopt.h> /* should be pretty obvious */ | 99 | #include <getopt.h> /* should be pretty obvious */ |
99 | 100 | ||
101 | #include <sys/stat.h> /* ulimit */ | ||
100 | #include <sys/types.h> | 102 | #include <sys/types.h> |
101 | #include <sys/wait.h> | 103 | #include <sys/wait.h> |
102 | #include <signal.h> | 104 | #include <signal.h> |
@@ -323,9 +325,9 @@ static int builtin_pwd(struct child_prog *child); | |||
323 | static int builtin_read(struct child_prog *child); | 325 | static int builtin_read(struct child_prog *child); |
324 | static int builtin_shift(struct child_prog *child); | 326 | static int builtin_shift(struct child_prog *child); |
325 | static int builtin_source(struct child_prog *child); | 327 | static int builtin_source(struct child_prog *child); |
326 | static int builtin_ulimit(struct child_prog *child); | ||
327 | static int builtin_umask(struct child_prog *child); | 328 | static int builtin_umask(struct child_prog *child); |
328 | static int builtin_unset(struct child_prog *child); | 329 | static int builtin_unset(struct child_prog *child); |
330 | static int builtin_not_written(struct child_prog *child); | ||
329 | /* o_string manipulation: */ | 331 | /* o_string manipulation: */ |
330 | static int b_check_space(o_string *o, int len); | 332 | static int b_check_space(o_string *o, int len); |
331 | static int b_addchr(o_string *o, int ch); | 333 | static int b_addchr(o_string *o, int ch); |
@@ -390,8 +392,11 @@ static void free_pipe(struct pipe *pi); | |||
390 | * still be set at the end. */ | 392 | * still be set at the end. */ |
391 | static struct built_in_command bltins[] = { | 393 | static struct built_in_command bltins[] = { |
392 | {"bg", "Resume a job in the background", builtin_fg_bg}, | 394 | {"bg", "Resume a job in the background", builtin_fg_bg}, |
395 | {"break", "Exit for, while or until loop", builtin_not_written}, | ||
393 | {"cd", "Change working directory", builtin_cd}, | 396 | {"cd", "Change working directory", builtin_cd}, |
397 | {"continue", "Continue for, while or until loop", builtin_not_written}, | ||
394 | {"env", "Print all environment variables", builtin_env}, | 398 | {"env", "Print all environment variables", builtin_env}, |
399 | {"eval", "Construct and run shell command", builtin_not_written}, | ||
395 | {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec}, | 400 | {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec}, |
396 | {"exit", "Exit from shell()", builtin_exit}, | 401 | {"exit", "Exit from shell()", builtin_exit}, |
397 | {"export", "Set environment variable", builtin_export}, | 402 | {"export", "Set environment variable", builtin_export}, |
@@ -399,8 +404,11 @@ static struct built_in_command bltins[] = { | |||
399 | {"jobs", "Lists the active jobs", builtin_jobs}, | 404 | {"jobs", "Lists the active jobs", builtin_jobs}, |
400 | {"pwd", "Print current directory", builtin_pwd}, | 405 | {"pwd", "Print current directory", builtin_pwd}, |
401 | {"read", "Input environment variable", builtin_read}, | 406 | {"read", "Input environment variable", builtin_read}, |
407 | {"return", "Return from a function", builtin_not_written}, | ||
408 | {"set", "Set/unset shell options", builtin_not_written}, | ||
402 | {"shift", "Shift positional parameters", builtin_shift}, | 409 | {"shift", "Shift positional parameters", builtin_shift}, |
403 | {"ulimit","Controls resource limits", builtin_ulimit}, | 410 | {"trap", "Trap signals", builtin_not_written}, |
411 | {"ulimit","Controls resource limits", builtin_not_written}, | ||
404 | {"umask","Sets file creation mask", builtin_umask}, | 412 | {"umask","Sets file creation mask", builtin_umask}, |
405 | {"unset", "Unset environment variable", builtin_unset}, | 413 | {"unset", "Unset environment variable", builtin_unset}, |
406 | {".", "Source-in and run commands in a file", builtin_source}, | 414 | {".", "Source-in and run commands in a file", builtin_source}, |
@@ -640,16 +648,21 @@ static int builtin_source(struct child_prog *child) | |||
640 | return (status); | 648 | return (status); |
641 | } | 649 | } |
642 | 650 | ||
643 | static int builtin_ulimit(struct child_prog *child) | ||
644 | { | ||
645 | printf("builtin_ulimit not written\n"); | ||
646 | return EXIT_FAILURE; | ||
647 | } | ||
648 | |||
649 | static int builtin_umask(struct child_prog *child) | 651 | static int builtin_umask(struct child_prog *child) |
650 | { | 652 | { |
651 | printf("builtin_umask not written\n"); | 653 | mode_t new_umask; |
652 | return EXIT_FAILURE; | 654 | const char *arg = child->argv[1]; |
655 | char *end; | ||
656 | if (arg) { | ||
657 | new_umask=strtoul(arg, &end, 8); | ||
658 | if (*end!='\0' || end == arg) { | ||
659 | return EXIT_FAILURE; | ||
660 | } | ||
661 | } else { | ||
662 | printf("%.3o\n", (unsigned int) (new_umask=umask(0))); | ||
663 | } | ||
664 | umask(new_umask); | ||
665 | return EXIT_SUCCESS; | ||
653 | } | 666 | } |
654 | 667 | ||
655 | /* built-in 'unset VAR' handler */ | 668 | /* built-in 'unset VAR' handler */ |
@@ -663,6 +676,12 @@ static int builtin_unset(struct child_prog *child) | |||
663 | return EXIT_SUCCESS; | 676 | return EXIT_SUCCESS; |
664 | } | 677 | } |
665 | 678 | ||
679 | static int builtin_not_written(struct child_prog *child) | ||
680 | { | ||
681 | printf("builtin_%s not written\n",child->argv[0]); | ||
682 | return EXIT_FAILURE; | ||
683 | } | ||
684 | |||
666 | static int b_check_space(o_string *o, int len) | 685 | static int b_check_space(o_string *o, int len) |
667 | { | 686 | { |
668 | /* It would be easy to drop a more restrictive policy | 687 | /* It would be easy to drop a more restrictive policy |
@@ -926,8 +945,12 @@ static int setup_redirects(struct child_prog *prog, int squirrel[]) | |||
926 | if (squirrel && redir->fd < 3) { | 945 | if (squirrel && redir->fd < 3) { |
927 | squirrel[redir->fd] = dup(redir->fd); | 946 | squirrel[redir->fd] = dup(redir->fd); |
928 | } | 947 | } |
929 | dup2(openfd, redir->fd); | 948 | if (openfd == -3) { |
930 | close(openfd); | 949 | close(openfd); |
950 | } else { | ||
951 | dup2(openfd, redir->fd); | ||
952 | close(openfd); | ||
953 | } | ||
931 | } | 954 | } |
932 | } | 955 | } |
933 | return 0; | 956 | return 0; |
@@ -1216,6 +1239,11 @@ static int run_pipe_real(struct pipe *pi) | |||
1216 | if (strcmp(child->argv[0], x->cmd) == 0 ) { | 1239 | if (strcmp(child->argv[0], x->cmd) == 0 ) { |
1217 | int squirrel[] = {-1, -1, -1}; | 1240 | int squirrel[] = {-1, -1, -1}; |
1218 | int rcode; | 1241 | int rcode; |
1242 | if (x->function == builtin_exec && child->argv[1]==NULL) { | ||
1243 | debug_printf("magic exec\n"); | ||
1244 | setup_redirects(child,NULL); | ||
1245 | return EXIT_SUCCESS; | ||
1246 | } | ||
1219 | debug_printf("builtin inline %s\n", child->argv[0]); | 1247 | debug_printf("builtin inline %s\n", child->argv[0]); |
1220 | /* XXX setup_redirects acts on file descriptors, not FILEs. | 1248 | /* XXX setup_redirects acts on file descriptors, not FILEs. |
1221 | * This is perfect for work that comes after exec(). | 1249 | * This is perfect for work that comes after exec(). |
@@ -1569,7 +1597,8 @@ static int setup_redirect(struct p_context *ctx, int fd, redir_type style, | |||
1569 | if (redir->dup == -2) return 1; /* syntax error */ | 1597 | if (redir->dup == -2) return 1; /* syntax error */ |
1570 | if (redir->dup != -1) { | 1598 | if (redir->dup != -1) { |
1571 | /* Erik had a check here that the file descriptor in question | 1599 | /* Erik had a check here that the file descriptor in question |
1572 | * is legit; I postpone that to "run time" */ | 1600 | * is legit; I postpone that to "run time" |
1601 | * A "-" representation of "close me" shows up as a -3 here */ | ||
1573 | debug_printf("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup); | 1602 | debug_printf("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup); |
1574 | } else { | 1603 | } else { |
1575 | /* We do _not_ try to open the file that src points to, | 1604 | /* We do _not_ try to open the file that src points to, |
@@ -1775,10 +1804,16 @@ static int redirect_dup_num(struct in_str *input) | |||
1775 | if (ch != '&') return -1; | 1804 | if (ch != '&') return -1; |
1776 | 1805 | ||
1777 | b_getch(input); /* get the & */ | 1806 | b_getch(input); /* get the & */ |
1778 | while (ch=b_peek(input),isdigit(ch)) { | 1807 | ch=b_peek(input); |
1808 | if (ch == '-') { | ||
1809 | b_getch(input); | ||
1810 | return -3; /* "-" represents "close me" */ | ||
1811 | } | ||
1812 | while (isdigit(ch)) { | ||
1779 | d = d*10+(ch-'0'); | 1813 | d = d*10+(ch-'0'); |
1780 | ok=1; | 1814 | ok=1; |
1781 | b_getch(input); | 1815 | b_getch(input); |
1816 | ch = b_peek(input); | ||
1782 | } | 1817 | } |
1783 | if (ok) return d; | 1818 | if (ok) return d; |
1784 | 1819 | ||
diff --git a/shell/hush.c b/shell/hush.c index b8e4a55c2..e58ac44b3 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -45,24 +45,25 @@ | |||
45 | * fancy forms of Parameter Expansion | 45 | * fancy forms of Parameter Expansion |
46 | * Arithmetic Expansion | 46 | * Arithmetic Expansion |
47 | * <(list) and >(list) Process Substitution | 47 | * <(list) and >(list) Process Substitution |
48 | * reserved words: case, esac, function | 48 | * reserved words: case, esac, select, function |
49 | * Here Documents ( << word ) | 49 | * Here Documents ( << word ) |
50 | * Functions | 50 | * Functions |
51 | * Major bugs: | 51 | * Major bugs: |
52 | * job handling woefully incomplete and buggy | 52 | * job handling woefully incomplete and buggy |
53 | * reserved word execution woefully incomplete and buggy | 53 | * reserved word execution woefully incomplete and buggy |
54 | * to-do: | 54 | * to-do: |
55 | * port selected bugfixes from post-0.49 busybox lash | 55 | * port selected bugfixes from post-0.49 busybox lash - done? |
56 | * finish implementing reserved words | 56 | * finish implementing reserved words: for, while, until, do, done |
57 | * change { and } from special chars to reserved words | ||
58 | * builtins: break, continue, eval, return, set, trap, ulimit | ||
59 | * test magic exec | ||
57 | * handle children going into background | 60 | * handle children going into background |
58 | * clean up recognition of null pipes | 61 | * clean up recognition of null pipes |
59 | * have builtin_exec set flag to avoid restore_redirects | 62 | * have builtin_exec set flag to avoid restore_redirects |
60 | * figure out if "echo foo}" is fixable | ||
61 | * check setting of global_argc and global_argv | 63 | * check setting of global_argc and global_argv |
62 | * control-C handling, probably with longjmp | 64 | * control-C handling, probably with longjmp |
63 | * VAR=value prefix for simple commands | 65 | * VAR=value prefix for simple commands |
64 | * follow IFS rules more precisely, including update semantics | 66 | * follow IFS rules more precisely, including update semantics |
65 | * write builtin_eval, builtin_ulimit, builtin_umask | ||
66 | * figure out what to do with backslash-newline | 67 | * figure out what to do with backslash-newline |
67 | * explain why we use signal instead of sigaction | 68 | * explain why we use signal instead of sigaction |
68 | * propagate syntax errors, die on resource errors? | 69 | * propagate syntax errors, die on resource errors? |
@@ -97,6 +98,7 @@ | |||
97 | #include <fcntl.h> | 98 | #include <fcntl.h> |
98 | #include <getopt.h> /* should be pretty obvious */ | 99 | #include <getopt.h> /* should be pretty obvious */ |
99 | 100 | ||
101 | #include <sys/stat.h> /* ulimit */ | ||
100 | #include <sys/types.h> | 102 | #include <sys/types.h> |
101 | #include <sys/wait.h> | 103 | #include <sys/wait.h> |
102 | #include <signal.h> | 104 | #include <signal.h> |
@@ -323,9 +325,9 @@ static int builtin_pwd(struct child_prog *child); | |||
323 | static int builtin_read(struct child_prog *child); | 325 | static int builtin_read(struct child_prog *child); |
324 | static int builtin_shift(struct child_prog *child); | 326 | static int builtin_shift(struct child_prog *child); |
325 | static int builtin_source(struct child_prog *child); | 327 | static int builtin_source(struct child_prog *child); |
326 | static int builtin_ulimit(struct child_prog *child); | ||
327 | static int builtin_umask(struct child_prog *child); | 328 | static int builtin_umask(struct child_prog *child); |
328 | static int builtin_unset(struct child_prog *child); | 329 | static int builtin_unset(struct child_prog *child); |
330 | static int builtin_not_written(struct child_prog *child); | ||
329 | /* o_string manipulation: */ | 331 | /* o_string manipulation: */ |
330 | static int b_check_space(o_string *o, int len); | 332 | static int b_check_space(o_string *o, int len); |
331 | static int b_addchr(o_string *o, int ch); | 333 | static int b_addchr(o_string *o, int ch); |
@@ -390,8 +392,11 @@ static void free_pipe(struct pipe *pi); | |||
390 | * still be set at the end. */ | 392 | * still be set at the end. */ |
391 | static struct built_in_command bltins[] = { | 393 | static struct built_in_command bltins[] = { |
392 | {"bg", "Resume a job in the background", builtin_fg_bg}, | 394 | {"bg", "Resume a job in the background", builtin_fg_bg}, |
395 | {"break", "Exit for, while or until loop", builtin_not_written}, | ||
393 | {"cd", "Change working directory", builtin_cd}, | 396 | {"cd", "Change working directory", builtin_cd}, |
397 | {"continue", "Continue for, while or until loop", builtin_not_written}, | ||
394 | {"env", "Print all environment variables", builtin_env}, | 398 | {"env", "Print all environment variables", builtin_env}, |
399 | {"eval", "Construct and run shell command", builtin_not_written}, | ||
395 | {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec}, | 400 | {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec}, |
396 | {"exit", "Exit from shell()", builtin_exit}, | 401 | {"exit", "Exit from shell()", builtin_exit}, |
397 | {"export", "Set environment variable", builtin_export}, | 402 | {"export", "Set environment variable", builtin_export}, |
@@ -399,8 +404,11 @@ static struct built_in_command bltins[] = { | |||
399 | {"jobs", "Lists the active jobs", builtin_jobs}, | 404 | {"jobs", "Lists the active jobs", builtin_jobs}, |
400 | {"pwd", "Print current directory", builtin_pwd}, | 405 | {"pwd", "Print current directory", builtin_pwd}, |
401 | {"read", "Input environment variable", builtin_read}, | 406 | {"read", "Input environment variable", builtin_read}, |
407 | {"return", "Return from a function", builtin_not_written}, | ||
408 | {"set", "Set/unset shell options", builtin_not_written}, | ||
402 | {"shift", "Shift positional parameters", builtin_shift}, | 409 | {"shift", "Shift positional parameters", builtin_shift}, |
403 | {"ulimit","Controls resource limits", builtin_ulimit}, | 410 | {"trap", "Trap signals", builtin_not_written}, |
411 | {"ulimit","Controls resource limits", builtin_not_written}, | ||
404 | {"umask","Sets file creation mask", builtin_umask}, | 412 | {"umask","Sets file creation mask", builtin_umask}, |
405 | {"unset", "Unset environment variable", builtin_unset}, | 413 | {"unset", "Unset environment variable", builtin_unset}, |
406 | {".", "Source-in and run commands in a file", builtin_source}, | 414 | {".", "Source-in and run commands in a file", builtin_source}, |
@@ -640,16 +648,21 @@ static int builtin_source(struct child_prog *child) | |||
640 | return (status); | 648 | return (status); |
641 | } | 649 | } |
642 | 650 | ||
643 | static int builtin_ulimit(struct child_prog *child) | ||
644 | { | ||
645 | printf("builtin_ulimit not written\n"); | ||
646 | return EXIT_FAILURE; | ||
647 | } | ||
648 | |||
649 | static int builtin_umask(struct child_prog *child) | 651 | static int builtin_umask(struct child_prog *child) |
650 | { | 652 | { |
651 | printf("builtin_umask not written\n"); | 653 | mode_t new_umask; |
652 | return EXIT_FAILURE; | 654 | const char *arg = child->argv[1]; |
655 | char *end; | ||
656 | if (arg) { | ||
657 | new_umask=strtoul(arg, &end, 8); | ||
658 | if (*end!='\0' || end == arg) { | ||
659 | return EXIT_FAILURE; | ||
660 | } | ||
661 | } else { | ||
662 | printf("%.3o\n", (unsigned int) (new_umask=umask(0))); | ||
663 | } | ||
664 | umask(new_umask); | ||
665 | return EXIT_SUCCESS; | ||
653 | } | 666 | } |
654 | 667 | ||
655 | /* built-in 'unset VAR' handler */ | 668 | /* built-in 'unset VAR' handler */ |
@@ -663,6 +676,12 @@ static int builtin_unset(struct child_prog *child) | |||
663 | return EXIT_SUCCESS; | 676 | return EXIT_SUCCESS; |
664 | } | 677 | } |
665 | 678 | ||
679 | static int builtin_not_written(struct child_prog *child) | ||
680 | { | ||
681 | printf("builtin_%s not written\n",child->argv[0]); | ||
682 | return EXIT_FAILURE; | ||
683 | } | ||
684 | |||
666 | static int b_check_space(o_string *o, int len) | 685 | static int b_check_space(o_string *o, int len) |
667 | { | 686 | { |
668 | /* It would be easy to drop a more restrictive policy | 687 | /* It would be easy to drop a more restrictive policy |
@@ -926,8 +945,12 @@ static int setup_redirects(struct child_prog *prog, int squirrel[]) | |||
926 | if (squirrel && redir->fd < 3) { | 945 | if (squirrel && redir->fd < 3) { |
927 | squirrel[redir->fd] = dup(redir->fd); | 946 | squirrel[redir->fd] = dup(redir->fd); |
928 | } | 947 | } |
929 | dup2(openfd, redir->fd); | 948 | if (openfd == -3) { |
930 | close(openfd); | 949 | close(openfd); |
950 | } else { | ||
951 | dup2(openfd, redir->fd); | ||
952 | close(openfd); | ||
953 | } | ||
931 | } | 954 | } |
932 | } | 955 | } |
933 | return 0; | 956 | return 0; |
@@ -1216,6 +1239,11 @@ static int run_pipe_real(struct pipe *pi) | |||
1216 | if (strcmp(child->argv[0], x->cmd) == 0 ) { | 1239 | if (strcmp(child->argv[0], x->cmd) == 0 ) { |
1217 | int squirrel[] = {-1, -1, -1}; | 1240 | int squirrel[] = {-1, -1, -1}; |
1218 | int rcode; | 1241 | int rcode; |
1242 | if (x->function == builtin_exec && child->argv[1]==NULL) { | ||
1243 | debug_printf("magic exec\n"); | ||
1244 | setup_redirects(child,NULL); | ||
1245 | return EXIT_SUCCESS; | ||
1246 | } | ||
1219 | debug_printf("builtin inline %s\n", child->argv[0]); | 1247 | debug_printf("builtin inline %s\n", child->argv[0]); |
1220 | /* XXX setup_redirects acts on file descriptors, not FILEs. | 1248 | /* XXX setup_redirects acts on file descriptors, not FILEs. |
1221 | * This is perfect for work that comes after exec(). | 1249 | * This is perfect for work that comes after exec(). |
@@ -1569,7 +1597,8 @@ static int setup_redirect(struct p_context *ctx, int fd, redir_type style, | |||
1569 | if (redir->dup == -2) return 1; /* syntax error */ | 1597 | if (redir->dup == -2) return 1; /* syntax error */ |
1570 | if (redir->dup != -1) { | 1598 | if (redir->dup != -1) { |
1571 | /* Erik had a check here that the file descriptor in question | 1599 | /* Erik had a check here that the file descriptor in question |
1572 | * is legit; I postpone that to "run time" */ | 1600 | * is legit; I postpone that to "run time" |
1601 | * A "-" representation of "close me" shows up as a -3 here */ | ||
1573 | debug_printf("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup); | 1602 | debug_printf("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup); |
1574 | } else { | 1603 | } else { |
1575 | /* We do _not_ try to open the file that src points to, | 1604 | /* We do _not_ try to open the file that src points to, |
@@ -1775,10 +1804,16 @@ static int redirect_dup_num(struct in_str *input) | |||
1775 | if (ch != '&') return -1; | 1804 | if (ch != '&') return -1; |
1776 | 1805 | ||
1777 | b_getch(input); /* get the & */ | 1806 | b_getch(input); /* get the & */ |
1778 | while (ch=b_peek(input),isdigit(ch)) { | 1807 | ch=b_peek(input); |
1808 | if (ch == '-') { | ||
1809 | b_getch(input); | ||
1810 | return -3; /* "-" represents "close me" */ | ||
1811 | } | ||
1812 | while (isdigit(ch)) { | ||
1779 | d = d*10+(ch-'0'); | 1813 | d = d*10+(ch-'0'); |
1780 | ok=1; | 1814 | ok=1; |
1781 | b_getch(input); | 1815 | b_getch(input); |
1816 | ch = b_peek(input); | ||
1782 | } | 1817 | } |
1783 | if (ok) return d; | 1818 | if (ok) return d; |
1784 | 1819 | ||