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.
This commit is contained in:
parent
7e1273edf7
commit
83a2ae2184
73
hush.c
73
hush.c
@ -45,24 +45,25 @@
|
|||||||
* fancy forms of Parameter Expansion
|
* fancy forms of Parameter Expansion
|
||||||
* Arithmetic Expansion
|
* Arithmetic Expansion
|
||||||
* <(list) and >(list) Process Substitution
|
* <(list) and >(list) Process Substitution
|
||||||
* reserved words: case, esac, function
|
* reserved words: case, esac, select, function
|
||||||
* Here Documents ( << word )
|
* Here Documents ( << word )
|
||||||
* Functions
|
* Functions
|
||||||
* Major bugs:
|
* Major bugs:
|
||||||
* job handling woefully incomplete and buggy
|
* job handling woefully incomplete and buggy
|
||||||
* reserved word execution woefully incomplete and buggy
|
* reserved word execution woefully incomplete and buggy
|
||||||
* to-do:
|
* to-do:
|
||||||
* port selected bugfixes from post-0.49 busybox lash
|
* port selected bugfixes from post-0.49 busybox lash - done?
|
||||||
* finish implementing reserved words
|
* finish implementing reserved words: for, while, until, do, done
|
||||||
|
* change { and } from special chars to reserved words
|
||||||
|
* builtins: break, continue, eval, return, set, trap, ulimit
|
||||||
|
* test magic exec
|
||||||
* handle children going into background
|
* handle children going into background
|
||||||
* clean up recognition of null pipes
|
* clean up recognition of null pipes
|
||||||
* have builtin_exec set flag to avoid restore_redirects
|
* have builtin_exec set flag to avoid restore_redirects
|
||||||
* figure out if "echo foo}" is fixable
|
|
||||||
* check setting of global_argc and global_argv
|
* check setting of global_argc and global_argv
|
||||||
* control-C handling, probably with longjmp
|
* control-C handling, probably with longjmp
|
||||||
* VAR=value prefix for simple commands
|
* VAR=value prefix for simple commands
|
||||||
* follow IFS rules more precisely, including update semantics
|
* follow IFS rules more precisely, including update semantics
|
||||||
* write builtin_eval, builtin_ulimit, builtin_umask
|
|
||||||
* figure out what to do with backslash-newline
|
* figure out what to do with backslash-newline
|
||||||
* explain why we use signal instead of sigaction
|
* explain why we use signal instead of sigaction
|
||||||
* propagate syntax errors, die on resource errors?
|
* propagate syntax errors, die on resource errors?
|
||||||
@ -97,6 +98,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <getopt.h> /* should be pretty obvious */
|
#include <getopt.h> /* should be pretty obvious */
|
||||||
|
|
||||||
|
#include <sys/stat.h> /* ulimit */
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@ -323,9 +325,9 @@ static int builtin_pwd(struct child_prog *child);
|
|||||||
static int builtin_read(struct child_prog *child);
|
static int builtin_read(struct child_prog *child);
|
||||||
static int builtin_shift(struct child_prog *child);
|
static int builtin_shift(struct child_prog *child);
|
||||||
static int builtin_source(struct child_prog *child);
|
static int builtin_source(struct child_prog *child);
|
||||||
static int builtin_ulimit(struct child_prog *child);
|
|
||||||
static int builtin_umask(struct child_prog *child);
|
static int builtin_umask(struct child_prog *child);
|
||||||
static int builtin_unset(struct child_prog *child);
|
static int builtin_unset(struct child_prog *child);
|
||||||
|
static int builtin_not_written(struct child_prog *child);
|
||||||
/* o_string manipulation: */
|
/* o_string manipulation: */
|
||||||
static int b_check_space(o_string *o, int len);
|
static int b_check_space(o_string *o, int len);
|
||||||
static int b_addchr(o_string *o, int ch);
|
static int b_addchr(o_string *o, int ch);
|
||||||
@ -390,8 +392,11 @@ static void free_pipe(struct pipe *pi);
|
|||||||
* still be set at the end. */
|
* still be set at the end. */
|
||||||
static struct built_in_command bltins[] = {
|
static struct built_in_command bltins[] = {
|
||||||
{"bg", "Resume a job in the background", builtin_fg_bg},
|
{"bg", "Resume a job in the background", builtin_fg_bg},
|
||||||
|
{"break", "Exit for, while or until loop", builtin_not_written},
|
||||||
{"cd", "Change working directory", builtin_cd},
|
{"cd", "Change working directory", builtin_cd},
|
||||||
|
{"continue", "Continue for, while or until loop", builtin_not_written},
|
||||||
{"env", "Print all environment variables", builtin_env},
|
{"env", "Print all environment variables", builtin_env},
|
||||||
|
{"eval", "Construct and run shell command", builtin_not_written},
|
||||||
{"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec},
|
{"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec},
|
||||||
{"exit", "Exit from shell()", builtin_exit},
|
{"exit", "Exit from shell()", builtin_exit},
|
||||||
{"export", "Set environment variable", builtin_export},
|
{"export", "Set environment variable", builtin_export},
|
||||||
@ -399,8 +404,11 @@ static struct built_in_command bltins[] = {
|
|||||||
{"jobs", "Lists the active jobs", builtin_jobs},
|
{"jobs", "Lists the active jobs", builtin_jobs},
|
||||||
{"pwd", "Print current directory", builtin_pwd},
|
{"pwd", "Print current directory", builtin_pwd},
|
||||||
{"read", "Input environment variable", builtin_read},
|
{"read", "Input environment variable", builtin_read},
|
||||||
|
{"return", "Return from a function", builtin_not_written},
|
||||||
|
{"set", "Set/unset shell options", builtin_not_written},
|
||||||
{"shift", "Shift positional parameters", builtin_shift},
|
{"shift", "Shift positional parameters", builtin_shift},
|
||||||
{"ulimit","Controls resource limits", builtin_ulimit},
|
{"trap", "Trap signals", builtin_not_written},
|
||||||
|
{"ulimit","Controls resource limits", builtin_not_written},
|
||||||
{"umask","Sets file creation mask", builtin_umask},
|
{"umask","Sets file creation mask", builtin_umask},
|
||||||
{"unset", "Unset environment variable", builtin_unset},
|
{"unset", "Unset environment variable", builtin_unset},
|
||||||
{".", "Source-in and run commands in a file", builtin_source},
|
{".", "Source-in and run commands in a file", builtin_source},
|
||||||
@ -640,16 +648,21 @@ static int builtin_source(struct child_prog *child)
|
|||||||
return (status);
|
return (status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int builtin_ulimit(struct child_prog *child)
|
|
||||||
{
|
|
||||||
printf("builtin_ulimit not written\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int builtin_umask(struct child_prog *child)
|
static int builtin_umask(struct child_prog *child)
|
||||||
{
|
{
|
||||||
printf("builtin_umask not written\n");
|
mode_t new_umask;
|
||||||
return EXIT_FAILURE;
|
const char *arg = child->argv[1];
|
||||||
|
char *end;
|
||||||
|
if (arg) {
|
||||||
|
new_umask=strtoul(arg, &end, 8);
|
||||||
|
if (*end!='\0' || end == arg) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("%.3o\n", (unsigned int) (new_umask=umask(0)));
|
||||||
|
}
|
||||||
|
umask(new_umask);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* built-in 'unset VAR' handler */
|
/* built-in 'unset VAR' handler */
|
||||||
@ -663,6 +676,12 @@ static int builtin_unset(struct child_prog *child)
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int builtin_not_written(struct child_prog *child)
|
||||||
|
{
|
||||||
|
printf("builtin_%s not written\n",child->argv[0]);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
static int b_check_space(o_string *o, int len)
|
static int b_check_space(o_string *o, int len)
|
||||||
{
|
{
|
||||||
/* It would be easy to drop a more restrictive policy
|
/* It would be easy to drop a more restrictive policy
|
||||||
@ -926,8 +945,12 @@ static int setup_redirects(struct child_prog *prog, int squirrel[])
|
|||||||
if (squirrel && redir->fd < 3) {
|
if (squirrel && redir->fd < 3) {
|
||||||
squirrel[redir->fd] = dup(redir->fd);
|
squirrel[redir->fd] = dup(redir->fd);
|
||||||
}
|
}
|
||||||
dup2(openfd, redir->fd);
|
if (openfd == -3) {
|
||||||
close(openfd);
|
close(openfd);
|
||||||
|
} else {
|
||||||
|
dup2(openfd, redir->fd);
|
||||||
|
close(openfd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -1216,6 +1239,11 @@ static int run_pipe_real(struct pipe *pi)
|
|||||||
if (strcmp(child->argv[0], x->cmd) == 0 ) {
|
if (strcmp(child->argv[0], x->cmd) == 0 ) {
|
||||||
int squirrel[] = {-1, -1, -1};
|
int squirrel[] = {-1, -1, -1};
|
||||||
int rcode;
|
int rcode;
|
||||||
|
if (x->function == builtin_exec && child->argv[1]==NULL) {
|
||||||
|
debug_printf("magic exec\n");
|
||||||
|
setup_redirects(child,NULL);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
debug_printf("builtin inline %s\n", child->argv[0]);
|
debug_printf("builtin inline %s\n", child->argv[0]);
|
||||||
/* XXX setup_redirects acts on file descriptors, not FILEs.
|
/* XXX setup_redirects acts on file descriptors, not FILEs.
|
||||||
* This is perfect for work that comes after exec().
|
* 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,
|
|||||||
if (redir->dup == -2) return 1; /* syntax error */
|
if (redir->dup == -2) return 1; /* syntax error */
|
||||||
if (redir->dup != -1) {
|
if (redir->dup != -1) {
|
||||||
/* Erik had a check here that the file descriptor in question
|
/* Erik had a check here that the file descriptor in question
|
||||||
* is legit; I postpone that to "run time" */
|
* is legit; I postpone that to "run time"
|
||||||
|
* A "-" representation of "close me" shows up as a -3 here */
|
||||||
debug_printf("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup);
|
debug_printf("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup);
|
||||||
} else {
|
} else {
|
||||||
/* We do _not_ try to open the file that src points to,
|
/* 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)
|
|||||||
if (ch != '&') return -1;
|
if (ch != '&') return -1;
|
||||||
|
|
||||||
b_getch(input); /* get the & */
|
b_getch(input); /* get the & */
|
||||||
while (ch=b_peek(input),isdigit(ch)) {
|
ch=b_peek(input);
|
||||||
|
if (ch == '-') {
|
||||||
|
b_getch(input);
|
||||||
|
return -3; /* "-" represents "close me" */
|
||||||
|
}
|
||||||
|
while (isdigit(ch)) {
|
||||||
d = d*10+(ch-'0');
|
d = d*10+(ch-'0');
|
||||||
ok=1;
|
ok=1;
|
||||||
b_getch(input);
|
b_getch(input);
|
||||||
|
ch = b_peek(input);
|
||||||
}
|
}
|
||||||
if (ok) return d;
|
if (ok) return d;
|
||||||
|
|
||||||
|
73
shell/hush.c
73
shell/hush.c
@ -45,24 +45,25 @@
|
|||||||
* fancy forms of Parameter Expansion
|
* fancy forms of Parameter Expansion
|
||||||
* Arithmetic Expansion
|
* Arithmetic Expansion
|
||||||
* <(list) and >(list) Process Substitution
|
* <(list) and >(list) Process Substitution
|
||||||
* reserved words: case, esac, function
|
* reserved words: case, esac, select, function
|
||||||
* Here Documents ( << word )
|
* Here Documents ( << word )
|
||||||
* Functions
|
* Functions
|
||||||
* Major bugs:
|
* Major bugs:
|
||||||
* job handling woefully incomplete and buggy
|
* job handling woefully incomplete and buggy
|
||||||
* reserved word execution woefully incomplete and buggy
|
* reserved word execution woefully incomplete and buggy
|
||||||
* to-do:
|
* to-do:
|
||||||
* port selected bugfixes from post-0.49 busybox lash
|
* port selected bugfixes from post-0.49 busybox lash - done?
|
||||||
* finish implementing reserved words
|
* finish implementing reserved words: for, while, until, do, done
|
||||||
|
* change { and } from special chars to reserved words
|
||||||
|
* builtins: break, continue, eval, return, set, trap, ulimit
|
||||||
|
* test magic exec
|
||||||
* handle children going into background
|
* handle children going into background
|
||||||
* clean up recognition of null pipes
|
* clean up recognition of null pipes
|
||||||
* have builtin_exec set flag to avoid restore_redirects
|
* have builtin_exec set flag to avoid restore_redirects
|
||||||
* figure out if "echo foo}" is fixable
|
|
||||||
* check setting of global_argc and global_argv
|
* check setting of global_argc and global_argv
|
||||||
* control-C handling, probably with longjmp
|
* control-C handling, probably with longjmp
|
||||||
* VAR=value prefix for simple commands
|
* VAR=value prefix for simple commands
|
||||||
* follow IFS rules more precisely, including update semantics
|
* follow IFS rules more precisely, including update semantics
|
||||||
* write builtin_eval, builtin_ulimit, builtin_umask
|
|
||||||
* figure out what to do with backslash-newline
|
* figure out what to do with backslash-newline
|
||||||
* explain why we use signal instead of sigaction
|
* explain why we use signal instead of sigaction
|
||||||
* propagate syntax errors, die on resource errors?
|
* propagate syntax errors, die on resource errors?
|
||||||
@ -97,6 +98,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <getopt.h> /* should be pretty obvious */
|
#include <getopt.h> /* should be pretty obvious */
|
||||||
|
|
||||||
|
#include <sys/stat.h> /* ulimit */
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@ -323,9 +325,9 @@ static int builtin_pwd(struct child_prog *child);
|
|||||||
static int builtin_read(struct child_prog *child);
|
static int builtin_read(struct child_prog *child);
|
||||||
static int builtin_shift(struct child_prog *child);
|
static int builtin_shift(struct child_prog *child);
|
||||||
static int builtin_source(struct child_prog *child);
|
static int builtin_source(struct child_prog *child);
|
||||||
static int builtin_ulimit(struct child_prog *child);
|
|
||||||
static int builtin_umask(struct child_prog *child);
|
static int builtin_umask(struct child_prog *child);
|
||||||
static int builtin_unset(struct child_prog *child);
|
static int builtin_unset(struct child_prog *child);
|
||||||
|
static int builtin_not_written(struct child_prog *child);
|
||||||
/* o_string manipulation: */
|
/* o_string manipulation: */
|
||||||
static int b_check_space(o_string *o, int len);
|
static int b_check_space(o_string *o, int len);
|
||||||
static int b_addchr(o_string *o, int ch);
|
static int b_addchr(o_string *o, int ch);
|
||||||
@ -390,8 +392,11 @@ static void free_pipe(struct pipe *pi);
|
|||||||
* still be set at the end. */
|
* still be set at the end. */
|
||||||
static struct built_in_command bltins[] = {
|
static struct built_in_command bltins[] = {
|
||||||
{"bg", "Resume a job in the background", builtin_fg_bg},
|
{"bg", "Resume a job in the background", builtin_fg_bg},
|
||||||
|
{"break", "Exit for, while or until loop", builtin_not_written},
|
||||||
{"cd", "Change working directory", builtin_cd},
|
{"cd", "Change working directory", builtin_cd},
|
||||||
|
{"continue", "Continue for, while or until loop", builtin_not_written},
|
||||||
{"env", "Print all environment variables", builtin_env},
|
{"env", "Print all environment variables", builtin_env},
|
||||||
|
{"eval", "Construct and run shell command", builtin_not_written},
|
||||||
{"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec},
|
{"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec},
|
||||||
{"exit", "Exit from shell()", builtin_exit},
|
{"exit", "Exit from shell()", builtin_exit},
|
||||||
{"export", "Set environment variable", builtin_export},
|
{"export", "Set environment variable", builtin_export},
|
||||||
@ -399,8 +404,11 @@ static struct built_in_command bltins[] = {
|
|||||||
{"jobs", "Lists the active jobs", builtin_jobs},
|
{"jobs", "Lists the active jobs", builtin_jobs},
|
||||||
{"pwd", "Print current directory", builtin_pwd},
|
{"pwd", "Print current directory", builtin_pwd},
|
||||||
{"read", "Input environment variable", builtin_read},
|
{"read", "Input environment variable", builtin_read},
|
||||||
|
{"return", "Return from a function", builtin_not_written},
|
||||||
|
{"set", "Set/unset shell options", builtin_not_written},
|
||||||
{"shift", "Shift positional parameters", builtin_shift},
|
{"shift", "Shift positional parameters", builtin_shift},
|
||||||
{"ulimit","Controls resource limits", builtin_ulimit},
|
{"trap", "Trap signals", builtin_not_written},
|
||||||
|
{"ulimit","Controls resource limits", builtin_not_written},
|
||||||
{"umask","Sets file creation mask", builtin_umask},
|
{"umask","Sets file creation mask", builtin_umask},
|
||||||
{"unset", "Unset environment variable", builtin_unset},
|
{"unset", "Unset environment variable", builtin_unset},
|
||||||
{".", "Source-in and run commands in a file", builtin_source},
|
{".", "Source-in and run commands in a file", builtin_source},
|
||||||
@ -640,16 +648,21 @@ static int builtin_source(struct child_prog *child)
|
|||||||
return (status);
|
return (status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int builtin_ulimit(struct child_prog *child)
|
|
||||||
{
|
|
||||||
printf("builtin_ulimit not written\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int builtin_umask(struct child_prog *child)
|
static int builtin_umask(struct child_prog *child)
|
||||||
{
|
{
|
||||||
printf("builtin_umask not written\n");
|
mode_t new_umask;
|
||||||
return EXIT_FAILURE;
|
const char *arg = child->argv[1];
|
||||||
|
char *end;
|
||||||
|
if (arg) {
|
||||||
|
new_umask=strtoul(arg, &end, 8);
|
||||||
|
if (*end!='\0' || end == arg) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("%.3o\n", (unsigned int) (new_umask=umask(0)));
|
||||||
|
}
|
||||||
|
umask(new_umask);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* built-in 'unset VAR' handler */
|
/* built-in 'unset VAR' handler */
|
||||||
@ -663,6 +676,12 @@ static int builtin_unset(struct child_prog *child)
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int builtin_not_written(struct child_prog *child)
|
||||||
|
{
|
||||||
|
printf("builtin_%s not written\n",child->argv[0]);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
static int b_check_space(o_string *o, int len)
|
static int b_check_space(o_string *o, int len)
|
||||||
{
|
{
|
||||||
/* It would be easy to drop a more restrictive policy
|
/* It would be easy to drop a more restrictive policy
|
||||||
@ -926,8 +945,12 @@ static int setup_redirects(struct child_prog *prog, int squirrel[])
|
|||||||
if (squirrel && redir->fd < 3) {
|
if (squirrel && redir->fd < 3) {
|
||||||
squirrel[redir->fd] = dup(redir->fd);
|
squirrel[redir->fd] = dup(redir->fd);
|
||||||
}
|
}
|
||||||
dup2(openfd, redir->fd);
|
if (openfd == -3) {
|
||||||
close(openfd);
|
close(openfd);
|
||||||
|
} else {
|
||||||
|
dup2(openfd, redir->fd);
|
||||||
|
close(openfd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -1216,6 +1239,11 @@ static int run_pipe_real(struct pipe *pi)
|
|||||||
if (strcmp(child->argv[0], x->cmd) == 0 ) {
|
if (strcmp(child->argv[0], x->cmd) == 0 ) {
|
||||||
int squirrel[] = {-1, -1, -1};
|
int squirrel[] = {-1, -1, -1};
|
||||||
int rcode;
|
int rcode;
|
||||||
|
if (x->function == builtin_exec && child->argv[1]==NULL) {
|
||||||
|
debug_printf("magic exec\n");
|
||||||
|
setup_redirects(child,NULL);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
debug_printf("builtin inline %s\n", child->argv[0]);
|
debug_printf("builtin inline %s\n", child->argv[0]);
|
||||||
/* XXX setup_redirects acts on file descriptors, not FILEs.
|
/* XXX setup_redirects acts on file descriptors, not FILEs.
|
||||||
* This is perfect for work that comes after exec().
|
* 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,
|
|||||||
if (redir->dup == -2) return 1; /* syntax error */
|
if (redir->dup == -2) return 1; /* syntax error */
|
||||||
if (redir->dup != -1) {
|
if (redir->dup != -1) {
|
||||||
/* Erik had a check here that the file descriptor in question
|
/* Erik had a check here that the file descriptor in question
|
||||||
* is legit; I postpone that to "run time" */
|
* is legit; I postpone that to "run time"
|
||||||
|
* A "-" representation of "close me" shows up as a -3 here */
|
||||||
debug_printf("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup);
|
debug_printf("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup);
|
||||||
} else {
|
} else {
|
||||||
/* We do _not_ try to open the file that src points to,
|
/* 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)
|
|||||||
if (ch != '&') return -1;
|
if (ch != '&') return -1;
|
||||||
|
|
||||||
b_getch(input); /* get the & */
|
b_getch(input); /* get the & */
|
||||||
while (ch=b_peek(input),isdigit(ch)) {
|
ch=b_peek(input);
|
||||||
|
if (ch == '-') {
|
||||||
|
b_getch(input);
|
||||||
|
return -3; /* "-" represents "close me" */
|
||||||
|
}
|
||||||
|
while (isdigit(ch)) {
|
||||||
d = d*10+(ch-'0');
|
d = d*10+(ch-'0');
|
||||||
ok=1;
|
ok=1;
|
||||||
b_getch(input);
|
b_getch(input);
|
||||||
|
ch = b_peek(input);
|
||||||
}
|
}
|
||||||
if (ok) return d;
|
if (ok) return d;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user