sizeof(table)/sizof(table[0]) not working - c

H i am building a basic shell in c and i need to know the size of the array i am populating with user input. Here is the code.
/*
* Tp1.c
*
* Created on: 25 janv. 2014
* Author: shong
*/
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
void cd_handler(int argc, char *argv[]);
int lire(char *chaine, int longueur);
char** init_command(char *str);
int main(int argc, char *argv[]) {
//printf("La valeur de argc est: %d", argc);
while(1){
printf("Log710H2014%>");
char str[200];
lire(str, 200);
char** comms = init_command(str);
printf("%s", comms[1]);
if(strcmp(comms[0], "cd") == 0){
int commArgsC = sizeof(comms)/sizeof(comms[0]);
cd_handler(commArgsC, comms);
}else if (strcmp(comms[0], "exit") == 0){
exit(0);
}
}
}
}
void cd_handler(int argc, char *argv[]){
char cwd[256];
char * directory;
if(argc < 2){
directory = getenv("HOME");
}else if (argc == 2){
directory = argv[1];
}else{
exit(1);
}
if (chdir(directory) == -1) {
printf ("chdir failed - %s\n", strerror (errno));
}else{
if (getcwd(cwd, sizeof(cwd)) == NULL)
perror("getcwd() error");
else
printf("current working directory is: %s\n", cwd);
}
}
char** init_command(char* str){
char ** res = NULL;
char * p = strtok (str, " ");
int n_spaces = 0, i;
while (p) {
res = realloc (res, sizeof (char*) * ++n_spaces);
if (res == NULL){
exit (-1);
}
res[n_spaces-1] = p;
p = strtok (NULL, " ");
}
res = realloc (res, sizeof (char*) * (n_spaces+1));
res[n_spaces] = 0;
//print the result
//for (i = 0; i < (n_spaces+1); ++i)
//printf ("res[%d] = %s\n", i, res[i]);
//free the memory allocated
//free (res);
return res;
}
int lire(char *chaine, int longueur)
{
char *positionEntree = NULL;
if (fgets(chaine, longueur, stdin) != NULL)
{
positionEntree = strchr(chaine, '\n');
if (positionEntree != NULL)
{
//*positionEntree = '\0'; // On remplace ce caractère par \0
}
return 1;
}
else
{
return 0; // on renvoie 0 s'il y a eu une erreur
}
}
The problem is that sizeof(comms) always return 8, no matter the number of elements in comm.

comms is a pointer, so on a 64-bit machine it will have a size of 8 bytes. C has no knowledge about the size of what it points to. You'll have to return the size from the function that allocates the storage and keep track of it yourself.

The behavior sizeof is dependent on what type of variable it is applied to.
If the variable is a pointer, as in the question, sizeof simply evaluates to the size of the pointer type in bytes:
int *y; //y points to an int... maybe an array? Who knows?
printf("%d",sizeof(y)); //No idea how y has been allocated. Defaults to sizeof(int*)
If the variable was declared as an array, sizeof returns the size of the entire array. For instance:
int y[4]; //y is exactly four ints in memory
printf("%d",sizeof(y)); //sizeof knows this, and evaluates to sizeof(int)*4
This is why the sizeof(table)/sizeof(table[0]) would work for an array. However, it does not work pointers as demonstrated above. In short, passing an array as an argument destroys any information regarding how much data is in that array, and you must pass the size separately. This is referred to as "array decay."
The difference between pointers and arrays is very subtle. Most of the time, the two can be used interchangeably, but there are two critical differences:
The difference in the behavior of sizeof as discussed previously.
Arrays cannot be assigned as pointers can. This relates to the fact that they are of constant size. For instance:
char **table;
//table can be assigned different values....
table = NULL;
//...multiples times, if wanted
table = malloc(sizeof(char*)*20);
However,
//table is constant
char *table[20];
//it's individual elements can be assigned as usual...
table[0] = malloc(1);
//...but attempts to change where table points to will fail
table = NULL; //This will cause a compilation error.

Related

Fill a dynamic 2D array with words of multiples text files in C

with this C code I want to fill a dynamic array of char with the words parsed in each differents text files returned by the readdir function.
#include <string.h>
#include <malloc.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
char *getWord(FILE *fp){
char word[100];
int ch, i=0;
while(EOF!=(ch=fgetc(fp)) && !isalpha(ch))
;//skip
if(ch == EOF)
return NULL;
do{
word[i++] = tolower(ch);
}while(EOF!=(ch=fgetc(fp)) && isalpha(ch));
word[i]='\0';
return strdup(word);
}
int readWords(const char * fn,char ** arr){
char *word;
FILE * fp;
int size=0;
//read first time to get the number of words
fp=fopen(fn,"r");
while(word=getWord(fp)){
size++;
}
fclose(fp);
printf("size:%d\n",size);
arr = (char **) malloc (size * sizeof (char *));
//reopen to insert in dynamic array
fp=fopen(fn,"r");
for (int i = 0; i < size; i++){
arr[i] = (char *) malloc (100 * sizeof (char));
word=getWord(fp);
// printf("%s",word);
strcpy(arr[i],word);
}
fclose(fp);
return size;
}
int main(int narg, char *arg[] )
{
struct dirent *de; // Pointer for directory entry
char absdir[256];
char absfn[256];
FILE * F;
int s;
// opendir() returns a pointer of DIR type.
DIR *dr = opendir(arg[1]);
strcpy(absdir,arg[1]);
strcat(absdir,"/");
if (dr == NULL) // opendir returns NULL if couldn't open directory
{
printf("Could not open current directory" );
return 0;
}
while ((de = readdir(dr)) != NULL){
if (de->d_type!=4){ // is file?
strcpy(absfn,absdir);
strcat(absfn,de->d_name);
printf("abs_fn:%s \n",absfn);
char** arr_words;
s=readWords(absfn,&arr_words);
printf("%s number of words:%d\n", absfn,s);
for (int i=0;i<s;i++)
printf("word %d:%s\n",i,arr_words[i]);
//free 2D array
for(int i=0;i<s;i++)
free(arr_words[i]);
free(arr_words);
}
}
closedir(dr);
return 0;
}
the words are well parsed with getWord but i can't find the solution to fill the char **arr passed in argument and to use it latter in the main function.
I want to allocated the char **arr_words dynamically with the following form
arr_words[0]=First word of file text
arr_words[1]=Second word
arr_words[s-1]=Last Word
the Readword function return the number of words read in each files.
thanks
Your bug is a more advanced one of the problem described here: Dynamic memory access only works inside function
arr is a local variable. It's a pointer to the first pointer in an array of pointers. In order to return this to main (hang on tight now), your function need to pass a pointer to a pointer to the first pointer in an array of pointers...
Or in plain C, the dreaded three-star programming, char***. But this is actually a valid use for it, about the only valid use of three levels of indirection that exists.
Alternatively, you can just use the return for the char** and skip this extra level of indirection, which might be more readable.
Which form to use is a bit subjective, but you need to change your function to one of these:
int readWords(const char * fn, char*** arr)
{
...
*arr = malloc (size * sizeof (char *))
...
}
or
char** readWords(const char * fn, int* wordsRead)
{
...
arr = malloc (size * sizeof (char *));
...
*wordsRead = size;
return arr;
}
#include <string.h>
#include <malloc.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
char *getWord(FILE *fp){
char word[100];
int ch, i=0;
while(EOF!=(ch=fgetc(fp)) && !isalpha(ch))
;//skip
if(ch == EOF)
return NULL;
do{
word[i++] = tolower(ch);
}while(EOF!=(ch=fgetc(fp)) && isalpha(ch));
word[i]='\0';
return strdup(word);
}
char** readWords(const char * fn,int *wordsRead){
char *word;
char**arr;
FILE * fp;
int size=0;
//read first time to get the number of words
fp=fopen(fn,"r");
while(word=getWord(fp)){
size++;
}
fclose(fp);
printf("size:%d\n",size);
arr = malloc (size * sizeof (char *));
//reopen to insert in dynamic array
fp=fopen(fn,"r");
for (int i = 0; i < size; i++){
arr[i] = (char *) malloc (100 * sizeof (char));
word=getWord(fp);
// printf("%s",word);
strcpy(arr[i],word);
}
fclose(fp);
*wordsRead = size;
return arr;
}
int main(int narg, char *arg[] )
{
struct dirent *de; // Pointer for directory entry
char absdir[256];
char absfn[256];
FILE * F;
int s;
int wordsRead;
char buffer[100];
// opendir() returns a pointer of DIR type.
DIR *dr = opendir(arg[1]);
strcpy(absdir,arg[1]);
strcat(absdir,"/");
if (dr == NULL) // opendir returns NULL if couldn't open directory
{
printf("Could not open current directory" );
return 0;
}
while ((de = readdir(dr)) != NULL){
if (de->d_type!=4){ // is file?
strcpy(absfn,absdir);
strcat(absfn,de->d_name);
printf("abs_fn:%s \n",absfn);
char **arr_words;
arr_words=readWords(absfn,&wordsRead);
printf("%s number of words:%d\n", absfn,wordsRead);
for (int i=0;i<wordsRead;i++){
printf("word %d:%s\n",i,arr_words[i]);
}
//free array of pointers
for(int i=0;i<s;i++)
free(arr_words[i]);
free(arr_words);
}
}
closedir(dr);
return 0;
}

C: Losing strings when exiting functions

I'm trying to implement a basic shell, I have several functions within it that deal with strings, trying to find file names, implement something equivalent to *argv[] and so on.
I have strings in main(), which are passed to a function to be populated. Next the program returns to main(), which passes the strings to another function to be acted upon.
I was debugging with lldb and found that I was successfully populating the strings with the correct values in the first function but upon exiting the function, re-entering main() the output_str string was NULL again. I thought strings, since they point to space in memory would retain values. They seem to for all but one case, when flag = 1 in the code below.
I can't figure out what's happening as the values seem to only be lost after the final } of the function.
Edited to add complete code, hope it isn't too large.
The code works with say, cat input.txt but not with cat input.txt>output.txt when I try to redirect the output from stdout to a file
Thank you for your help in advance.
Here is the function .c file:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
void sig_handler(int signo)
{
if (signo == SIGINT)
{
fprintf(stdout, "\n");
fflush(stdout);
}
}
int check_redirect(char *line, int flag)
{
int n = 0;
if (line == NULL) return (flag);
else
{
do
{
if (line[n] == '>') flag = 1;
n++;
}while (line[n] != '\0');
}
return (flag);
}
void string_breakdown(char *line, char **output_str, int count, char* temp, char *filename, int *f, int *saved_stdout, int flag, int debug)
{
char *sep = " \n";
char *delim = ">\n";
if (line != NULL)
{
temp = strtok(line, delim);
while (temp != NULL)
{
output_str[count] = temp;
if (debug) fprintf(stderr, "1:%s\n2:%s\n3:%s\n", line, temp, output_str[count]);
count++;
output_str = realloc (output_str, (count + 1) * sizeof (char *) );
temp = strtok(NULL, delim);
}
if (flag)
{
count = 0;
strcpy(filename, output_str[1]);
output_str[1] = NULL;
*saved_stdout = dup(1);
*f = open(filename , O_WRONLY|O_CREAT|O_TRUNC, 0666);
dup2(*f, 1);
temp = strtok(*output_str[0], sep);
while (temp != NULL)
{
output_str[count] = temp;
//if (debug) fprintf(stderr, "1:%s\n2:%s\n3:%s\n", line, temp, output_str[count]);
count++;
output_str = realloc (output_str, (count + 1) * sizeof (char *));
temp = strtok(NULL, sep);
}
}
else
{
count = 0;
temp = strtok(line, sep);
while (temp != NULL)
{
output_str[count] = temp;
if (debug) fprintf(stderr, "1:%s\n2:%s\n3:%s\n", line, temp, output_str[count]);
count++;
output_str = realloc (output_str, (count + 1) * sizeof (char *));
temp = strtok(NULL, sep);
}
}
}
}
void com_exec(char *line, char **output_str, char *filename, int *f, int *saved_stdout, int flag, int debug)
{
char *command = malloc(sizeof(char *));
command = output_str[0];
char *name = "HOME";
int ret_val = 0;
pid_t child_pid;
int child_status;
if (command == NULL);
else if (strcmp("cd", command) == 0)
{
if (output_str[1] == NULL) output_str[1] = getenv(name);
ret_val = 0;
ret_val = chdir(output_str[1]);
if (ret_val) perror(NULL);
}
else
{
child_pid = fork ();
if (child_pid == 0)
{
if (debug)
{
system(line);
fprintf(stderr, "Post System Pre Exec\n1:%s\n2:%s\n3:%s\n", line, output_str[0], command);
sleep(2);
}
execvp(command, output_str);
if (flag)
{
close(*f);
dup2(*saved_stdout, 1);
close(*saved_stdout);
}
fprintf (stdout, "Unknown command\n");
exit (0);
}
else
{
if (flag)
{
close(*f);
dup2(*saved_stdout, 1);
close(*saved_stdout);
}
signal(SIGINT, sig_handler);
usleep(500000);
//Parent process waits for child to finish
if (debug) fprintf (stderr, "parent waiting\n");
wait(&child_status);
waitpid(child_pid, &child_status, 0);
signal(SIGINT, SIG_DFL);
}
}
Here is the functions .h file:
#ifndef SHELL_H_INCLUDED
#define SHELL_H_INCLUDED
void sig_handler(int signo);
int prompt(char *line, size_t len, ssize_t read);
int check_redirect(char *line, int flag);
void string_breakdown(char *line, char **output_str, int count, char* temp, char *filename, int *f, int *saved_stdout, int flag, int debug);
void com_exec(char *line, char **output_str, char *filename, int *f, int *saved_stdout, int flag, int debug);
#endif // LINKLAYER_H_INCLUDED
Below is main.c, where the function is called.
#include <unistd.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include "shell.h"
int main(void)
{
int debug = 0;
char *line = NULL;
size_t len = 0;
ssize_t read = 0;
int flag = 0;
int f = 0;
int saved_stdout = 0;
do
{
flag = 0;
//read = prompt(line, len, read);
char buffer[15];
time_t now = time(NULL);
strftime(buffer, 15, "[%d/%m %H:%M]", localtime(&now) );
fprintf(stdout, "%s # ", buffer);
signal(SIGINT, SIG_IGN);
read = getline (&line, &len, stdin);
signal(SIGINT, SIG_DFL);
flag = check_redirect(line, flag);
char **output_str = malloc(sizeof(char *));
int count = 0;
char* temp = NULL;
char *filename = malloc(sizeof(char *));
string_breakdown(line, output_str, count, temp, filename, &f, &saved_stdout, flag, debug); // function call of problem function
com_exec(line, output_str, filename, &f, &saved_stdout, flag, debug);
} while (read != EOF);
if (debug) fprintf(stderr, "parent exiting\n");
else fprintf(stdout, "\n");
return 0;
}
output_str = realloc (output_str, (count + 1) * sizeof (char *) );
This line re-assigns the value of the the local parameter variable output_str, but the new value in no way makes it back to the caller of the string_breakdown function - meaning that the pointer it has will probably be left dangling, and will cause problems when used ("undefined behavior", manifesting in strange program behavior or crashing).
You need to understand that within the function, output_str is a local variable. You can change its value, but that won't affect the value of any variable in the caller.
You call the function from main:
string_breakdown(line, output_str, count, temp, filename, &f, &saved_stdout, flag, debug); // The call of the above function
main also uses output_str as the variable name, but again, this is a different variable. One variable is local to main, the other is local to string_breakdown, even though they share the same name. Due to the realloc call above, the pointer value in main's output_str will most likely be invalid on return from string_breakdown, because it is not updated to point to the newly allocated memory. That's why you are "losing" the string values on return from the function - the output_str variable in main is no longer actually pointing to the array of strings, which has been moved to a different location via realloc.
Typically you resolve this kind of problem by adding another level of indirection, changing the output_str parameter from a char ** to a char ***:
void string_breakdown(char *line, char ***output_str, int count, char* temp, char *filename, int *f, int *saved_stdout, int flag, int debug)
and
(*output_str)[count] = temp;
and
*output_str = realloc (*output_str, (count + 1) * sizeof (char *) );
and so on. You need to adjust the call in main as well:
string_breakdown(line, &output_str, count, temp, filename, &f, &saved_stdout, flag, debug); // The call of the above function
Because you are passing a pointer to main's output_str variable, the called function is now able to modify its value.
You should also understand that string_breakdown as written modifies the string which the line parameter points to. That's because it uses strtok, and strtok replaces delimiters with nul bytes as it processes the string. So, it is odd that you pass this modified line buffer to com_exec after processing it with string_breakdown.
I get several warnings when I try to compile your code; main.c uses fprintf but doesn't #include <stdio.h>, and uses malloc but doesn't #include <stdlib.h>.
your realloc does nothing.
you mean *output_ptr = realloc....
actually it does something, but its really bad
this is also wrong
output_str[count] = temp;
and this
filename = output_str[1];
you need to distinguish - a pointer to your buffer, a pointer to the pointer to your buffer.
char * buffer = *output_str; // to remove the confusion
strcpy(&buffer[count], temp); // assigning pointers doesnt copy things
filename = buffer[1]; // is hat what you mean - filename is one char

Seg fault when printing array after passing to function, please explain behaviour

So i am attempting to pass a string array (char** arguments) to a function, fill the array with values and then print those values after returning from the function. The problem occurs when I try to print the first value of "arguments" which gives me a segmentation fault. Why is this? when I print the values in the "getArguments" function all goes as expected. I am new to C and yes this is an assignment. I am not looking for you to write this code for me however I would like an explanation of this behaviour as I try to understand this concept.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define BUFFERSIZE 81
int getArguments(char** arguments, char* argument);
void getPath(char* pathBuffer);
int checkForDirectoryChange(char **arguments, int num_args);
int main(int argc, char *argv[]){
char * command;
char ** arguments = NULL;
char * cd_path;
int len, pid, ret_code, cd_requested = 1;
char buffer[BUFFERSIZE];
/* Get user input and the first token */
printf("Enter a command: > ");
command = fgets(buffer,BUFFERSIZE,stdin);
printf("The command entered was %s",buffer);
len = strlen(buffer);
if(buffer[len-1] == '\n')
buffer[len-1]='\0';
cd_requested = getArguments(arguments, command);
printf("The argument passed is now: %s\n", arguments[0]);
if(cd_requested == 0){
fprintf(stdout,"Change directory requested.\n");
}
/*
char * pathBuf;
getPath(pathBuf);
free the memory allocated */
/*
pid = fork();
if(pid){
wait(NULL);
}else{
ret_code = execvp(*arguments, arguments);
if(ret_code){
printf("The fork failed, exiting.");
exit(0);
}
}*/
}
int getArguments(char** arguments, char* command){
int n_spaces = 0,i;
char *token;
token = strtok(command, " ");
/* Loop until we have gotten all of the tokens */
while (token) {
arguments = realloc (arguments, sizeof (char*) * ++n_spaces);
if (arguments == NULL){
printf("Memory allocation failed: token - %d\n", n_spaces);
exit (-1); /* memory allocation failed */
}
arguments[n_spaces-1] = token;
token = strtok (NULL, " ");
}
/* realloc one extra element for the last NULL */
arguments = realloc (arguments, sizeof (char*) * (n_spaces+1));
arguments[n_spaces] = 0;
/* print the result */
for (i = 0; i < (n_spaces+1); ++i)
printf ("arguments[%d] = %s\n", i, arguments[i]);
return strcmp("cd",arguments[0]);
}
int checkForDirectoryChange(char** arguments, int num_args){
return 0;
}
void getPath(char* pathBuffer){
size_t n;
n = confstr(_CS_PATH, NULL, (size_t) 0);
pathBuffer = malloc(n);
if (pathBuffer == NULL)
abort();
confstr(_CS_PATH, pathBuffer, n);
}
It is because getArguments() only reassigned the copy of pointer to pointer of characters inside itself. arguments in main() was not updated.
You should define getArguments() as
int getArguments(char*** arguments, char* command) {
/* ... */
while (token) {
*arguments = realloc (*arguments, sizeof (char*) * ++n_spaces);
if (*arguments == NULL){
printf("Memory allocation failed: token - %d\n", n_spaces);
exit (-1); /* memory allocation failed */
}
(*arguments)[n_spaces-1] = token;
token = strtok (NULL, " ");
}
/* ... */
}
And call it as the following inside main().
cd_requested = getArguments(&arguments, command);

Char * that points to an array of chars = "ls\0" does not equal the string equivalent "ls\0"

I'm writing a shell program, and when passing values into execv() I need a char pointer to the name of the program (which is ls), and I need a pointer to an array of arrays of char pointers for the arguments.
I go through and parse the user's input, and if I type in ls, if I print my char *, printf() prints out "ls". So I am parsing the line correctly and storing the correct information. When I pass this to execv(), it says bad path name, but if I change my pointer manually to progs[0] = "ls", then it works. If I compare the two strings, strcmp(mypointer, "ls"), it shows that although mypointer prints out "ls", it is not equivalent to "ls".
Does anyone know why?
Here is my shell process code:
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
char **progs;
char ***arguments;
char **mode;
char pathname[] = "/bin/";
int main(int argc, char ** argv){
//printf("\n %s \n", progs);
//fflush(stdout);
char buff[100];
FILE *p;
p = fdopen(0, "r"); //opens FD 0 (Stdin) as a stream
char * pathname;
while(1){
//printf("I'm at the top");
if(isatty(1) == 1){ //check to see if stdout is going to the terminal
printstart(); //if so, print
}
fgets(buff, 100, p); // Gets the input from the stdin and puts it in buff
int processid = fork(); //fork into child to complete task
if(processid == 0){
//initialize all variables
int numcmd = countcmd(buff);
int pipes = countpipes(buff);
int i;
int j;
//allocate memory for tokenization
progs = malloc(numcmd * sizeof(char *));
arguments = malloc((numcmd) * sizeof(char *));
mode = malloc((numcmd*2) * sizeof(char *));
for(i = 0; i < numcmd; i++){
progs[i] = malloc(10* sizeof(char *));
mode[i] = malloc(10 * sizeof(char *));
mode[2*numcmd-1-i] = malloc(10 * sizeof(char*));
arguments[i] = malloc(15 * sizeof(char *));
for(j = 0; j < 15; j++){
arguments[i][j] = malloc(15 * sizeof(char*));
}
}
/////////////////////////////////////////////////////////////////////
parse(buff); //parses input and places it in the static progs
for(i = 0; i < 1; i++){
printf("\n This is progs %s", arguments[0][0]);
char temp[25];
//strcpy(temp, "/bin/");
strcpy(temp, progs[0]);
//strcat(temp, ' \0');
//*progs = "ls";
char * ptr = progs[0];
for(;*ptr != '\0';){
printf("This is what pointer poitns to %c \n", *ptr++);
}
printf("This is the program: <<%s>>", progs[0]);
fflush(stdout);
char * argument[2];
argument[0] = "ls";
argument[1] = '\0';
char * hell = "l\0";
printf("This is the value of comparison %d\n", strcmp(progs[0], hell));
char **temparg = arguments[0];
//char temp[20] = progs[0];
errno = 0;
execvp("ls", *argument);
char * error = strerror(errno);
printf("This is the error %s", error);
return;
}
}
else{
int status;
waitpid(processid, &status, WIFEXITED(status));
}
}
return 0;
}
Here is my parse() code:
#include <string.h>
#include <stdio.h>
#include "myshell.h"
int parse(char * buff){
//Initialize all variables and pointers
int cmd = 0;
int argument = 0;
int mod = 0;
int j = 0;
int hitargs = 0;
int gotcommand = 0;
int multiarg = 0;
char ** argptr = arguments[cmd];
char * ptr1 = progs[cmd];
char * argptr2 = argptr[argument];
char * ptr2 = mode[mod];
while(buff[j] != '\0'){
switch(buff[j]){
case ';':
cmd++;
argument = 0;
multiarg = 1;
*argptr2++ = '\0';
argptr = arguments[cmd];
argptr2 = argptr[argument];
ptr1 = progs[cmd];
*ptr2 = buff[j];
mod += 2;
ptr2 = mode[mod];
case ' ':
if(gotcommand == 0){
break;
}
else{
if(hitargs == 0){
hitargs = 1;
*ptr1++ = '\0';
argument++;
argptr2 = argptr[argument];
}
else{
argument++;
argptr2 = argptr[argument];
}
break;
}
default:
if(gotcommand == 0){
*ptr1++ = (char) buff[j];
*argptr2++ = buff[j];
gotcommand = 1;
}
else if(gotcommand == 1 && hitargs == 0){
*ptr1++ = (char) buff[j];
*argptr2++ = buff[j];
}
else if(gotcommand == 1 && hitargs == 1){
*argptr2++ = buff[j];
}
}
j++;
}
*argptr2++ = '\0';
*ptr1++ = '\0';
int cmdflag = 0;
int spaceflag = 0;
int argflag = 0;
int cmdct = 1; //account for null
int argumentct = 1; //account for null termination
return 1;
}
Sorry about the random printf statements.
Your main program should include myshell.h like your parsing code does.
It would be helpful to have the missing functions (such as countcmd() and countpipes() — it isn't clear precisely what those do, though we can guess a bit from their names).
You should have include <unistd.h> in your main program.
You should have been getting warnings about undeclared functions (such as fork()), and you should be heeding and fixing those warnings.
If you were not getting those warnings, you need to add more warning options to your compilation.
If you use gcc, using -Wall is a good starting point.
With the warnings enabled, you get to see:
shex.c:95: warning: passing argument 2 of ‘execvp’ from incompatible pointer type
shex.c:99: warning: ‘return’ with no value, in function returning non-void
The latter is best handled via return EXIT_FAILURE; or exit(EXIT_FAILURE);.
The former is triggered by execvp("ls", *argument);.
It might be better to use a plural word (arguments?) for something that contains many arguments.
...Oh, I see, there is a global variable arguments.
You declare char ***arguments;. Ouch! I have used a triple pointer occasionally, but only very occasionally. That's one too many levels of pointer most of the time, and especially for this exercise.
Then there's also a local variable char *argument[2];.
The correct call is, therefore, execvp("ls", argument);.
I would immediately expand on the waiting code to at least print the information:
else
{
int status;
int corpse = waitpid(processid, &status, WIFEXITED(status));
printf("Command exited: PID = %d; status = 0x%.4X\n", corpse, status);
}
In your parsing code, the case ';': drops through into the case ' ':. If that's intentional, document it (with a comment such as /* DROP THROUGH */); if not, insert the missing break. Your default: case should probably have a break after it too. It isn't quite crucial, but it is conventional and protects you from drop through if a new case needs to be handled.
The string of five variables declared at the end of parse() are superfluous; they're local variables that are never used (as the compiler tells you).
At the top of the main() you do:
FILE *p;
p = fdopen(0, "r"); //opens FD 0 (Stdin) as a stream
This is not necessary; stdin is already open as a stream.
You read from p (aka stdin) with fgets(). On the whole, this is good (better than gets()), but you need to be aware that fgets() includes a newline which gets() does not, and you need to check the return status (you might not read anything). You should also use sizeof():
if (fgets(buff, sizeof(buff), stdin) == 0)
...error - read failed...
The memory allocation is extraordinarily complex. For a fixed size input like char buff[100];, you can afford to use non-dynamic allocation. There can't be more than about 50 arguments total, since they're blank separated.
Etc.
not looked into the entire code so there may be other bugs but ith regard to the 'ls' issue you need to add the following to the parse code :
while (buff[j] != '\0' && buff[j] != '\n')
this ensures you don't add 'newline' to the command pointer at *ptr1++ = (char) buff[j];
Note fgets if it encounters new line would stop reading but would include it as part of the string.

C realloc inside a function

Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
void mp3files(char** result, int* count, const char* path) {
struct dirent *entry;
DIR *dp;
dp = opendir(path);
if (dp == NULL) {
printf("Error, directory or file \"%s\" not found.\n", path);
return;
}
while ((entry = readdir(dp))) {
if ((result = (char**) realloc(result, sizeof (char*) * ((*count) + 1))) == NULL) {
printf("error");
return;
}
result[*count] = entry->d_name;
(*count)++;
}
closedir(dp);
}
int main() {
int* integer = malloc(sizeof (int));
*integer = 0;
char** mp3FilesResult = malloc(sizeof (char*));
mp3files(mp3FilesResult, integer, ".");
for (int i = 0; i < *integer; i++) {
printf("ok, count: %d \n", *integer);
printf("%s\n", mp3FilesResult[i]);
}
return (EXIT_SUCCESS);
}
It gives me segmentation fault. However, when I put this loop:
for (int i = 0; i < *integer; i++) {
printf("ok, count: %d \n", *integer);
printf("%s\n", mp3FilesResult[i]);
}
in the end of mp3files function, it works. And when I change the third parameter of mp3files function from "." to a directory which contains less then 4 files or directories, it works great. In other words, when variable mp3FilesResult points at less then 4 strings, it doesn't fail with segmentation fault.
Why does it keep doing it ?
Thanks in advance and sorry for my english.
You pass in a char **, a pointer to a pointer to char, which is representing pointer to "string" which is representing "array of string". If you want to reallocate that array you must pass it by reference (pass a pointer to it) so you need a "pointer to array of string", or a char ***:
... myfunc(char ***result, ...)
{
*result = realloc(*result, ...); // writing *result changes caller's pointer
}
...
char **data = ...;
myfunc(&data, ...);

Resources