Skip to content
Snippets Groups Projects
Commit f833938a authored by Dixon Tirtayadi's avatar Dixon Tirtayadi
Browse files

Part A

parent 396690e9
No related branches found
No related tags found
No related merge requests found
# Running make in parta must produce an executable called 550shell.
CC = gcc
CFLAGS = -g -Wall
all: shell
shell: shell.o
$(CC) $(CFLAGS) -o sh shell.c
shell.o: shell.c
$(CC) $(CFLAGS) -c $<
clean:
/bin/rm -f sh *.o *~
\ No newline at end of file
......@@ -13,7 +13,254 @@ Can:
- Use reasonable, hard-coded maximum sizes to simplify your code.
*/
int main(void) {
char buf[128]; // Assume max char
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
// Remove leading and trailing space from str
char *trimwhitespace(char *str);
// Split line into commands terminated by "|", and store it into *cmds.
// Returns the number of commands there are.
int getCommands(char*** cmds, char* line);
// Set up pipe and execute command cmds[i]. To be called in the child thread.
void childRun(int i, int n_cmds, char* cmds[], int old_pipe[], int new_pipe[]);
int main() {
char *line = NULL;
size_t len = 0;
ssize_t nread;
int status;
char** cmds = malloc(sizeof(char*)); // Array for commands
printf("Shell is starting...\n");
printf("$ ");
// Loop until EOF reading stdin
while ((nread = getline(&line, &len, stdin)) != -1) {
line[nread - 1] = '\0'; // Remove \n character
// Get the commands from the user input line
int n_cmds = getCommands(&cmds, line);
// Loop through commands and execute it while piping stuff to next command
int old_pipe[2];
for (int i = 0; i < n_cmds; i++) {
int new_pipe[2];
// If its not the last command, open a pipe
if (i != n_cmds - 1) {
if (pipe(new_pipe) == -1) {
perror("pipe creation fails");
exit(EXIT_FAILURE);
}
}
// Call the program
if (fork() == 0) {
// Child: set up pipe and execute the command by calling helper
childRun(i, n_cmds, cmds, old_pipe, new_pipe);
} else {
// Parent: wait for the child
if (i > 0) {
// Not the first command, close previous old_pipe so no leak
close(old_pipe[0]);
close(old_pipe[1]);
}
if (i < n_cmds - 1) {
// Not the last command, change pipe for the next iteration use
old_pipe[0] = new_pipe[0];
old_pipe[1] = new_pipe[1];
}
wait(&status);
}
}
// Print prompt for next user command
printf("$ ");
}
// Free stuff up
free(cmds);
free(line);
exit(EXIT_SUCCESS);
}
int getCommands(char*** cmds, char* line) {
char* token; // For splitting string input into tokens separated by ' '
token = strtok(line, "|");
int n_cmds = 0;
// Loop through tokens and get all commands, store it in an array first
while(token != NULL) {
*cmds = realloc(*cmds, sizeof(char*) * ++n_cmds);
if (*cmds == NULL) {
perror("realloc fails");
exit(EXIT_FAILURE);
}
char** cmd_arr = *cmds;
cmd_arr[n_cmds-1] = trimwhitespace(token);
token = strtok(NULL, "|");
}
return n_cmds;
}
// Code from https://stackoverflow.com/questions/122616/how-do-i-trim-leading-trailing-whitespace-in-a-standard-way
// Pretty straightforward code, was looking to find a library function to do this but found this in stackoverflow,
// used it instead.
char *trimwhitespace(char *str) {
char *end;
// Trim leading space
while(isspace((unsigned char)*str)) str++;
if(*str == 0) // All spaces?
return str;
// Trim trailing space
end = str + strlen(str) - 1;
while(end > str && isspace((unsigned char)*end)) end--;
// Write new null terminator character
end[1] = '\0';
return str;
}
void childRun(int i, int n_cmds, char* cmds[], int old_pipe[], int new_pipe[]) {
// child: execute the program
if (i > 0) {
// Not the first command, read from pipe instead of stdin
dup2(old_pipe[0], 0);
close(old_pipe[0]);
close(old_pipe[1]);
}
if (i < n_cmds - 1) {
// Not the last command, output to pipe instead of stdout
dup2(new_pipe[1], 1);
close(new_pipe[0]);
close(new_pipe[1]);
}
// Execute the command
char *args[2];
args[0] = cmds[i];
args[1] = NULL; // Args need to be terminated by NULL
execvp(cmds[i], args);
printf("Cannot run the command \"%s\"\n", cmds[i]);
// Note that if it reaches here, valgrind will report "still reachable" memory leak
// because the main process is still holding memory, but it's kinda a false report
// because the allocation will be cleaned up at the end.
exit(EXIT_SUCCESS); // This is a case where the cmd is not runnable, just exit.
}
// Testing code can be cleaned up later
// ----------------------------------------------------------------------------------
// int main() {
// char *line = NULL;
// size_t len = 0;
// ssize_t nread;
// int status;
// char* token; // For splitting string input into tokens separated by ' '
// char** args = malloc(sizeof(char*)); // Array for arguments
// if (args == NULL) {
// exit(-1);
// }
// printf("Shell is starting \n");
// printf("> ");
// // Loop until EOF reading stdin
// while ((nread = getline(&line, &len, stdin)) != -1) {
// int n_spaces = 0;
// line[nread - 1] = '\0'; // Remove \n character
// printf("Retrieved line of length %zd with value %s\n", nread, line);
// // fwrite(line, nread, 1, stdout);
// token = strtok(line, " ");
// /* walk through other tokens */
// while(token != NULL) {
// printf("%s\n", token);
// args = realloc(args, sizeof (char*) * ++n_spaces);
// if (args == NULL) {
// exit (-1); /* memory allocation failed */
// }
// args[n_spaces-1] = token;
// token = strtok(NULL, " ");
// // if (token == '|') {
// // // pipe stuff
// // }
// }
// // Args needs to end with null as last argument
// args = realloc(args, sizeof (char*) * (n_spaces+1));
// args[n_spaces] = NULL;
// // Call the program
// if (fork() == 0) {
// execvp(args[0], args); // child: execute the program
// } else {
// wait(&status); // parent: wait for the child before next prompt
// }
// printf("> ");
// }
// // Free stuff up
// free(args);
// free(line);
// exit(1);
// }
// ----------------------------------------------------------------------------------
// int main(int argc, char *argv[]) {
// int pipefd[2];
// pid_t cpid;
// char buf;
// if (argc != 2) {
// fprintf(stderr, "Usage: %s <string>\n", argv[0]);
// exit(EXIT_FAILURE);
// }
// if (pipe(pipefd) == -1) {
// perror("pipe");
// exit(EXIT_FAILURE);
// }
// cpid = fork();
// if (cpid == -1) {
// perror("fork");
// exit(EXIT_FAILURE);
// }
// if (cpid == 0) { /* Child reads from pipe */
// close(pipefd[1]); /* Close unused write end */
// while (read(pipefd[0], &buf, 1) > 0)
// write(STDOUT_FILENO, &buf, 1);
// write(STDOUT_FILENO, "\n", 1);
// close(pipefd[0]);
// _exit(EXIT_SUCCESS);
// } else { /* Parent writes argv[1] to pipe */
// close(pipefd[0]); /* Close unused read end */
// write(pipefd[1], argv[1], strlen(argv[1]));
// close(pipefd[1]); /* Reader will see EOF */
// wait(NULL); /* Wait for child */
// exit(EXIT_SUCCESS);
// }
// }
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment