Linux mini shell

Minishell

    A. First Edition

    This is purely for fun and it is my roommate's homework.

    B.The problem

# Objective

The main objective of this programming assignment is to introduce the student to C/C++ programming in the Linux environment. Specifically, the student will implement a C/C++ program that will use Linux system calls and commands.

# ¡¤ Problem specification

A shell is an interactive program that interprets user commands for the operating system (i.e. Linux). The commands are entered on a command line interface and each command can be specified with some options and arguments. Linux provides different shells (e.g. bourne, csh, tcsh, bash, korn,¡) that can handle mostly the same commands with some syntactical differences. In addition, the user can also implement his (or her) own shell to fit his (or her) particular needs.

System calls represent calls to functions that are implemented in the kernel of the Linux operating system. The programmer must use system calls in order to request system services because he (or she) is restricted direct access to the system resources for protection and security reasons.

You are required to write a C/C++ language program that will be used as a command shell for file management. You must use Linux system calls and commands for file and for process management to implement the functionalities of your shell. You should name the program minishell.c and save it under a directory named lab4. Your program will behave similarly to the Linux shell and should display a prompt (i.e. minishell>) at start-up. The user should then enter one of the shell commands (with appropriate options and arguments) at the prompt followed by <Enter>, and the shell should again display the prompt after processing the command. If a command is invalid the shell should display an appropriate error message. In order to compile and link your program, you will need to implement a make file.

# ¡¤ Functionalities of the shell program

¡¤ Shell termination.

- Syntax : exit.

- Example : minishell>exit.

- The exit command should terminate the execution of the shell and control should be returned to the Linux shell using the void exit(int status) system call.

¡¤ Changing the current directory.

- Syntax : cd directory.

- Example : minishell>cd ~/lab4.

- The cd command changes the current working directory of the user to the specified directory (if exists) using the int chdir(const char *path) system call.

- If the specified directory does not exist then the message ¡°The directory directory does not exist¡± should be displayed on the screen.

¡¤ Listing the content of the current directory.

- Syntax : dir [directory].

- Example : minishell>dir ~/lab4.

- The dir command lists all the files that are in the specified directory (if exists) using the DIR *opendir(const char *dirname), int closedir(DIR *dirptr), and struct dirent *readdir(DIR *dirptr) system calls. If no directory argument is specified (enclosed brackets indicate an optional argument) then the content of the current directory should be displayed.

- If the specified directory does not exist then the message ¡°The directory directory does not exist¡± should be displayed on the screen.

- You should implement a function void dir(char *directory) that will call the appropriate system calls.

¡¤ Deleting a file.

- Syntax : del file.

- Example : minishell>del ~/lab4/minishell.o

- The del command deletes the specified file using the int unlink(char *pathname) system call.

- If the specified file does not exist then the message ¡°The file file does not exist¡± should be displayed on the screen. File existence should be checked with the int access(char *pathname, int amode)

system call.

¡¤ Renaming a file.

- Syntax : ren srcfile dstfile.

- Example : minishell>ren ~/lab4/minishell.c ~/lab4/minishell1.c.

- The ren command renames a source file srcfile (if exists) into a destination file dstfile using the int rename(const char *oldpath, const char *newpath) system call.

- If the source file srcfile does not exist then the message ¡°The source file srcfile does not exist.¡± should be displayed on the screen.

- If the source file srcfile and the destination file dstfile are the same then the message ¡°The source file srcfile and the destination file dstfile can not be the same.¡± should be displayed on the screen.

¡¤ Copying a file.

- Syntax : copy srcfile dstfile.

- Example : minishell>copy ~/lab4/minishell.o ~/lab4/minishell.o.

- The copy command copies a source file srcfile (if exists) to a destination file dstfile by using the Linux cp command. Consequently, the copy command must be implemented as an external minishell command. Thus, the minishell must create a new child process to run the cp command using the pid_t fork( ), int execv(const char *path, char *const argv[]) and

pid_t wait(int *status) system calls

.

Your shell should wait the end of execution of the child process before accepting new user commands.

# ¡¤ Test cases

- minishell>cd ~/lab4

- minishell>dir

- minishell>copy ~/minishell.c ~/minishell1.c

- minishell>del ~/minishell1.c

- minishell>dir

- minishell>ren minishell.c minishell2.c

- minishell>cd ..

- minishell>dir ~/lab4

- minishell>exit

# ¡¤ Submission

- A diskette that contains the source, object and executable programs.

- A listing of the make file.

- A printed copy of the source and listing files.

- Sample output data for all the test cases that are specified.

C.The idea of program

The idea is straightforward and the code is a little bit tricky since I am using my favorite

function pointer.

    D.The major functions

    E.Further improvement

    ¡¡

    F.File listing

    1. minishell.c

    ¡¡

    file name: myhead.h

    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <string.h>
    #include <errno.h>
    #include <dirent.h>

    extern int errno;

    #define CommandNumber 6
    #define MaxCommandLine 128
    #define MaxParamNumber 2

    char* promptStr="minishell>";
    char* delim=" \n";
    char* helpStr=
    "Syntax: exit\nSyntax: cd directory\nSyntax: dir [directory]\nSyntax: del file\nSyntax: ren srcfile dstfile\nSyntax: copy srcfile dstfile\n";

    enum CommandType
    {
    	ExitType, CDType, DirType, DelType, RenType, CopyType
    };

    char* commandStr[CommandNumber]=
    {
    	"exit", "cd", "dir", "del", "ren", "copy"
    };

    void exitShell(char* params[], int paramNumber);
    void changeDir(char* params[], int paramNumber);
    void listDir(char* params[], int paramNumber);
    void delFile(char* params[], int paramNumber);
    void renFile(char* params[], int paramNumber) ;
    void copyFile(char* params[], int paramNumber);

    void (*commandArray[CommandNumber])(char* params[], int paramNumber)=
    {
    	exitShell, changeDir, listDir, delFile, renFile, copyFile
    };

    int parseCommand(char* cmdStr, char* params[], int* paramNumber);
    void dodir(char* path);

    void printPrompt();

    void errhandle(char* msg);

    int main(int argc, char* argv[])
    {
    	char buf[MaxCommandLine];
    	int n, paramNumber;
    	int commandType;
    	char* params[4];
    	printPrompt();
    	while ((n=read(STDIN_FILENO, buf, MaxCommandLine))>0)
    	{
    		buf[n]='\0';
    		commandType=parseCommand(buf, params, &paramNumber);
    		if (commandType==-1)
    		{
    			printf("illegal command\n%s");
    		}
    		else
    		{
    			commandArray[commandType](params, paramNumber);
    		}
    		printPrompt();
    	}
    	return 0;
    }

    int parseCommand(char* buf, char* params[], int* paramNumber)
    {
    	int i;
    	*paramNumber=0;
    	if ((params[*paramNumber]=strtok(buf, delim))!=NULL)
    	{
    		for (i=CommandNumber-1; i>=0; i--)
    		{
    			if (strcmp(params[*paramNumber], commandStr[i])==0)
    			{
    				break;
    			}
    		}
    		//when not found, i==-1
    		if (i==-1)
    		{
    			return i;
    		}
    	}
    	else
    	{
    		return -1;
    	}
    	(*paramNumber)++;
    	//the maximum param number is only 2, so I test for 3 to see if strtok return NULL
    	while (1)
    	{
    		if ((params[*paramNumber]=strtok(NULL, delim))==NULL)
    		{
    			break;
    		}
    		(*paramNumber)++;
    		if (*paramNumber==4)
    		{
    			//this means the param number is more than 2 and it is wrong
    			return -1;
    		}
    	}
    	return i;
    }

    void exitShell(char* params[], int paramNumber)
    {
    	exit(0);
    }

    void renFile(char* params[], int paramNumber)
    {
    	if (paramNumber!=3)
    	{
    		printf(helpStr);
    	}
    	else
    	{
    		if (strcmp(params[1], params[2])==0)
    		{
    			printf("The source file %s and the destination file %s can not be the same.\n", params[1], params[2]);
    		}
    		else
    		{
    			if (access(params[1], F_OK)<0)
    			{
    				printf("The source file %s does not exist.\n", params[1]);
    			}
    			else
    			{
    				if (rename(params[1], params[2])<0)
    				{
    					printf("cannot rename file from %s to file %s\n", params[1], params[2]);
    				}
    			}
    		}
    	}
    }

    void copyFile(char* params[], int paramNumber)
    {
    	int status;
    	if (paramNumber!=3)
    	{
    		printf(helpStr);
    	}
    	else
    	{
    		if (fork()==0)
    		{
    			execv("/bin/cp", params);
    			exit(0);
    		}
    		else
    		{
    			if (wait(&status)<0)
    			{
    				printf("wait error\n");
    			}
    		}
    	}
    }

    void delFile(char* params[], int paramNumber)
    {
    	if (paramNumber!=2)
    	{
    		printf(helpStr);
    	}
    	else
    	{
    		if (access(params[1], F_OK)<0)
    		{
    			printf("The file %s does not exist\n", params[1]);
    		}
    		else
    		{
    			if (unlink(params[1])<0)
    			{
    				printf("cannot delete file %s\n", params[1]);
    			}
    		}
    	}
    }

    void changeDir(char* params[], int paramNumber)
    {
    	if (paramNumber!=2)
    	{
    		printf(helpStr);
    	}
    	else
    	{
    		if (chdir(params[1])<0)
    		{
    			if (errno==ENOTDIR||errno==ENOENT)
    			{
    				printf("%The directory %s does not exist\n", params[1]);
    			}
    		}
    	}
    }

    void dodir(char* path)
    {
    	DIR* dp;
    	struct dirent* dirnode;
    	if ((dp=opendir(path))!=NULL)
    	{
    		while ((dirnode=readdir(dp))!=NULL)
    		{
    			printf("%s\n", dirnode->d_name);
    		}
    	}
    	else
    	{
    		printf("The directory %s does not exist\n", path);
    	}
    }

    void listDir(char* params[], int paramNumber)
    {
    	if (paramNumber!=1&&paramNumber!=2)
    	{
    		printf(helpStr);
    	}
    	else
    	{
    		if (paramNumber==1)
    		{
    			dodir(".");
    		}
    		else
    		{
    			dodir(params[1]);
    		}
    	}
    }

    void printPrompt()
    {
    	if (write(STDOUT_FILENO, promptStr, strlen(promptStr))!=strlen(promptStr))
    	{
    		errhandle("cannot write to stdout");
    	}
    }

    void errhandle(char* msg)
    {
    	perror(msg);
    	exit(1);
    }

    How to run?

[qingz_hu@alamanni ~/lab5] % ls
dirview.exe lab4 mini.exe minishell.c minishell.exe
[qingz_hu@alamanni ~/lab5] % ./minishell.exe
minishell>dir
.
..
minishell.c
mini.exe
lab4
dirview.exe
minishell.exe
minishell>cd lab4
minishell>dir
.
..
backup.c
dirview.exe
minishell>copy ../dirview.exe ./dirview.bak
minishell>dir
.
..
backup.c
dirview.exe
dirview.bak
minishell>del dirview.bak
minishell>dir
.
..
backup.c
dirview.exe
minishell>ren dirview.exe dirview.o
minishell>dir
.
..
backup.c
dirview.o
minishell>exit
[qingz_hu@alamanni ~/lab5] %
¡¡