What is this shell doing? - c

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.

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.

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

c program is compiling and running but I am getting a weird output in the terminal

so I have a method that moves a string from a file to a char array in c, but when I try to print it out I am getting a weird output in the terminal that looks like a bunch of for each char spot and each box has 4 0's and 1's.
Here's the my code:
int main(int argc, char** argv){
if(argc != 3){
printf("not valid # of arguments");
return 1;
}
struct stat info;
int status;
status = stat(argv[2], &info);
if(status != 0){
printf("Error, errno = %d\n", errno);
return 1;
}
//command line argument is file
if(S_ISREG (info.st_mode)){
printf("%s is a file \n", argv[2]);
char *string1;
string1 = getFileString(argv[2]);
printf("string in file is %s \n", string1);
free(string1);
return 0;
}
if(S_ISDIR(info.st_mode)){
printf("%s is a directory \n", argv[2]);
openDirRec(argv[2]);
//what to do if command line argument is directory
}
return 0;
}
char* getFileString(char *fileName){
FILE* qp;
qp = fopen(fileName, "r");
char ch;
struct stat st;
if(stat(fileName, &st) != 0) {
return NULL;
}
/*int sizeCheck = 0;
while((ch=fgetc(qp))!=EOF){
sizeCheck++;
}
*/
int sizeCheck = st.st_size;
if(sizeCheck == 0){
return NULL;
}
else{
//fseek(qp, SEEK_SET, 0);
char *fileString;
fileString = (char*)malloc(sizeof(char) * sizeCheck + 1);
memset(fileString, 0, sizeCheck + 1);
//rewind(qp);
int count = 0;
while((ch=fgetc(qp)!=EOF)){
fileString[count] = ch;
count++;
}
printf("%s\n", fileString);
fileString[sizeCheck] = '\0';
fclose(qp);
return fileString;
}
}
This line is the culprit.
while((ch=fgetc(qp)!=EOF))
Due to operator precedence, that is equivalent to:
while(ch = (fgetc(qp)!=EOF) )
what you need is a little rearrangement of the parantheses.
while((ch=fgetc(qp)) != EOF)

Custom shell only taking one argument

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.

Formatting output of a binary search using printf in C?

I'm having some trouble with the output of this program. I need to print the verbs on one line, and I need to print a separate statement in the case that there are no verbs. For ex.
"talk and walk" should print "The verbs are: talk walk"
while "hello there" should print "There are no verbs"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int binary_search(char *list_of_words[], int size, char *target){
int bottom= 0;
int mid;
int top = size - 1;
int found = 0;
while(bottom <= top && !found){
mid = (bottom + top)/2;
if (strcmp(list_of_words[mid], target) == 0){
//printf("%s found at location %d.\n", target, mid+1);
found = 1;
} else if (strcmp(list_of_words[mid], target) > 0){
top = mid - 1;
} else if (strcmp(list_of_words[mid], target) < 0){
bottom = mid + 1;
}
}
if (found == 1)
return mid;
else
return -1;
}
int main(int argc, char* argv[]){
char *input = strtok(argv[1], " \"\n");
char *verbs[5] = { "do", "make", "take", "talk", "walk" };
int position;
int check = 0;
while (input != NULL) {
//printf("%s\n", input);
position = binary_search(verbs, 5, input);
if (position != -1)
printf("The verbs are: %s\n", verbs[position]);
check = 1;
input = strtok(NULL, " ");
}
if (check == 0){
printf("There are no verbs\n");
}
return 0;
}
Any ideas?
It seems to be working fine, but you need to add parenthesis around
if (position != -1)
printf("The verbs are: %s\n", verbs[position]);
check = 1;
like in
if (position != -1) {
printf("The verbs are: %s\n", verbs[position]);
check = 1;
}
otherwise check is always set 1 in the loop.
And if you do not want to repeat "The verbs are:" , add a check for that
if (position != -1) {
if (first) {
printf("The verbs are:");
first = 0;
check = 1;
}
printf(" %s", verbs[position]);
}
int main(int argc, char* argv[]){
char *input = strtok(argv[1], " \"\n");
char *verbs[5] = { "do", "make", "take", "talk", "walk" };
char match[5] = {0};
int position;
int check = 0;
while (input != NULL) {
//printf("%s\n", input);
position = binary_search(verbs, 5, input);
if (position != -1){
//printf("The verbs are: %s\n", verbs[position]);
match[position]=1;//match[position] = check = 1;
check = 1;
}
input = strtok(NULL, " ");
}
if (check == 0){
printf("There are no verbs\n");
} else {
int i;
printf("The verbs are: ");
for(i=0;i<5;++i)
if(match[i])
printf("%s ", verbs[i]);
printf("\n");
}
return 0;
}
If you're more interested in just having the search done, rather than implementing it yourself (i.e., assuming "implement a search" is not your actual task), you should use the standard library's little-known hero, bsearch().
Note that this requires the input data (the array you're searching in) to be sorted, but yours seems to be since you're already working on a binary search.

Resources