Custom shell only taking one argument - c

So I have this code for a shell:
#include <stdio.h>
#include <stdlib.h>
#define MAX_NUM_ARGS 256
#define SIZE 256
void orders(char *command[SIZE]);
int main() {
char buffer[SIZE]= "";
//char input_args[MAX_NUM_ARGS];
char **input_args = NULL;
int i = 0;// counting variable
int blah = 0;
printf("Welcome to the AY shell.\n");
while(1){
//initialize array of strings
//first free any prevously allocated memory
if (input_args != NULL)
{ //memory has been allocated free it
for (i = 0; i <MAX_NUM_ARGS; i++)
{
free(input_args[i]);
}
}
//free array of strings
free(input_args);
//new allocate memory
input_args = (char**) malloc(sizeof(char*) * MAX_NUM_ARGS);
//check return value for error
if (input_args == NULL)
{
printf("We are out of memory. =(\n");
continue;
//print error: out of memory, exit with a error code
exit(0);
}
//allocate memory for each string
for (i = 0; i <MAX_NUM_ARGS; i++)
{
input_args[i]= (char*)malloc(sizeof(char) * MAX_NUM_ARGS);
if(input_args[i] == NULL)
{//error
printf("Error, the input is empty.");
continue;
}//end of if statement
}//end of for loop
printf("~$: "); //prompts the user for input
fgets(buffer, sizeof(buffer), stdin);
//if the user types in exit, quit
if (strcmp(buffer, "exit\n") == 0){
exit(0);
} //end of if statement
//if user types in clear, wipe the screen and repeat the lop
else if(strcmp(buffer, "clear\n")==0){
system("clear");
continue;
}//end of else if
//should the user punch in nothing, repeat the loop
else if (strcmp(buffer, "\n") == 0) {
continue;
}//end of else if
input_args[1] = NULL;
for (i = 0; i < SIZE; i++) {
if(buffer[i] != '\n' && buffer[i] != ' ' && buffer[i] != '\t'){
input_args[0][i] = buffer[i];
} //end of if statement
else{
input_args[0][i] = '\0';
}//end of else statment
}//end of for loop
//if the input doesn't fall under the conditionals above, execute orders.
orders(input_args);
} //end of while loop
return 0;
}//end of main function
void orders(char *command[SIZE]){
//handles the commands of the shell
int retval = 0; //return value
int pid = 0;
int childValue = 0;
pid = fork();
if (pid != 0){
// printf("I'm the parent, waiting on the child.\n");//debug
pid = waitpid(-1, &childValue,0);
// printf("Child %d returned a value of %x in hex.\n", pid, childValue);
return;//return backs to the main prompt
}//end of if statement
else{
// printf("I am the first child.\n");
retval = execvp(command[0], command);
exit(2);
if (retval != -1){
//print error!
printf("Invalid command!\n");
exit(2);
}
}//end of else block
}//end of orders function
Now, it executes clear, exit, and single word commands just well, like ls, or pwd. However, multi-line commands such as "vim " don't work, nor changing directories.
What am I doing wrong?
I'm suspecting the retval = execvp(command[0], command); is causing problems, but I'm not too entirely sure. Any thoughts? I don't want a direct answer, since this is homework, just a push in the right direction.

This section:
input_args[1] = NULL;
for (i = 0; i < SIZE; i++) {
if(buffer[i] != '\n' && buffer[i] != ' ' && buffer[i] != '\t'){
input_args[0][i] = buffer[i];
} //end of if statement
else{
input_args[0][i] = '\0';
}//end of else statment
}//end of for loop
limits input_args to only have the first index be used. I assume this is where you would find a way to have a j++; inside the else clause and use input_args[j][i] or something similar...
And your last comment matches this, since your retval = execvp(command[0], command); is also only using the first item from the list.

Related

Stack smashing in file io in c

void issueBook(){
printf("Hii!\n");
//printf("Enter your student ID: ");
//int stdID;
//scanf("%d", &stdID);
printf("Enter the book ID\n");
int bookID;
scanf("%d", &bookID);
FILE *in_file = fopen("recordLib.txt", "r");
FILE *fp = fopen("temp.txt", "w");
struct stat sb;
stat("recordLib.txt", &sb);
char *file_contents = malloc(1024);
int mark = 0;
char ID[] = "";
while (fscanf(in_file, "%[^\n] ", file_contents) != EOF) {
int size = strlen(file_contents);
int countCom = 0;
char ID[] = "";
//printf("%d\n", size);
if(mark == 0){
for(int i=0; i<size; i++){
//printf("asdfsd\n");
//printf("%c", file_contents[i]);
if(countCom == 0 && (file_contents[i] != ',')){
strncat(ID, &file_contents[i], 1);
//printf("%c\n", file_contents[i]);
}
else if(atoi(ID) != bookID){
break;
}
else if((file_contents[i] == ',') && (countCom < 3) && (atoi(ID) == bookID)){
//printf("%c\n", file_contents[i]);
//printf("%s\n", ID);
countCom++;
}
else{
//printf("%c\n", file_contents[i]);
if(file_contents[i] == '1'){
printf("Sorry!! someone has already issued the book");
mark = 2;
break;
}
else if(file_contents[i] == '0'){
file_contents[i] = '1';
mark = 1;
break;
}
}
}
}
fwrite(file_contents, 1, size, fp);
fwrite("\n", 1, 1, fp);
}
fclose(fp);
fclose(in_file);
remove("recordLib.txt");
rename("temp.txt", "recordLib.txt");
//printf("%d\n", s);
//if(mark == 1){
// updateStu();
//}
free(file_contents);
}
I have made this function which takes the bookID from the user and then searches for it in the file:
Searching: It reads line by line and as we already know till first comma ',' it would be book ID so it will store in another string and then convert it to int. Now it will compare it with entered ID if it matches then it will update the 0 at the end to 1, else move to next line.
Error: I always getting stack smashing error when the function again returns to the main function. I am unable to find the thing that is causing the error. Also that when I run in VS code, it runs successfully but in Linux this problem occurs! Please help. Thanks in advance.

How to choose different words to be shown using if statement?

Whenever I make ch[index]==0, it gives me first word from text file but whenever I select ch[index]==1, it gives me nothing. How to make this if statement working?
#include <stdio.h>
#include<stdlib.h>
int main(){
FILE * fr = fopen("/home/bilal/Documents/file.txt","r");
char ch[100];
int index = 0;
if(fr != NULL){
while((ch[index] = fgetc(fr)) != EOF){
if(index[ch]==1){ // here is if statement
if(ch[index] == ' ') {
ch[index] = '\0';
printf("Here is your %s: \n",ch);
index = 0;
}
else { index++; }
}
}
fclose(fr);
}
else{ printf("Unable to read file."); }
return 0;
}
For a start you have an fclose(fr) inside your i loop, but then you never open the file again. You are also incrementing i inside the loop a second time, which is never good in practice.
try this:
for (int i=0; i<8; i++){
fr = fopen("/home/bilal/Documents/file.txt","r");
index = 0;
if(fr != NULL){
and remove the fopen from the top.
There is probably a better way than opening and closing the file on each loop iteration.
Here is a code that should work and show the second word, although I didn't test it:
#include <stdio.h>
//#include <stdlib.h> //not needed
int main(void)
{
FILE* fr = fopen("/home/bilal/Documents/file.txt", "r");
char ch[100];
int index = 0, c, i = 0;
//for loop is useless
if (fr == NULL)
{
printf("Unable to read file.");
return 0;
//I prefer error checking without a giant if statement, but it's up to you
}
c = fgetc(fr); //fgetc() returns an int, not char
while (c != EOF)
{
ch[index] = c;
if (ch[index] == ' ')
{
if (i == 1)
{
ch[index] = '\0';
printf("Here is your string: %s\n", ch); //The format seemed weird to me, that's why I changed it, use what you need
}
index = 0;
i++;
}
else
index++;
c = fgetc(fr);
}
fclose(fr);
//return 0; //not needed in C99 or later
}

Writing my own shell in c - free() causing problems

I'm trying to write my own shell, but something is not working with the allocations and free. I reviewed my code over and over, and I cannot understand why my free function is causing me problems... When I don't use it, everything works fine, but when I use it, the code stops working after the second iteration...
I would really appreciate your help...
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <limits.h> //for PATH_MAX - longest path name permited in linux
#include <sys/wait.h>
#include <fcntl.h>
typedef struct{
char **parametersArray; //this array contains the command and the parameters
int size_of_array; //the number of strings in the array
int toFileFlag; //if we wnat to write to file
char *toFile; //name of file to write to
int fromFileFlag;//if we wnat to read from file
char *fromFile; //name of file to read to
}UserInput;
int runInBackground = 0; //is command running in background? if so, runInBackground=1;
//********************************************************************************************************************
//functions list:
UserInput* inputTokenization(char *a); //recieve string of the user input, and returns pointer to struct of UserInput.
void execCommand(UserInput *user_input); //exec the requestd command with the parameters given
void free_All_Allocations(UserInput *userinput);
//*******************************************************************************************************************
int main(int argc, char *argv[])
{
char userInputTxt[LINE_MAX]; //the line the user enters (hopefully command+parameters)
UserInput *u_i;
int i = 0;
while(1)
{
i = 0;
printf("\033[1;35m"); //print in with color (purple)
printf("### "); //### is the prompt I chose
fflush(stdout);
memset(userInputTxt, 0, LINE_MAX); //cleaning array from previous iteration
read(0, userInputTxt, LINE_MAX);
if(strcmp(userInputTxt, "exit\n") == 0) //exit the program if the user enters "exit"
exit(EXIT_SUCCESS);
u_i = inputTokenization(userInputTxt); //parsing the char array userInputTxt
execCommand(u_i);
free_All_Allocations(u_i);
}
}
UserInput* inputTokenization(char *a)
{
int i=0, size;
size = strlen(a);
UserInput *user_input = (UserInput*)malloc(sizeof(UserInput)*1);
if(user_input == NULL)
{
perror("failed to allocate memory");
exit(EXIT_FAILURE);
}
user_input->fromFileFlag = 0;
user_input->toFileFlag = 0;
user_input->size_of_array = 2;
//counting how many token we have
while(i<size)
{
if(a[i] == ' ')
(user_input->size_of_array)++;
if (a[i] != '<' || a[i] != '>' )
break;
i++;
}
printf("%d\n", user_input->size_of_array);
//we don't want to change original array(a), so we'll copy a to tmp and use tmp
char *tmp = (char*)malloc(size+1);
if(tmp == NULL)
{
perror("failed to allocate memory");
exit(EXIT_FAILURE);
}
strncpy(tmp, a, size-1);
//we'll allocate array of arrays. It's size: number of tokens in the original array, even though we might not use all of it-
//some tokens might be name of file to read or write to
user_input->parametersArray = (char**)malloc(user_input->size_of_array);
if(user_input->parametersArray == NULL)
{
perror("failed to allocate memory");
exit(EXIT_FAILURE);
}
i=0;
char* token = strtok(tmp, " ");
user_input->parametersArray[i] = (char*)malloc(strlen(token)+1);
if(user_input->parametersArray[i] == NULL)
{
perror("failed to allocate memory");
exit(EXIT_FAILURE);
}
strcpy(user_input->parametersArray[i], token);
i++;
while(token != NULL)
{
token = strtok(NULL, " ");
if(token !=NULL)
{
if(strcmp(token, "<") != 0 && strcmp(token, ">") !=0 && strcmp(token, "&") != 0)
{
user_input->parametersArray[i] = (char*)malloc(strlen(token)+1);
if(user_input->parametersArray[i] == NULL)
{
perror("failed to allocate memory");
exit(EXIT_FAILURE);
}
strcpy(user_input->parametersArray[i], token);
i++;
continue;
}
if(strcmp(token, "<") == 0)
{
user_input->fromFileFlag = 1;
token = strtok(NULL, " ");
if(token !=NULL)
{
user_input->fromFile = (char*)malloc(strlen(token)+1);
if(user_input->fromFile == NULL)
{
perror("failed to allocate memory");
exit(EXIT_FAILURE);
}
strcpy(user_input->fromFile, token);
}
}
if(strcmp(token, ">") == 0)
{
user_input->toFileFlag = 1;
token = strtok(NULL, " ");
if(token != NULL)
{
user_input->toFile = (char*)malloc(strlen(token)+1);
if(user_input->toFile == NULL)
{
perror("failed to allocate memory");
exit(EXIT_FAILURE);
}
strcpy(user_input->toFile, token);
}
}
if(strcmp(token, "&") == 0)
{
runInBackground = 1;
break;
}
}
}
user_input->parametersArray[i] = NULL;
free(tmp);
return user_input;
}
void execCommand(UserInput *user_input)
{
pid_t pid;
int status;
pid = fork();
if(pid == -1) //fork failed
{
perror("fork() failed");
exit(EXIT_FAILURE);
}
if(pid == 0) //child process
{
if(user_input->fromFileFlag == 1) //if we have file to read from
{
close(0);
if(open(user_input->fromFile, O_RDONLY) == -1)
{
perror("open file to read failed");
exit(EXIT_FAILURE);
}
}
if(user_input->toFileFlag == 1) //if we have file to write to
{
close(1);
if(open(user_input->toFile, O_WRONLY | O_CREAT, 0766) == -1)
{
perror("open file to write failed");
exit(EXIT_FAILURE);
}
}
if(execvp(user_input->parametersArray[0], user_input->parametersArray) == -1)
{
perror("execvp() failed");
exit(EXIT_FAILURE);
}
}
if(runInBackground == 0) //as long as this is the only command to execute,
waitpid(pid, &status, 0); //wait until chile process (execvp) finish. Otherwise, father process go again, and chile process run in background
}
void free_All_Allocations(UserInput *userinput)
{
int i=0;
while(userinput->parametersArray[i] != NULL)
{
free(userinput->parametersArray[i]);
i++;
}
free(userinput->parametersArray);
if(userinput->fromFileFlag == 1)
free(userinput->fromFile);
if(userinput->toFileFlag == 1)
free(userinput->toFile);
free(userinput);
}
I recommend the use of valgrind.
Compile with your code with the flag -ggdb3 and then execute valgrind with your program. It'll show you all the invalid reads and writes during the execution of the program. Not only that, it'll tell you exactly in what line they happen and the corresponding function call trace.
This question is a great starting point if you're a beginner to valgrind.
One problem is that your "counting how many token we have" is wrong. It will exit on the first iteration, because the condition a[i] != '<' || a[i] != '>' will always be true. I think the comparison you want is a[i] == '<' || a[i] == '>', which will exit the loop if either of those characters is found. This will result in user_input->size_of_array being 2 (or 3, if the first character in a is a space). Later, when you actually pull tokens from the string, you write past allocated memory if there are more than two (or possibly three) tokens.
This counting loop itself is flawed, because it counts differently than the loop that actually extracts the tokens. (For example, if a token is "a>b", your counter would stop but the tokenizer loop would treat that as a token and keep going.) It would be better to use the same sort of loop to count the token, using strtok, or better yet use a way to dynamically resize your parameters array so you only need to make one pass and don't need to count. With either loop, multiple adjacent spaces result in an empty token.
But that isn't all. The count being wrong isn't currently an issue because of the next problem: the allocation for user_input->parametersArray uses the wrong size. Since you want user_input->size_of_array elements, you should use
user_input->parametersArray = malloc(user_input->size_of_array * sizeof(char *));
or, to avoid problems with the proper type, you can go with
user_input->parametersArray = malloc(user_input->size_of_array * sizeof(*user_input->parametersArray));
Note that I have removed the cast of the return value from malloc. It is not necessary in C, and can lead to subtle problems if the type used in the cast is not the correct one.
Another problem is the call to strncpy. Since the length of the string is size, the terminating nul character will not be copied. Since you now the buffer is big enough, you should just use strcpy (or strcpy_s if your compiler is new enough to support it).

execvp with "ls" not working in custom shell

What I am doing: Creating a custom shell for a class project. Just started implementing my fork and exec features.
My Issue: ls works fine until I change directories with cd. After the first cd, ls requires the . to list files in current directory. After that, it seems to lose all functionality.
I've included my code below. Any help (including pointing out obvious newb mistakes) is appreciated. I am in the early stages of grasping these concepts.
int main(){
char *input = malloc(sizeof(char*) * BUFFERSIZE);
char *token;
char **token_array;
int i;
int return_status;
printf("%s", BOILERPLATE);
if (input == NULL){
printf("JASH: Buffer Allocation Failed");
return 1;
}
// Shell Loop
while(1){
i = 0;
printf("JASH:> ");
fgets(input, BUFFERSIZE, stdin);
token_array = malloc(sizeof(char)*strlen(input));
token = strtok(input, " \n()<>|&;");
while(token != NULL){
token_array[i] = token;
token = strtok(NULL, " \n()<>|&;");
i++;
}
// print tokens to confirm correct parsing of input
printf("%s : %s\n", token_array[0], token_array[1]);
//printf("%s\n", token_array[0]);
if(exec_args(token_array) == 0){
// programm exit successfully
free(token_array);
free(input);
return 0;;
}
} // Loop End
// free input buffer before exit
free(token_array);
free(input);
return 0;
}
int exec_args(char ** t){
int i;
if(strcmp(t[0], "exit") == 0
|| strcmp(t[0], "quit") == 0
|| strcmp(t[0], "logout") == 0){
// call built in function for exit
exit(0);
}
if (strcmp(t[0], "cd") == 0){
// call built in function for chdir
if(chdir(t[1]) == 0){
printf("Successfully changed directories to %s\n", t[1]);
return 1;
} else {
printf("-JASH: cd: %s: No such file or directory\n", t[1]);
return 1;
}
}
if (strcmp(t[0], "pwd") == 0) {
char * p = malloc(sizeof(char*) * 100);
getcwd(p, 100);
printf("%s\n", p);
free(p);
return 1;
}
if(strcmp(t[0], "help") == 0){
printf("\n%s\n Help Documentation Coming soon!\n\n", BOILERPLATE);
return 1;
}
if(fork() == 0){
if(execvp(t[0], t)){
perror("JASH");
}
} else {
int status;
wait(&status);
printf("%d", status);
}
return 1;
}

What is this shell doing?

I was presented with this code as an example of how to make a 'mini shell' but I have no idea what it is doing-- I understand most things in the main function, but the functions above it initializing I'm really quite lost on.
Any basics on how a shell works and some of the lower level functions in C would be much appreciated.
log_t Log;
void printLog(log_t *l){
log_entry_t *tmp = l->tail;
while(tmp != NULL){
printf("%s\n", tmp->value);
tmp = tmp->prev;
}
}
int countArg(char *line){
unsigned int i, count = 0;
for(i = 0; i < strlen(line); i++){
if(line[i] == ' ')
count++;
}
return count + 1;
}
char **makeargv(char *line){
char *buff;
char **retv;
unsigned int i, count, arg_num = 0;
buff = malloc((strlen(line) + 1) * sizeof(char));
strcpy(buff, line);
arg_num = countArg(buff);
retv = malloc((arg_num + 1) * sizeof(char*));
retv[arg_num] = NULL;
retv[0] = buff;
for(i = 0, count = 1; buff[i]!='\0'; i++){
if(buff[i] == ' '){
buff[i]='\0';
retv[count] = buff + i + 1;
count++;
}
}
return retv;
}
int main()
{
log_init(&Log);
char * line;
while(1){
size_t size;
char * cwd = NULL;
char * query = NULL;
char * match = NULL;
int f_exit = 0,
f_nbi=0,
f_match=0,
f_sys = 0,
f_path=0;
// print out the requested prompt
cwd = getcwd(NULL,0);
printf("$(pid=%d)%s$ ", getpid(), cwd );
free(cwd);
line = NULL;
fflush(stdout);
getline(&line, &size, stdin);
line[strlen(line)-1] = '\0';
while(1){
if(strcmp(line, "exit") == 0){
if(f_match) printf("%s matches %s\n", query, line);
f_exit = 1;
printf("Command executed by pid=%d\n",getpid());
log_destroy(&Log);
break;
}
else if(strncmp(line, "cd ", 3) == 0){
if(f_match) printf("%s matches %s\n", query, line);
printf("Command executed by pid=%d\n",getpid());
log_push(&Log, line);
if(chdir(line + 3) != 0){
printf("%s: No such file or directory\n",(line + 3));
}
break;
}
else if(strcmp(line, "!#") == 0){
if(f_match) printf("%s matches %s\n", query, line);
printf("Command executed by pid=%d\n",getpid());
printLog(&Log);
break;
}
else if(strstr(line,"!")==line){
if(f_match) printf("%s matches %s\n", query, line);
query = line+1;
match = log_search(&Log, query);
if(match==NULL){
printf("Command executed by pid=%d\n",getpid());
printf("No Match\n");
break;
}
else{
f_match = 1;
printf("Command executed by pid=%d\n",getpid());
line = malloc((strlen(match)+1)*sizeof(char));
strcpy(line, match);
continue;
}
}
else{
f_nbi = 1;
log_push(&Log,line);
if(strstr(line,"/"))
f_path = 1;
break;
}
}
if(f_exit)
break;
if(f_nbi){
pid_t pid = fork();
if(pid == 0){ //child
if(f_match){
printf("%s matches %s\n", query, line);
free(query-1);
}
printf("Command executed by pid=%d\n",getpid());
char **argv = makeargv(line);
f_sys = 0;
if(f_path)
f_sys = execv(argv[0],&argv[0]);
else
f_sys = execvp(argv[0],&argv[0]);
if(f_sys == -1)
printf("%s: not found\n",line);
free(argv[0]);
free(argv);
log_destroy(&Log);
exit(0);
}
else { //parent
waitpid(pid, NULL, WUNTRACED);
if(f_match)
free(query-1);
}
}
free(line);
line = NULL;
}
if(line != NULL){
free(line);
line = NULL;
}
return 0;
}
The printLog() function is simply printing all the data from a linked list of records in a structure type given the name log_t by a typedef. It starts at the tail of the list and works backwards. It is not clear whether the list is single-linked or double-linked, but the prev usually only appears in a double-linked list.
The countArgs() function is a crude way of determining how many arguments are present on a command line. It is crude because it does not take into account quotes or multiple adjacent spaces. However, those merely mean it overestimates the number of arguments, which is not serious. It doesn't recognize tabs as separators, which is unusual for a shell.
The makeargv() function splits a command line into a series of space separated words and returns the list of words to the calling function. It effectively parses the command line, making sure there's a null pointer at the end of the command line. Again, it is simplistic, but sufficient. It is also inconsistently named compared with the other two functions.

Resources