#include #include #include #include #include #include #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; }