Heap based char nested array - arrays

I want to pass a heap based array that contains sub arrays for arguments to a function that uses execvp, why a heap based array, because I splitting user input into a list of arguments
int split(char * str, const char * token , char *** arr)
{
int c = 1;
char * ptr = strtok(str , token);
(*arr)[0] = malloc(1024);
strcpy((*arr)[0], "-c");
while (ptr != NULL)
{
(*arr)[c] = malloc(1024);
strcpy((*arr)[c], ptr);
c++;
*arr = realloc(*arr, sizeof(char*)*(c+1));
ptr = strtok(NULL, token);
}
(*arr)[c] = NULL;
return c;
}
int run(char * args[])
{
int link[2];
pid_t pid;
char foo[4096];
if (pipe(link)==-1)
return -1;
if ((pid = fork()) == -1)
return -1;
if(pid == 0){
dup2 (link[1], STDOUT_FILENO);
close(link[0]);
close(link[1]);
execvp("/bin/bash", args);
} else {
close(link[1]);
int nbytes = read(link[0], foo, sizeof(foo));
printf("Output: (%.*s)\n", nbytes, foo);
wait(NULL);
}
return 0;
}
int main()
{
char * tmp = malloc(1024);
/* list of pointers */
char ** arr = malloc(16);
/* buffer to hold user input */
char input[1024];
scanf("%[^\n]", input);
//printf("%s\n",input);
int x = split(input, " ", &arr);
for (int i=0; i<x;i++)
{
printf("%s ",arr[i]);
}
run(arr);
char * arr2[] = {getenv("SHELL"), "-c" ,"ls","-la", NULL};
run(arr2);
}
split takes the user input and create a list of pointers where the first element is -c and the last is NULL and the user input is in the middle next it is being passed to the run function that attempts to run the command with /bin/bash but throws the error
ls -la
/usr/bin/ls: /usr/bin/ls: cannot execute binary file
-c ls -la Output: ()
where as if I where to create a stack based array of arrays then pass it to the run function everything works fine and I get the directory listings

I used getenv in the split function and then passed the whole array to run this way every thing is in the right order
int split(char * str, const char * token , char *** arr)
{
int c = 2;
char * ptr = strtok(str , token);
(*arr)[0] = malloc(1024);
/* added this as the first element in the array */
strcpy((*arr)[0], getenv("SHELL"));
(*arr)[1] = malloc(1024);
/* added this as the second element in the array */
strcpy((*arr)[1], "-c");
while (ptr != NULL)
{
(*arr)[c] = malloc(1024);
strcpy((*arr)[c], ptr);
c++;
*arr = realloc(*arr, sizeof(char*)*(c+1));
ptr = strtok(NULL, token);
}
(*arr)[c] = NULL;
return c;
}
int run(char * args[])
{
int link[2];
pid_t pid;
char foo[4096];
if (pipe(link)==-1)
return -1;
if ((pid = fork()) == -1)
return -1;
if(pid == 0){
dup2 (link[1], STDOUT_FILENO);
close(link[0]);
close(link[1]);
/* /bin/bash to args[0] */
execvp(args[0], args);
} else {
close(link[1]);
int nbytes = read(link[0], foo, sizeof(foo));
printf("Output: (%.*s)\n", nbytes, foo);
wait(NULL);
}
return 0;
}

Related

C program that executes command fails with sleep

I wrote a program that read a bash file line by line and execute its command written inside.
It seems to execute standard command normally (albeit there is not output on the terminal), but when it reads the sleep command, there is an error:
sleep 3: missing operand
Try 'sleep 3 --help' for more information.
char** string_separator(char* string, const char a_delim)
{
char** result = 0;
size_t count = 0;
char* tmp = string;
char* last_dot = 0;
char delimiter[2];
delimiter[0] = a_delim;
delimiter[1] = 0;
while (*tmp)
{
if (a_delim == *tmp)
{
count++;
last_dot = tmp;
}
tmp++;
}
count += last_dot < (string + strlen(string) - 1);
count++;
result = malloc(sizeof(char*) * count);
if (result)
{
size_t idx = 0;
char* token = strtok(string,delimiter);
while (token)
{
assert(idx < count);
*(result + idx++) = strdup(token);
token = strtok(0, delimiter);
}
assert(idx == count - 1);
*(result + idx) = 0;
}
return result;
}
int main(int argc, char *argv[]) {
for (int i = 1; i < argc; i++) {
FILE * fp;
fp = fopen(argv[i], "r");
char buf[100];
int bytes_read = 0;
char * inputCopy = malloc(255 * sizeof(char));
const char delim[] = " ";
while (fgets(buf, sizeof buf, fp) != NULL) {
if (strstr(buffer, "#!/")) {
} else {
char ** strtoken = string_separator(buf, '\n');
char * firstWord = getFirstWord(buf, inputCopy, delim);
pid_t pid;
pid = fork();
if (pid == 0) {
execvpe(firstWord, strtoken, NULL);
exit(1);
} else {
int status;
waitpid(pid, & status, 0);
}
}
}
}
return 0;
}
Why is the program not working with the sleep command?
EDIT: Made the modification in the code such as function definition and indentation, I think the problem might be with the string_separator function, it might not separate the string into char array as expected.

Segmentation fault because of a recursive function

I am not very skilled in C programing. So when I'm writing this recursive function, I'm facing segmentation fault (core dumped).
int ispra(char* dir, char* proc, int num, int origin) {
FILE *newfile;
//printf("%s\n", dir);
char* s;
char* buff;
s = (char*) calloc(100, sizeof(char));
strcpy(s, dir);
strcat(s, "/");
strcat(s, proc);
strcat(s, "/status");
newfile = fopen(s, "r");
while(1) {
buff = (char*) calloc(100, sizeof(char));
int pid;
char* gran = (char*) calloc(10, sizeof(char));
int ppid;
fscanf(newfile, "%s", buff);
if (strcmp(buff, "Pid:") == 0) {
fscanf(newfile, "%d", &pid);
if (pid == num) {
return 1;
}
}
if (strcmp(buff, "PPid:") == 0) {
fscanf(newfile, "%s", gran);
ppid = atoi(gran);
if (ppid == origin) {
return 0;
}
int new = ispra(dir, gran, num, origin);
break;
}
free(buff);
}
free(s);
return -1;
}
So basically, the function reads from a given file and looks for a match with a variable num. It all works fine until I add a recursive call. Recursive call is for getting variable new.
When the recursive call is present, I get a fault and I have no idea how to make it work.

Issue with libreadline when completing an upper case directory

this is a piece of a shell that I'm creating. I'm having some trouble using libreadline, because when the shell gets loaded and I try to cd in a directory using autocompletion (so pressing TAB), after I press enter I access the directory but I get some strange output before another prompt is printed. I noticed that this happens only when the name of the directory starts with an upper case letter.
Example: "user:: ~ % cd Github" <-- written pressing tab to autocomplete Github
Next prompt is: "8b�/�user :: Github %"
I really cannot understand why, this is something really strange for me.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>
#include <readline/history.h>
#include <readline/readline.h>
#include "flush.h"
#include "env.h"
#include "fget.h"
char * flush_builtins[] =
{
"cd",
"help",
"exit",
"ver",
"fget"
};
int flush_num_builtins() {
return sizeof(flush_builtins) / sizeof(char *);
}
int (*flush_func[]) (char **) =
{
&flush_cd,
&help,
&exit_flush,
&ver,
&fget
};
static int flush_startp(char **args)
{
pid_t pid;
int status;
pid = fork();
if (pid == 0)
{
if (execvp(args[0], args) == -1)
{
fprintf(stderr, "flush: command not found\n");
}
exit(1);
}
else if (pid < 0)
{
fprintf(stderr, "flush: command not found\n");
}
else
{
do
{
waitpid(pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
return 1;
}
static int flush_exec(char **args)
{
int i;
if (args[0] == NULL)
{
return 1;
}
for (i = 0; i < flush_num_builtins(); i++)
{
if (strcmp(args[0], flush_builtins[i]) == 0) {
return (*flush_func[i])(args);
}
}
return flush_startp(args);
}
static char * flush_read(void)
{
fflush(stdout);
char *line_read = malloc(sizeof(char) * LINE_BUF);
char *prompt = malloc(sizeof(char) * LINE_BUF);
char *current, buffer[TOK_BUF];
current = getcwd(buffer, TOK_BUF);
strcat(prompt, get_user());
strcat(prompt, " :: ");
if (strcmp(current, get_home()) == 0)
{
strcat(prompt, "~");
}
else
{
strcat(prompt, get_cwd());
}
strcat(prompt, " % ");
line_read = readline(prompt);
if (line_read && *line_read)
{
add_history(line_read);
}
return line_read;
free(prompt);
free(line_read);
free(current);
}
static char **flush_getargs(char * line)
{
int bufsize = TOK_BUF;
int i = 0;
char **tokens = malloc(bufsize * sizeof(char *));
char **token;
if (!tokens)
{
fprintf(stderr, "allocation error\n");
exit(1);
}
token = strtok(line, DELIM);
while (token != NULL)
{
tokens[i] = token;
i++;
token = strtok(NULL, DELIM);
}
tokens[i] = NULL;
return tokens;
}
static void flush_loop(void)
{
char *line;
char **args;
int status;
do
{
line = flush_read();
args = flush_getargs(line);
status = flush_exec(args);
free(line);
free(args);
} while (status);
}
static void handler(int num)
{
signal(SIGINT, handler);
flush_loop();
fflush(stdout);
}
int main()
{
init();
signal(SIGINT, handler);
flush_loop();
return 0;
}
You can not use strcat with a non \0 terminated string:
char *prompt = malloc(sizeof(char) * LINE_BUF);
char *current, buffer[TOK_BUF];
current = getcwd(buffer, TOK_BUF);
strcat(prompt, get_user());
Use strcpy instead of strcat, or calloc instead of malloc

How to implement history function?

I am new to C programming and currently learning this into a course. I'm facing an issues while trying to practice the below history function.
I'm able to display the shell commands. However, when I type history, the past shell commands are not getting saved into the history buffer.
Can anyone help me to find where I went wrong?
Here is my code:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#define BUFSIZE 20
#define MAX_WORD_IN_LINE 20
int tokenize(char *str, char **args)
{
int i, argc = 0;
char *token;
token = strtok(str," \t\n");
for(i=0; token!=NULL;i++)
{
args[i] = token;
printf("args[%d] = %s\n", i, args[i]);
token = strtok(NULL, " \t\n");
argc++;
}
return argc;
}
void display_strings(char **p)
{
if (p == NULL) return;
while(*p != NULL){
printf("%s\n",*p);
p++;
}
}
int history(char *hist[], int current){
int i = current;
int hist_num = 1;
do {
if (hist[i]) {
printf("%4d %s\n", hist_num, hist[i]);
hist_num++;
}
i = (i + 1) % BUFSIZE;
} while (i != current);
return 0;
}
int main(void){
char *args[MAX_WORD_IN_LINE];
char buffer[BUFSIZE];
char *hist[BUFSIZE];
int i,current=0;
pid_t pid;
int argc;
for(i=0;i<BUFSIZE;i++)
hist[i]= NULL;
while(1) {
memset(args,0,MAX_WORD_IN_LINE);
printf("osh> ");
fgets(buffer, BUFSIZE, stdin);
argc = tokenize(buffer, args);
//display_strings(args);
// skip on empty command
if (argc == 0) continue;
if (strcmp(args[0],"quit") == 0) break;
else if (strcmp(args[0], "hello") == 0) printf("Hello there. How are you?\n");
else if (strcmp(args[0],"history")==0) history(hist,current);
else {
pid = fork();
if (pid == 0) {
hist[current]=strdup(args[0]);
current++;
execvp(args[0], args);
return 0;
}
You need to make a copy of the string that args[0] points to when you save it in hist. Currently, you're just assigning the pointer to the current args[0], and it will be overwritten by the next command. When you print the history, you'll just get the last command repeatedly. So use:
hist[current] = strdup(args[0]);

Can't solve Segmentation fault (core dumped) in c

we are writing a program that has to mimic the Linux shell.
It is consisted of some parts. Every part does what the previous part did, plus something extra.
Part one runs single commands like ls, pwd etc. No parameters no
redirection.
Part two runs single commands plus redirection.
This is where we are stuck.. We compile myShell 2 without errors. But when we are typing any command we get the segmentation fault.
We're sorry for writing almost all out code here(myShell 1 not included), we just wanted to make sure you have everything you need to tell us where we are wrong.
functions.h
#ifndef FUNCTIONS_H
#define FUNCTIONS_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#define max 260
extern int argCounter(char *argLine,char *delim);
extern void myExec(char *argLine,int howMany,char *delim);
extern char **argArray(char* argLine,int howMany,char *args[],char *delim);
void myExecRedir(char **tokens,char* argLine, int howMany);
#endif
functions.c
#include "functions.h"
int argCounter(char *argLine,char *delim)
{
char *temp;
memcpy(temp,argLine,strlen(argLine));
char *pointer = strtok(temp, delim);
int counter = 0;
while (pointer != NULL)
{
counter++;
pointer = strtok(NULL, delim);
}
return counter;
}
void myExec(char *argLine,int howMany,char *delim)
{
char temp[max];
memset(temp,0,max);
char *args[howMany];
argLine[strlen(argLine)-1] = '\0';
//argArray breaks the argLine and returns an array that contains each argument
argArray(argLine,howMany,args,delim);
execvp(args[0],args);
perror("ERROR: wrong command!");
}
char **argArray(char *argLine,int howMany,char *args[],char *delim)
{
args[howMany] = NULL;
char *pointer = strtok(argLine, delim);
int counter = 0;
while (pointer != NULL)
{
args[counter]=pointer;
counter++;
pointer = strtok(NULL, delim);
}
return args;
}
void myExecRedir(char **tokens,char* argLine, int howMany)
{
char *delim3="<";
char *delim4=">";
int howManyIn= argCounter(argLine,delim3);
int howManyOut= argCounter(argLine,delim4);
if(howManyOut= howManyIn)
{
int fdin = open(tokens[1],O_RDWR);
int fdout = open(tokens[2],O_RDWR);
if(dup2(fdin,fdout) >= 0)
{
tokens[1]=NULL;
tokens[2]=NULL;
execvp(tokens[0],tokens);
}
else
{
printf("ERROR in dup2\n");
}
}
else if(howManyIn== 0) //means we only have > redirection
{
int fdout = open(tokens[1],O_RDWR);
if(dup2(2,fdout) >= 0)
{
tokens[2]=NULL;
execvp(tokens[0],tokens);
}
else
{
printf("ERROR in dup2\n");
}
}
else //means we only have redirection
{
int fdin = open(tokens[1],O_RDWR);
if(dup2(fdin,1) >= 0)
{
tokens[2]=NULL;
execvp(tokens[0],tokens);
}
else
{
printf("ERROR in dup2\n");
}
}
}
myShell2.c
#include "functions.h"
int main()
{
printf("myshell2>");
pid_t pid,waitPid;
//WE TRIED WITHOU ALLOCATING MEMORY AS WELL
char *argLine = (char *)malloc(max);
char **args = (char **)malloc(max);
char **args2 =( char **)malloc(max);
char **temp = (char **)malloc(max);
char *delim="><";
char *delim2=" ";
int i,howMany,howMany2,status;
while(fgets(argLine,max,stdin) != NULL)
{
howMany= argCounter(argLine,delim);//howMany redirections
args=argArray(argLine,howMany,args,delim);
if (howMany == 1)//means we are at myShell 1
{
howMany2= argCounter(argLine,delim2);
if(howMany2 ==1)//checking if the command has any parameters (like ls -l)
{
printf("myshell2>");
pid = fork();
if (pid < 0)
{
perror("ERROR: Fork failed.\n");
return -1;
}
else if(pid == 0)
{
myExec(args[0],howMany2,delim2);
perror("ERROR: Child should never arrive here.\n");
}
else
{
waitPid = wait(&status);
if (waitPid == -1)
{
perror("ERROR: Waitpid failed.\n");
return -1;
}
}
}
else
{
printf("ERROR: Wrong number of Arguments!\n");//can't run on myshell 2
return(0);
}
}
//means we have redirection (< or >)
for (i=0; i<howMany; i++)
{
argArray(args[i],2,args2,delim2);//args2 contains the tokens without spaces(delim2)
temp[i] = args2[0];
howMany2 = argCounter(args[i],delim2);
if(howMany2 > 1) // eg. ls -l should not run here
{
printf("ERROR: Wrong number of Arguments!\n");//myShell3 should be running this
return(0);
}
}
printf("myshell2>");
pid = fork();
if (pid < 0)
{
perror("ERROR: Fork failed.\n");
return -1;
}
else if(pid == 0)
{
myExecRedir(temp,argLine,howMany);
perror("ERROR: Child should never arrive here.\n");
}
else
{
waitPid = wait(&status);
if (waitPid == -1)
{
perror("ERROR: Waitpid failed.\n");
return -1;
}
}
}
}
Thank you in advance.
char *temp;
memcpy(temp,argLine,strlen(argLine));
char *pointer = strtok(temp, delim);
...
is wrong, you need to reserve space with malloc:
size_t len = strlen(argline);
char *temp = malloc(len + 1);
if (temp == NULL) return 0;
memcpy(temp, argLine, len);
/* NUL-terminate the string */
temp[len] = '\0';
char *pointer = strtok(temp, delim);
...
free(temp);
or you can use the non-standard function (but available on many implementations) strdup:
char *temp = strdup(argline);
if (temp == NULL) return 0;
char *pointer = strtok(temp, delim);
...
free(temp);
char **args = (char **)malloc(max);
is also wrong, if you need to reserve space for n pointers to char use:
char **args = malloc(sizeof(*args) * n); /* don't cast malloc */
or
char **args = malloc(sizeof(char *) * n);

Resources