You can do exactly what you are attempting, but if you declare an array of pointers with automatic storage duration, then you must be sure to limit the number of tokens you allocate to number you declare to protect your array bounds. You should also consider allocating 1 additional pointer to allow you to store NULL as the next pointer after your last valid token to serve as a sentinel NULL marking the end of valid pointers in your array. (just as is done with *argv[], and is needed by the execv, execvp and execve functions)
To operate on a string-literal, no modification of the input string can occur. The easiest way to handle separating words on a ' ' (space), is simply to use a pair of pointers (a start and end-pointer) where you move the end-pointer down the string until you find a space, then allocate storage for the number of characters between start and end (+1 for the nul-terminating character) and then just copy the characters to your newly allocated storage and nul-terminate the string.
As your loop advances your end-pointer by one so it points to the next character after the space, set your start pointer to your end pointer so it is at the beginning of the next token. Advance your array of pointer index to the next pointer and set to NULL for the sentinel after you copy each word.
Additionally, you need to keep a state variable, a simple int to use as a 1 (true)/0 (false) flag to track if you are in-word reading characters, or out reading spaces. That allows you to skip over leading spaces, multiple included spaces between tokens, or trailing spaces after the last token. This allows you to split "ls -l" or " ls -l " into the same two tokens "ls" and "-l".
Your parseUserInput() can be written as:
#define MAXARG 6
void parseUserInput (char **userInput, const char *input)
{
int i = 0, in = 0; /* index, and in/out of word flag */
const char *p = input, *ep = p; /* pointer and end-pointer */
while (i < MAXARG) { /* loop while pointers remain */
if (!*ep || *ep == ' ') { /* if at nul-char or space */
size_t len = ep - p; /* get length of token */
if (in && len) { /* in-word and chars in token */
/* allocate/validate storage for token */
if (!(userInput[i] = malloc (len + 1))) {
perror ("malloc-userInput[i]");
break;
}
memcpy (userInput[i], p, len); /* copy len chars to storage */
userInput[i++][len] = 0; /* nul-terminate, advance index */
userInput[i] = NULL; /* set next pointer NULL */
}
if (!*ep) /* if at end, break */
break;
in = 0; /* set in-word flag 0 (false) */
}
else { /* normal word char */
if (!in) /* if not in-word */
p = ep; /* update start to end-pointer */
in = 1; /* set in-word flag 1 (true) */
}
ep++; /* advance to next character */
}
}
(note: the order of the parameters are switched to make the order consistent with strcpy, memcpy, etc...)
Adding a short main() with your example you can do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXARG 6
void parseUserInput (char **userInput, const char *input)
{
int i = 0, in = 0; /* index, and in/out of word flag */
const char *p = input, *ep = p; /* pointer and end-pointer */
while (i < MAXARG) { /* loop while pointers remain */
if (!*ep || *ep == ' ') { /* if at nul-char or space */
size_t len = ep - p; /* get length of token */
if (in && len) { /* in-word and chars in token */
/* allocate/validate storage for token */
if (!(userInput[i] = malloc (len + 1))) {
perror ("malloc-userInput[i]");
break;
}
memcpy (userInput[i], p, len); /* copy len chars to storage */
userInput[i++][len] = 0; /* nul-terminate, advance index */
userInput[i] = NULL; /* set next pointer NULL */
}
if (!*ep) /* if at end, break */
break;
in = 0; /* set in-word flag 0 (false) */
}
else { /* normal word char */
if (!in) /* if not in-word */
p = ep; /* update start to end-pointer */
in = 1; /* set in-word flag 1 (true) */
}
ep++; /* advance to next character */
}
}
int main (void) {
char *in = "ls -l",
*parsedInput[MAXARG + 1] = { NULL }; /* add +1 for sentinel NULL at end */
parseUserInput (parsedInput, in); /* note: parameter order change */
for (char **p = parsedInput; *p; p++) { /* loop over filled pointers */
puts (*p);
free (*p); /* don't forget to free what you allocate */
}
}
(note: don't forget to free() the storage you have allocated)
Example Use/Output
$ ./bin/splitinput
ls
-l
Look things over and let me know if you have further questions.
char**in the function fix it? Since it might not be by reference if it’s an array of pointers. Can’t remember exactly how that worked.