I have allocated space for a char*** array that stores parsed commands and arguments entered into my shell program.
Here are some example command line inputs:
ls -la
cd ..
/usr/bin/wc -l inputs/data/long
Here's how the char*** array stores the commands - it parses by white space:
{{"ls", "-la"}, {"cd", ".."}, {"/usr/bin/wc", "-l", "inputs/data/long"}}
Here's how I generate the char*** array:
/*
* initializes history
* returns array of pointers to arrays of pointers to char arrays
*/
char*** inithistory(){
char*** history;
history = (char***) calloc(HIST_SIZE, sizeof(char**));
return history;
}
/*
* adds a tokenized command and arguments to the history
*/
int addToHistory(char*** history, char** tokenized, int* argcnts, int argcnt, int cmdcnt) {
if (cmdcnt >= HIST_SIZE) {
cmdcnt = HIST_SIZE-1;
// remove oldest element and shift others up
for (int i = 0; i < HIST_SIZE-1; i++) {
history[i] = history[i+1];
argcnts[i] = argcnts[i+1];
}
}
// add latest element
history[cmdcnt] = tokenized;
argcnts[cmdcnt] = argcnt;
return 1;
}
/*
* returns the length of a char array
*/
int lengthof(char* string) {
int count = 0;
char temp = string[0];
while (temp) {
temp = string[count];
count++;
}
if (count==0) {
return 0;
}
return count-1;
}
/*
* returns array of pointers to char arrays containing parsed command and arguments e.g. {"ls", "-la"},
* char* input is the command line input
*/
char** tokenize(char* input, int argc) {
char* token;
char** tokenized;
tokenized = (char**) calloc(argc+1, sizeof(char*));
token = strtok(input, " "); // tokenize user input
int i = 0;
while (token != NULL) {
int length = lengthof(token);
if (i == argc-1) {
token[length-1] = '\0';
}
tokenized[i] = (char*) calloc(length, sizeof(char));
tokenized[i] = token;
token = strtok(NULL, " ");
i++;
}
tokenized[argc] = NULL;
return tokenized;
}
When I try to free the char*** array above, I get the following error:
*** glibc detected *** cs475sh: free(): invalid pointer: 0x000000000150f9b3 ***
======= Backtrace: =========
/lib64/libc.so.6[0x36bb075dee]
/lib64/libc.so.6[0x36bb078c3d]
cs475sh[0x4015e9]
cs475sh[0x400d37]
cs475sh[0x400bf0]
/lib64/libc.so.6(__libc_start_main+0xfd)[0x36bb01ed1d]
cs475sh[0x400a59]
======= Memory map: ========
00400000-00402000 r-xp 00000000 00:14 12006791923606037921 /home/Documents/HW1/shell-handout/cs475sh
00601000-00602000 r--p 00001000 00:14 12006791923606037921 /home/Documents/HW1/shell-handout/cs475sh
00602000-00603000 rw-p 00002000 00:14 12006791923606037921 /home/Documents/HW1/shell-handout/cs475sh
0150f000-01530000 rw-p 00000000 00:00 0 [heap]
36bac00000-36bac20000 r-xp 00000000 fd:00 918063 /lib64/ld-2.12.so
36bae20000-36bae21000 r--p 00020000 fd:00 918063 /lib64/ld-2.12.so
36bae21000-36bae22000 rw-p 00021000 fd:00 918063 /lib64/ld-2.12.so
36bae22000-36bae23000 rw-p 00000000 00:00 0
36bb000000-36bb18a000 r-xp 00000000 fd:00 918804 /lib64/libc-2.12.so
36bb18a000-36bb38a000 ---p 0018a000 fd:00 918804 /lib64/libc-2.12.so
36bb38a000-36bb38e000 r--p 0018a000 fd:00 918804 /lib64/libc-2.12.so
36bb38e000-36bb390000 rw-p 0018e000 fd:00 918804 /lib64/libc-2.12.so
36bb390000-36bb394000 rw-p 00000000 00:00 0
36bd800000-36bd816000 r-xp 00000000 fd:00 918982 /lib64/libgcc_s-4.4.7-20120601.so.1
36bd816000-36bda15000 ---p 00016000 fd:00 918982 /lib64/libgcc_s-4.4.7-20120601.so.1
36bda15000-36bda16000 rw-p 00015000 fd:00 918982 /lib64/libgcc_s-4.4.7-20120601.so.1
7f1088000000-7f1088021000 rw-p 00000000 00:00 0
7f1088021000-7f108c000000 ---p 00000000 00:00 0
7f108c410000-7f108c413000 rw-p 00000000 00:00 0
7f108c42f000-7f108c433000 rw-p 00000000 00:00 0
7ffc6e541000-7ffc6e557000 rw-p 00000000 00:00 0 [stack]
7ffc6e55b000-7ffc6e55c000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
Aborted
Here's how I free the array:
/*
* char*** history is the array that needs to be freed,
* int cmd is the number of parsed commands in history (char** pointers),
* int* argcnts holds the number of arguments per command (each argument is
* char* - count includes the command itself)
*/
void exitProg(int code, char*** history, int* argcnts, int cmdcnt) {
if (!code) {
exit(code);
}
if (!cmdcnt) {
exit(code);
}
for (int i = 0; i < cmdcnt; i++) {
for (int j = 0; j < argcnts[i]; j++) {
free(history[i][j]);
}
free(history[i]);
}
free(history);
exit(code);
}
I know it's redundant to free memory before exiting the program, but it's required for the assignment.
I've noticed that the error is only given after attempting to execute free(history[i][j]);
.
There was a memory leak as MikeCAT pointed out:
tokenized[i] = (char*) calloc(length, sizeof(char)); tokenized[i] = token; Oh, memory leak.
This problem was solved by with a simple strcpy as John Bollinger stated:
Not only is that a memory leak, it's a likely cause for the segfault. You probably want the second of those statements to be strcpy(tokenized[i], token);
Freeing the memory now works without fault.