The way "switch" works in C is very weird. It behaves more like a "goto", where each "case ...:" is a label to which it can jump.
So when the character is '-', it starts just before the "if (0)" (this part is hidden within the ARG_BEGIN macro), and as you noted, it will never enter that block. However, when the character is 'a', 'b', 'c', or nothing (the end-of-string marker), it will jump directly to the corresponding "case ...:" label, even though it's within that "unreachable" block.
Because there's really a switch statement (hidden behind a macro) that will jump to labels within that block.
The fact that the if condition is false means it won't just run the whole block straight through but you can still jump to a label in it. A goto statement would also allow you to jump into an otherwise-unreachable block.
The whole `if` portion is for if the second character of the argument (after the initial `-`) is a second `-` [0].
In that case, the switch jumped to `case '-':` hidden within the macro, and the if statement is about processing long arguments (like `--help`). arguments only available as a short flag should never get executed as part of a short flag, so putting them inside the if(0) case is an easy option.
An alternative without if(0) of any variety would be:
ARG_BEGIN {
if (ARG_LONG("reverse")) case 'r': {
reverse = 1;
ARG_FLAG();
} else if (ARG_LONG("input")) case 'i': {
input = ARG_VAL();
} else if (ARG_LONG("output")) case 'o': {
output = ARG_VAL();
} else if (ARG_LONG("help")) case 'h': case '?': {
printf("Usage: %s [OPTION...] [STRING...]\n", argv0);
puts("Example usage of arg.h\n");
puts("Options:");
puts(" -a, set a to true");
puts(" -b, set a to true");
puts(" -c, set a to true");
puts(" -r, --reverse set reverse to true");
puts(" -i, --input=STR set input string to STR");
puts(" -o, --output=STR set output string to STR");
puts(" -h, --help display this help and exit");
return EXIT_SUCCESS;
} else { default:
fprintf(stderr,
"%s: invalid option '%s'\n"
"Try '%s --help' for more information.\n",
argv0, *argv, argv0);
return EXIT_FAILURE;
}
break;
case 'a': a = 1; ARG_FLAG(); break;
case 'b': b = 1; ARG_FLAG(); break;
case 'c': c = 1; ARG_FLAG(); break;
case '\0': readstdin = 1; break;
} ARG_END;
The downside is that those few short flag only arguments no longer line up nicely with the others, and they come after the default, which could be a little bit confusing.
Footnote:
[0] Except if that second dash is the last character of the argument, in which case -- is the end of flags marker, meaning any further arguments that begin with `-` are just funky positional paramaters
So when the character is '-', it starts just before the "if (0)" (this part is hidden within the ARG_BEGIN macro), and as you noted, it will never enter that block. However, when the character is 'a', 'b', 'c', or nothing (the end-of-string marker), it will jump directly to the corresponding "case ...:" label, even though it's within that "unreachable" block.