// 已实现的功能:
// 1.普通的shell命令 例如:ls -la; vim smsh3.c
// 2.变量存储与替换 例如:set 显示所有的变量
// a=x; x=who am i 添加变量或者修改变量的值
// echo $x; $x 变量替换
// 3.简单的if..then ..fi 语句
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <ctype.h>
#define DFL_PROMPT ">"
#define MAXARGNUM 50
#define MAXSTRLEN 100
#define MAXVARS 100
#define FALSE 0
#define TRUE 1
extern char **environ;
static char **table;
static int cur_len = 0;
enum states {NEUTRAL,WANT_THEN,THEN_BLOCK};
enum results {SUCCESS, FAIL};
static int if_state = NEUTRAL;
static int if_result = SUCCESS;
static int last_stat = 0;
int AddVar(char*); // 添加,修改变量
int FindVar(char*); // 变量替换
void Load(); // 下载系统变量
void List(); // 列出所有变量
int Check(char*); // 检测变量名是否合格
char* VarReplace(char* ); // 变量替换
int buildin_command(char*sbuf); // 内建命令
char** splitline(char*); // 分拆字符串
void setup(); // 设置
int is_control_command(char*);
int do_control_command(char **);
int ok_to_execute();
int process(char**);
int syn_err(char*);
void fatal(char*,char*,int);
int execute(char**);
void freelist(char **);
char *next_cmd(char *,FILE*);
void *emalloc(size_t n);
void *erealloc(void *p,size_t n);
// 主函数
int main(){
char *cmdline = (char*)malloc(sizeof(char)*BUFSIZ);
char **arglist;
setup();
Load();
printf(">");
while(fgets(cmdline,BUFSIZ,stdin)){
if((arglist = splitline(cmdline)) != NULL){
process(arglist);
}
printf(">");
}
return 0;
}
// 分拆字符串
char** splitline(char* strbuf){
char **arglist=(char**)malloc(sizeof(char*)*MAXARGNUM);
char *wordbuf = (char*)malloc(sizeof(char)*MAXSTRLEN);
int wordlen=0,wordsnum=0;
int start = 0,i = 0;
int len = strlen(strbuf);
while(isspace(strbuf[start])) start++;
for(i = start;i<len;i++){
if(isspace(strbuf[i])){
wordbuf[wordlen]='\0';
if(wordbuf[0] == '$'){
wordbuf = VarReplace(wordbuf);
}
int l = strlen(wordbuf);
arglist[wordsnum]=(char*)malloc(sizeof(char)*(l+1));
strcpy(arglist[wordsnum],wordbuf);
wordsnum++;
wordlen = 0;
}
else wordbuf[wordlen++] = strbuf[i];
}
return arglist;
}
void setup(){
signal(SIGINT,SIG_IGN);
signal(SIGQUIT,SIG_IGN);
}
int process(char **args){
int rv = 0;
if(args[0] == NULL)
rv = 0;
else if(is_control_command(args[0]))
rv = do_control_command(args);
else if(ok_to_execute()){
if(buildin_command(args[0]) == 0)
rv = execute(args);
}
return rv;
}
int ok_to_execute(){
int rv = 1;
if(if_state == WANT_THEN){
syn_err("then expected");
rv = 0;
}
else if(if_state == THEN_BLOCK && if_result == SUCCESS)
rv = 1;
else if(if_state == THEN_BLOCK && if_result == FAIL)
rv = 0;
return rv;
}
int is_control_command(char*s){
return (strcmp(s,"if") == 0 ||
strcmp(s,"then") == 0 ||
strcmp(s,"fi") == 0);
}
int do_control_command(char**args){
char *cmd = args[0];
int rv = -1;
if(strcmp(cmd,"if") == 0){
if(if_state != NEUTRAL)
rv = syn_err("if unexpected");
else{
last_stat = process(args+1);
if_result = (last_stat == 0 ? SUCCESS : FAIL);
if_state = WANT_THEN;
rv = 0;
}
}
else if(strcmp(cmd,"then") == 0){
if(if_state != WANT_THEN)
rv = syn_err("then unexpected");
else{
if_state = THEN_BLOCK;
process(args+1);
rv = 0;
}
}
else if(strcmp(cmd,"fi") == 0){
if(if_state != THEN_BLOCK)
rv = syn_err("fi unexpected");
else{
if_state = NEUTRAL;
rv = 0;
}
}
else
fatal("internal error processing:",cmd,2);
}
void fatal(char *s1,char *s2,int n){
fprintf(stderr,"Error:%s,%s\n",s1,s2);
exit(n);
}
int syn_err(char* msg){
if_state = NEUTRAL;
fprintf(stderr,"syntax,error:%s\n",msg);
return -1;
}
int execute(char *argv[]){
int pid;
int child_info = -1;
if(argv[0] == NULL)
return 0;
if(strcmp(argv[0],"exit") == 0) exit(0);
if((pid = fork()) == -1)
perror("fork");
else if(pid == 0){
signal(SIGINT,SIG_DFL);
signal(SIGQUIT,SIG_DFL);
execvp(argv[0],argv);
perror("cannot execute command");
exit(1);
}
else{
if(wait(&child_info) == -1)
perror("wait");
return child_info;
}
}
void freelist(char **list){
char **cp = list;
while(*cp)
free(*cp++);
free(list);
}
char *next_cmd(char *prompt,FILE *fp){
char *buf;
int bufspace = 0;
int pos = 0;
int c;
printf("%s",prompt);
while((c = getc(fp)) != EOF){
if(pos+1 >= bufspace){
if(bufspace == 0)
buf = (char*)emalloc(BUFSIZ);
else
buf = (char*)erealloc(buf,bufspace + BUFSIZ);
bufspace += BUFSIZ;
}
if(c == '\n')
break;
buf[pos++] = c;
}
if(c == EOF && pos == 0) return NULL;
buf[pos] = '\0';
return buf;
}
void *emalloc(size_t n){
void *rv;
if((rv = malloc(n)) == NULL)
fatal("out of memory","",1);
return rv;
}
void *erealloc(void *p,size_t n){
void *rv;
if((rv = realloc(p,n)) == NULL)
fatal("realloc() failed","",1);
return rv;
}
int buildin_command(char *strbuf){
if(strcmp(strbuf,"set") == 0){
List();
return TRUE;
}
else{
char *cp = strchr(strbuf,'=');
int rv = FALSE;
if(cp!=NULL)
rv = AddVar(strbuf);
return rv;
}
return FALSE;
}
void Load(){
int size = 2;
cur_len = 0;
table = (char**)calloc(sizeof(char*),MAXVARS);
for(;environ[cur_len];cur_len++){
table[cur_len] = (char*)calloc(sizeof(char),strlen(environ[cur_len])+1);
strcpy(table[cur_len],environ[cur_len]);
if(cur_len>=MAXVARS)
table = (char**)realloc(table,sizeof(char)*(MAXVARS)*size++);
}
}
int AddVar(char *strbuf){
if(!Check(strbuf)){
printf("illegal name\n");
return 0;
}
int pos = FindVar(strbuf);
if(pos >= 0){
table[pos]=(char*)calloc(sizeof(char),strlen(strbuf)+1);
strcpy(table[pos],strbuf);
}
else{
int len = strlen(strbuf);
table[cur_len]=(char*)calloc(sizeof(char*),len+1);
strcpy(table[cur_len],strbuf);
cur_len++;
}
return 1;
}
int Check(char *name){
if(isdigit(*name)) return 0;
return 1;
}
void List(){
int i;
for(i=0;i<cur_len;i++)
printf("%s\n",table[i]);
}
int FindVar(char *name){
char *str1 = strchr(name,'=');
*str1='\0';
int i=0;
int find=0;
for(;i<cur_len;i++){
char *str2=strchr(table[i],'=');
*str2='\0';
if(strcmp(name,table[i]) == 0){
find =1;
*str2='=';
break;
}
*str2='=';
}
*str1='=';
if(find) return i;
else return -1;
}
// 变量替换
char* VarReplace(char* strbuf){
if(*strbuf == '$'){
int i;
for(i=0;i<cur_len;i++){
char *var = strchr(table[i],'=');
*var='\0';
if(strcmp(table[i],strbuf+1) == 0){
strbuf = (char*)calloc(sizeof(char),strlen(var+1)+2);
strcpy(strbuf,var+1);
*var='=';
break;
}
*var='=';
}
}
return strbuf;
}