Dec 16
公布期末设计Smallshell的完整源代码:
/* ************************************************* * * Small shell on Unix/Linux * * This shell finished all the functions which * are required. And I added new inner functions * "help" and "exit" by myself. * * Enjoy it! * * author: Longqianzhi 용성지 * Stu_ID: 12094817 * data: Dec/27/2010 * * *************************************************/ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/wait.h> #include <fcntl.h> #include <signal.h> #define MAX_CMD_ARG 10 #define MAX_CMD_LIST 10 const char *prompt = "# "; char* cmdlist[MAX_CMD_LIST]; char* cmdargs[MAX_CMD_ARG]; char cmdline[BUFSIZ]; void fatal(char *str); void execute_cmd(char *cmd); void execute_cmdline(char* cmdline); int makeargv(char *s, const char *delimiters, char** argvp, int MAX_LIST); void help(); void cd(char *path); void catchint(int); void join(); /* Use one pipe to communication.*/ int join2(); /* Use two pipe to communication.*/ int dpipe_test(); /* It's useless.Just for test.*/ void parse_filter(char *cmd); /* A filter for argument*/ int main(int argc, char**argv){ int i=0; static struct sigaction act; /*Cope with the signal*/ act.sa_handler=catchint; sigfillset(&(act.sa_mask)); sigaction(SIGINT,&act,NULL); sigaction(SIGQUIT,&act,NULL); while (1) { fputs(prompt, stdout); fgets(cmdline, BUFSIZ, stdin); cmdline[ strlen(cmdline) -1] ='\0'; execute_cmdline(cmdline); printf("\n"); } return 0; } void fatal(char *str){ perror(str); exit(1); } void execute_cmdline(char* cmdline){ int count = 0; int i=0; count = makeargv(cmdline, "!", cmdlist, MAX_CMD_LIST); /* Execute the commands with different way.*/ /*Just a single command.*/ if(count==1){ /* First of all,try to make sure whether * the command is "cd". */ if(cmdlist[0][0]=='c'&&cmdlist[0][1]=='d'){ parse_filter(cmdlist[0]); count = makeargv(cmdlist[0], " \t", cmdargs, MAX_CMD_ARG); if(!strcmp("cd",cmdargs[0])){ if(chdir(cmdargs[1])) fatal("cd"); return; } } /* Fork a child process to run every * commands except "cd" */ switch(fork()) { case -1: fatal("fork error"); case 0: execute_cmd(cmdlist[0]); default: wait(NULL); fflush(stdout); } } /*Execute two programs with a pipe.*/ if(count==2){ join(); } /*Execute three programs with two pipes.*/ if(count==3){ join2(); } /*There are too many commands*/ if(count>=4){ printf("Too many pipes!\n"); return; } } /* ***************************************** * Execute a single commands with arguments. * * *****************************************/ void execute_cmd(char *cmd){ int i=0; int count = 0; parse_filter(cmd); count = makeargv(cmd, " \t", cmdargs, MAX_CMD_ARG); /* * Recognize some inner functions * like cd,help,exit and so on. * Also you can add some new inner * functions by yourself here. * */ if(!strcmp("help",cmdargs[0])){ help(); return ; } if(!strcmp("exit",cmdargs[0])){ kill(getppid(),SIGKILL); /*kill parent process*/ raise(SIGKILL); /*kill myself*/ exit(0); } execvp(cmdargs[0], cmdargs); fatal("exec error"); } /* ***************************************** * Count the arguments'number in a command. * * ***************************************/ int makeargv(char *s, const char *delimiters, char** argvp, int MAX_LIST) { int i = 0; int numtokens = 0; char *snew = NULL; if( (s==NULL) || (delimiters==NULL) ) return -1; snew = s + strspn(s, delimiters); if( (argvp[numtokens]=strtok(snew, delimiters)) == NULL ) return numtokens; numtokens = 1; while(1){ if( (argvp[numtokens]=strtok(NULL, delimiters)) == NULL) break; if(numtokens == (MAX_LIST-1)) return -1; numtokens++; } return numtokens; } /* ********************************* * Inner fnuction: help(). * Show some infomations to the stdin * ***********************************/ void help(){ printf("\n * *************************************** *"); printf("\n * *"); printf("\n * WELCOME TO SMALL SHELL ! *"); printf("\n * *"); printf("\n * *************************************** *"); printf("\n * *"); printf("\n * There are 3 inner functions in shell. *"); printf("\n * *"); printf("\n * 1) cd :Change the current directory. *"); printf("\n * 2) exit :Quit from the small shell. *"); printf("\n * 3) help :Show this message for help. *"); printf("\n * *"); printf("\n * IF YOU FIND ANY BUGS OR *"); printf("\n * HAVE ANY QUESTIONS *"); printf("\n * *"); printf("\n * mail to me:longqianzhi@gmail.com *"); printf("\n * *"); printf("\n * *************************************** *\n"); exit(0); } /* *************************** * Inner function:cd(). * Change the current directory. * * ***************************/ void cd(char *path){ if(chdir(path)) fatal("cd"); } /* ************************************ * The function about act.sa_handler * If press the Ctrl+c, resume the shell. * * ************************************/ void catchint(int signo){ printf("\nReceive a signo=%d\n",signo); printf("Returning to the shell\n"); /* Init the cmdline Array. * If not,the shell will try to run * commands in the memory. */ cmdline[0]='\0'; } /* ************************************ * An easy way to use double pipe. * * But this function is just for test and * debug in the project,because I wrote * a new one by myself. The new one is * called "join2". * * ***********************************/ int dpipe_test(){ /*build command string with normal format.*/ char newstr[128]; strcpy(newstr,cmdlist[0]); strcat(newstr,"|"); strcat(newstr,cmdlist[1]); strcat(newstr,"|"); strcat(newstr,cmdlist[2]); /*call system shell to run the commmands*/ system(newstr); return 0; } /* ************************************** * Delete special character from argument. * If there is any special charater, * this function will change them to '\32' * * ***************************************/ void parse_filter(char *cmd){ int i=0; int CMAX=strlen(cmd); while(cmd[i]!=' '&&i<CMAX) i++; if((++i)<CMAX){ if (cmd[i++]=='-') { while(cmd[i]!=' '&&i<CMAX){ if(cmd[i]<'0'||(cmd[i]>'9'&&cmd[i]<'A')|| (cmd[i]>'Z'&&cmd[i]<'a')||cmd[i]>'z'){ char tmp=cmd[i-1]; cmd[i]=tmp; } i++; } } } } /* *********************************** * Link the child process's stdout to * grandchild process's stdin with pipe. * * ***********************************/ void join(){ int p[2],status; switch (fork()){ case -1:fatal("1st fork call in join()"); case 0:break; default:wait(&status); return; } /* Try to make a new pipe.*/ if(pipe(p)==-1) fatal("pipe call in join()"); switch(fork()){ case -1:fatal("2nd fork call in join()"); case 0: dup2(p[1],1); close(p[0]); close(p[1]); execute_cmd(cmdlist[0]); fatal("1st execvp call in join()"); default: dup2(p[0],0); close(p[0]); close(p[1]); execute_cmd(cmdlist[1]); fatal("2nd execvp call in join()"); } } /* ************************************* * Link three child/grandchild processes * one by one with two pipes. * * ************************************/ int join2(){ int status,p1[2],p2[2]; /*Test the double pipe.*/ if(dpipe_test()==0) return 0; /* Grandparent process */ switch(fork()){ case -1:fatal("1st fork call"); case 0:break; default: wait(&status); return status; } /* Parent process.Run the last command.*/ if(pipe(p1)<0) fatal ("pipe call"); /*make a pipe.*/ switch(fork()){ case -1:fatal("2nd fork call"); case 0:break; default: dup2(p1[0],0); close(p1[0]); close(p1[1]); execute_cmd(cmdlist[2]); fatal("1st exec call"); } /* Try to make a new pipe.*/ if(pipe(p2)<0) fatal("pipe call"); /* Child process.Run the second command.*/ switch(fork()){ case -1:fatal("3th fork call"); case 0:break; default: dup2(p1[1],1); dup2(p2[0],0); close(p1[0]); close(p1[1]); close(p2[0]); close(p2[1]); execute_cmd(cmdlist[1]); fatal("2nd exec call"); } /* Grandchild process.Run the first command.*/ dup2(p2[1],1); close(p2[0]); close(p2[1]); execute_cmd(cmdlist[0]); fatal("3th exec call"); }