How to exit a shell program - c

I'm making simple shell program and trying to exit it if the user enters "exit" and I've tried a few different keywords such as exit(), return 0, break;
This is my code:
void read_command(char path[], char *args[], char input[]) {
char *array[MAX], *ptr;
char *inputptr;
if ((inputptr = strchr(input, '\n')) != NULL) {
*inputptr = '\0';
}
int i = 0;
char *p = strtok(input, " ");
while (p != NULL) {
array[i++] = p;
p = strtok(NULL, " ");
}
for (int j = 0; j < i; j++) {
args[j] = array[j];
}
}
int main() {
char path[MAX];
char *args[MAX] = {NULL};
int status;
char input[MAX];
while (TRUE) {
printf(">> ");
fgets(input, sizeof(input), stdin);
if (fork() != 0) {
if (waitpid(-1, &status, 0) < 0) {
perror("waitpid error ");
}
} else {
read_command(path, args, input);
if (strcmp(input, "exit") == 0) {
exit(0);
}
strcpy(path, "/bin/");
strcat(path, args[0]);
if (execve(path, args, 0) < 0) {
perror("exec error ");
return EXIT_FAILURE;
}
}
}
return EXIT_SUCCESS;
}
When I return the strcomp() value it does give me 0 so I'm not sure why it's not working the program seems to completely ignore that exit statement and just carries on with the code, can someone explain how I could to this? Thank you.

You're calling exit in the child process that you just forked off. Instead, read the command in the parent process and then either exit or fork. By the way, you should really be checking that fork does not return -1:
read_command(path, args, input);
if (strcmp(input, "exit") == 0)
/* Don't pass zero here, that's not portable. */
exit(EXIT_SUCCESS);
pid_t child;
switch ((child = fork())) {
case -1:
perror("fork failed");
exit(EXIT_FAILURE);
case 0:
// call exec
default:
/* Don't pass -1 here if you know which child to wait for.
Also, you can just pass NULL if to status */
if (waitpid(child, NULL, 0) < 0)
perror("waitpid error ");
}

Related

Signal returned from SIGCHLD appears to be wrong

I've written a basic shell in C, and I'm trying to catch a SIGTSTP signal from a child process. To do this, I've set up a handler for SIGCHLD, but the signal number being returned is 20, when it should be 24.
I have my SIGCHLD handler:
signal(SIGCHLD, trapChld);
void trapChld(int signo) {
printf("%d", signo);
}
This prints signal 20 when kill -SIGTSTP child_pid is run. Why might this be happening?
Here's my full code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
int statusCode;
int foregroundMode = 0;
int bg = 0;
int bgPsArray[20];
int bgPsCount = 0;
int i;
char line[256];
pid_t popBgProcess() {
int size = sizeof(bgPsArray)/sizeof(bgPsArray[0]);
if (size > 0) {
return bgPsArray[size+1];
} else {
return 0;
}
}
void trapInterrupt(int _) {
int childStatus;
pid_t child;
while ((child = popBgProcess())) {
if(child != getpid()) {
kill(child, SIGKILL);
waitpid(child, &childStatus, 0);
}
}
}
void trapChld(int signo) {
printf("%d", signo);
if(signo == 24) {
if(foregroundMode == 0) {
write(1, "Entering foreground-only mode (& is now ignored)\n", 49);
write(1, ": ", 2);
fflush(stdout);
foregroundMode = 1;
} else {
write(1, "Exiting foreground-only mode\n", 29);
write(1, ": ", 2);
fflush(stdout);
foregroundMode = 0;
}
}
}
int getCommand() {
printf(": ");
fflush(stdout);
if(fgets(line, sizeof(line), stdin) != NULL) {
char *position = strchr(line, '\n');
*position = '\0'; // Replace '\n' with '\0'
if(foregroundMode == 1) { // Foreground mode on
if((position = strchr(line, '&')) != NULL) {
*position = '\0'; // Replace '&' with '\0'
}
bg = 0; // Ignore '&' so do not create background process
} else { // Foreground mode off
if((position = strchr(line, '&')) != NULL) {
*position = '\0'; // Replace '&' with '\0'
bg = 1; // Is a background process
} else {
bg = 0;
}
}
} else { // If input is null
return 0;
}
return 1;
}
void checkProcessCompletion() {
int status;
for(i=0; i<bgPsCount; i++) {
if(waitpid(bgPsArray[i], &status, WNOHANG) > 0) {
if(WIFEXITED(status)) { // If exit
printf("Background PID %d is done: exit value %d\n", bgPsArray[i], WEXITSTATUS(status));
fflush(stdout);
} else if(WIFSIGNALED(status)) { // If signal
printf("Background PID %d is done: terminated by signal %d\n", bgPsArray[i], WTERMSIG(status));
fflush(stdout);
}
}
}
}
int runCommand(int cmd) {
if(cmd == 0) { // Return if there was no command
return 0;
} else if(strcmp(line, "exit") == 0) {
exit(0);
} else if(strstr(line, "#")) { // Comment input (do nothing)
} else if(strcmp(line, "status") == 0) {
printf("exit value %d\n", statusCode);
fflush(stdout);
}
else if(strncmp("cd", line, strlen("cd")) == 0) {
if(line[2] == ' ') { // If space after 'cd' expect directory
char cwd[1024];
getcwd(cwd, sizeof(cwd));
char *path = strstr(line, " ");
if(path) {
path += 1;
char *value;
value = malloc(strlen(path));
memcpy(value, path, strlen(path));
*(value + strlen(path)) = 0;
sprintf(cwd, "%s/%s", cwd, value); // Directory to change to
free(value);
}
chdir(cwd); // cd to new directory
} else { // cd with no argument
char *home = getenv("HOME");
chdir(home); // cd to HOME directory
}
}
else { // System commands
pid_t pid, ppid;
int status;
char *command;
char *args[256];
int argCount;
command = strtok(line, " ");
// Create args array for execvp
args[0] = command;
argCount = 1;
args[argCount] = strtok(NULL, " ");
while(args[argCount] != NULL) { // Add arguments to array
argCount++;
args[argCount] = strtok(NULL, " ");
}
if((pid = fork()) < 0) { // Fork fails
perror("fork");
fflush(stdout);
exit(1);
}
if(pid == 0) { // Child process
for(i=0; i<argCount; i++) {
if(strcmp(args[i], "<") == 0) { // Redirecting input
if(access(args[i+1], R_OK) == -1) { // File is unreadable
perror("access");
fflush(stdout);
} else { // File is readable
int file = open(args[i+1], O_RDONLY, 0);
dup2(file, STDIN_FILENO);
close(file);
execvp(command, &command);
}
}
else if(strcmp(args[i], ">") == 0) { // Redirecting output
int file = creat(args[i+1], 7777);
dup2(file, STDOUT_FILENO);
close(file);
execvp(command, args);
} else { // No redirection
execvp(command, args);
}
}
perror("execvp"); // Error for execvp
exit(1);
} else { // Parent process
if (bg == 1) { // Background process
int status;
int process;
printf("Background PID: %d\n", pid);
fflush(stdout);
bgPsArray[bgPsCount] = pid; // Add process to background process array
bgPsCount++;
process = waitpid(pid, &status, WNOHANG);
} else { // Foreground process
int status;
waitpid(pid, &status, 0); // Wait on the process
if(WIFEXITED(status)) {
statusCode = WEXITSTATUS(status);
}
}
}
}
return 1;
}
int main(int argc, char *argv[], char *envp[]) {
// Creating 'junk' manually is necessary because output redirection is broken,
// and a large portion of the grading script is depedent upon it's existence.
FILE *fp = fopen("junk", "ab+");
const char *text;
fprintf(fp, "Junk in junkfile\n");
fclose(fp);
signal(SIGINT, trapInterrupt);
signal(SIGCHLD, trapChld);
while(1) {
checkProcessCompletion(); //Check the processes
int cmd = getCommand(); // Get command from user
int result = runCommand(cmd);
if (result == 0) {
break;
}
}
return 0;
}
You haven't told us what platform you're running on, so this is just a guess, but perhaps it's because that platform defines SIGTSTP as 20?
Linux does, for example:
$ grep SIGTSTP /usr/include/asm/signal.h
#define SIGTSTP 20
A better question is why do you think it should be 24? On AIX it's 18. On HP-UX it's 25. Various Cygwin headers define it as 8, 18, or 24 (because the Cygwin headers come from glib and are full of platform-specific conditional-compilation shenanigans); 18 is the actual value used at runtime.
On Solaris, now, it happens to be 24. I believe Solaris 2 inherited that from SVR4, and subsequent Solaris releases kept it. But the signal numbers are not standardized by any of the applicable specifications (SUS and its ancestors, such as POSIX and XPG3).
Don't assume the signal values are fixed. That's why you have signal.h.
Oh, and sigaction(2) is preferable to signal(2) on platforms that support it, which is most of them.

Pass parameter to another function using C

int main(void){
int n, user_length;
char userid[30];
char password[11];
if ((n = read(STDIN_FILENO, userid, 10)) == -1) {
perror("read");
exit(1);
} else if(n == 0) {
fprintf(stderr, "Error: could not read from stdin");
exit(1);
}
if (userid[n-1] == '\n')
userid[n-1] = '\0';
else
userid[n] = '\0';
if ((n = read(STDIN_FILENO, password, 10)) == -1) {
perror("read");
exit(1);
} else if (n == 0) {
fprintf(stderr, "Error: could not read from stdin");
exit(1);
}
if (password[n-1] == '\n')
password[n-1] = '\0';
else
password[n] = '\0';
strcat(userid, ":");
user_length = strlen(userid);
strcat(userid, password);
FILE *fp = fopen(PASSWORD_FILE, "r");
if (!fp) {
perror("fopen");
exit(1);
}
char line[MAXLINE];
while(fgets(line, sizeof(line) - 1, fp)) {
line[strlen(line) - 1] = '\0';
if (strcmp(userid, line) == 0)
exit(0); // found match
else if(strncmp(userid, line, user_length) == 0)
exit (2); // invalid password
}
exit(3); // no such user
}
Above is the implementation of validate.c, but how do I pass value such as userid and password to the function by using pipe(),dup2 or execl()
I used the following`
int main(void) {
char userid[10];
char password[10];
int pid;
int p[2][4];
char other[MAXSIZE];
/* Read a user id and password from stdin */
printf("User id:\n");
scanf("%s", userid);
printf("Password:\n");
scanf("%s", password);
/*Your code here*/
if (pipe(p[1]) == -1) {
perror("pipe");
}
if (pipe(p[0]) == -1) {
perror("pipe");
}
pid = fork();
if (pid != 0) {
close(p[1][0]);
close(p[0][0]);
dup2(p[1][1],STDIN_FILENO);
dup2(p[0][1],STDIN_FILENO);
close(p[1][1]);
close(p[0][1]);
int status;
if (wait(&status)!= -1) {
if (WIFEXITED(status)) {
printf("[%d] Child exited with %d\n", getpid(), WEXITSTATUS(status));
switch(WEXITSTATUS(status)){
case 0:
printf("found match\n");
break;
case 2:
printf("invalid password\n");
break;
case 3:
printf("No such user\n");
break;
default:
printf("error has occur\n");
break;
};
} else {
printf("[%d] Child exited abnormally\n", getpid());
}
}
} else if (pid == 0) {
close(p[1][1]);
close(p[0][1]);
dup2(p[1][0], fileno(stdout));
dup2(p[1][0], fileno(stdout));
execl("validate",other);
printf("what\n");
close(p[1][0]);
close(p[0][0]);
} else {
perror("fork");
exit(1);
}
return 0;
}
But the prompt always asks me for re-entering the input. What is wrong with this approach?( Note: I "execl" "validate" because it is an executable file that has been already created. The execl() I wrote simply calls the validate.c function )
As I said in the comments you probably do not need to spawn another process for this but You have an error in the way you call execl.
This:
execl("validate",other);
Should be:
execl(filename,list of arguments, NULL);
This is the documentation page. They use (char *) 0 which is the same as using NULL.

Program is not stopping on EOF

Currently when I hit ctrl+d it keeps on printing > ERROR over and over again until I suspend the program (ctrl+z). I have tried various ways to fix this but it breaks the program in other ways.
int main()
{
char *command;
char **parameters;
int status;
size_t buffsize = 0;
while(1)
{
command = NULL;
printf("> ");
getline(&command, &buffsize, stdin);
command[strlen(command)-1] = '\0';
parameters = tokenize(command);
if (!strcmp(command, "exit"))
{
exit(1);
}
if (fork() != 0)
{
waitpid(-1, &status, 0);
}
else
{
status = execvp(command, parameters);
if (status == -1)
{
printf("ERROR\n");
exit(1);
}
}
free(command);
}
return 0;
}
EDIT: Here is the fix. Thank you to jil
if(getline(&command, &buffsize, stdin)) == -1) {
return 0;
}
Maybe you should check for EOF then and act accordingly. man getline says:
return -1 on failure to read a line (including end-of-file condition)
So try something like:
if (getline(&command, &buffsize, stdin) == -1)
return 0;

Implementing unlimited piping in shell using C

I'm trying to implement a C shell that allows for unlimited unidirectional pipes using the character '>'
So it can handle ls -A > tail > grep '.zip'
I understand that pipes are supposed to talk between processes, but I thought I came up with an idea that could use one pipe and multiple children.
This is what I have so far
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
/*#include <wait.h>*/
char *args[1000][1000];//array of arguments
int args_count = 0;//count of the arguments in the array
int runCommand(char **arguments, int *fd, int pipeHasSomeData, int baseCase) {
pid_t pid;
int x = 0;
int status;
pid = fork();
if(pid != 0) {
waitpid(pid, &status, 0);
if(baseCase) {
if(WIFEXITED(status))
{
if(WEXITSTATUS(status) == 0)
{
/*it worked*/
} else if(WEXITSTATUS(status) == 255) {
printf("The program %s does not exist \n", arguments[0]);
} else {
printf("ERROR: Error code: %d", WEXITSTATUS(status));
}
}
else
{
printf("There was a problem that is not normal");
}
printf("\n \n");
}
return 1;
} else {
if(pipeHasSomeData == 1) {// read from the pipe
dup2(fd[0], 0);//read from pipe
}
if(baseCase == 0) {// not the base case
dup2(fd[1], 1);//write to pipe
} else {
close(fd[1]);//close write
}
exit(execvp(arguments[0], arguments));
return 0;
}
}
int execute_commands(char *arguments[1000][1000], int pd[2] = NULL) {
int current_count = args_count;
int iterator = 0;
int fd[2];
int useAPipeInCommand = 0;
pipe(fd);
while(iterator <= args_count) {//go through and execute all the commands
if(current_count == 0) {//base case
return runCommand(arguments[iterator], fd, useAPipeInCommand, 1);
} else {
runCommand(arguments[iterator], fd, useAPipeInCommand, 0);
useAPipeInCommand = 1;
}
iterator++;
current_count--;
}//end while
return 1;
}
int main () {
int i = 0;
char text[1024]; /* the input line */
char *tok2;
while (1) { /* repeat until done .... */
fflush(stdin);
fflush(stdout);
printf("Shell -> "); /* display a prompt */
*text = 0;
fgets(text, sizeof text, stdin); /* read in the command line */
fflush(stdout);
printf("\n");
char * tok = strtok(text, " \n\t");
if (strcmp(tok, "exit") == 0) { /* is it an "exit"? */
return 0; /* exit if it is */
}
if (strcmp(tok, " ") == 0) { /* is it an "exit"? */
continue; /* exit if it is */
}
tok2 = tok;
memset(args, 0, sizeof(args[0][0]) * 1000 * 1000);//clear the arguments array
args_count = 0;
int count = 0;
while(tok2 != NULL) {
if(strcmp(tok2, ">") != 0) {
args[args_count][count] = tok2;
count++;
tok2 = strtok(NULL, " \n\t");
} else {//pipe was found, up the argument counter and set count to 0
args[args_count][count] = NULL;
args_count++;
count = 0;
tok2 = strtok(NULL, " \n\t");
}
}
args[args_count][count] = NULL;
execute_commands(args);
}//end while
return 0;
}
It is running the single base case no problem but the shell freezes when I do a pipe. Any ideas on the issue?
Correct answer from Comments by #beau-bouchard and #rici:
Pipes have a (small) finite buffer; you cannot write more than a little bit to the pipe without blocking unless the other end of the pipe is being read.
For a correct implementation, check out "multiple pipes in C" Coding multiple pipe in C
--UPDATE:
Here is my final working code for anyone that is having a similar issue:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <wait.h>
int READ = 0;
int WRITE = 1;
char *args[1000][1000];//array of arguments
int args_count = 0;//count of the arguments in the array
int execute_commands(char *arguments[1000][1000]) {
int pd[2];
int iterator = 0;
int fd[2];
int f_in = 0;
while(iterator <= args_count) {//go through and execute all the commands
pid_t pid;
int status;
pipe(fd);
pid = fork();
if(pid != 0) {
waitpid(pid, &status, 0);//wait for child to exit
close(fd[WRITE]);//close the writing end
if(WIFEXITED(status))
{
if(WEXITSTATUS(status) == 0)
{
/*it worked*/
} else if(WEXITSTATUS(status) == 255) {
printf("The program %s does not exist \n", arguments[iterator][0]);
} else {
printf("ERROR: Error code: %d", WEXITSTATUS(status));
}
}
else
{
printf("There was a problem that is not normal %d", status);
}
f_in = fd[READ];//set the pipe to the in
if(iterator == args_count) {
printf("\n \n");
}
//return 1;
} else {
dup2(f_in, 0);
if(iterator != args_count) {//its not the main value
dup2(fd[WRITE], 1);//write to pipe
}
close(fd[READ]);
exit(execvp(arguments[iterator][0], arguments[iterator]));
return 0;
}
iterator++;
}//end while
return 1;
}
int main () {
int i = 0;
char text[1024]; /* the input line */
char *tok2;
while (1) { /* repeat until done .... */
fflush(stdin);
fflush(stdout);
printf("Shell -> "); /* display a prompt */
*text = 0;
fgets(text, sizeof text, stdin); /* read in the command line */
fflush(stdout);
printf("\n");
char * tok = strtok(text, " \n\t");
if (strcmp(tok, "exit") == 0) { /* is it an "exit"? */
return 0; /* exit if it is */
}
if (strcmp(tok, " ") == 0) { /* is it an "exit"? */
continue; /* exit if it is */
}
tok2 = tok;
memset(args, 0, sizeof(args[0][0]) * 1000 * 1000);//clear the arguments array
args_count = 0;
int count = 0;
while(tok2 != NULL) {
if(strcmp(tok2, ">") != 0) {
args[args_count][count] = tok2;
count++;
tok2 = strtok(NULL, " \n\t");
} else {//pipe was found, up the argument counter and set count to 0
args[args_count][count] = NULL;
args_count++;
count = 0;
tok2 = strtok(NULL, " \n\t");
}
}
args[args_count][count] = NULL;
execute_commands(args);
}//end while
return 0;
}

Piping in C - Error in Command 2

So, I thought I was on the right track with trying to imitate the bash shell, but I'm having issues piping. I am getting an error executing the second command. I was wondering if someone could explain to me how to fix this and why it's going wrong.
I'm very new to C & Linux commands so any supplemental information that could help me along the way would be also be appreciated.
Thank you so much for your time. My code is below, but there is a lot of it. My issue is occurring in the exec_pipe function. I would normally include what I have used for input and what I am getting for output, but my sample input is actually executable files my professor gave us for testing. Unfortunately, mine is not working like it does in the shell. I am just getting my error print out:
Inside Case 5
Inside Exec_Pipe
Error in Pipe EXECVP cmd2
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdbool.h>
#include <time.h>
#include <limits.h>
#include <fcntl.h>
#include <sys/wait.h>
#define BUFSIZE 1024
#define CSTRSIZE 100
#define CMDSIZE 30
#define DEBUG 1
//I referenced our blackboard source code files to create the fork functions and to deal with file descriptors
void exec_cmd(char** cmd1){
pid_t pid;
if((pid = fork()) < 0){
printf("Child Process Failed\n");
}else if(pid == 0){
if(execvp(cmd1[0], cmd1) < 0){
printf("Execution Failed\n");
exit(1);
}
}else{
wait(NULL);
}
}
void exec_cmd_in(char** cmd1, char* infile){
pid_t pid;
int fdi;
if((pid = fork()) < 0){
printf("Child Process Failed\n");
}else if(pid == 0){
fdi = open(infile, O_RDONLY);
if(fdi == -1){
printf("No Infile");
}
}
}
void exec_cmd_opt_in_append(char** cmd1, char* infile, char* outfile){
/* pid_t pid;
int fdi, fdo;
if((pid = fork()) < 0){
printf("Child Process Failed\n");
}else if(pid == 0){
fdo = open(outfile, O_RDWR | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
if(fdo == -1){
printf("No Outfile");
}
if(dup2(fdi, 0) == -1){
printf("Infile not updated");
}
if(dup2(fdo, 1) == -1){
printf("Outfile not updated");
}
close(fdi);
close(fdo);
if(execvp(cmd1[0], cmd1) < 0){
printf("Execution Failed\n");
exit(1);
}
}else{
wait(NULL);
} */
}
void exec_cmd_opt_in_write(char** cmd1, char* infile, char* outfile){
/* pid_t pid;
int fdi, fdo;
if((pid = fork()) < 0 ){
printf("Fork Error");
exit(1);
}else if(pid == 0 ){
fdo = open(outfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if(fdo == -1){
printf("No Outfile");
}
if(dup2(fdi, 0) == -1){
printf("Infile not updated");
}
if(dup2(fdo, 1) == -1){
printf("Outfile not updated");
}
close(fdi);
close(fdo);
if(execvp(cmd1[0], cmd1) < 0){
printf("Execution Failed\n");
exit(1);
}
}else{
wait(NULL);
}
*/
}
void exec_pipe(char** cmd1, char** cmd2){
pid_t pid;
int pipefd[2];
// pipe[1] is the write end of the pipe
// pipe[0] is the read end of the pipe
// making a pipe
printf("Inside Exec_Pipe\n");
pid = fork();
switch(pid){
case -1:
//error in fork
printf("Fork Error\n");
//Exit
exit(1);
case 0:
//child
break;
default:
//parent
wait(NULL);
}
//This will be executed by child process
if(pipe(pipefd) < 0 ) {
//error condition
printf("Pipe Error");
exit(1);
}
pid = fork();
switch(pid){
case -1:
//error in fork
printf("Fork Error\n");
//Exit
case 0:
//child
close(STDIN_FILENO);
//direct STDOUT to the pipe
dup2(pipefd[1], STDOUT_FILENO);
//Close descriptors
close(pipefd[0]);
close(pipefd[1]);
//Execute Command1
execvp(cmd1[0], cmd1);
//execvp should not return, so if it does
//there is an error!
printf("Error in EXECVP cmd1");
exit(1);
default:
//parent
close(STDIN_FILENO);
//direct input to the pipe
dup2(pipefd[0],STDIN_FILENO);
//close descriptors
close(pipefd[0]);
close(pipefd[1]);
//execute command 2
execvp(cmd2[0],cmd2);
//if execvp makes it back, error condition
printf("Error in Pipe EXECVP cmd2");
exit(1);
}
}
void exec_pipe_opt_in_append(char** cmd1, char** cmd2, char* infile, char* outfile){
}
void exec_pipe_opt_in_write(char** cmd1, char** cmd2, char* infile, char* outfile){
}
int parse_command(char* line, char** cmd1, char** cmd2, char* infile, char* outfile){
/*
(1)Create a bunch of flags to compare for the right return value
(2)Loop over the entire line and set the flags
(3)Add a bunch of if statements to compare flags
(4)If there is more than one flag for pipe, we can't handle it. Regurn 9.
(5)If there is &, we can't handle.
(6)Return the right value
*/
int pipe_found = 0;
int input_found = 0;
int redirection = 0;
int i = 0;
int spaces = 0;
int append = 0;
int special = 0;
while(line[i] != '\0'){
if(line[i] == '|'){
pipe_found++;
}
if(line[i] == '<'){
input_found = 1;
}
if((line[i] == '&') || (line[i] == '*') || (line[i] == '^') || (line[i] == '%') || (line[i] == '#') || (line[i] == '!') || (line[i] == '#') || (line[i] == '(') || (line[i] == ')')){
special = 1;
}
if(line[i] == '>'){
redirection = 1;
if(line[i+1] == '>'){
append = 1;
}
}
if(line[i] == ' '){
spaces++;
}
i++;
}
if((strlen(line) >=4) && (line[0] == 'q') && (line[1] == 'u') && (line[2] == 'i') && (line[3] == 't')){
return 0;
}
if((pipe_found == 0) && (special == 0)){
if((redirection == 0) && (input_found == 0)){
return 1;
}else if((redirection == 0) && (input_found == 1)){
return 2;
}else if(append == 1){
return 3;
}else if(redirection == 1){
return 4;
}
}else if((pipe_found == 1) && (special == 0)){
if((redirection == 0) && (input_found == 0)){
return 5;
}else if((redirection == 0) && (input_found == 1)){
return 6;
}else if(append == 1){
return 7;
}else if(redirection == 1){
return 8;
}
}
return 9;
}
//I referenced StackOverflow and some online libraries to get this tokenize function
char ** tokenize(char *str, char *delim, unsigned int *number_tokens) {
char *pch = strtok(str, delim);
unsigned int ntok = 0;
if(pch != NULL) {
ntok = 1;
}else{
return NULL;
}
char **tokens = realloc(NULL, sizeof(char *)*ntok);
tokens[ntok-1] = pch;
while(pch != NULL) {
pch = strtok(NULL, delim);
ntok++;
tokens = realloc(tokens, sizeof(char *)*ntok);
tokens[ntok-1] = pch;
}
if(number_tokens) {
*number_tokens = ntok;
}
return tokens;
}
//I referenced StackOverflow.com for this trim function
char *trim(char *str) {
char *end;
if(str == NULL){
return NULL;
}
while(isspace(*str)){
str++;
}
end = str + strlen(str) - 1;
while(end > str && isspace(*end)) {
end--;
}
*(end+1) = 0;
return str;
}
int main(int argc, char *argv[]){
int returnValue = 0;
char *infile = NULL;
char *outfile = NULL;
char **cmd = NULL;
char **cmd1_tokens = NULL;
char **cmd2_tokens = NULL;
char *input;
int current_cmd = 0;
/*
(1)If the user does not enter a command line argument, get one after typing "myshell-%"
(2)Call parse_command on the user input to get the right return value
(3)Begin parsing the user input within main
*/
if(argc == 1){
printf("myshell-%%\n");
fgets (input, 20, stdin);
returnValue = parse_command(input, cmd1_tokens, cmd2_tokens, infile, outfile);
cmd = tokenize(input, "|", NULL);
}else{
returnValue = parse_command(argv[1], cmd1_tokens, cmd2_tokens, infile, outfile);
cmd = tokenize(argv[1], "|", NULL);
}
int infileIt = 0;
while(cmd[current_cmd] != NULL) {
unsigned int number_tokens = 0;
char **infile_token = tokenize(cmd[current_cmd], "<", &number_tokens);
if(number_tokens > 1){
while(infile_token[infileIt] != NULL){
infileIt++;
}
}
if(infile_token[1] != NULL) {
number_tokens = 0;
char **infile_outfile_token = tokenize(infile_token[1], ">", &number_tokens);
if(number_tokens > 1){
infile = infile_outfile_token[0];
infile = infile_token[1];
}
}
number_tokens = 0;
char **outfile_token = tokenize(cmd[current_cmd], ">", &number_tokens);
if(number_tokens > 1){
outfile = outfile_token[1];
}
current_cmd++;
}
//Trim the in/outfiles
infile = trim(infile);
outfile = trim(outfile);
/*
Start breaking up cmd[0] and cmd[1] into smaller chunks and saving into the appropriate cmd
*/
cmd1_tokens = tokenize(cmd[0], " ", NULL);
if(cmd[1] != NULL){
cmd2_tokens = tokenize(cmd[1], " ", NULL);
}
int cmd1Args = 0;
while(cmd1_tokens[cmd1Args] != NULL){
cmd1Args++;
}
int cmd2Args= 0;
if(cmd2_tokens != NULL){
while(cmd2_tokens[cmd2Args] != NULL){
cmd2Args++;
}
}
int iterator = 0;
while((iterator < cmd1Args) && (cmd1Args != 0)){
printf("Cmd1: %s\n", cmd1_tokens[iterator]);
iterator++;
}
iterator = 0;
while((iterator < cmd2Args)&&(cmd2Args != 0)){
printf("Cmd2: %s\n", cmd2_tokens[iterator]);
iterator++;
}
if(infile != NULL){
printf("Infile: %s\n", infile);
}
if(outfile != NULL){
printf("Outfile: %s\n", outfile);
}
/*Use a switch statement to process all the return values (0 ot 9) of parse_command.
Our program should execute the “line” if the return code from parse_command
function is 0 to 8, that is the line is deemed “valid”. For return code 9,
our program simply output ”Not handled at this time!”.*/
switch(returnValue){
case 0 :
printf("Exiting Program.\n");
exit(1);
break;
case 1 :
printf("Inside Case 1\n");
exec_cmd(cmd1_tokens);
break;
case 2 :
printf("Inside Case 2\n");
exec_cmd_in(cmd1_tokens, infile);
break;
case 3 :
printf("Inside Case 3\n");
exec_cmd_opt_in_append(cmd1_tokens, infile, outfile);
break;
case 4 :
printf("Inside Case 4\n");
exec_cmd_opt_in_write(cmd1_tokens, infile, outfile);
break;
case 5 :
printf("Inside Case 5\n");
exec_pipe(cmd1_tokens, cmd2_tokens);
break;
case 6 :
printf("Inside Case 6\n");
//exec_pipe_in(cmd1_tokens, cmd2_tokens, infile);
break;
case 7 :
printf("Inside Case 7\n");
exec_pipe_opt_in_append(cmd1_tokens, cmd2_tokens, infile, outfile);
break;
case 8 :
printf("Inside Case 8\n");
exec_pipe_opt_in_write(cmd1_tokens, cmd2_tokens, infile, outfile);
break;
default :
printf("Inside Case 9\n");
printf("Not handled at this time!\n");
}
return 0;
}
Without having access to the input file that you're giving it, it's a little hard to say what's going on, but here are some tips for debugging it.
First, when something you don't understand is happening, it can be a good idea to strip it down to a minimal, working, self contained example that demonstrates the problem. Sometimes, just the process of cutting it down to that small example can help you to find the problem; but if not, it gives you a much smaller example to ask about.
Next, when putting in these print statements to debug what's going on, give yourself a little more context. Especially in the one that indicates an error; print out what the error is, and what the arguments were to the function that failed. Rather than just:
printf("Error in Pipe EXECVP cmd2");
You can use strerror to get a string representing the error number:
printf("Error %d in Pipe EXECVP cmd2: %s\n", errno, strerror(errno));
And you can also print out what the command and all of your arguments were:
for (char **arg = cmd2; *arg != NULL; ++arg) {
printf("cmd2[%ld] = %s", arg - cmd2, *arg);
}
Between printing out the actual error and printing out the command name and all of the arguments, that should help you to debug the problem.
If you could add that information to your question, and maybe cut your example down to a more minimal example as well as showing a minimal example of the input that causes a problem, we could probably help out a lot more.

Resources