📄 lash.c
字号:
int status; int prognum = 0; while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { for (job = j_list->head; job; job = job->next) { prognum = 0; while (prognum < job->num_progs && job->progs[prognum].pid != childpid) prognum++; if (prognum < job->num_progs) break; } /* This happens on backticked commands */ if(job==NULL) return; if (WIFEXITED(status) || WIFSIGNALED(status)) { /* child exited */ job->running_progs--; job->progs[prognum].pid = 0; if (!job->running_progs) { printf(JOB_STATUS_FORMAT, job->jobid, "Done", job->text); last_jobid=0; remove_job(j_list, job); } } else { /* child stopped */ job->stopped_progs++; job->progs[prognum].is_stopped = 1;#if 0 /* Printing this stuff is a pain, since it tends to * overwrite the prompt an inconveinient moments. So * don't do that. */ if (job->stopped_progs == job->num_progs) { printf(JOB_STATUS_FORMAT, job->jobid, "Stopped", job->text); }#endif } } if (childpid == -1 && errno != ECHILD) bb_perror_msg("waitpid");}#elsestatic void checkjobs(struct jobset *j_list){}static void free_job(struct job *cmd){}static void remove_job(struct jobset *j_list, struct job *job){}#endif#ifdef CONFIG_LASH_PIPE_N_REDIRECTS/* squirrel != NULL means we squirrel away copies of stdin, stdout, * and stderr if they are redirected. */static int setup_redirects(struct child_prog *prog, int squirrel[]){ int i; int openfd; int mode = O_RDONLY; struct redir_struct *redir = prog->redirects; for (i = 0; i < prog->num_redirects; i++, redir++) { switch (redir->type) { case REDIRECT_INPUT: mode = O_RDONLY; break; case REDIRECT_OVERWRITE: mode = O_WRONLY | O_CREAT | O_TRUNC; break; case REDIRECT_APPEND: mode = O_WRONLY | O_CREAT | O_APPEND; break; } openfd = open(redir->filename, mode, 0666); if (openfd < 0) { /* this could get lost if stderr has been redirected, but bash and ash both lose it as well (though zsh doesn't!) */ bb_perror_msg("error opening %s", redir->filename); return 1; } if (openfd != redir->fd) { if (squirrel && redir->fd < 3) { squirrel[redir->fd] = dup(redir->fd); } dup2(openfd, redir->fd); close(openfd); } } return 0;}static void restore_redirects(int squirrel[]){ int i, fd; for (i=0; i<3; i++) { fd = squirrel[i]; if (fd != -1) { /* No error checking. I sure wouldn't know what * to do with an error if I found one! */ dup2(fd, i); close(fd); } }}#elsestatic inline int setup_redirects(struct child_prog *prog, int squirrel[]){ return 0;}static inline void restore_redirects(int squirrel[]){}#endifstatic inline void cmdedit_set_initial_prompt(void){#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT PS1 = NULL;#else PS1 = getenv("PS1"); if(PS1==0) PS1 = "\\w \\$ ";#endif}static inline void setup_prompt_string(char **prompt_str){#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT /* Set up the prompt */ if (shell_context == 0) { free(PS1); PS1=xmalloc(strlen(cwd)+4); sprintf(PS1, "%s %s", cwd, ( geteuid() != 0 ) ? "$ ":"# "); *prompt_str = PS1; } else { *prompt_str = PS2; }#else *prompt_str = (shell_context==0)? PS1 : PS2;#endif}static int get_command(FILE * source, char *command){ char *prompt_str; if (source == NULL) { if (local_pending_command) { /* a command specified (-c option): return it & mark it done */ strcpy(command, local_pending_command); free(local_pending_command); local_pending_command = NULL; return 0; } return 1; } if (source == stdin) { setup_prompt_string(&prompt_str);#ifdef CONFIG_FEATURE_COMMAND_EDITING /* ** enable command line editing only while a command line ** is actually being read; otherwise, we'll end up bequeathing ** atexit() handlers and other unwanted stuff to our ** child processes (rob@sysgo.de) */ cmdedit_read_input(prompt_str, command); return 0;#else fputs(prompt_str, stdout);#endif } if (!fgets(command, BUFSIZ - 2, source)) { if (source == stdin) printf("\n"); return 1; } return 0;}static char* itoa(register int i){ static char a[7]; /* Max 7 ints */ register char *b = a + sizeof(a) - 1; int sign = (i < 0); if (sign) i = -i; *b = 0; do { *--b = '0' + (i % 10); i /= 10; } while (i); if (sign) *--b = '-'; return b;}char * strsep_space( char *string, int * ix){ char *token, *begin; begin = string; /* Short circuit the trivial case */ if ( !string || ! string[*ix]) return NULL; /* Find the end of the token. */ while( string && string[*ix] && !isspace(string[*ix]) ) { (*ix)++; } /* Find the end of any whitespace trailing behind * the token and let that be part of the token */ while( string && string[*ix] && isspace(string[*ix]) ) { (*ix)++; } if (! string && *ix==0) { /* Nothing useful was found */ return NULL; } token = xmalloc(*ix+1); token[*ix] = '\0'; strncpy(token, string, *ix); return token;}static int expand_arguments(char *command){ int total_length=0, length, i, retval, ix = 0; expand_t expand_result; char *tmpcmd, *cmd, *cmd_copy; char *src, *dst, *var; const char *out_of_space = "out of space during expansion"; int flags = GLOB_NOCHECK#ifdef GLOB_BRACE | GLOB_BRACE#endif#ifdef GLOB_TILDE | GLOB_TILDE#endif ; /* get rid of the terminating \n */ chomp(command); /* Fix up escape sequences to be the Real Thing(tm) */ while( command && command[ix]) { if (command[ix] == '\\') { const char *tmp = command+ix+1; command[ix] = bb_process_escape_sequence( &tmp ); memmove(command+ix + 1, tmp, strlen(tmp)+1); } ix++; } /* Use glob and then fixup environment variables and such */ /* It turns out that glob is very stupid. We have to feed it one word at a * time since it can't cope with a full string. Here we convert command * (char*) into cmd (char**, one word per string) */ /* We need a clean copy, so strsep can mess up the copy while * we write stuff into the original (in a minute) */ cmd = cmd_copy = bb_xstrdup(command); *command = '\0'; for (ix = 0, tmpcmd = cmd; (tmpcmd = strsep_space(cmd, &ix)) != NULL; cmd += ix, ix=0) { if (*tmpcmd == '\0') break; /* we need to trim() the result for glob! */ trim(tmpcmd); retval = glob(tmpcmd, flags, NULL, &expand_result); free(tmpcmd); /* Free mem allocated by strsep_space */ if (retval == GLOB_NOSPACE) { /* Mem may have been allocated... */ globfree (&expand_result); bb_error_msg(out_of_space); return FALSE; } else if (retval != 0) { /* Some other error. GLOB_NOMATCH shouldn't * happen because of the GLOB_NOCHECK flag in * the glob call. */ bb_error_msg("syntax error"); return FALSE; } else { /* Convert from char** (one word per string) to a simple char*, * but don't overflow command which is BUFSIZ in length */ for (i=0; i < expand_result.gl_pathc; i++) { length=strlen(expand_result.gl_pathv[i]); if (total_length+length+1 >= BUFSIZ) { bb_error_msg(out_of_space); return FALSE; } strcat(command+total_length, " "); total_length+=1; strcat(command+total_length, expand_result.gl_pathv[i]); total_length+=length; } globfree (&expand_result); } } free(cmd_copy); trim(command); /* Now do the shell variable substitutions which * wordexp can't do for us, namely $? and $! */ src = command; while((dst = strchr(src,'$')) != NULL){ var = NULL; switch(*(dst+1)) { case '?': var = itoa(last_return_code); break; case '!': if (last_bg_pid==-1) *(var)='\0'; else var = itoa(last_bg_pid); break; /* Everything else like $$, $#, $[0-9], etc. should all be * expanded by wordexp(), so we can in theory skip that stuff * here, but just to be on the safe side (i.e., since uClibc * wordexp doesn't do this stuff yet), lets leave it in for * now. */ case '$': var = itoa(getpid()); break; case '#': var = itoa(argc-1); break; case '0':case '1':case '2':case '3':case '4': case '5':case '6':case '7':case '8':case '9': { int ixx=*(dst+1)-48+1; if (ixx >= argc) { var='\0'; } else { var = argv[ixx]; } } break; } if (var) { /* a single character construction was found, and * already handled in the case statement */ src=dst+2; } else { /* Looks like an environment variable */ char delim_hold; int num_skip_chars=0; int dstlen = strlen(dst); /* Is this a ${foo} type variable? */ if (dstlen >=2 && *(dst+1) == '{') { src=strchr(dst+1, '}'); num_skip_chars=1; } else { src=dst+1; while(isalnum(*src) || *src=='_') src++; } if (src == NULL) { src = dst+dstlen; } delim_hold=*src; *src='\0'; /* temporary */ var = getenv(dst + 1 + num_skip_chars); *src=delim_hold; src += num_skip_chars; } if (var == NULL) { /* Seems we got an un-expandable variable. So delete it. */ var = ""; } { int subst_len = strlen(var); int trail_len = strlen(src); if (dst+subst_len+trail_len >= command+BUFSIZ) { bb_error_msg(out_of_space); return FALSE; } /* Move stuff to the end of the string to accommodate * filling the created gap with the new stuff */ memmove(dst+subst_len, src, trail_len+1); /* Now copy in the new stuff */ memcpy(dst, var, subst_len); src = dst+subst_len; } } return TRUE;}/* Return cmd->num_progs as 0 if no command is present (e.g. an empty line). If a valid command is found, command_ptr is set to point to the beginning of the next command (if the original command had more then one job associated with it) or NULL if no more commands are present. */static int parse_command(char **command_ptr, struct job *job, int *inbg){ char *command; char *return_command = NULL; char *src, *buf; int argc_l = 0; int done = 0; int argv_alloced; int saw_quote = 0; char quote = '\0'; int count; struct child_prog *prog;#ifdef CONFIG_LASH_PIPE_N_REDIRECTS int i; char *chptr;#endif /* skip leading white space */ while (**command_ptr && isspace(**command_ptr)) (*command_ptr)++; /* this handles empty lines or leading '#' characters */ if (!**command_ptr || (**command_ptr == '#')) { job->num_progs=0; return 0; } *inbg = 0; job->num_progs = 1; job->progs = xmalloc(sizeof(*job->progs)); /* We set the argv elements to point inside of this string. The memory is freed by free_job(). Allocate twice the original length in case we need to quote every single character. Getting clean memory relieves us of the task of NULL terminating things and makes the rest of this look a bit cleaner (though it is, admittedly, a tad less efficient) */ job->cmdbuf = command = xcalloc(2*strlen(*command_ptr) + 1, sizeof(char)); job->text = NULL; prog = job->progs; prog->num_redirects = 0; prog->is_stopped = 0; prog->family = job;#ifdef CONFIG_LASH_PIPE_N_REDIRECTS prog->redirects = NULL;#endif argv_alloced = 5; prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced); prog->argv[0] = job->cmdbuf; buf = command; src = *command_ptr; while (*src && !done) { if (quote == *src) { quote = '\0'; } else if (quote) { if (*src == '\\') { src++; if (!*src) { bb_error_msg("character expected after \\"); free_job(job); return 1; } /* in shell, "\'" should yield \' */ if (*src != quote) { *buf++ = '\\'; *buf++ = '\\'; } } else if (*src == '*' || *src == '?' || *src == '[' || *src == ']') *buf++ = '\\'; *buf++ = *src; } else if (isspace(*src)) { if (*prog->argv[argc_l] || saw_quote) { buf++, argc_l++; /* +1 here leaves room for the NULL which ends argv */ if ((argc_l + 1) == argv_alloced) { argv_alloced += 5; prog->argv = xrealloc(prog->argv, sizeof(*prog->argv) * argv_alloced); } prog->argv[argc_l] = buf; saw_quote = 0; } } else switch (*src) { case '"': case '\'': quote = *src; saw_quote = 1; break; case '#': /* comment */ if (*(src-1)== '$') *buf++ = *src; else done = 1; break;#ifdef CONFIG_LASH_PIPE_N_REDIRECTS case '>': /* redirects */ case '<': i = prog->num_redirects++; prog->redirects = xrealloc(prog->redirects, sizeof(*prog->redirects) * (i + 1)); prog->redirects[i].fd = -1; if (buf != prog->argv[argc_l]) { /* the stuff before this character may be the file number being redirected */ prog->redirects[i].fd = strtol(prog->argv[argc_l], &chptr, 10); if (*chptr && *prog->argv[argc_l]) { buf++, argc_l++; prog->argv[argc_l] = buf; } } if (prog->redirects[i].fd == -1) { if (*src == '>') prog->redirects[i].fd = 1; else prog->redirects[i].fd = 0; } if (*src++ == '>') { if (*src == '>') prog->redirects[i].type = REDIRECT_APPEND, src++; else prog->redirects[i].type = REDIRECT_OVERWRITE; } else { prog->redirects[i].type = REDIRECT_INPUT; } /* This isn't POSIX sh compliant. Oh well. */ chptr = src; while (isspace(*chptr)) chptr++; if (!*chptr) { bb_error_msg("file name expected after %c", *(src-1)); free_job(job); job->num_progs=0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -