Is this a good C program for splitting a command line without doing any expansion on it? Don't worry too much about main()
and the output -- those are for testing.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
void pullwhitespace(char **input);
int main (int argc, char **argv)
{
if (argc != 2) {
fputs("Must have exactly one command line argument\n", stderr);
return 64;
}
char *input = argv[1];
char *outstring = malloc(strlen(argv[1]) + 1);
char *outptr = outstring;
char a;
pullwhitespace(&input);
NORMAL:
/* We are in an unquoted part of the input */
switch ((a = *input++))
{
case ' ' :
case '\t':
*outptr++ = '\0';
pullwhitespace(&input);
goto NORMAL;
case '\n':
case '\0':
goto DONE;
case '\\':
if (((a = *input++)) == '\n') {
break;
} else if (a) {
*outptr++ = a;
goto NORMAL;
} else {
goto FAIL;
}
case '\'':
goto QUOTE;
case '"':
goto DQUOTE;
default:
*outptr++ = a;
goto NORMAL;
}
assert(0);
QUOTE:
/* We are in a single quoted string */
while (((a = *input++)) != '\'') {
if (a) {
*outptr++ = a;
} else {
goto FAIL;
}
}
goto NORMAL;
DQUOTE:
/* We are in a double quoted string */
switch ((a = *input++))
{
case '\0':
goto FAIL;
case '\\':
{
char b = *input++;
switch (b)
{
case '\0':
goto FAIL;
default:
*outptr++ = a;
case '"':
case '\\':
*outptr++ = b;
case '\n':
break;
}
}
goto DQUOTE;
default:
*outptr++ = a;
goto DQUOTE;
case '"':
goto NORMAL;
}
assert(0);
FAIL:
fprintf(stderr, "%s: Invalid string input\n", argv[0]);
return 1;
assert(0);
DONE:
*outptr++ = '\0';
*outptr++ = '\0';
errno = 0;
fwrite(outstring, 1, (size_t)(outptr - outstring), stdout);
int error = errno;
if(ferror(stdout)) {
fprintf(stderr, "%s: write error: %s\n", argv[0], strerror(error));
return 1;
}
fflush(stdout);
if ( errno != 0) {
fprintf(stderr, "%s: write error: %s\n", argv[0], strerror(error));
return 1;
}
return 0;
}
void pullwhitespace (char **inputptr)
{
char *input = *inputptr;
char a;
while (( a = *input)) {
if (a != ' ' && a != '\t') {
return;
}
input++;
}
}
I know that it is FULL of goto statements (more than every other program I have ever made put together) -- is that okay?
The reason for the goto
statements is that this is meant as a lightweight state machine. Intended applications include parsing command line arguments or (possibly - this is far fetched) for the parser (not supporting code) to be the basis of a kernel module to parse shebang lines, allowing multiple arguments to be passed to a shebang interpreter.
goto
s? Those are frowned upon in C... in fact, I'm frowning right now. :-( \$\endgroup\$goto
s need not be feared irrationally, and I've seen them in in lexers before, including in the CPython lexer (tokenizer.c), that said, it's good to figure out the alternatives, which you may end up liking more. \$\endgroup\$