Reputation: 1063
I am trying to implement a custom shell on "batch-file mode". Let's say I have a file named "batchfile" containing:
ls –l
pwd
ps
touch hello
ls -l ; cat file ; grep foo file2
ls -l && cat file
quit
Calling ./myShell batchfile
, I want to execute the commands separately. But when I try to read the lines from the file using fgets()
and then store them to an array (char * batch_cmds[512]
) I get :
Segmentation fault (core dumped)
This is my code so far:
int main(int argc, char *argv[]){
if (argc >=2){
char str[512];
char *batch_cmds[512];
int i=0;
FILE *fp;
fp = fopen(argv[1], "r");
while(fgets(str,512, fp)!=NULL){
strcpy(batch_cmds[i], str);
i++;
}
fclose(fp);
I can 't understand why this error pops.
Upvotes: 2
Views: 861
Reputation: 35154
You have reserved space for the pointers themselves, but not for each string to which each such pointer shall point to. Each batch_cmds[i]
points "somewhere", at least not to an object you allocated; when you then call strcpy(batch_cmds[i], str);
, you write the contents of str
to "somewhere" and produce undefined behaviour (e.g. a crash; actually statement batch_cmds[i]
accesses an uninitialized array and is for itself already undefined behaviour; yet this alone seldomly leads to a crash).
Instead of
strcpy(batch_cmds[i], str);
write
batch_cmds[i] = strdup(str);
Command strdup
does both - (1) reserving memory large enough to hold the contents of str
and (2) copying the contents of str
then. This is equivalent to batch_cmds[i] = malloc(strlen(str)+1); strcpy(batch_cmds[i], str));
.
Additionally, check that i
is <512
, such that batch_cmds[i]
will not exceed array bounds.
Upvotes: 4
Reputation: 21542
The line:
char *batch_cmds[512];
Declares an array of 512 pointers to char
, but those pointers are uninitialized (wild, dangling, take your pick).
Your options:
Declare a 2D array on the stack:
char batch_cmds[512][512];
Use dynamic memory management to allocate the strings on the heap:
char *batch_cmds[512];
int i;
for(i = 0; i < 512; i++)
{
batch_cmds[i] = malloc(string_length);
if(batch_cmds[i] == NULL)
{
// handle alloc error
}
}
// Code that uses batch_cmds...
for(i = 0; i < 512; i++)
{
free(batch_cmds[i]);
batch_cmds[i] = NULL;
}
Use strdup
to assign to the pointers to char
in batch_cmds
(demonstrated in Stephan Lechner's answer). Note that strdup
also uses heap memory behind the scenes so the returned pointers must also be passed to free
when you're done with them to avoid memory leaks.
Upvotes: 1