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");

}