?? hush.c
字號:
/* vi: set sw=4 ts=4: *//* * sh.c -- a prototype Bourne shell grammar parser * Intended to follow the original Thompson and Ritchie * "small and simple is beautiful" philosophy, which * incidentally is a good match to today's BusyBox. * * Copyright (C) 2000,2001 Larry Doolittle <larry@doolittle.boa.org> * * Credits: * The parser routines proper are all original material, first * written Dec 2000 and Jan 2001 by Larry Doolittle. The * execution engine, the builtins, and much of the underlying * support has been adapted from busybox-0.49pre's lash, which is * Copyright (C) 1999,2000 by Lineo, inc. and Erik Andersen * written by Erik Andersen <andersee@debian.org>. That, in turn, * is based in part on ladsh.c, by Michael K. Johnson and Erik W. * Troan, which they placed in the public domain. I don't know * how much of the Johnson/Troan code has survived the repeated * rewrites. * * Other credits: * simple_itoa() was lifted from boa-0.93.15 * b_addchr() derived from similar w_addchar function in glibc-2.2 * setup_redirect(), redirect_opt_num(), and big chunks of main() * and many builtins derived from contributions by Erik Andersen * miscellaneous bugfixes from Matt Kraai * * There are two big (and related) architecture differences between * this parser and the lash parser. One is that this version is * actually designed from the ground up to understand nearly all * of the Bourne grammar. The second, consequential change is that * the parser and input reader have been turned inside out. Now, * the parser is in control, and asks for input as needed. The old * way had the input reader in control, and it asked for parsing to * take place as needed. The new way makes it much easier to properly * handle the recursion implicit in the various substitutions, especially * across continuation lines. * * Bash grammar not implemented: (how many of these were in original sh?) * $@ (those sure look like weird quoting rules) * $_ * ! negation operator for pipes * &> and >& redirection of stdout+stderr * Brace Expansion * Tilde Expansion * fancy forms of Parameter Expansion * aliases * Arithmetic Expansion * <(list) and >(list) Process Substitution * reserved words: case, esac, select, function * Here Documents ( << word ) * Functions * Major bugs: * job handling woefully incomplete and buggy * reserved word execution woefully incomplete and buggy * to-do: * port selected bugfixes from post-0.49 busybox lash - done? * 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 * clean up recognition of null pipes * check setting of global_argc and global_argv * control-C handling, probably with longjmp * follow IFS rules more precisely, including update semantics * figure out what to do with backslash-newline * explain why we use signal instead of sigaction * propagate syntax errors, die on resource errors? * continuation lines, both explicit and implicit - done? * memory leak finding and plugging - done? * more testing, especially quoting rules and redirection * document how quoting rules not precisely followed for variable assignments * maybe change map[] to use 2-bit entries * (eventually) remove all the printf's * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <ctype.h> /* isalpha, isdigit */#include <unistd.h> /* getpid */#include <stdlib.h> /* getenv, atoi */#include <string.h> /* strchr */#include <stdio.h> /* popen etc. */#include <glob.h> /* glob, of course */#include <stdarg.h> /* va_list */#include <errno.h>#include <fcntl.h>#include <getopt.h> /* should be pretty obvious */#include <sys/stat.h> /* ulimit */#include <sys/types.h>#include <sys/wait.h>#include <signal.h>/* #include <dmalloc.h> *//* #define DEBUG_SHELL */#ifdef BB_VER#include "busybox.h"#include "cmdedit.h"#else#define applet_name "hush"#include "standalone.h"#define hush_main main#undef BB_FEATURE_SH_FANCY_PROMPT#endif#define SPECIAL_VAR_SYMBOL 03#define FLAG_EXIT_FROM_LOOP 1#define FLAG_PARSE_SEMICOLON (1 << 1) /* symbol ';' is special for parser */#define FLAG_REPARSING (1 << 2) /* >=2nd pass */typedef enum { REDIRECT_INPUT = 1, REDIRECT_OVERWRITE = 2, REDIRECT_APPEND = 3, REDIRECT_HEREIS = 4, REDIRECT_IO = 5} redir_type;/* The descrip member of this structure is only used to make debugging * output pretty */struct {int mode; int default_fd; char *descrip;} redir_table[] = { { 0, 0, "()" }, { O_RDONLY, 0, "<" }, { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" }, { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" }, { O_RDONLY, -1, "<<" }, { O_RDWR, 1, "<>" }};typedef enum { PIPE_SEQ = 1, PIPE_AND = 2, PIPE_OR = 3, PIPE_BG = 4,} pipe_style;/* might eventually control execution */typedef enum { RES_NONE = 0, RES_IF = 1, RES_THEN = 2, RES_ELIF = 3, RES_ELSE = 4, RES_FI = 5, RES_FOR = 6, RES_WHILE = 7, RES_UNTIL = 8, RES_DO = 9, RES_DONE = 10, RES_XXXX = 11, RES_IN = 12, RES_SNTX = 13} reserved_style;#define FLAG_END (1<<RES_NONE)#define FLAG_IF (1<<RES_IF)#define FLAG_THEN (1<<RES_THEN)#define FLAG_ELIF (1<<RES_ELIF)#define FLAG_ELSE (1<<RES_ELSE)#define FLAG_FI (1<<RES_FI)#define FLAG_FOR (1<<RES_FOR)#define FLAG_WHILE (1<<RES_WHILE)#define FLAG_UNTIL (1<<RES_UNTIL)#define FLAG_DO (1<<RES_DO)#define FLAG_DONE (1<<RES_DONE)#define FLAG_IN (1<<RES_IN)#define FLAG_START (1<<RES_XXXX)/* This holds pointers to the various results of parsing */struct p_context { struct child_prog *child; struct pipe *list_head; struct pipe *pipe; struct redir_struct *pending_redirect; reserved_style w; int old_flag; /* for figuring out valid reserved words */ struct p_context *stack; int type; /* define type of parser : ";$" common or special symbol */ /* How about quoting status? */};struct redir_struct { redir_type type; /* type of redirection */ int fd; /* file descriptor being redirected */ int dup; /* -1, or file descriptor being duplicated */ struct redir_struct *next; /* pointer to the next redirect in the list */ glob_t word; /* *word.gl_pathv is the filename */};struct child_prog { pid_t pid; /* 0 if exited */ char **argv; /* program name and arguments */ struct pipe *group; /* if non-NULL, first in group or subshell */ int subshell; /* flag, non-zero if group must be forked */ struct redir_struct *redirects; /* I/O redirections */ glob_t glob_result; /* result of parameter globbing */ int is_stopped; /* is the program currently running? */ struct pipe *family; /* pointer back to the child's parent pipe */ int sp; /* number of SPECIAL_VAR_SYMBOL */ int type;};struct pipe { int jobid; /* job number */ int num_progs; /* total number of programs in job */ int running_progs; /* number of programs running */ char *text; /* name of job */ char *cmdbuf; /* buffer various argv's point into */ pid_t pgrp; /* process group ID for the job */ struct child_prog *progs; /* array of commands in pipe */ struct pipe *next; /* to track background commands */ int stopped_progs; /* number of programs alive, but stopped */ int job_context; /* bitmask defining current context */ pipe_style followup; /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */ reserved_style r_mode; /* supports if, for, while, until */};struct close_me { int fd; struct close_me *next;};struct variables { char *name; char *value; int flg_export; int flg_read_only; struct variables *next;};/* globals, connect us to the outside world * the first three support $?, $#, and $1 */char **global_argv;unsigned int global_argc;unsigned int last_return_code;extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */ /* "globals" within this file */static char *ifs;static char map[256];static int fake_mode;static int interactive;static struct close_me *close_me_head;static const char *cwd;static struct pipe *job_list;static unsigned int last_bg_pid;static unsigned int last_jobid;static unsigned int shell_terminal;static char *PS1;static char *PS2;struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 };struct variables *top_vars = &shell_ver;#define B_CHUNK (100)#define B_NOSPAC 1typedef struct { char *data; int length; int maxlen; int quote; int nonnull;} o_string;#define NULL_O_STRING {NULL,0,0,0,0}/* used for initialization: o_string foo = NULL_O_STRING; *//* I can almost use ordinary FILE *. Is open_memstream() universally * available? Where is it documented? */struct in_str { const char *p; char peek_buf[2]; int __promptme; int promptmode; FILE *file; int (*get) (struct in_str *); int (*peek) (struct in_str *);};#define b_getch(input) ((input)->get(input))#define b_peek(input) ((input)->peek(input))#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"struct built_in_command { char *cmd; /* name */ char *descr; /* description */ int (*function) (struct child_prog *); /* function ptr */};/* belongs in busybox.h */static inline int max(int a, int b) { return (a>b)?a:b;}/* This should be in utility.c */#ifdef DEBUG_SHELLstatic void debug_printf(const char *format, ...){ va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end(args);}#elsestatic inline void debug_printf(const char *format, ...) { }#endif#define final_printf debug_printfstatic void __syntax(char *file, int line) { error_msg("syntax error %s:%d", file, line);}#define syntax() __syntax(__FILE__, __LINE__)/* Index of subroutines: *//* function prototypes for builtins */static int builtin_cd(struct child_prog *child);static int builtin_env(struct child_prog *child);static int builtin_eval(struct child_prog *child);static int builtin_exec(struct child_prog *child);static int builtin_exit(struct child_prog *child);static int builtin_export(struct child_prog *child);static int builtin_fg_bg(struct child_prog *child);static int builtin_help(struct child_prog *child);static int builtin_jobs(struct child_prog *child);static int builtin_pwd(struct child_prog *child);static int builtin_read(struct child_prog *child);static int builtin_set(struct child_prog *child);static int builtin_shift(struct child_prog *child);static int builtin_source(struct child_prog *child);static int builtin_umask(struct child_prog *child);static int builtin_unset(struct child_prog *child);static int builtin_not_written(struct child_prog *child);/* o_string manipulation: */static int b_check_space(o_string *o, int len);static int b_addchr(o_string *o, int ch);static void b_reset(o_string *o);static int b_addqchr(o_string *o, int ch, int quote);static int b_adduint(o_string *o, unsigned int i);/* in_str manipulations: */static int static_get(struct in_str *i);static int static_peek(struct in_str *i);static int file_get(struct in_str *i);static int file_peek(struct in_str *i);static void setup_file_in_str(struct in_str *i, FILE *f);static void setup_string_in_str(struct in_str *i, const char *s);/* close_me manipulations: */static void mark_open(int fd);static void mark_closed(int fd);static void close_all();/* "run" the final data structures: */static char *indenter(int i);static int free_pipe_list(struct pipe *head, int indent);static int free_pipe(struct pipe *pi, int indent);/* really run the final data structures: */static int setup_redirects(struct child_prog *prog, int squirrel[]);static int run_list_real(struct pipe *pi);static void pseudo_exec(struct child_prog *child) __attribute__ ((noreturn));static int run_pipe_real(struct pipe *pi);/* extended glob support: */static int globhack(const char *src, int flags, glob_t *pglob);static int glob_needed(const char *s);static int xglob(o_string *dest, int flags, glob_t *pglob);/* variable assignment: */static int is_assignment(const char *s);/* data structure manipulation: */static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input);static void initialize_context(struct p_context *ctx);static int done_word(o_string *dest, struct p_context *ctx);static int done_command(struct p_context *ctx);static int done_pipe(struct p_context *ctx, pipe_style type);/* primary string parsing: */static int redirect_dup_num(struct in_str *input);static int redirect_opt_num(o_string *o);static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end);static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch);static char *lookup_param(char *src);static char *make_string(char **inp);static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input);static int parse_string(o_string *dest, struct p_context *ctx, const char *src);static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, int end_trigger);/* setup: */static int parse_stream_outer(struct in_str *inp, int flag);static int parse_string_outer(const char *s, int flag);static int parse_file_outer(FILE *f);/* job management: */static int checkjobs(struct pipe* fg_pipe);static void insert_bg_job(struct pipe *pi);static void remove_bg_job(struct pipe *pi);/* local variable support */static char **make_list_in(char **inp, char *name);static char *insert_var_value(char *inp);static char *get_local_var(const char *var);static void unset_local_var(const char *name);static int set_local_var(const char *s, int flg_export);/* Table of built-in functions. They can be forked or not, depending on * context: within pipes, they fork. As simple commands, they do not. * When used in non-forking context, they can change global variables * in the parent shell process. If forked, of course they can not. * For example, 'unset foo | whatever' will parse and run, but foo will * still be set at the end. */static struct built_in_command bltins[] = { {"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}, {"continue", "Continue for, while or until loop", builtin_not_written}, {"env", "Print all environment variables", builtin_env}, {"eval", "Construct and run shell command", builtin_eval}, {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec}, {"exit", "Exit from shell()", builtin_exit}, {"export", "Set environment variable", builtin_export}, {"fg", "Bring job into the foreground", builtin_fg_bg}, {"jobs", "Lists the active jobs", builtin_jobs}, {"pwd", "Print current directory", builtin_pwd}, {"read", "Input environment variable", builtin_read}, {"return", "Return from a function", builtin_not_written}, {"set", "Set/unset shell local variables", builtin_set}, {"shift", "Shift positional parameters", builtin_shift}, {"trap", "Trap signals", builtin_not_written}, {"ulimit","Controls resource limits", builtin_not_written}, {"umask","Sets file creation mask", builtin_umask}, {"unset", "Unset environment variable", builtin_unset}, {".", "Source-in and run commands in a file", builtin_source}, {"help", "List shell built-in commands", builtin_help}, {NULL, NULL, NULL}};static const char *set_cwd(void){ if(cwd==unknown) cwd = NULL; /* xgetcwd(arg) called free(arg) */ cwd = xgetcwd((char *)cwd); if (!cwd) cwd = unknown; return cwd;}/* built-in 'eval' handler */static int builtin_eval(struct child_prog *child){ char *str = NULL; int rcode = EXIT_SUCCESS; if (child->argv[1]) { str = make_string(child->argv + 1); parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_PARSE_SEMICOLON); free(str); rcode = last_return_code; } return rcode;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -