online compiler and debugger for c/c++

code. compile. run. debug. share.
Source Code   
Language
/**************************************** * Title : ECS 150 HW 1 * Author: NAUSHEEN SUJELA ****************************************/ #include <string.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> #include <sys/types.h> #include <dirent.h> #include <sys/stat.h> #include <fcntl.h> #define MAX_CMD_SZ 512 #define MAX_NUM_ARGS 16 #define MAX_NUM_CMDS_IN_PIPE 10 /** * Command struct * Used as a reference to create command struct: * https://stackoverflow.com/questions/10162152/how-to-work-with-string-fields-in-a-c-struct */ typedef struct { /* Basics */ char *program; char *args[MAX_NUM_ARGS+1]; char *toPrint[MAX_NUM_ARGS+1]; int numArguments; int printCount; /* Input + output redirection */ bool hasInputRedirection; bool hasOutputRedirection; char *fin; char *fout; /* Background commands */ bool hasBackgroundCommand; char *backgroundCommand; } command; /* Pipe struct */ typedef struct { int pipeCount; char *all_commands[MAX_NUM_CMDS_IN_PIPE + 1]; } completePipe; /* Creates pipe structure to hold all pipe details */ completePipe *createPipe(int numCmds, char *all_cmds[MAX_NUM_CMDS_IN_PIPE + 1]) { completePipe *p = malloc(MAX_CMD_SZ * MAX_NUM_CMDS_IN_PIPE); p->pipeCount = numCmds; /* Fill up the commands array of the pipe. */ for(int i = 0; i < numCmds; i++) { p->all_commands[i] = all_cmds[i]; } return p; } /* Creates command structure to hold all command details */ command *createCommand(char *program, char* args[MAX_NUM_ARGS+1], char *toPrint[MAX_NUM_ARGS+1], int numArgs, int printCount, bool hasInputRedirection, bool hasOutputRedirection, char *fin, char *fout) { command *c = malloc(MAX_CMD_SZ); c->program = program; int i, j; /* Full command to be printed at the end */ for(j = 0; j < printCount; j++) { c->toPrint[j] = toPrint[j]; } /* Fill up the arguments array to be passed into exec */ for(i = 0; i < numArgs; i++) { c->args[i] = args[i]; } /* Null-terminate both arrays */ c->args[i] = 0; c->toPrint[j] = 0; /* Pass in other necessary information */ c->numArguments = numArgs; c->printCount = printCount; c->hasInputRedirection = hasInputRedirection; c->hasOutputRedirection = hasOutputRedirection; c->fin = fin; // This will be NULL if there is no input/output file c->fout = fout; // if(c->hasOutputRedirection) { // printf("Output file has been stored. It's called %s\n", fout); // } return c; } /* Removes trailing newline */ void removeTrailingNewline(char *str) { if ( (strlen(str) > 0) && (str[strlen(str) - 1] == '\n')) { str[strlen (str) - 1] = '\0'; } } /* Finds a particular character in a char array */ bool findElement(char *symbol, char *str) { char *c = str; while (*c) { if (strchr (symbol, *c)) return true; c++; } return false; } /** * Credit for removeChar function: * https://stackoverflow.com/questions/5457608/how-to-remove-the-character-at-a-given-index-from-a-string-in-c */ void removeChar(char *str, char toRemove) { char *src, *dst; for (src = dst = str; *src != '\0'; src++) { *dst = *src; if (*dst != toRemove) dst++; } *dst = '\0'; } /* Remove a substring from a string */ void removeSubstring(char *s,const char *toremove) { while( (s = strstr(s,toremove)) ) memmove(s,s+strlen(toremove),1+strlen(s+strlen(toremove))); } /** * Credit for strtok_single function: * https://stackoverflow.com/questions/8705844/need-to-know-when-no-data-appears-between-two-token-separators-using-strtok * Used to tokenize over single instance of a delimiter. */ char *strtok_single (char * str, char const * delims) { static char * src = NULL; char * p, * ret = 0; if (str != NULL) src = str; if (src == NULL) return NULL; if ((p = strpbrk (src, delims)) != NULL) { *p = 0; ret = src; src = ++p; } else if (*src) { ret = src; src = NULL; } return ret; } /* Redirects input to specified file descriptor. */ void redirectInput(char *file, char *args[MAX_NUM_ARGS]) { int fd; if (open(file, O_RDWR)) { fd = open(file, O_RDWR); dup2(fd, STDIN_FILENO); close(fd); } } /* Redirects output to specified file descriptor. */ void redirectOutput(char *file, char *args[MAX_NUM_ARGS]) { // printf("Redirecting output...\n"); int fd; if (open(file, O_RDWR|O_CREAT, 0666)) { // printf("FILE EXISTS. REDIRECTING OUTPUT TO FILE NOW...\n"); fd = open(file, O_RDWR|O_CREAT, 0666); // printf("fd: %d\n", fd); dup2(fd, STDOUT_FILENO); close(fd); } } /* ------------- PIPE + COMMAND PARSERS ------------- */ /* Parse user input over | and create a pipe. */ completePipe *pipeHandler(char *complete_user_input) { /* Copy user input to a buffer to parse for pipes. */ char *copy_pipe = (char *)malloc(strlen(complete_user_input) + 1); strcpy(copy_pipe, complete_user_input); char *store_commands[MAX_NUM_CMDS_IN_PIPE + 1]; /* Parsing for pipes. */ char *pipe_tok = strtok(copy_pipe, "|"); int i = 0; while (pipe_tok != NULL) { // puts(pipe_tok); store_commands[i] = malloc(strlen(pipe_tok) + 1); strcpy(store_commands[i], pipe_tok); pipe_tok = strtok(NULL, "|"); i++; } completePipe *pipe = createPipe(i, store_commands); return pipe; } /** * Parse and process a command. Parse over >, <, and whitespace. * Build instance of command struct based on parsed information. */ command *cmdHandler(char *cmd, char *all_args[MAX_NUM_ARGS + 1]) { /* Full command to be printed at the end */ char *print_arr[MAX_NUM_ARGS+1]; /* Preliminary variables */ char *fin = NULL; char *fout = NULL; bool inputRedirectionActivated = false; bool outputRedirectionActivated = false; /* Copy cmd to a buffer to parse for input */ char *copy_input = (char *)malloc(strlen(cmd) + 1); strcpy(copy_input, cmd); /* Copy cmd to a buffer to parse for output */ char *copy_output = (char *)malloc(strlen(cmd) + 1); strcpy(copy_output, cmd); /* Parsing for input redirection. */ char *input_tok = strtok(copy_input, "<"); if (input_tok != NULL) { // printf("Token: %s\n", tok); input_tok = strtok(NULL, "<"); if (input_tok != NULL) { inputRedirectionActivated = true; fin = input_tok; removeSubstring(fin, " "); // printf("Token: %s\n", fin); } } /* Parsing for output redirection */ char *output_tok = strtok(copy_output, ">"); if (output_tok != NULL) { // printf("Token: %s\n", output_tok); output_tok = strtok(NULL, ">"); if (output_tok != NULL) { outputRedirectionActivated = true; fout = output_tok; removeSubstring(fout, " "); // printf("Token: %s\n", fout); } } /* Begin parsing over whitespace */ char delims[] = " "; // printf("TEST TO MAKE SURE CMD REMAINS UNALTERED: "); // printf("%s\n", cmd); char *token = strtok(cmd, delims); int i = 0; int j = 0; while (token != NULL && i < MAX_NUM_ARGS) { // printf("TOKEN: %s\n", token) /* Add "<" and ">" symbols to print arr but not args arr */ if (strcmp(token, "<") == 0 || strcmp(token, ">") == 0) { print_arr[j] = malloc(strlen(token) + 1); strcpy(print_arr[j], token); j++; // printf("SEPARATED < \n"); token = strtok(NULL, " "); } // bool hasBg = false; // char *bg; // else if (findElement("&", token)) { // hasBg = true; // bg = malloc(strlen(token)); // not + 1 since we'll be removing & // token.removeSubstring("&"); // strcpy(bg, token); // token = strtok(NULL, " "); // } // WE'RE TRYING TO GET THE ARGUMENT BEFORE <, <, AND THE ARGUMENT AFTER else if (findElement("<", token)) { // printf("NOT SEPARATED < \n"); char* ptr = malloc(strlen(token) + 1); /* Gets position of < in string */ int pos; ptr = strstr(token, "<"); pos = token - ptr; // If <file.txt if (strstr(token, fin) && pos == 0) { // printf("CASE <file.txt\n"); all_args[i] = malloc(strlen(fin) + 1); strcpy(all_args[i], fin); i++; print_arr[j] = malloc(strlen(token) + 1); strcpy(print_arr[j], token); j++; token = strtok(NULL, " "); } // Else if word<file.txt else if (strstr(token, fin) && pos != 0) { // printf("CASE word<file.txt\n"); all_args[i] = malloc(strlen(token) + 1); strcpy(all_args[i], token); removeChar(all_args[i], '<'); removeSubstring(all_args[i], fin); // printf("AFTER REMOVING SUBSTRING: %s\n", all_args[i]); i++; print_arr[j] = malloc(strlen(token) + 1); strcpy(print_arr[j], token); j++; all_args[i] = malloc(strlen(token) + 1); strcpy(all_args[i], fin); // printf("Just added: %s\n", all_args[i]); // printf("%s\n", all_args[i]); i++; token = strtok(NULL, " "); } // Else if word< else if (!strstr(token, fin)) { // printf("CASE word<\n"); all_args[i] = malloc(strlen(token) + 1); strcpy(all_args[i], token); removeChar(all_args[i], '<'); // printf("AFTER REMOVING CHAR: "); // printf("%s\n", all_args[i]); i++; print_arr[j] = malloc(strlen(token) + 1); strcpy(print_arr[j], token); j++; token = strtok(NULL, " "); } continue; } // Copy all tokens to printed array print_arr[j] = malloc(strlen(token) + 1); strcpy(print_arr[j], token); j++; // Copy token to arguments array if it is not a >, <, or output file if (outputRedirectionActivated == true && strcmp(fout, token) == 0) { token = strtok(NULL, " "); continue; } all_args[i] = malloc(strlen(token) + 1); strcpy(all_args[i], token); i++; // Move on to the next token token = strtok(NULL, " "); } // Null-terminate the arguments array. // This is why all_args contains an extra element. all_args[i] = 0; print_arr[j] = 0; command *com = createCommand(all_args[0], all_args, print_arr, i, j, inputRedirectionActivated, outputRedirectionActivated, fin, fout); // printf("Outpur redirection debugging: %d\t%s\n", com->hasInputRedirection, com->fout); return com; } /* Built-in exit command */ int myExit(bool activeStatus) { activeStatus = false; fprintf(stderr, "Bye...\n"); return EXIT_SUCCESS; } /* Built-in pwd command */ void myPwd() { char current_dir[1024]; if (getcwd(current_dir, 1024) != NULL) { fprintf(stdout, "%s\n", current_dir); } else { perror("pwd error"); } } /* Built-in cd command */ void myCd(char* arg) { if (arg == NULL) { // printf("Going to HOME directory\n"); chdir(getenv("HOME")); } else if (strcmp(arg, "..") == 0) { // printf("Going to previous directory\n"); chdir(".."); } else { // Check if specified directory exists DIR *checkDir = opendir(arg); if (checkDir) { // printf("Going to specified directory\n"); chdir(arg); } else { fprintf(stderr, "Error: no such directory\n"); } } } /* ------------ PIPING FUNCTION ------------ */ void runPipe (completePipe *p) { int numCommands = p->pipeCount; // numCommands must be >= 2 int status; int i = 0; pid_t pid; int pipefds[2*numCommands]; /* Have the parent process create the pipes from the start */ for (i = 0; i < numCommands; i++) { if(pipe(pipefds + i*2) < 0) { perror("runPipe malfunction"); exit(EXIT_FAILURE); } } int j = 0; int k = 0; while (k < numCommands) { pid = fork(); /* CHILD PROCESS */ if (pid == 0){ printf("Inside a child process\n"); // If not last command: if (k != numCommands) { if(dup2(pipefds[j + 1], 1) < 0){ perror("dup2 malfunction"); exit(EXIT_FAILURE); } } // If not first command: if(k != 0){ if(dup2(pipefds[j-2], 0) < 0){ perror("dup2 malfunction");///j-2 0 j+1 1 exit(EXIT_FAILURE); } } // Close pipes after dup2 for(int m = 0; m < 2*numCommands; m++){ close(pipefds[i]); } // Create command char *all_args[MAX_NUM_ARGS + 1]; command *command = cmdHandler(p->all_commands[k], all_args); if( execvp(command->program, command->args) < 0 ) { perror("Pipe execvp failure"); exit(EXIT_FAILURE); } } // END CHILD else if (pid < 0) { perror("fork/pid error"); exit(EXIT_FAILURE); } k++; j = j + 2; } /* Parent is in charge of closing the pipes; waits for child processes to finish */ for(i = 0; i < 2 * numCommands; i++){ close(pipefds[i]); } for(i = 0; i < numCommands + 1; i++) wait(&status); } /* ---------------------------------------- MAIN PROGRAM ---------------------------------------- */ int main(int argc, char *argv[]) { int status; bool active = true; int start = 0; while (active == true) { if (start == 0) { char *startInstructions = "----------\nWelcome to Nausheen's Shell!\nHere, you can try out a variety of standard terminal commands. E.g. pwd, ls, mkdir, cd, touch, cat, and echo.\n----------\n"; fprintf(stdout, "%s\n", startInstructions); start = 1; } char *prompt = "sshell$ "; fprintf(stdout, "%s", prompt); char cmd[MAX_CMD_SZ]; fgets(cmd, MAX_CMD_SZ, stdin); // <-- could be a piped command /* * Prints command line to stdout if fgets just read from a file and not * the terminal (which is the case with the test script) */ if (!isatty(STDIN_FILENO)) { printf("%s", cmd); fflush(stdout); } /* Remove newline. */ removeTrailingNewline(cmd); // completePipe *the_pipe = pipeHandler(cmd); // if (the_pipe->pipeCount > 1) { // runPipe(the_pipe); // continue; // } /* Parse, process, and execute each individual command in the pipe. */ char *all_args[MAX_NUM_ARGS + 1]; command *command = cmdHandler(cmd, all_args); /* -- HANDLE BUILT-IN COMMANDS -- */ // --- EXIT --- if (strcmp(command->program, "exit") == 0) { myExit(active); // exit break; } // --- CD --- else if (strcmp(command->program, "cd") == 0) { myCd(all_args[1]); // cd fprintf(stderr, "%s", "+ completed '"); // Print command + statement of completion for(int k = 0; k < command->printCount; k++) { if (k == command->printCount-1) { fprintf(stderr, "%s", command->toPrint[k]); } else fprintf(stderr, "%s ", command->toPrint[k]); } fprintf(stderr, "%s%d%s", "' [", WEXITSTATUS(status), "]\n"); continue; } // --- PWD --- else if (strcmp(command->program, "pwd") == 0) { myPwd(); // pwd fprintf(stderr, "%s", "+ completed '"); // Print command + statement of completion for(int k = 0; k < command->printCount; k++) { if (k == command->printCount-1) { fprintf(stderr, "%s", command->toPrint[k]); } else fprintf(stderr, "%s ", command->toPrint[k]); } fprintf(stderr, "%s%d%s", "' [", WEXITSTATUS(status), "]\n"); continue; } /* Fork the process.*/ fflush(stdout); pid_t process_ID = fork(); // fork() returns child process ID to parent process /* ------- THE CHILD PROCESS -------- */ /* This process takes care of commands with input redirection, output redirection, and all "regular" commands to be executed using an execvp system call. */ if(process_ID == 0) { /* --- BEGIN: Executing a command with INPUT redirection --- */ if (command->hasInputRedirection && command->fin!=NULL) { /* Check if input file exists */ int fileInCheck = open(command->fin, O_RDWR); // printf("Filecheck %d\n", fileCheck); if (fileInCheck == -1) { fprintf(stderr, "Error: cannot open input file: %s\n", command->fin); break; } redirectInput(command->fin, command->args); execvp(command->program, command->args); fprintf(stderr, "Error: cannot open input file: %s\n", command->fin); break; } else if (command->hasInputRedirection && command->fin == NULL) { fprintf(stderr, "Error: no input file\n"); } /* --- END: Executing a command with INPUT redirection --- */ /* --- BEGIN: Executing a command with OUTPUT redirection --- */ else if (command->hasOutputRedirection && command->fout!=NULL) { /* Check if output file exists */ int fileOutCheck = open(command->fout, O_RDWR|O_CREAT, 0666); if (fileOutCheck == -1) { fprintf(stderr, "Error: cannot open output file: %s\n", command->fin); break; } redirectOutput(command->fout, command->args); execvp(command->program, command->args); fprintf(stderr, "Error: cannot open output file: %s\n", command->fin); break; } else if (command->hasOutputRedirection && command->fout == NULL) { fprintf(stderr, "Error: no output file\n"); } /* --- END: Executing a command with OUTPUT redirection --- */ execvp(command->program, command->args); fprintf(stdout, "Error: command not found\n"); _exit(1); } /* ----- RETURN TO THE PARENT PROCESS ----- */ else { /* Handle background commands */ if (findElement("&", command->program)) waitpid(process_ID, &status, 0); if ( (process_ID = wait(&status)) == -1) { perror("Wait error"); } // If child process exited successfully: else if ( WIFEXITED(status) != 0 ) { fprintf(stderr, "%s", "+ completed '"); for(int k = 0; k < command->printCount; k++) { if (k == command->printCount-1) { fprintf(stderr, "%s", command->toPrint[k]); } else fprintf(stderr, "%s ", command->toPrint[k]); } fprintf(stderr, "%s%d%s", "' [", WEXITSTATUS(status), "]\n"); } } } return EXIT_SUCCESS; }

Compiling Program...

Command line arguments:
Standard Input: Interactive Console Text
×

                

                

Program is not being debugged. Click "Debug" button to start program in debug mode.

#FunctionFile:Line
VariableValue
RegisterValue
ExpressionValue