Repository: vvy/wshell Branch: master Commit: 328695314a7a Files: 10 Total size: 21.0 KB Directory structure: gitextract_6f06dpcs/ ├── .gitignore ├── README.md ├── builtin_command.c ├── makefile ├── parsing.c ├── read_command.c ├── test.c ├── type_prompt.c ├── wshell.c └── wshell.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.[oa] *~ ================================================ FILE: README.md ================================================ # Introduction A simple simulation of shell in Linux for learning. A blog (in Chinese) of introduction and how to build it :http://www.cnblogs.com/wuyuegb2312/p/3399566.html # Usage type   make wshell for wshell read input by fgets() or   make wshell_r    for wshell with readline lib.It need to install libreadline5-dev first. ## Attention   Because of the lack of support for regular expressions , typing file's names such as "*.c" are not support.    # File List ### wshell.c   main program ### type_prompt.c   print out the prompt of wshell including path,hostname ### read_command.c   read command input, and analyse the command and parameter(s). ### builtin_command.c   support some built-in command,such as exit,quit,about, and cd. ### test.c   a test program, helloworld, which can be executed in wshell. ### parsing.c   analyses user's input line and tell them to wshell.    # Update log ## 2015 ### 11.08 -Make the README.md be a real markdown file. -Bug fix1: Redirection operator ">>" will append a file, not trunc it to 0-size. As noted by [caiminfeng](http://www.cnblogs.com/caiminfeng/) . -Bug fix2: When inputed "cd", it would be core dump before. As noted by [guokesong](https://github.com/guokesong) in [issues2](https://github.com/vvy/wshell/issues/2). ## 2014 ### 05.14 -Bug fix: In readline mode, the spaces alloced by readline() was freed only once.Now they are all freed in the end of read_command(). As noted by [xingfe123](http://home.cnblogs.com/u/614123/) ### 04.14 -Bug fix: Add handler of SIGCHLD. Earlier wshell would ignore the zombie process in background. The handler only process the background process whose pid are saved in a array. -Adjust some indents, although they are not errors. ## 2013 ### 12.8 -Bug fix: Output redirect function was not completed. '>' still is for make new file,but '>>' append to any file now. ### 11.3 -Implemented a simple parsing function parse() to analyses the user input. -Support I/O redirect, background running, and pipe. -Bug fix: Typing ctrl+d will normal exit wshell. before, segment fault when using readline lib. Lack of free() somewhere, which may lead to memory leak. ### 11.1 -Implemented some builtin command,such as exit/quit, about, and, cd. -Bug fix: Never free the memory of command and parameters before, which may cause memory leak. To solve it, read_command() has been rewrited, and is more like the one in Linux. -Rewrite read_command(): There is no need to malloc for command and parameters any more. All these just are pointers now. -Supply two versions: Using readline lib and not.the former need installing readline lib. Type "make wshell_r" to make the former,and "make wshell" for another. ### 10.31 -Use execvp() instead of execve() to handle with commands like ls. -Detect whether the shell is executed as root by geteuid() -Bug fix: execv()'s arg[0] usually is the name of command without path. earlier version is not correct. -Plan to do: a built-in cd command. ##10.16 -A shell with basic function: get input and execute the test program(hello world). # 简介 为了学习而编写的Linux下shell的简单模拟实现。 介绍如何一步一步编写该程序的博客:http://www.cnblogs.com/wuyuegb2312/p/3399566.html # 用法 输入   make wshell 构建使用fgets()接收命令的wshell   make wshell_r    构建使用readline接收命令的wshell。需要安装libreadline5-dev或其他类似的库。 ## 注意   由于不支持正则表达式,含有通配符如"*.c"这样的文件名是不支持的。 不存在的命令不会提示。    # 文件列表 ### wshell.c   主程序 ### type_prompt.c   拼接当前路径、主机名的提示符。 ### read_command.c   读取输入的命令,并分离出其中的参数。 ### builtin_command.c   提供对基本内建命令的支持,含exit、quit、about、cd等。 ### test.c   用来在wshell中执行的测试程序。 ### parsing.c   分析用户输入(是否使用了管道、重定向、后台执行等)。    # 更新记录 ## 2015 ### 11.08 -让README.md成为了真正的markdown文件 -调整部分缩进 -Bug修复1: 重定向符">>"将正确地在文件存在时做追加,而非把它变成0大小的文件。 感谢[caiminfeng](http://www.cnblogs.com/caiminfeng/) 的指出。 -Bug修复2: 当输入不带参数的 `cd`时,会导致崩溃。 感谢 [guokesong](https://github.com/guokesong) 在 [issues2](https://github.com/vvy/wshell/issues/2) 指出。 ## 2014 ### 05.14 -Bug修复: 在readline模式下, readline()申请的内存不会被释放。 感谢[xingfe123](http://home.cnblogs.com/u/614123/) 指出。 ### 04.14 -Bug修复: 增加了SIGCHLD信号的处理器。较早版本的wshell不会处理后台的僵尸进程。 但是该处理器只会处理事先保存了pid的子进程。 -调整了不会导致错误的缩进。 ## 2013 ### 12.08 -Bug修复: 输出重定向尚未完成。 '>' 仍然会创建新文件,但是'>>'会往文件末尾追加。 ### 11.03 -实现了用于分析用户输入的函数parse()。 -支持输入输出重定向、管道、后台运行。 -Bug修复: 输入ctrl+d会正常退出wshell了。此前,在使用readline库时会段错误。 由于某处缺少free(), 会内存泄漏。 ### 11.01 -实现了一些内建命令,包括`exit` 、 `quit` 、 `about` 、 `cd`。 -Bug修复: 从未释放命令和参数的空间,导致内存泄漏。 重写read_command()以解决这个问题,现在的行为更像Linux的了。 -重写read_command(): 现在没必要为command和parameter释放内存了,它们只是指针。 -提供了两个版本: 使用readline库和不使用的。前者需要事先安装这个库。 输入"make wshell_r"可以生成前者,而"make wshell"会生成后者。 ### 10.31 -用execvp()代替execve()处理 `ls` 这种命令。 -通过geteuid()判断shell是否执行在root用户下。 -Bug修复: execv()'的arg[0]一般是不含路径的命令,上一个版本处理有误。 -下一步计划: 支持内建命令 `cd`。 ##10.16 -一个具有基本功能的shell: 获取输入并执行测试程序(hello world). ================================================ FILE: builtin_command.c ================================================ /* * ===================================================================================== * Filename: builtin_command.c * Description: * Version: 1.0 * Created: 2013.11.01 15h31m28s * Author: wuyue (wy), vvuyve@gmail.com * Company: UESTC * ===================================================================================== */ #include "wshell.h" int builtin_command(char *command, char **parameters) { extern struct passwd *pwd; if(strcmp(command,"exit")==0 || strcmp(command,"quit")==0) exit(0); else if(strcmp(command,"about") == 0) { printf("This is a simulation of shell (bash) in Linux.\n"); return 1; } else if(strcmp(command,"cd")==0) { char *cd_path = NULL; if(parameters[1] == NULL) //make "cd" to "cd .." as in bash { parameters[1] = malloc(3 * sizeof(char)); parameters[1][0]= '.'; parameters[1][1]= '.'; parameters[1][2]= '\0'; } if(parameters[1][0] == '~') { cd_path = malloc(strlen(pwd->pw_dir)+strlen(parameters[1])); //'~' makes length 1 more,but instead of '\0' if(cd_path == NULL) { printf("cd:malloc failed.\n"); } strcpy(cd_path,pwd->pw_dir); strncpy(cd_path+strlen(pwd->pw_dir),parameters[1]+1,strlen(parameters[1])); //printf("path with ~:\n%s\n",cd_path); } else { cd_path = malloc(strlen(parameters[1]+1)); if(cd_path == NULL) { printf("cd:malloc failed.\n"); } strcpy(cd_path,parameters[1]); } if(chdir(cd_path)!= 0) printf("-wshell: cd: %s:%s\n",cd_path,strerror(errno)); free(cd_path); } return 0; } ================================================ FILE: makefile ================================================ wshell: wshell.h wshell.c type_prompt.c read_command.c builtin_command.c parsing.c test gcc wshell.c type_prompt.c read_command.c builtin_command.c parsing.c -o wshell wshell_r: wshell.h wshell.c type_prompt.c read_command.c builtin_command.c parsing.c test gcc wshell.c type_prompt.c read_command.c builtin_command.c parsing.c -o wshell -D READLINE_ON -I /usr/include -lreadline -ltermcap test: test.c gcc test.c -o test clean: rm -f wshell test ================================================ FILE: parsing.c ================================================ /* * ===================================================================================== * Filename: parsing.c * Description: * Version: 1.0 * Created: 2013.11.02 15h44min23s * Author: wuyue (wy), vvuyve@gmail.com * Company: UESTC * ===================================================================================== */ #include "wshell.h" int parse_info_init(struct parse_info *info) { info->flag = 0; info->in_file = NULL; info->out_file = NULL; info->command2 = NULL; info->parameters2 = NULL; return 0; } int parsing(char **parameters,int ParaNum,struct parse_info *info) { int i; parse_info_init(info); if(strcmp(parameters[ParaNum-1],"&") ==0) { info->flag |= BACKGROUND; parameters[ParaNum-1] = NULL; ParaNum--; } for(i=0;iflag |= IN_REDIRECT; info->in_file = parameters[i+1]; parameters[i] = NULL; i+=2; } else if(strcmp(parameters[i],">")==0) { info->flag |= OUT_REDIRECT; info->out_file = parameters[i+1]; parameters[i] = NULL; i+=2; } else if(strcmp(parameters[i],">>")==0) { info->flag |= OUT_REDIRECT_APPEND; info->out_file = parameters[i+1]; parameters[i] = NULL; i+=2; } else if(strcmp(parameters[i],"|")==0) { char* pCh; info->flag |= IS_PIPED; parameters[i] = NULL; info->command2 = parameters[i+1]; info->parameters2 = ¶meters[i+1]; for(pCh = info->parameters2[0]+strlen(info->parameters2[0]); pCh!=&(info->parameters2[0][0]) && *pCh!='/';pCh--) ; if(*pCh == '/') pCh++; info->parameters2[0] = pCh; break; } else i++; } #ifdef DEBUG printf("\nbackground:%s\n",info->flag&BACKGROUND?"yes":"no"); printf("in redirect:"); info->flag&IN_REDIRECT?printf("yes,file:%s\n",info->in_file):printf("no\n"); printf("out redirect:"); info->flag&OUT_REDIRECT?printf("yes,file:%s\n",info->out_file):printf("no\n"); printf("pipe:"); info->flag&IS_PIPED?printf("yes,command:%s %s %s\n",info->command2,info->parameters2[0],info->parameters2[1]):printf("no\n"); #endif return 1; } ================================================ FILE: read_command.c ================================================ /* * ===================================================================================== * Filename: read_command.c * Description: * Version: 1.0 * Created: 2013.10.21 14h12min24s * Author: wuyue (wy), vvuyve@gmail.com * Company: UESTC * ===================================================================================== */ #include "wshell.h" #ifdef READLINE_ON #include #include #endif //return value: number of parameters //0 represents only command without any parameters //-1 represents wrong input int read_command(char **command,char **parameters,char *prompt) { #ifdef READLINE_ON free(buffer); buffer = readline(prompt); if(feof(stdin)) { printf("\n"); exit(0); } #else printf("%s",prompt); char* Res_fgets = fgets(buffer,MAXLINE,stdin); if(Res_fgets == NULL) { printf("\n"); exit(0); } #endif if(buffer[0] == '\0') return -1; char *pStart,*pEnd; int count = 0; int isFinished = 0; pStart = pEnd = buffer; while(isFinished == 0) { while((*pEnd == ' ' && *pStart == ' ') || (*pEnd == '\t' && *pStart == '\t')) { pStart++; pEnd++; } if(*pEnd == '\0' || *pEnd == '\n') { if(count == 0) return -1; break; } while(*pEnd != ' ' && *pEnd != '\0' && *pEnd != '\n') pEnd++; if(count == 0) { char *p = pEnd; *command = pStart; while(p!=pStart && *p !='/') p--; if(*p == '/') p++; //else //p==pStart parameters[0] = p; count += 2; #ifdef DEBUG printf("\ncommand:%s\n",*command); #endif } else if(count <= MAXARG) { parameters[count-1] = pStart; count++; } else { break; } if(*pEnd == '\0' || *pEnd == '\n') { *pEnd = '\0'; isFinished = 1; } else { *pEnd = '\0'; pEnd++; pStart = pEnd; } } parameters[count-1] = NULL; #ifdef DEBUG /*input analysis*/ printf("input analysis:\n"); printf("pathname:[%s]\ncommand:[%s]\nparameters:\n",*command,parameters[0]); int i; for(i=0;i int main() { printf("Hello world!\n"); return 0; } ================================================ FILE: type_prompt.c ================================================ /* * ===================================================================================== * Filename: type_prompt.c * Description: * Version: 1.0 * Created: 2013.10.16 20h18min28s * Author: wuyue (wy), vvuyve@gmail.com * Company: UESTC * ===================================================================================== */ //#include #include "wshell.h" const int max_name_len = 256; const int max_path_len = 1024; void type_prompt(char *prompt) { extern struct passwd *pwd; char hostname[max_name_len]; char pathname[max_path_len]; int length; pwd = getpwuid(getuid()); getcwd(pathname,max_path_len); if(gethostname(hostname,max_name_len)==0) sprintf(prompt,"[Wshell]%s@%s:",pwd->pw_name,hostname); else sprintf(prompt,"[Wshell]%s@unknown:",pwd->pw_name); //printf("pathname: %s,length:%d\npw_dir:%s,length:%d\n", //pathname,strlen(pathname),pwd->pw_dir,strlen(pwd->pw_dir)); length = strlen(prompt); if(strlen(pathname) < strlen(pwd->pw_dir) || strncmp(pathname,pwd->pw_dir,strlen(pwd->pw_dir))!=0) sprintf(prompt+length,"%s",pathname); else sprintf(prompt+length,"~%s",pathname+strlen(pwd->pw_dir)); length = strlen(prompt); if(geteuid()==0) sprintf(prompt+length,"#"); else sprintf(prompt+length,"$"); return; } //int main() //{ // type_prompt(); // return 0; //} ================================================ FILE: wshell.c ================================================ /* * ===================================================================================== * Filename: wshell.c * Description: * Version: 1.0 * Created: 2013.10.16 17h12min19s * Author: wuyue (wy), vvuyve@gmail.com * Company: UESTC * ===================================================================================== */ #include "wshell.h" #define TRUE 1 #define MAXPIDTABLE 1024 pid_t BPTable[MAXPIDTABLE]; void sig_handler(int sig) { pid_t pid; int i; for(i=0;i 0) { printf("process %d exited.\n",pid); BPTable[i] = 0; //clear } else if(pid < 0) { if(errno != ECHILD) perror("waitpid error"); } //else:do nothing. //Not background processses has their waitpid() in wshell. } return; } void proc(void) { int status,i; char *command = NULL; char **parameters; int ParaNum; char prompt[MAX_PROMPT]; struct parse_info info; pid_t ChdPid,ChdPid2; parameters = malloc(sizeof(char *)*(MAXARG+2)); buffer = malloc(sizeof(char) * MAXLINE); if(parameters == NULL || buffer == NULL) { printf("Wshell error:malloc failed.\n"); return; } //arg[0] is command //arg[MAXARG+1] is NULL if(signal(SIGCHLD,sig_handler) == SIG_ERR) perror("signal() error"); while(TRUE) { int pipe_fd[2],in_fd,out_fd; type_prompt(prompt); ParaNum = read_command(&command,parameters,prompt); if(-1 == ParaNum) continue; ParaNum--;//count of units in buffer parsing(parameters,ParaNum,&info); if(builtin_command(command,parameters)) continue; if(info.flag & IS_PIPED) //command2 is not null { if(pipe(pipe_fd)<0) { printf("Wshell error:pipe failed.\n"); exit(0); } } if((ChdPid = fork())!=0) //wshell { if(info.flag & IS_PIPED) { if((ChdPid2=fork()) == 0) //command2 { close(pipe_fd[1]); close(fileno(stdin)); dup2(pipe_fd[0], fileno(stdin)); close(pipe_fd[0]); execvp(info.command2,info.parameters2); } else { close(pipe_fd[0]); close(pipe_fd[1]); waitpid(ChdPid2,&status,0); //wait command2 } } if(info.flag & BACKGROUND) { printf("Child pid:%u\n",ChdPid); int i; for(i=0;i #include #include #include #include #include #include #include #include #include #include #define MAX_PROMPT 1024 #define MAXLINE 4096 //the length of all args is ARG_MAX #define MAXARG 20 struct parse_info; struct passwd *pwd; char *buffer; void type_prompt(char*); int read_command(char **,char **,char*); int builtin_command(char *,char **); int parsing(char **,int,struct parse_info *); void proc(void); void sig_handler(int sig); #ifndef STRUCT_PARSE_INFO #define STRUCT_PARSE_INFO #define BACKGROUND 1 #define IN_REDIRECT 2 #define OUT_REDIRECT 4 #define OUT_REDIRECT_APPEND 8 #define IS_PIPED 16 struct parse_info { int flag; char* in_file; char* out_file; char* command2; char** parameters2; }; #endif