There's one issue that's not been mentioned yet, and that as to do with the use of
getline().
getline() uses
malloc() to get space for the output buffer, so by not calling
free(), you're constantly loosing memory.
The quick way to solve that is to do it in your
for() statement e.g.
for( ;; free(command)) {
However, now you are constantly calling
malloc()/free(), and we can do better. The first time you call
getline(), using a given buffer, you need to make sure that
len parameter is 0. Subsequent calls. using the same buffer, use the same len parameter, and
getline() will only allocate more memory when it needs to.
getline also returns the number of chars read (not including the final '\0'). Knowing that we can do the following:
1) pass the buffer and its
allocated length into
userInput(), so
getline() can reuse the buffer.
2) use the returned length from
getline() to trim off the trailing '\n' in the input
3) if we return the value of
getline(), we can test for -1, which is returned on error, including EOF. This means an input of
CTL-D can be used to end the shell.
Since
getline() calls
malloc() only when it needs to, we have gained some efficiency, and now we only have to call
free() once!
So what we have is the following changes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
ssize_t userInput(char **buffer, size_t *buflen)
{
ssize_t rlen = getline(buffer, bufflen, stdin);
if(rlen > 0) /* trim off trailing newline */
buff[rlen--] = '\0';
return rlen;
}
void driverLoop(void)
{
/* ... */
char *command = NULL;
size_t lenCommand = 0;;
/* ... */
for(;;) {
ssize_t bytesRead = 0;
fputs("> ", stdout); /* avoids all the printf overhead. */
bytesRead = userInput(&command, &lenCommand);
if( bytesRead == -1 ) {
break; /* EOF? maybe we should do some error checking? */
} else if (bytesRead == 0) { /* empty input, can continue */
continue;
}
/* ... */
}
free(command);
}
| |
You might also consider using
vfork() rather than
fork(), since you don't do anything but call
execvp()[/t]t immediately after. Using [tt]vfork() means that you don't copy the program memory pages, which could be a win if this gets large. The usual way of making sure that nothing but an
exec gets called after [tt]vfork[/t] is something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13
|
pid_t child;
if( (child = vfork()) == 0) ) {
execvp( /* args */ );
perror("exec");
exit(1); /* otherwise we get an Abort, core dumped! even for fork! */
}
else if ( child == -1 ) { /* note we don't need to cast to pid_t here */
perror("fork");
}
else {
/* parent waits for child */
wait( /* args */ );
}
| |
oops the definition of userInput was
ssize_t userInput[char **buffer, size_t bufflen)
. That was wrong. bufflen should have been a size_t*. Fixed. my bad.