/* 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) 2000 by Lineo, Inc., and
* written by Erik Andersen <andersen@lineo.com>, <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
*/
#define __U_BOOT__
#ifdef __U_BOOT__
#include <malloc.h> /* malloc, free, realloc*/
#include <linux/ctype.h> /* isalpha, isdigit */
#include <common.h> /* readline */
#include <hush.h>
#include <command.h> /* find_cmd */
/*cmd_boot.c*/
extern int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]); /* do_bootd */
#endif
#ifdef CFG_HUSH_PARSER
#ifndef __U_BOOT__
#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 */
#if 1
#include "busybox.h"
#include "cmdedit.h"
#else
#define applet_name "hush"
#include "standalone.h"
#define hush_main main
#undef CONFIG_FEATURE_SH_FANCY_PROMPT
#define BB_BANNER
#endif
#endif
#define SPECIAL_VAR_SYMBOL 03
#ifndef __U_BOOT__
#define FLAG_EXIT_FROM_LOOP 1
#define FLAG_PARSE_SEMICOLON (1 << 1) /* symbol ';' is special for parser */
#define FLAG_REPARSING (1 << 2) /* >= 2nd pass */
#endif
#ifdef __U_BOOT__
DECLARE_GLOBAL_DATA_PTR;
#define EXIT_SUCCESS 0
#define EOF -1
#define syntax() syntax_err()
#define xstrdup strdup
#define error_msg printf
#else
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, "<>" }
};
#endif
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;
#ifndef __U_BOOT__
struct redir_struct *pending_redirect;
#endif
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? */
};
#ifndef __U_BOOT__
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 */
};
#endif
struct child_prog {
#ifndef __U_BOOT__
pid_t pid; /* 0 if exited */
#endif
char **argv; /* program name and arguments */
#ifdef __U_BOOT__
int argc; /* number of program arguments */
#endif
struct pipe *group; /* if non-NULL, first in group or subshell */
#ifndef __U_BOOT__
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 */
#endif
int sp; /* number of SPECIAL_VAR_SYMBOL */
int type;
};
struct pipe {
#ifndef __U_BOOT__
int jobid; /* job number */
#endif
int num_progs; /* total number of programs in job */
#ifndef __U_BOOT__
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 */
#endif
struct child_prog *progs; /* array of commands in pipe */
struct pipe *next; /* to track background commands */
#ifndef __U_BOOT__
int stopped_progs; /* number of programs alive, but stopped */
int job_context; /* bitmask defining current context */
#endif
pipe_style followup; /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */
reserved_style r_mode; /* supports if, for, while, until */
};
#ifndef __U_BOOT__
struct close_me {
int fd;
struct close_me *next;
};
#endif
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 */
#ifndef __U_BOOT__
char **global_argv;
unsigned int global_argc;
#endif
unsigned int last_return_code;
int nesting_level;
#ifndef __U_BOOT__
extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */
#endif
/* "globals" within this file */
static uchar *ifs;
static char map[256];
#ifndef __U_BOOT__
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;
#else
static int flag_repeat = 0;
static int do_repeat = 0;
static struct variables *top_vars = NULL ;
#endif /*__U_BOOT__ */
#define B_CHUNK (100)
#define B_NOSPAC 1
typedef 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;
#ifndef __U_BOOT__
char peek_buf[2];
#endif
int __promptme;
int promptmode;
#ifndef __U_BOOT__
FILE *file;
#endif
int (*get) (struct in_str *);
int (*peek) (struct in_str *);
};
#define b_getch(input) ((input)->get(input))
#define b_peek(input) ((input)->peek(input))
#ifndef __U_BOOT__
#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 */
};
#endif
/* This should be in utility.c */
#ifdef DEBUG_SHELL
#ifndef __U_BOOT__
static void debug_printf(const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
}
#else
#define debug_printf printf /* U-Boot debug flag */
#endif
#else
static inline void debug_printf(const char *format, ...) { }
#endif
#define final_printf debug_printf
#ifdef __U_BOOT__
static void syntax_err(void) {
printf("syntax error\n");
}
#else
static void __syntax(char *file, int line) {
error_msg("syntax error %s:%d", file, line);
}
#define syntax() __syntax(__FILE__, __LINE__)
#endif
#ifdef __U_BOOT__
static void *xmalloc(size_t size);
static void *xrealloc(void *ptr, size_t size);
#else
/* 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);
#endif
/* 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);
|