While it is not 100% clear what all you are trying to accomplish with your code, your use of the multiple pointers is a bit awkward.
The first thing that should be sounding ALARM BELLS for you is your need to explicitly cast to (char)
. If you ever find yourself trying to cast to get around compiler warnings or error -- STOP -- you are doing something wrong.
If your goal is to provide up to BUFFERSIZE
arguments to execvp
(or the like), then you simply need to declare myargv
as an array of pointers to char, e.g.
char *myargv[BUFFERSIZE] = {NULL}; /* array of pointers - init NULL */
Each of the pointers returned by strtok
can be used as the argument array for execvp
and if you initialize the array to all NULL
pointers and fill no more than BUFFERSIZE - 1
, you ensure you always provide an array of arguments to execvp
and provide the required sentinel NULL
after the final argument.
You can declare your delimiters for strtok
any way you like, but since you are properly defining constants with #define
, there is no reason not to add a constant for your strtok
delimiters as well, e.g.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFERSIZE 256
#define PROMPT "myShell >> "
#define DELIM " \n"
If you are not using argc
or argv
in your code, then the proper declaration for main()
is:
int main (void) {
(see: C11 Standard §5.1.2.2.1 Program startup p1 (draft n1570). See also: See What should main() return in C and C++?)
If you are only reading the line and tokenizing the line to use with execvp
, then declaring and initializing your variables within the loop scope ensures they are properly re-initialized each iteration, e.g.
while (1) {
size_t ndx = 0, /* line index */
n = 0; /* line alloc size (0, getline decides) */
ssize_t nchr = 0; /* return (chars read by getline) */
char *line = NULL, /* buffer to read each line */
*myargv[BUFFERSIZE] = {NULL}; /* array of pointers - init NULL */
By declaring your inputSize
, my nchr
above as ssize_t
(the proper return type for POSIX getline
), you can simplify your test for EOF
, e.g.
fputs (PROMPT, stdout);
if ((nchr = getline (&line, &n, stdin)) == -1) {
putchar ('\n'); /* tidy up with newline */
break;
}
All that remains is tokenizing line
and assigning the pointers to myargv
at the proper index (ndx
). You can use a while
loop, but a for
provides a convenient way to tokenize with strtok
, e.g.
for (char *p = strtok (line, DELIM); p; p = strtok (NULL, DELIM)) {
myargv[ndx] = p; /* points within line, duplicate as req'd */
printf ("token: %s\n", myargv[ndx++]);
if (ndx == BUFFERSIZE - 1) /* preserve sentinel NULL */
break;
}
/* call to execvp, etc. here */
(Note: by simply assigning the pointer to token to myargv[ndx]
, myargv[ndx]
points to the location of the string within line
. You must make use of the pointers while line
remains in scope. Otherwise, you need to allocate memory for each token, assign the starting address for the new block of memory to myargv[ndx]
and copy the token to the new block of memory. (either malloc
and strcpy
, or strdup
if you have it))
Lastly, do not forget, getline
allocates, so do not forget to free()
the memory allocated when you are done with it, e.g.
free (line); /* don't forget to free memory allocated by getline */
}
Putting it altogether, you could handle tokenizing your line with something similar to:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFERSIZE 256
#define PROMPT "myShell >> "
#define DELIM " \n"
int main (void) {
while (1) {
size_t ndx = 0, /* line index */
n = 0; /* line alloc size (0, getline decides) */
ssize_t nchr = 0; /* return (chars read by getline) */
char *line = NULL, /* buffer to read each line */
*myargv[BUFFERSIZE] = {NULL}; /* array of pointers - init NULL */
fputs (PROMPT, stdout);
if ((nchr = getline (&line, &n, stdin)) == -1) {
putchar ('\n'); /* tidy up with newline */
break;
}
for (char *p = strtok (line, DELIM); p; p = strtok (NULL, DELIM)) {
myargv[ndx] = p; /* points within line, duplicate as req'd */
printf ("token: %s\n", myargv[ndx++]);
if (ndx == BUFFERSIZE - 1) /* preserve sentinel NULL */
break;
}
/* call to execvp, etc. here */
free (line); /* don't forget to free memory allocated by getline */
}
return 0;
}
Example Use/Output
$ ./bin/getlinestrtok
myShell >> my dog has fleas
token: my
token: dog
token: has
token: fleas
myShell >> my cat has none
token: my
token: cat
token: has
token: none
myShell >> happy cat
token: happy
token: cat
myShell >>
Look things over and let me know if you have further questions.