I am trying to write my own Shell in C. I have a problem. I wrote my own _strtok function that uses strtok but returns all the tokens as an array of strings. For testing I use the string "ls -laR" defined in the main function. I get the valgrind error "Invalid write of size 8" when trying to malloc the number of chars in the second pointer in the array of strings named "Doubl". Why is it doing this? I am allocating the proper number of pointers to strings in the doubl array. Any insight or help would be appreciated
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
char **_strtok(char *str, char *delim)
{
char **doubl;
char *s = str;
char *string;
int i = 0;
while (*s)
{
if (*s == *delim)
i++;
s++;
}
doubl = malloc(sizeof(char *) * i + 1);
i = 0;
string = strtok(str, delim);
while (1)
{
doubl[i] = malloc(sizeof(char) * strlen(string) + 1);
strcpy(doubl[i], string);
i++;
if (string == NULL)
break;
string = strtok(NULL, delim);
}
return (doubl);
}
char *get_path(char **env)
{
char **check = env;
char *path = NULL;
char pth[] = "PATH";
int i, j, stop = 0;
for (i = 0; check[i] && stop == 0; i++)
{
for (j = 0; j < 4 && stop == 0; j++)
{
if (check[i][j] != pth[j])
break;
if (check[i][j] == pth[j] && j == 3)
{
path = malloc(strlen(check[i]));
strcpy(path, check[i]);
stop = 1;
}
}
}
return (path);
}
char **cmd_to_arg(char **cmd, char **env)
{
/* FREE PATH BEFORE END */
char *path = get_path(env);
char *slash = "/";
char **args = NULL, **check = _strtok(path, ":"), **checkStart = check, **cmdStart = cmd;
int status = -1, i = 0, j;
while (*checkStart)
{
strcat(*checkStart, slash);
strcat(*checkStart, cmd[0]);
status = access(*checkStart, F_OK | X_OK);
printf("%s\n", *checkStart);
if (status == 0)
break;
checkStart++;
}
for(;*cmdStart; i++, cmdStart++)
printf("%d\n", i);
args = malloc(sizeof(char *) * i);
args[0] = malloc(strlen(*checkStart));
strcpy(args[0], *checkStart);
puts(args[0]);
for (j = 1; j < i && cmd[j] != NULL; j++)
{
//printf("%d\n", j);
args[j] = malloc(strlen(cmd[j]) * sizeof(char));
strcpy(args[j], cmd[j]);
puts(args[j]);
}
return (args);
}
int main(int ac, char **av, char **env)
{
(void)ac, (void)av, (void)env;
char line[] = "ls laR";
//size_t size = 0;
char **cmd; //**cmdStart;
//int i = 0, j = 0;
cmd = _strtok(line, " ");
cmd = cmd_to_arg(cmd, env);
return (0);
}
Related
I want to split a string by a delimiter and keep the delimiter in the token list
I have a function that do the same thing as strtok but with a string delimiter (instead of a set of chars) but it doesn't keep the delimiter and can't take an array of delimiters as argument
This is a function that split a string into tokens as strtok does but taking a delimiter
static char *strtokstr(char *str, char *delimiter)
{
static char *string;
char *end;
char *ret;
if (str != NULL)
string = str;
if (string == NULL)
return string;
end = strstr(string, delimiter);
if (end == NULL) {
char *ret = string;
string = NULL;
return ret;
}
ret = string;
*end = '\0';
string = end + strlen(delimiter);
return ret;
}
I want to have a char **split(char *str, char **delimiters_list) that split a string by a set of delimiters and keep the delimiter in the token list
I think I also need a function to count the number of tokens so i can malloc the return of my split function
// delimiters is an array containing ["&&", "||" and NULL]
split("ls > file&&foo || bar", delimiters) should return an array containing ["ls > file", "&&", "foo ", "||", " bar"]
How that can be achieved ?
First, you have a memory error here :
static char *string;
if (str != NULL)
string = str;
if (string == NULL)
return string;
If stris NULL, string is not initialised and you use a uninitialised value in comparaison.
if you want copy a string, you must use the strdup function, the = will just copy the pointer and not the pointer content.
And here a way to do it :
#include <stdlib.h>
#include <string.h>
char *get_delimiters(char *str, char **delims)
{
for (int i = 0; delims[i]; i++)
if (!strncmp(str, delims[i], strlen(delims[i])))
return delims[i];
return NULL;
}
char **split(char *str, char **delimiters)
{
char *string = strdup(str);
char **result = NULL;
int n = 0;
char *delim = NULL;
for (int i = 0; string[i]; i++)
if (get_delimiters(string + i, delimiters))
n++;
result = malloc((n * 2 + 2) * sizeof(char *));
if (!result)
return NULL;
result[0] = string;
n = 1;
for (int i = 0; string[i]; i++) {
delim = get_delimiters(string + i, delimiters);
if (delim) {
string[i] = '\0';
result[n++] = delim;
result[n++] = string + i + strlen(delim);
}
}
result[n] = NULL;
return result;
}
result :
[0] 'ls > file'
[1] '&&'
[2] 'foo '
[3] '||'
[4] ' bar'
remember result and string are malloced, so you must free the result and result[0]
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
char **split(char *str, char **delimiters, int number_of_delimiters, int *number_of_rows_in_return_array);
int main()
{
char **split_str;
char *delimiters[] = {
"&&",
"||"
};
int rows_in_returned_array;
split_str = split("ls > file&&foo || bar && abc ||pqwe", delimiters, 2 , &rows_in_returned_array);
int i;
for (i = 0 ; i < rows_in_returned_array ; ++i)
{
printf("\n%s\n", split_str[i]);
}
return 0;
}
char **split(char *str, char **delimiters, int number_of_delimiters, int *number_of_rows_in_return_array)
{
//temporary storage for array to be returned
char temp_store[100][200];
int row = 0;//row size of array that will be returned
char **split_str;
int i, j, k, l, mark = 0;
char temp[100];
for (i = 0 ; str[i] != '\0' ; ++i)
{
//Iterating through all delimiters to check if any is str
for (j = 0 ; j < number_of_delimiters ; ++j )
{
l = i;
for (k = 0 ; delimiters[j][k] != '\0' ; ++k)
{
if (str[i] != delimiters[j][k])
{
break;
}
++l;
}
//This means delimiter is in string
if (delimiters[j][k] == '\0')
{
//store the string before delimiter
strcpy(temp_store[row], &str[mark]);
temp_store[row ++][i - mark] = '\0';
//store string after delimiter
strcpy(temp_store[row], &str[i]);
temp_store[row ++][k] = '\0';
//mark index where this delimiter ended
mark = l;
//Set i to where delimiter ends and break so that outermost loop
//can iterate from where delimiter ends
i = l - 1;
break;
}
}
}
//store the string remaining
strcpy(temp_store[row++], &str[mark]);
//Allocate the split_str and store temp_store into it
split_str = (char **)malloc(row * sizeof(char *));
for (i=0 ; i < row; i++)
{
split_str[i] = (char *)malloc(200 * sizeof(char));
strcpy(split_str[i], temp_store[i]);
}
*number_of_rows_in_return_array = row;
return split_str;
}
This should probably work. Note that I have passed int * number_of_rows_in_return_array by ref because we need to know the row size of the retuned array.
I went into abstraction. First I created a "sentence" library, that allows for manipulating NULL terminated list of strings (char*). I wrote some initial accessors (sentence_init, sentence_size, sentence_free, sentence_add_str etc.).
Then I went to split, witch becomes really, really easy then - if a delimeter is found, add the string up the delimeter to the sentence and add the delimeter to the sentence. Then increment the string pointer position. If the delimeter is not found, add the remaining string to the sentence.
There is a real problem with double pointers tho, because char ** is not implicitly convertible to const char **. For production code, I would probably aim to refactor the code, and try to take const-correctness into account.
#define _GNU_SOURCE 1
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
#include <stdbool.h>
/*
* sentence - list of words
*/
/* ----------------------------------------------------------- */
// if this would be production code, I think I would go with a
// struct word_t { char *word; }; struct sentence_t { struct word_t *words; };
// Note: when sentence_add_* fail - they free *EVERYTHING*, so it doesn't work like realloc
// shared_ptr? Never heard of it.
char **sentence_init(void) {
return NULL;
}
size_t sentence_size(char * const *t) {
if (t == NULL) return 0;
size_t i;
for (i = 0; t[i] != NULL; ++i) {
continue;
}
return i;
}
void sentence_free(char * const *t) {
if (t == NULL) return;
for (char * const *i = t; *i != NULL; ++i) {
free(*i);
}
free((void*)t);
}
void sentence_printex(char * const *t, const char *fmt1, const char *delim, const char *end) {
for (char * const *i = t; *i != NULL; ++i) {
printf(fmt1, *i);
if (*(i + 1) != NULL) {
printf(delim);
}
}
printf(end);
}
void sentence_print(char * const *t) {
sentence_printex(t, "%s", " ", "\n");
}
void sentence_print_quote_words(char * const *t) {
sentence_printex(t, "'%s'", " ", "\n");
}
bool sentence_cmp_const(const char * const *t, const char * const *other) {
const char * const *t_i = t;
const char * const *o_i = other;
while (*t_i != NULL && o_i != NULL) {
if (strcmp(*t_i, *o_i) != 0) {
return false;
}
++t_i;
++o_i;
}
return *t_i == NULL && *o_i == NULL;
}
// thet's always funny, because "dupa" in my language means "as*"
char **sentence_add_strdupped(char **t, char *strdupped) {
const size_t n = sentence_size(t);
const size_t add = 1 + 1;
const size_t new_n = n + add;
void * const pnt = realloc(t, new_n * sizeof(char*));
if (pnt == NULL) goto REALLOC_FAIL;
// we have to have place for terminating NULL pointer
assert(new_n >= 2);
t = pnt;
t[new_n - 2] = strdupped;
t[new_n - 1] = NULL;
// ownership of str goes to t
return t;
// ownership of str stays in the caller
REALLOC_FAIL:
sentence_free(t);
return NULL;
}
char **sentence_add_strlened(char **t, const char *str, size_t len) {
char *strdupped = malloc(len + 1);
if (strdupped == NULL) goto MALLOC_FAIL;
memcpy(strdupped, str, len);
strdupped[len] = '\0';
t = sentence_add_strdupped(t, strdupped);
if (t == NULL) goto SENTENCE_ADD_STRDUPPED_FAIL;
return t;
SENTENCE_ADD_STRDUPPED_FAIL:
free(strdupped);
MALLOC_FAIL:
sentence_free(t);
return NULL;
}
char **sentence_add_str(char **t, const char *str) {
const size_t str_len = strlen(str);
return sentence_add_strlened(t, str, str_len);
}
/* ----------------------------------------------------------- */
/**
* Puff. Run strstr for each of the elements inside NULL delimeters dellist.
* If any returns not NULL, return the pointer as returned by strstr
* And fill dellist_found with the pointer inside dellist (can be NULL).
* Finally! A 3 star award is mine!
*/
char *str_find_any_strings(const char *str,
const char * const *dellist,
const char * const * *dellist_found) {
assert(str != NULL);
assert(dellist != NULL);
for (const char * const *i = &dellist[0]; *i != NULL; ++i) {
const char *found = strstr(str, *i);
if (found != NULL) {
if (dellist_found != NULL) {
*dellist_found = i;
}
// __UNCONST(found)
return (char*)found;
}
}
return NULL;
}
/**
* Split the string str according to the list od delimeters dellist
* #param str
* #param dellist
* #return returns a dictionary
*/
char **split(const char *str, const char * const *dellist) {
assert(str != NULL);
assert(dellist != NULL);
char **sen = sentence_init();
while (*str != '\0') {
const char * const *del_pnt = NULL;
const char *found = str_find_any_strings(str, dellist, &del_pnt);
if (found == NULL) {
// we don't want an empty string to be the last...
if (*str != '\0') {
sen = sentence_add_str(sen, str);
if (sen == NULL) return NULL;
}
break;
}
// Puff, so a delimeter is found at &str[found - str]
const size_t idx = found - str;
sen = sentence_add_strlened(sen, str, idx);
if (sen == NULL) return NULL;
assert(del_pnt != NULL);
const char *del = *del_pnt;
assert(del != NULL);
assert(*del != '\0');
const size_t del_len = strlen(del);
sen = sentence_add_strlened(sen, del, del_len);
if (sen == NULL) return NULL;
str += idx + del_len;
}
return sen;
}
int main()
{
char **sentence = split("ls > file&&foo || bar", (const char*[]){"&&", "||", NULL});
assert(sentence != NULL);
sentence_print_quote_words(sentence);
printf("cmp = %d\n", sentence_cmp_const((void*)sentence, (const char*[]){"ls > file", "&&", "foo ", "||", " bar", NULL}));
sentence_free(sentence);
return 0;
}
The program will output:
'ls > file' '&&' 'foo ' '||' ' bar'
cmp = 1
I am writing a microshell program as homework at my university.
Everything goes well besides one function that's not doing exactly what I'd wish it did.
I am quite new to C programming, always used higher level languages.
In fact the only times I worked with C was fiddling around with Arduino.
So I've got a line of what user inputs on the prompt. I'm trying to split it into an array of strings separated by space.
I initialized an array with
char **args = NULL;
args = malloc(sizeof(char *) * LINE_LENGTH);
And I'm sending it to a function parse_line(line, args)
The function looks like this:
bool parse_line(char *line, char **arr) {
size_t i = 0;
char *point;
point = strtok(line, " ");
while (point != NULL) {
arr[i] = malloc(strlen(point) + 1);
strcpy(arr[i], point);
point = strtok(NULL, " ");
i++;
}
arr[i] = NULL;
if (!arr)
return false;
return true;
}
The thing is that afterwards in the arr resides only first token from the splitted up line.
I am debugging it and though variable 'point' gets the right values, they aren't copied into my array. Why? I don't know.
Oh.. and the line is an array of chars, dynamic one.
char * line = NULL;
line = read_input_line();
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#define LINE_LENGTH 50
void clear_screen();
void display_prompt(bool clearscr);
char * read_input_line();
void print_line(char *line);
bool parse_line(char *line, char **arr);
int main() {
bool initialRun = true;
while (true) {
// display prompt on the screen
display_prompt(initialRun);
if (initialRun)
initialRun = false;
// read input line from terminal
char * line = NULL;
line = read_input_line();
// basic commands
if (line == NULL)
continue;
if (strcmp(line, "exit") == 0) {
free(line);
exit(EXIT_SUCCESS);
} else if (strcmp(line, "clear") == 0) {
clear_screen();
continue;
}
// parse line into array
char **args = NULL;
args = malloc(sizeof(char *) * LINE_LENGTH);
if (!parse_line(line, args)) {
printf("Error during parsing command \n");
continue;
}
}
return 0;
}
void clear_screen() {
printf("\e[2J\e[H");
}
void display_prompt(bool clearscr) {
if (clearscr)
clear_screen();
printf(" > ");
}
char * read_input_line() {
char * line = (char *)malloc(sizeof(char) * LINE_LENGTH);
if (!fgets(line, LINE_LENGTH, stdin))
return NULL;
size_t len = strlen(line);
if (len > 0 && line[len-1] == '\n') {
line[--len] = '\0';
}
return line;
}
void print_line(char *line) {
printf("%s \n", line);
}
bool parse_line(char *line, char **arr) {
size_t i = 0;
char *point;
point = strtok(line, " ");
while (point != NULL) {
arr[i] = (char *)malloc(strlen(point) + 1);
strcpy(arr[i], point);
point = strtok(NULL, " ");
i++;
}
arr[i] = NULL;
for (int j=0; j<i; j++) {
printf("%s\n", arr[i]);
}
if (!arr)
return false;
return true;
}
Input:
ls -l -h
arr[i] = NULL;
for (int j=0; j<i; j++) {
printf("%s\n", arr[i]);
}
You are not using j as index so you send NULL to printf(). It's undefined behavior.
I propose you an example of implementation(still not the best but for a beginner that enough):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define LINE_LENGTH 50
char **parse_line(char *line, size_t *n);
int main(void) {
while (true) {
// display prompt on the screen
printf(" > ");
// read input line from terminal
char line[LINE_LENGTH];
if (!fgets(line, sizeof line, stdin)) {
return 1;
}
line[strcspn(line, "\n")] = '\0';
if (strcmp(line, "exit") == 0) {
exit(EXIT_SUCCESS);
} else if (strcmp(line, "clear") == 0) {
printf("\e[2J\e[H");
continue;
}
// parse line into array
size_t n;
char **args = parse_line(line, &n);
if (!args) {
printf("Error during parsing command \n");
continue;
}
for (size_t i = 0; i < n; i++) {
printf("%s\n", args[i]);
}
}
}
char **parse_line(char *line, size_t *n) {
char **arr = malloc(sizeof *arr);
size_t i = 0;
for (char *token = strtok(line, " "); token != NULL; token = strtok(NULL, " ")) {
char **tmp = realloc(arr, sizeof *tmp * (i + 2));
if (tmp == NULL) {
for (size_t j = 0; j < i; j++) {
free(arr[j]);
}
free(arr);
return NULL;
}
arr = tmp;
arr[i] = malloc(strlen(token) + 1);
if (arr[i] == NULL) {
for (size_t j = 0; j < i; j++) {
free(arr[j]);
}
free(arr);
return NULL;
}
strcpy(arr[i], token);
i++;
}
arr[i] = NULL;
*n = i;
return arr;
}
In the code below, in "parse" function I am trying to get substring from the string "line". I am successfully printing the "method" variable, but "requesttarget" and "httpversion" variables are empty for some reason.
(ps all these printf's are also inside my parse function)
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
//prototypes
bool parse(const char* line, char* abs_path, char* query);
int strindex(char** pos, const char* str);
void substr(int start, int end, char* holder, const char* line);
int main(void)
{
const char* line = "GET /hello.php?name=Alice HTTP/1.1";
char* abs_path = NULL;
char* query = NULL;
if(parse(line, abs_path, query))
{
printf("It works!\n");
}
}
bool parse(const char* line, char* abs_path, char* query)
{
char* space;
int firstspace;
int secondspace;
char* method = malloc(50 * sizeof(char));
char* requesttarget = malloc(50 * sizeof(char));
char* httpversion = malloc(50 * sizeof(char));
space = strchr(line, ' ');
printf("%p\n", space);
//checks if strchr returns
if(space == NULL)
{
return false;
}
//index in INT of the character
firstspace = strindex(&space, line);
printf("%i\n", firstspace);
//stores the method
substr(0, firstspace, method, line);
space = strrchr(line, ' ');
printf("%p\n", space);
//index in INT of the character
secondspace = strindex(&space, line);
printf("%i\n", secondspace);
//checks if strchr returns
if(space == NULL)
{
return false;
}
//firstspace should come before secondspace
if(firstspace > secondspace)
{
return false;
}
//stores request - target
substr(firstspace + 1, secondspace, requesttarget, line);
//stores http-version
substr(secondspace + 1, strlen(line), httpversion, line);
printf("method: %s\n", method);
printf("requesttarget: %s\n", requesttarget);
printf("httpversion: %s\n", httpversion);
return true;
}
int strindex(char** pos, const char* str)
{
for(int i = 0, n = strlen(str); i < n; i++)
{
if((str + i) == *pos)
{
return i;
}
}
return -1;
}
void substr(int start, int end, char* holder, const char* line)
{
//char* holder = malloc(50 * sizeof(char));
int i = start;
for(; i < end; i++)
{
holder[i] = line[i];
}
holder[i] = '\0';
//return holder;
}
void substr(int start, int end, char* holder, const char* line)
{
//char* holder = malloc(50 * sizeof(char));
int i = start, j=0;
for(; i < end; i++)
{
holder[j++] = line[i];
}
holder[j] = '\0';
//return holder;
}
you were not storing data in holder from 2nd iteration properly.
from 2nd iteration start = 3 and end = 25. While storing in holder your index starts from 3, which is correct for line but not for holder.
Add one more variable to start the index for holder from 0.
A smaller version: (untested)
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
const char* line = "GET /hello.php?name=Alice HTTP/1.1";
char method[32], request[1024], version[32], *src, *dest, *end;
for(src=line, end=(dest=method)+sizeof(method)-1 ; *src!='\0' && *src!=' ' && dest<end; src++, dest++) *dest=*src;
*dest='\0';
while(*src==' ') src++;
for(end=(dest=request)+sizeof(request)-1 ; *src!='\0' && *src!=' ' && dest<end; src++, dest++) *dest=*src;
*dest='\0';
while(*src==' ') src++;
for(end=(dest=version)+sizeof(version)-1 ; *src!='\0' && *src!=' ' && dest<end; src++, dest++) *dest=*src;
*dest='\0';
printf("method: %s\n", method);
printf("requesttarget: %s\n", request);
printf("httpversion: %s\n", version);
}
I have such string (it's a string that represents a system path actually):
./home/user/dir1/dir2/
And now, I would like to be able to create presented directory tree, so I need to create home, user, dir1, dir2.
I know how to create dirs in C in Linux, but have troubles with chopping off the string. Basically, what I need now is to have an array of strings:
tmp[0] = "./home/";
tmp[1] = "./home/user/";
tmp[2] = "./home/user/dir1/";
tmp[3] = "./home/user/dir1/dir2/";
and if I will hav such array it would be pretty easy to make presented dir tree but how to split the string in that way?
This is a bit naive but it should get you started. It will handle paths that may or may not have a trailing / and also escaped paths, such as ./home/some\/user/dir1/dir2
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char path[] = "./home/user/dir1/dir2";
char *start = path;
char *end = start + strlen(path);
while (start < end) {
char *slash = strchr(start, '/');
if (slash) {
if (slash > path && *(slash - 1) == '\\') {
start = slash + 1;
continue;
}
*slash = 0;
}
if (strcmp(start, ".") != 0) {
/* Use 'path' for mkdir here */
/* mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) or whatever */
printf("going to make %s\n", path);
}
if (!slash) {
break;
}
*slash = '/';
start = slash + 1;
}
return 0;
}
I'd go for Sean's advice as "You should just exec() mkdir -p ... and save yourself the headache."
However if C is necessary, there you go:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUF_SIZE 100
int main(){
char s[] = "./home/user/dir1/dir2/";
char** tmp;
int i, j;
int size = 0;
char* token;
/* count the '/' characters */
char c = s[0];
int count = 0;
i = 0;
while (c != '\0'){
if (s[i] == '/')
count++;
i++;
c = s[i];
}
size = i;
/* ready the tmp array */
tmp = (char**)malloc(count);
for (i = 0; i < count; i++){
tmp[i] = (char*)malloc(BUF_SIZE);
for (j = 0; j < BUF_SIZE; ++j)
tmp[i][j] = '\0';
}
/* special treatment for our first tmp[] */
tmp[0][0] = '.';
tmp[0][1] = '/';
i = 0;
/* tokenize the main string */
token = strtok(s, "./");
while (token != NULL){
if (i > 0)
strcat(tmp[i], tmp[i - 1]);
strcat(tmp[i], token);
strcat(tmp[i], "/");
printf("%s\n", tmp[i]);
token = strtok(NULL, "/");
i++;
}
/* clean up */
for (i = 0; i < count; i++)
free(tmp[i]);
getchar();
return 0;
}
The output is:
./home/
./home/user/
./home/user/dir1/
./home/user/dir1/dir2/
I would use strtok to parse the directory names from the string using "/" as the delimiter.
see: http://www.cplusplus.com/reference/cstring/strtok/
Heres how I did it:
#include <stdio.h>
#include <string.h>
// Count the number of times the character appears in the string
size_t countInstances(char* str, char token) {
size_t count = 0;
while(*str) {
if(*str == token) {
count++;
}
str++;
}
return count;
}
int main() {
char path[] = "./home/user/dir1/dir2/"; // strtok might segfault if this is a pointer (rather than an array)
size_t count = countInstances(path, '/');
char** dirs = malloc(sizeof(*dirs) * count);
char* dir;
size_t i = 0;
dir = strtok(path, "/");
while(dir && i < count) {
dirs[i] = dir; // store reference
printf("%s\n",dir);
dir = strtok (NULL, "/");
i++;
}
free(dirs);
return 0;
}
output is:
.
home
user
dir1
dir2
I need to return a char** but when I try to do this, the compiler tells me that I want to return the address of a local variable. How can I do that? I know that I should allocate space for this variable but how? Here is my code, but the second printf doesn't appear and the function returns nothing:
char** parse_cmd(const char* cmdline) {
char** arguments = (char**)malloc(100);
int i;
int j=0, k=0;
printf("%s\n", cmdline);
for(i=0; i<100; i++) {
arguments[i] = malloc(100);
}
for(i = 0; i < strlen(cmdline); i ++) {
if(cmdline[i] != ' ') {
arguments[j][k] = cmdline[i];
k++;
} else {
arguments[j][k] = '\0';
j++;
k = 0;
}
}
printf("%s\n", arguments[1]);
return arguments;
}
You need to do multiple allocations. The first for the char** and then for each of the char*. E.g. something like
char **args = (char**)malloc(100);
int i;
for (i=0; i<100; i++)
args[i] = malloc(100);
// Rest of program
return args;
Here's the code I assembled - and tested. It uses dynamic memory allocation for both the argv argument list and for each argument as it is assembled. The function release_cmd() releases the allocated space. The function cleanup() is internal and releases allocated space on a failure, before returning a null double-pointer. This simplifies the error handling. There's a minimal test harness in the prompt() function and main(). I haven't run in under valgrind, but the MacOS X implementation of malloc() quite often spots problems so I'm moderately confident there's no gross memory abuse - but there could be an off-by-one leak. I haven't tested the cleanup() code by faking an allocation failure.
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void release_cmd(char **argv)
{
for (size_t i = 0; argv[i] != 0; i++)
free(argv[i]);
free(argv);
}
static char **cleanup(size_t argc, char **argv)
{
argv[argc] = 0;
release_cmd(argv);
return 0;
}
char **parse_cmd(const char* cmdline)
{
size_t argc = 2;
char **argv = malloc(argc * sizeof(char *));
if (argv == 0)
return 0;
size_t j = 0; // Index into argv
size_t len = strlen(cmdline);
for (size_t i = 0; i < len; i++)
{
while (isspace(cmdline[i]))
i++;
if (cmdline[i] == '\0')
break;
if (j > argc - 2)
{
size_t newc = (argc * 2);
char **newv = realloc(argv, newc * sizeof(char *));
if (newv == 0)
return cleanup(argc, argv);
argv = newv;
argc = newc;
}
size_t argl = 2; // Length of argument string
argv[j] = malloc(argl);
size_t k = 0; // Index into argv[j]
while (cmdline[i] != '\0' && !isspace(cmdline[i]))
{
if (k > argl - 2)
{
size_t newl = argl * 2;
char *news = realloc(argv[j], newl);
if (news == 0)
return cleanup(argc, argv);
argv[j] = news;
argl = newl;
}
argv[j][k++] = cmdline[i++];
}
argv[j][k] = '\0';
argv[j] = realloc(argv[j], k+1); // Shrink to fit!
j++;
}
argv[j] = 0;
argv = realloc(argv, (j+1)*sizeof(*argv)); // Shrink to fit!
return argv;
}
static int prompt(const char *prompt, char *buffer, size_t bufsiz)
{
printf("%s", prompt);
return (fgets(buffer, bufsiz, stdin) != 0);
}
int main(void)
{
char line[1024];
while (prompt("cmd? ", line, sizeof(line)) != 0)
{
char **argv = parse_cmd(line);
char **args = argv;
while (*args)
puts(*args++);
release_cmd(argv);
}
putchar('\n');
return 0;
}