288 lines
7.3 KiB
C
288 lines
7.3 KiB
C
#include <unistd.h>
|
|
#include <sys/wait.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
|
|
#define true 1
|
|
#define false 0
|
|
#define LEN 512
|
|
#define PATH "/bin/"
|
|
#define ERROR -1
|
|
#define EXIT_1 256
|
|
|
|
int getHostName(char* host);
|
|
int getUserName(char* name);
|
|
int runExec(char** args, int writeToFile);
|
|
void PrintTokens (char** tokens);
|
|
char** ReadTokens(FILE* stream, int* writeToFile) ;
|
|
void FreeTokens(char** tokens) ;
|
|
void checkIfQuit(char* word);
|
|
int getFileNameIndex(char** args);
|
|
void freeRemainderInArr(char** args, int beforLast);
|
|
|
|
|
|
|
|
int main()
|
|
{
|
|
int exitCode = 0;
|
|
char* host_n = (char *)malloc(sizeof(char));
|
|
char* user_n = (char *)malloc(sizeof(char));
|
|
char** args;
|
|
int writeToFile = false;
|
|
|
|
if(getHostName(host_n) == ERROR)
|
|
goto quit;
|
|
if(getUserName(user_n) == ERROR)
|
|
goto quit;
|
|
|
|
signal(SIGINT, SIG_IGN); // ignore SIGINT
|
|
|
|
while(1)
|
|
{
|
|
printf("%d %s@%s$ ",exitCode, user_n, host_n); // Print Prompt
|
|
args = ReadTokens(stdin, &writeToFile); // get user command and tokenize it to args, check if there is a redirect to file.
|
|
if(args != NULL) // if user did enter a command do the following
|
|
{
|
|
exitCode = runExec(args, writeToFile); // fork and run the process, return child exit code
|
|
FreeTokens(args); //in case of error while trying to create process - this method will free the memory already
|
|
if(exitCode == ERROR) // in case we need to exit.
|
|
goto quit;
|
|
}
|
|
writeToFile = false; // set the "boolean" back to default
|
|
}
|
|
|
|
quit:
|
|
free(host_n);
|
|
free(user_n);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Atemting to get the host name from the system
|
|
* @param host - the string that will hold the host name
|
|
* @return 0 if all went well, -1 (ERROR) otherwise
|
|
*/
|
|
int getHostName(char* host)
|
|
{
|
|
if(gethostname(host, 10) != 0)
|
|
{
|
|
fprintf(stderr, "%s\n", "Error: while getting host name");
|
|
return ERROR;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Atemting to get the user name from the system
|
|
* @param host - the string that will hold the user name
|
|
* @return 0 if all went well, -1 (ERROR) otherwise
|
|
*/
|
|
int getUserName(char* name)
|
|
{
|
|
if(getlogin_r(name, 10) != 0)
|
|
{
|
|
fprintf(stderr, "%s\n", "Error: while getting User name");
|
|
return ERROR;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* This function will atempt to create a new Process.
|
|
* and will return the exit status code of the child
|
|
* @param args - an array of strings that contains all the arguments from the user
|
|
* @return int - exit status of child
|
|
*/
|
|
int runExec(char** args, int writeToFile)
|
|
{
|
|
int status;
|
|
pid_t pid;
|
|
char* path = (char *)malloc(sizeof(char));
|
|
char* exe = (char *)malloc(sizeof(char));
|
|
strcpy(exe,args[0]);
|
|
strcpy(path, PATH);
|
|
strcat(path, exe);
|
|
|
|
pid = fork();
|
|
|
|
if(pid < 0)
|
|
{
|
|
fprintf(stderr, "%s\n", "Error: Could not create a new process");
|
|
return ERROR;
|
|
}
|
|
else if(pid == 0) // child
|
|
{
|
|
signal(SIGINT, SIG_DFL); // set the child SIGINT handler to default! -> ^C will kill child but not father.
|
|
|
|
if(writeToFile == true)
|
|
{
|
|
int i_lastArg = getFileNameIndex(args);
|
|
char* fileName = (char *)malloc(strlen(args[i_lastArg])*sizeof(char));
|
|
strcpy(fileName, args[i_lastArg]); //saving file name before cleaning the array
|
|
freeRemainderInArr(args, i_lastArg);
|
|
|
|
int fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC);
|
|
if(fd >= 0)
|
|
{
|
|
dup2(fd, 1); // make stdout go to file
|
|
close(fd);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "%s%s%s\n", "Unable to open file ",fileName," : printing out put to default stdout:");
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if(execv(path, args) != 0)
|
|
{
|
|
fprintf(stderr, "%s%s%s\n", "cshell: ", args[0] ,": command not found");
|
|
exit(1); //child error - chield needs to be closed manually. exit code 225;
|
|
}
|
|
}
|
|
else //pid > 0 father
|
|
waitpid(pid,&status,0);
|
|
|
|
free(path);
|
|
free(exe);
|
|
if(status == EXIT_1) //in my computer exit(1) returns 256 - ex demands wanted 255
|
|
return status-1;
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* this func will read a full line and will store all the arguments in an array (of char*).
|
|
* if the line will have only 1 word - it will call afunction to check if the word is "quit".
|
|
*
|
|
* @param stream FILE* - in our case stdin
|
|
* @return if all went well , an Array of arguements from the input line.
|
|
*/
|
|
char** ReadTokens(FILE* stream, int* writeToFile)
|
|
{
|
|
char** returnArr;
|
|
char input[LEN]; // the var to hold the line
|
|
char temp[LEN]; // will be a copy of the input line - so we can figure how many words we have
|
|
int wordCnt = 0; // word counter
|
|
int i = 0; //index for second iteration
|
|
fgets(input, LEN, stream); // get the linei
|
|
|
|
strcpy(temp, input); // copy the line
|
|
|
|
//strkot first iteration to count words (corrups temp var)
|
|
char* tok;
|
|
tok = strtok(temp," \n");
|
|
while (tok != NULL)
|
|
{
|
|
wordCnt++;
|
|
tok = strtok(NULL, " \n");
|
|
}
|
|
|
|
if(wordCnt == 0) //if there are no "words" in input - return null
|
|
return NULL;
|
|
|
|
|
|
returnArr = (char**)malloc((wordCnt + 1)*sizeof(char*)); //allocates the memory for the array
|
|
if(returnArr == NULL) //check if the malloc func finished successfuly
|
|
{
|
|
fprintf(stderr,"%s\n", "ERROR: unable to allocate memory.");
|
|
exit(1);
|
|
}
|
|
|
|
//strtok second iteration - puts all words in an index of the array
|
|
tok = strtok(input," \n");
|
|
while (tok != NULL)
|
|
{
|
|
returnArr[i] = (char *)malloc(strlen(tok)*sizeof(char));
|
|
strcpy(returnArr[i], tok);
|
|
if(wordCnt == 1)
|
|
checkIfQuit(tok);
|
|
if(i == wordCnt-2) // one before the last argument. to see if to put output into file.
|
|
if(strcmp(">", returnArr[i]) == 0)
|
|
*writeToFile = true;
|
|
tok = strtok(NULL, " \n");
|
|
i++;
|
|
}
|
|
|
|
returnArr[wordCnt] = NULL; // the last index will be NULL - so we know where the end is.
|
|
return returnArr;
|
|
}
|
|
|
|
/**
|
|
* if there is only one word in the input nile the ReadTokens func will call this func.
|
|
* it will check if user want to exit our program
|
|
*
|
|
* @param word - the one word to check
|
|
*/
|
|
void checkIfQuit(char* word)
|
|
{
|
|
if(strcmp(word, "exit") == 0)
|
|
exit(0);
|
|
}
|
|
|
|
/**
|
|
* this func will print an array of char*, it will know when to finish if the
|
|
* last index is NULL!
|
|
* @param tokens - char* Array
|
|
*/
|
|
void PrintTokens (char** tokens)
|
|
{
|
|
int i;
|
|
for (i = 0; tokens[i] != NULL; i++)
|
|
{
|
|
printf("%s\n", tokens[i]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* this will make sure no memory leeks will occurd.
|
|
* runs through all indexes change them to null and then free them.
|
|
* after that it will fraa the array pinter itself.
|
|
* @param tokens - an array to free
|
|
*/
|
|
void FreeTokens(char** tokens)
|
|
{
|
|
int i = 0;
|
|
while(tokens[i] != NULL) // free all the indexes except the last one (NULL)
|
|
{
|
|
tokens[i] = NULL;
|
|
free(tokens[i]);
|
|
i++;
|
|
}
|
|
free(tokens[i]); // free the last index
|
|
tokens = NULL; // free the array
|
|
free(tokens);
|
|
}
|
|
|
|
/**
|
|
* returns the index of the File name to redirect output to.
|
|
* @param args - an array of all the arguments
|
|
* @return int - index whare the fileName is in
|
|
*/
|
|
int getFileNameIndex(char** args)
|
|
{
|
|
int i = 0;
|
|
while(args[i] != NULL)
|
|
i++;
|
|
//we are at the end of the array.
|
|
return i-1; //the last arg must be the file name
|
|
}
|
|
|
|
/**
|
|
* this function will modify th args array, in case of redirecting.
|
|
* in that case, the 2 last args are ">" -> "filename"
|
|
* so, the function will put Null insted of ">", and will free
|
|
* the unneeded array cells.
|
|
* @param args - the array of arguments
|
|
* @param beforLast - the index of ">"
|
|
*/
|
|
void freeRemainderInArr(char** args, int beforLast)
|
|
{
|
|
free(args[beforLast+1]);
|
|
args[beforLast] = NULL;
|
|
free(args[beforLast]);
|
|
args[beforLast - 1] = NULL;
|
|
}
|