/* See LICENSE file for copyright and license details. */
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "test.h"
#include "libsimple-arg.h"
#define PARSER_BEGIN\
int old_argc = argc;\
char **old_argv = argv;\
{
#define PARSER_END\
;}\
assert(argv0 == old_argv[0]);\
assert(argv == &old_argv[old_argc - argc]);\
return argc
#define PARSER_END_NO_ARGV0\
}\
assert(argv0 == NULL);\
assert(argv == &old_argv[old_argc - argc]);\
return argc
#define SHORT_WITHOUT_ARG(SHORT_FLAG)\
do {\
assert(nparsed < sizeof(parsed) / sizeof(*parsed));\
assert(FLAG() == (SHORT_FLAG)[1]);\
parsed[nparsed].short_used = 1;\
parsed[nparsed].long_used = 0;\
parsed[nparsed].have_arg = 0;\
parsed[nparsed].short_flag[0] = (SHORT_FLAG)[0];\
parsed[nparsed].short_flag[1] = (SHORT_FLAG)[1];\
parsed[nparsed].short_flag[2] = '\0';\
parsed[nparsed].long_flag[0] = '\0';\
parsed[nparsed].argument[0] = '\0';\
nparsed += 1;\
} while (0)
#define SHORT_WITH_ARG(SHORT_FLAG)\
do {\
char *arg__;\
assert(nparsed < sizeof(parsed) / sizeof(*parsed));\
assert(FLAG() == (SHORT_FLAG)[1]);\
parsed[nparsed].short_used = 1;\
parsed[nparsed].long_used = 0;\
parsed[nparsed].have_arg = 1;\
parsed[nparsed].short_flag[0] = (SHORT_FLAG)[0];\
parsed[nparsed].short_flag[1] = (SHORT_FLAG)[1];\
parsed[nparsed].short_flag[2] = '\0';\
parsed[nparsed].long_flag[0] = '\0';\
assert(strlen(ARG()) < sizeof(parsed[nparsed].argument));\
assert((arg__ = ARG()));\
assert(arg__ == ARGNULL());\
stpcpy(parsed[nparsed].argument, ARG());\
nparsed += 1;\
} while (0)
#define SHORT_WITH_MISSING_ARG(SHORT_FLAG)\
do {\
assert(!ARGNULL());\
ARG();\
} while (0)
#define SHORT_WITH_ARGHERE(SHORT_FLAG)\
do {\
assert(nparsed < sizeof(parsed) / sizeof(*parsed));\
assert(FLAG() == ARGHERE()[0]);\
parsed[nparsed].short_used = 1;\
parsed[nparsed].long_used = 0;\
parsed[nparsed].have_arg = 1;\
parsed[nparsed].short_flag[0] = (SHORT_FLAG)[0];\
parsed[nparsed].short_flag[1] = (SHORT_FLAG)[1];\
parsed[nparsed].short_flag[2] = '\0';\
parsed[nparsed].long_flag[0] = '\0';\
assert(strlen(ARGHERE()) < sizeof(parsed[nparsed].argument));\
stpcpy(parsed[nparsed].argument, ARGHERE());\
nparsed += 1;\
} while (0)
#define ASSERT_ENTRY(SHORT_FLAG, LONG_FLAG, ARGUMENT)\
do {\
const char *volatile short_flag__ = (SHORT_FLAG);\
const char *volatile long_flag__ = (LONG_FLAG);\
const char *volatile argument__ = (ARGUMENT);\
assert(parsedi < nparsed);\
assert(parsed[parsedi].short_used == !!short_flag__);\
if (short_flag__) assert(!strcmp(parsed[parsedi].short_flag, short_flag__));\
assert(parsed[parsedi].long_used == !!long_flag__);\
if (long_flag__) assert(!strcmp(parsed[parsedi].long_flag, long_flag__));\
assert(parsed[parsedi].have_arg == !!argument__);\
if (argument__) assert(!strcmp(parsed[parsedi].argument, argument__));\
parsedi += 1;\
} while (0)
#define ASSERT_END()\
do {\
assert(parsedi == nparsed);\
parsedi = 0;\
} while (0)
struct entry {
char short_used;
char long_used;
char have_arg;
char short_flag[3];
char long_flag[256];
char argument[256];
};
static struct entry parsed[100];
static size_t nparsed = 0;
static size_t parsedi = 0;
static jmp_buf usage_jmp;
extern char *argv0_1;
extern char *argv0_2;
extern char *argv0_3;
extern char *argv0_0;
#define usage usage1
#define argv0 argv0_1
USAGE("-1");
#undef usage
#undef argv0
#define usage usage2
#define argv0 argv0_2
NUSAGE(2, "-2");
#undef usage
#undef argv0
#define usage usage3
#define argv0 argv0_3
NUSAGE(3, "");
#undef usage
#undef argv0
#define usage usage0
#define argv0 argv0_0
NUSAGE(0, NULL);
#undef usage
#undef argv0
#define apply(...) apply_(__VA_ARGS__, NULL)
static int
apply_(int (*func)(int argc, char *argv[]), ...)
{
va_list ap;
char **argv, **old_argv;
const char *arg;
size_t argc = 0, i;
int ret;
va_start(ap, func);
while (va_arg(ap, const char *))
argc += 1;
va_end(ap);
assert((argv = calloc(argc + 1, sizeof(*argv))));
assert((old_argv = calloc(argc + 1, sizeof(*argv))));
va_start(ap, func);
for (i = 0; i < argc; i++) {
assert((arg = va_arg(ap, const char *)));
assert((argv[i] = strdup(arg)));
}
va_end(ap);
memcpy(old_argv, argv, (argc + 1) * sizeof(*argv));
if (setjmp(usage_jmp)) {
ret = -1;
goto out;
}
nparsed = 0;
argv0 = NULL;
ret = func((int)argc, argv);
out:
va_start(ap, func);
for (i = 0; i < argc - (size_t)ret; i++) {
va_arg(ap, const char *);
free(old_argv[i]);
}
for (; i < argc; i++) {
assert(argv[i] == old_argv[i]);
assert(!strcmp(argv[i], va_arg(ap, const char *)));
free(old_argv[i]);
}
va_end(ap);
free(argv);
free(old_argv);
return ret;
}
static void
usage(void)
{
longjmp(usage_jmp, 1);
}
static int
parser1(int argc, char *argv[])
{
PARSER_BEGIN;
ARGBEGIN {
case 'a': SHORT_WITHOUT_ARG("-a"); break;
case 'b': SHORT_WITHOUT_ARG("-b"); break;
case 'c': SHORT_WITHOUT_ARG("-c"); break;
case 'd': SHORT_WITHOUT_ARG("-d"); break;
case 'e': SHORT_WITHOUT_ARG("-e"); break;
case 'A': SHORT_WITHOUT_ARG("-A"); break;
case 'B': SHORT_WITHOUT_ARG("-B"); break;
case 'C': SHORT_WITHOUT_ARG("-C"); break;
case 'D': SHORT_WITHOUT_ARG("-D"); break;
case 'E': SHORT_WITHOUT_ARG("-E"); break;
case 'x': SHORT_WITH_ARG("-x"); break;
case 'y': SHORT_WITH_ARG("-y"); break;
case 'z': SHORT_WITH_ARG("-z"); break;
case 'X': SHORT_WITH_ARG("-X"); break;
case 'Y': SHORT_WITH_ARG("-Y"); break;
case 'Z': SHORT_WITH_ARG("-Z"); break;
case '-': SHORT_WITHOUT_ARG("--"); break;
case ARGNUM: SHORT_WITH_ARGHERE("-#"); break;
case '@': SHORT_WITH_MISSING_ARG("-@"); break;
default:
usage();
} ARGEND;
PARSER_END;
}
static int
parser2(int argc, char *argv[])
{
PARSER_BEGIN;
SUBARGBEGIN {
case 'a': SHORT_WITHOUT_ARG("-a"); break;
case 'b': SHORT_WITHOUT_ARG("-b"); break;
case 'c': SHORT_WITHOUT_ARG("-c"); break;
case 'd': SHORT_WITHOUT_ARG("-d"); break;
case 'e': SHORT_WITHOUT_ARG("-e"); break;
case 'A': SHORT_WITHOUT_ARG("-A"); break;
case 'B': SHORT_WITHOUT_ARG("-B"); break;
case 'C': SHORT_WITHOUT_ARG("-C"); break;
case 'D': SHORT_WITHOUT_ARG("-D"); break;
case 'E': SHORT_WITHOUT_ARG("-E"); break;
case 'x': SHORT_WITH_ARG("-x"); break;
case 'y': SHORT_WITH_ARG("-y"); break;
case 'z': SHORT_WITH_ARG("-z"); break;
case 'X': SHORT_WITH_ARG("-X"); break;
case 'Y': SHORT_WITH_ARG("-Y"); break;
case 'Z': SHORT_WITH_ARG("-Z"); break;
case '-': SHORT_WITHOUT_ARG("--"); break;
case ARGNUM: SHORT_WITH_ARGHERE("-#"); break;
case '@': SHORT_WITH_MISSING_ARG("-@"); break;
default:
usage();
} ARGEND;
PARSER_END_NO_ARGV0;
}
static int
parser3(int argc, char *argv[])
{
PARSER_BEGIN;
NOFLAGS(0);
PARSER_END;
}
static int
parser4(int argc, char *argv[])
{
PARSER_BEGIN;
NOFLAGS(argc);
PARSER_END;
}
static int
parser5(int argc, char *argv[])
{
PARSER_BEGIN;
ARGBEGIN {
case 'a': SHORT_WITHOUT_ARG("-a"); break;
case 'x': SHORT_WITHOUT_ARG("-x"); break;
case '-': SHORT_WITHOUT_ARG("--"); break;
default:
usage();
} ARGALT('+') {
case 'a': SHORT_WITHOUT_ARG("+a"); break;
case 'y': SHORT_WITHOUT_ARG("+y"); break;
case '+': SHORT_WITHOUT_ARG("++"); break;
default:
usage();
} ARGALT('/') {
case 'a': SHORT_WITHOUT_ARG("/a"); break;
case 'z': SHORT_WITHOUT_ARG("/z"); break;
case '/': SHORT_WITHOUT_ARG("//"); break;
default:
usage();
} ARGALT('x') {
case 'a': SHORT_WITHOUT_ARG("xa"); break;
case 'b': SHORT_WITHOUT_ARG("xb"); break;
case 'x': SHORT_WITHOUT_ARG("xx"); break;
default:
usage();
} ARGEND;
PARSER_END;
}
static int
parser6(int argc, char *argv[])
{
PARSER_BEGIN;
ARGBEGIN2(1, 1) {
default:
argv[0] = LFLAG();
goto stop;
} ARGALT('+') {
case 'a': SHORT_WITHOUT_ARG("+a"); break;
case 'b': SHORT_WITHOUT_ARG("+b"); break;
case 'c': SHORT_WITHOUT_ARG("+c"); break;
case 'd': SHORT_WITHOUT_ARG("+d"); break;
case 'e': SHORT_WITHOUT_ARG("+e"); break;
case 'A': SHORT_WITHOUT_ARG("+A"); break;
case 'B': SHORT_WITHOUT_ARG("+B"); break;
case 'C': SHORT_WITHOUT_ARG("+C"); break;
case 'D': SHORT_WITHOUT_ARG("+D"); break;
case 'E': SHORT_WITHOUT_ARG("+E"); break;
case 'x': SHORT_WITH_ARG("+x"); break;
case 'y': SHORT_WITH_ARG("+y"); break;
case 'z': SHORT_WITH_ARG("+z"); break;
case 'X': SHORT_WITH_ARG("+X"); break;
case 'Y': SHORT_WITH_ARG("+Y"); break;
case 'Z': SHORT_WITH_ARG("+Z"); break;
case '+': SHORT_WITHOUT_ARG("++"); break;
case ARGNUM: SHORT_WITH_ARGHERE("+#"); break;
case '@': SHORT_WITH_MISSING_ARG("+@"); break;
default:
usage();
} ARGEND;
stop:
PARSER_END;
}
int
main(void)
{
const char *a1;
size_t i;
assert(apply(parser1) == 0);
ASSERT_END();
assert(apply(parser1, "-a") == 0);
ASSERT_END();
assert(apply(parser1, "-a", "-a", "-o") == -1);
ASSERT_ENTRY("-a", NULL, NULL);
ASSERT_END();
assert(apply(parser1, "-a", "-a", "-Y") == -1);
ASSERT_ENTRY("-a", NULL, NULL);
ASSERT_END();
for (i = 0; i < 5; i++) {
a1 = ((const char *[]){"--", "z", "-", "", "+a"})[i];
assert(apply(parser1, "argv[0]", "-abdexhello world", "-x", "hi", "-y", "-Y", a1, "-Z", "z") == 2 + !!i);
ASSERT_ENTRY("-a", NULL, NULL);
ASSERT_ENTRY("-b", NULL, NULL);
ASSERT_ENTRY("-d", NULL, NULL);
ASSERT_ENTRY("-e", NULL, NULL);
ASSERT_ENTRY("-x", NULL, "hello world");
ASSERT_ENTRY("-x", NULL, "hi");
ASSERT_ENTRY("-y", NULL, "-Y");
ASSERT_END();
}
assert(apply(parser1, "", "-a1000", "-200") == 0);
ASSERT_ENTRY("-a", NULL, NULL);
ASSERT_ENTRY("-#", NULL, "1000");
ASSERT_ENTRY("-#", NULL, "200");
ASSERT_END();
assert(apply(parser1, "", "-a", "-@") == -1);
ASSERT_ENTRY("-a", NULL, NULL);
ASSERT_END();
assert(apply(parser1, "", "-a-") == -1);
ASSERT_ENTRY("-a", NULL, NULL);
ASSERT_END();
for (i = 0; i < 5; i++) {
a1 = ((const char *[]){"--", "z", "-", "", "+a"})[i];
assert(apply(parser2, "-abdexhello world", "-x", "hi", "-y", "-Y", a1, "-Z", "z") == 2 + !!i);
ASSERT_ENTRY("-a", NULL, NULL);
ASSERT_ENTRY("-b", NULL, NULL);
ASSERT_ENTRY("-d", NULL, NULL);
ASSERT_ENTRY("-e", NULL, NULL);
ASSERT_ENTRY("-x", NULL, "hello world");
ASSERT_ENTRY("-x", NULL, "hi");
ASSERT_ENTRY("-y", NULL, "-Y");
ASSERT_END();
}
assert(apply(parser3, "x") == 0); ASSERT_END();
assert(apply(parser3, "y", "z") == 1); ASSERT_END();
assert(apply(parser3, "x", "z", "-a") == 2); ASSERT_END();
assert(apply(parser3, "y", "-z", "a") == -1); ASSERT_END();
assert(apply(parser3, "x", "--", "-a") == 1); ASSERT_END();
assert(apply(parser3, "", "--") == 0); ASSERT_END();
assert(apply(parser3, "-", "--x") == -1); ASSERT_END();
assert(apply(parser3, "", "+") == 1); ASSERT_END();
assert(apply(parser3, "-", "") == 1); ASSERT_END();
assert(apply(parser3, "", "-") == 1); ASSERT_END();
assert(apply(parser4, "x") == 0); ASSERT_END();
assert(apply(parser4, "y", "z") == -1); ASSERT_END();
assert(apply(parser4, "", "z", "-a") == -1); ASSERT_END();
assert(apply(parser4, "-", "-z", "a") == -1); ASSERT_END();
assert(apply(parser4, "", "--", "-a") == -1); ASSERT_END();
assert(apply(parser4, "-", "--") == 0); ASSERT_END();
assert(apply(parser4, "", "--x") == -1); ASSERT_END();
assert(apply(parser4, "-", "+") == -1); ASSERT_END();
assert(apply(parser4, "", "") == -1); ASSERT_END();
assert(apply(parser4, "-", "-") == -1); ASSERT_END();
for (i = 0; i < 4; i++) {
a1 = ((const char *[]){"--", "++", "//", "xx"})[i];
assert(apply(parser5, "<>", "-a", "+a", "/a", "xa", "-x", "+y", "/z", "xb", a1, "yy") == 1 + !!i);
ASSERT_ENTRY("-a", NULL, NULL);
ASSERT_ENTRY("+a", NULL, NULL);
ASSERT_ENTRY("/a", NULL, NULL);
ASSERT_ENTRY("xa", NULL, NULL);
ASSERT_ENTRY("-x", NULL, NULL);
ASSERT_ENTRY("+y", NULL, NULL);
ASSERT_ENTRY("/z", NULL, NULL);
ASSERT_ENTRY("xb", NULL, NULL);
ASSERT_END();
}
assert(apply(parser5, "<>", "-y") == -1); ASSERT_END();
assert(apply(parser5, "<>", "-z") == -1); ASSERT_END();
assert(apply(parser5, "<>", "-b") == -1); ASSERT_END();
assert(apply(parser5, "<>", "+x") == -1); ASSERT_END();
assert(apply(parser5, "<>", "+z") == -1); ASSERT_END();
assert(apply(parser5, "<>", "+b") == -1); ASSERT_END();
assert(apply(parser5, "<>", "/x") == -1); ASSERT_END();
assert(apply(parser5, "<>", "/y") == -1); ASSERT_END();
assert(apply(parser5, "<>", "/b") == -1); ASSERT_END();
assert(apply(parser5, "<>", "xy") == -1); ASSERT_END();
assert(apply(parser5, "<>", "xz") == -1); ASSERT_END();
for (i = 0; i < 6; i++) {
a1 = ((const char *[]){"++", "z", "+", "", "-a", "--"})[i];
assert(apply(parser6, "0", "+abdexhello world", "+x", "hi", "+y", "+Y", a1, "+Z", "z") == 3);
ASSERT_ENTRY("+a", NULL, NULL);
ASSERT_ENTRY("+b", NULL, NULL);
ASSERT_ENTRY("+d", NULL, NULL);
ASSERT_ENTRY("+e", NULL, NULL);
ASSERT_ENTRY("+x", NULL, "hello world");
ASSERT_ENTRY("+x", NULL, "hi");
ASSERT_ENTRY("+y", NULL, "+Y");
ASSERT_END();
}
argv0_1 = "[1]";
assert_exit(usage1());
assert(exit_status == 1);
assert_stderr("usage: [1] -1\n");
argv0_2 = "[2]";
assert_exit(usage2());
assert_stderr("usage: [2] -2\n");
assert(exit_status == 2);
argv0_3 = "[3]";
assert_exit(usage3());
assert_stderr("usage: [3]\n");
assert(exit_status == 3);
argv0_0 = "[0]";
assert_exit(usage0());
assert_stderr("usage: [0]\n");
assert(exit_status == 0);
return 0;
}