xopt => getopt_long. I give up on xopt. It didn't help me as much as I had hoped, and I had problems building for mingw with only some versions of GCC. Not worth any further time.

This commit is contained in:
2024-02-25 14:45:31 -05:00
parent 4cb82d81b7
commit 8c13f5dbba
32 changed files with 263 additions and 4426 deletions

View File

@ -11,8 +11,8 @@
#include "backtrace.h"
#include "sqlite3.h"
#include "xopt.h"
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
@ -41,47 +41,13 @@ struct backtrace_state* g_backtrace_state;
const char* k_db_path_default = "db.sqlite";
#define XOPT_PARSE( \
name, flags, options, config_ptr, argc, argv, extrac_ptr, extrav_ptr, err_ptr, autohelp_file, autohelp_usage, autohelp_prefix, autohelp_suffix, autohelp_spacer) \
do \
{ \
xoptContext* _xopt_ctx; \
*(err_ptr) = NULL; \
_xopt_ctx = xopt_context((name), (options), ((flags) ^ XOPT_CTX_POSIXMEHARDER), (err_ptr)); \
if (*(err_ptr)) \
break; \
*extrac_ptr = xopt_parse(_xopt_ctx, (argc), (argv), (config_ptr), (extrav_ptr), (err_ptr)); \
if ((config_ptr)->help) \
{ \
xoptAutohelpOptions __xopt_autohelp_opts; \
__xopt_autohelp_opts.usage = (autohelp_usage); \
__xopt_autohelp_opts.prefix = (autohelp_prefix); \
__xopt_autohelp_opts.suffix = (autohelp_suffix); \
__xopt_autohelp_opts.spacer = (autohelp_spacer); \
xopt_autohelp(_xopt_ctx, (autohelp_file), &__xopt_autohelp_opts, (err_ptr)); \
if (*(err_ptr)) \
goto __xopt_end_free_extrav; \
free(_xopt_ctx); \
goto xopt_help; \
} \
if (*(err_ptr)) \
goto __xopt_end_free_ctx; \
__xopt_end_free_ctx: \
free(_xopt_ctx); \
break; \
__xopt_end_free_extrav: \
free(*(extrav_ptr)); \
free(_xopt_ctx); \
break; \
} while (false)
#if !TARGET_OS_IPHONE && !defined(__ANDROID__)
static int _tf_command_test(const char* file, int argc, char* argv[]);
static int _tf_command_import(const char* file, int argc, char* argv[]);
static int _tf_command_export(const char* file, int argc, char* argv[]);
static int _tf_command_run(const char* file, int argc, char* argv[]);
static int _tf_command_sandbox(const char* file, int argc, char* argv[]);
static int _tf_command_usage(const char* file, int argc, char* argv[]);
static int _tf_command_usage(const char* file);
typedef struct _command_t
{
@ -101,140 +67,182 @@ const command_t k_commands[] = {
static int _tf_command_test(const char* file, int argc, char* argv[])
{
#if !defined(__ANDROID__)
typedef struct args_t
tf_test_options_t test_options =
{
const char* tests;
bool help;
} args_t;
xoptOption options[] = {
{ "tests", 't', offsetof(args_t, tests), NULL, XOPT_TYPE_STRING, NULL, "Comma-separated list of test names to run." },
{ "help", 'h', offsetof(args_t, help), NULL, XOPT_TYPE_BOOL, NULL, "Shows this help message." },
XOPT_NULLOPTION,
};
args_t args = { 0 };
const char** extras = NULL;
int extra_count = 0;
const char* err = NULL;
XOPT_PARSE(file, XOPT_CTX_KEEPFIRST | XOPT_CTX_STRICT, options, &args, argc, (const char**)argv, &extra_count, &extras, &err, stderr, "test [options]", "options:", NULL, 15);
if (err)
{
fprintf(stderr, "Error: %s\n", err);
return 2;
}
tf_test_options_t test_options = {
.exe_path = file,
.tests = args.tests,
};
bool show_usage = false;
while (!show_usage)
{
static struct option k_options[] =
{
{ "tests", required_argument, NULL, 't' },
{ "help", no_argument, NULL, 'h' },
{ 0 },
};
int c = getopt_long(argc, argv, "t:h", k_options, NULL);
if (c == -1)
{
break;
}
switch (c)
{
case '?':
case 'h':
default:
show_usage = true;
break;
case 't':
test_options.tests = optarg;
break;
}
}
for (int i = optind; i < argc; i++)
{
tf_printf("Unexpected argument: %s\n", argv[i]);
show_usage = true;
}
if (show_usage)
{
tf_printf("\n%s test [options]\n\n", file);
tf_printf("options\n");
tf_printf(" -t, --tests tests Comma-separated list of tests to run. (default: all)\n");
tf_printf(" -h, --help Show this usage information.\n");
return EXIT_FAILURE;
}
tf_tests(&test_options);
if (extras)
{
free((void*)extras);
}
return 0;
xopt_help:
if (extras)
{
free((void*)extras);
}
return EXIT_SUCCESS;
#else
return EXIT_FAILURE;
#endif
return 1;
}
static int _tf_command_import(const char* file, int argc, char* argv[])
{
typedef struct args_t
{
const char* user;
const char* db_path;
bool help;
} args_t;
const char* user = "import";
const char* db_path = k_db_path_default;
bool show_usage = false;
xoptOption options[] = {
{ "user", 'u', offsetof(args_t, user), NULL, XOPT_TYPE_STRING, NULL, "User into whose account apps will be imported (default: \"import\")." },
{ "db-path", 'd', offsetof(args_t, db_path), NULL, XOPT_TYPE_STRING, NULL, "Sqlite database path (default: db.sqlite)." },
{ "help", 'h', offsetof(args_t, help), NULL, XOPT_TYPE_BOOL, NULL, "Shows this help message." },
XOPT_NULLOPTION,
};
args_t args = { .user = "import", .db_path = k_db_path_default };
const char** extras = NULL;
int extra_count = 0;
const char* err = NULL;
XOPT_PARSE(file, XOPT_CTX_KEEPFIRST | XOPT_CTX_POSIXMEHARDER | XOPT_CTX_STRICT, options, &args, argc, (const char**)argv, &extra_count, &extras, &err, stderr,
"import [options] [paths] ...", "options:", NULL, 15);
if (err)
while (!show_usage)
{
fprintf(stderr, "Error: %s\n", err);
return 2;
static struct option k_options[] =
{
{ "user", required_argument, NULL, 'u' },
{ "db-path", required_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
{ 0 },
};
int c = getopt_long(argc, argv, "u:d:h", k_options, NULL);
if (c == -1)
{
break;
}
switch (c)
{
case '?':
case 'h':
default:
show_usage = true;
break;
case 'u':
user = optarg;
break;
case 'd':
db_path = optarg;
break;
}
}
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, args.db_path);
if (extra_count)
if (show_usage)
{
for (int i = 0; i < extra_count; i++)
tf_printf("\n%s import [options] [paths...]\n\n", file);
tf_printf("options:\n");
tf_printf(" -u, --user user User into whose account apps will be imported (default: \"import\").\n");
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", k_db_path_default);
tf_printf(" -h, --help Show this usage information.\n");
return EXIT_FAILURE;
}
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path);
if (optind < argc)
{
for (int i = optind; i < argc; i++)
{
tf_printf("Importing %s...\n", extras[i]);
tf_ssb_import(ssb, args.user, extras[i]);
tf_printf("Importing %s...\n", argv[i]);
tf_ssb_import(ssb, user, argv[i]);
}
}
else
{
tf_printf("Importing %s...\n", "apps");
tf_ssb_import(ssb, args.user, "apps");
tf_ssb_import(ssb, user, "apps");
}
tf_ssb_destroy(ssb);
if (extras)
{
free((void*)extras);
}
return 0;
xopt_help:
if (extras)
{
free((void*)extras);
}
return 1;
return EXIT_SUCCESS;
}
static int _tf_command_export(const char* file, int argc, char* argv[])
{
typedef struct args_t
{
const char* user;
const char* db_path;
bool help;
} args_t;
const char* user = "core";
const char* db_path = k_db_path_default;
bool show_usage = false;
xoptOption options[] = {
{ "db-path", 'd', offsetof(args_t, db_path), NULL, XOPT_TYPE_STRING, NULL, "Sqlite database path (default: db.sqlite)." },
{ "user", 'u', offsetof(args_t, user), NULL, XOPT_TYPE_STRING, NULL, "User into whose apps will be exported (default: \"core\")." },
{ "help", 'h', offsetof(args_t, help), NULL, XOPT_TYPE_BOOL, NULL, "Shows this help message." },
XOPT_NULLOPTION,
};
args_t args = { .user = "core", .db_path = k_db_path_default };
const char** extras = NULL;
int extra_count = 0;
const char* err = NULL;
XOPT_PARSE(file, XOPT_CTX_KEEPFIRST | XOPT_CTX_POSIXMEHARDER | XOPT_CTX_STRICT, options, &args, argc, (const char**)argv, &extra_count, &extras, &err, stderr,
"export [options] [paths] ...", "options:", NULL, 15);
if (err)
while (!show_usage)
{
fprintf(stderr, "Error: %s\n", err);
return 2;
}
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, args.db_path);
if (extra_count)
{
for (int i = 0; i < extra_count; i++)
static const struct option k_options[] =
{
tf_printf("Exporting %s...\n", extras[i]);
tf_ssb_export(ssb, extras[i]);
{ "user", required_argument, NULL, 'u' },
{ "db-path", required_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
{ 0 },
};
int c = getopt_long(argc, argv, "u:d:h", k_options, NULL);
if (c == -1)
{
break;
}
switch (c)
{
case '?':
case 'h':
default:
show_usage = true;
break;
case 'u':
user = optarg;
break;
case 'd':
db_path = optarg;
break;
}
}
if (show_usage)
{
tf_printf("\n%s export [options] [paths...]\n\n", file);
tf_printf("options:\n");
tf_printf(" -u, --user user User from whose account apps will be exported (default: \"core\").\n");
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", k_db_path_default);
tf_printf(" -h, --help Show this usage information.\n");
tf_printf("\n");
tf_printf("paths Paths of apps to export (example: /~core/ssb /~user/app).\n");
return EXIT_FAILURE;
}
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path);
if (optind < argc)
{
for (int i = optind; i < argc; i++)
{
tf_printf("Exporting %s...\n", argv[i]);
tf_ssb_export(ssb, argv[i]);
}
}
else
@ -253,25 +261,13 @@ static int _tf_command_export(const char* file, int argc, char* argv[])
for (int i = 0; i < (int)_countof(k_export); i++)
{
char buffer[256];
snprintf(buffer, sizeof(buffer), "/~%s/%s", args.user, k_export[i]);
snprintf(buffer, sizeof(buffer), "/~%s/%s", user, k_export[i]);
tf_printf("Exporting %s...\n", buffer);
tf_ssb_export(ssb, buffer);
}
}
tf_ssb_destroy(ssb);
if (extras)
{
free((void*)extras);
}
return 0;
xopt_help:
if (extras)
{
free((void*)extras);
}
return 1;
return EXIT_SUCCESS;
}
#endif
@ -412,21 +408,6 @@ static void _shed_privileges()
static int _tf_command_run(const char* file, int argc, char* argv[])
{
xoptOption options[] = {
{ "script", 's', offsetof(tf_run_args_t, script), NULL, XOPT_TYPE_STRING, NULL, "Script to run (default: core/core.js)." },
{ "ssb-port", 'b', offsetof(tf_run_args_t, ssb_port), NULL, XOPT_TYPE_INT, NULL, "Port on which to run SSB (default: 8008)." },
{ "http-port", 'p', offsetof(tf_run_args_t, http_port), NULL, XOPT_TYPE_INT, NULL, "Port on which to run Tilde Friends web server (default: 12345)." },
{ "https-port", 'q', offsetof(tf_run_args_t, https_port), NULL, XOPT_TYPE_INT, NULL, "Port on which to run secure Tilde Friends web server (default: 12346)." },
{ "db-path", 'd', offsetof(tf_run_args_t, db_path), NULL, XOPT_TYPE_STRING, NULL, "Sqlite database path (default: db.sqlite)." },
{ "count", 'n', offsetof(tf_run_args_t, count), NULL, XOPT_TYPE_INT, NULL, "Number of instances to run." },
{ "args", 'a', offsetof(tf_run_args_t, args), NULL, XOPT_TYPE_STRING, NULL, "Arguments of the form key=value,foo=bar,verbose=true." },
{ "one-proc", 'o', offsetof(tf_run_args_t, one_proc), NULL, XOPT_TYPE_BOOL, NULL, "Run everything in one process (unsafely!)." },
{ "zip", 'z', offsetof(tf_run_args_t, zip), NULL, XOPT_TYPE_STRING, NULL, "Zip archive from which to load files." },
{ "verbose", 'v', offsetof(tf_run_args_t, verbose), NULL, XOPT_TYPE_BOOL, NULL, "Log raw messages." },
{ "help", 'h', offsetof(tf_run_args_t, help), NULL, XOPT_TYPE_BOOL, NULL, "Shows this help message." },
XOPT_NULLOPTION,
};
tf_run_args_t args = {
.count = 1,
.script = "core/core.js",
@ -435,16 +416,86 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
.ssb_port = 8008,
.db_path = k_db_path_default,
};
const char** extras = NULL;
int extra_count = 0;
const char* err = NULL;
XOPT_PARSE(file, XOPT_CTX_KEEPFIRST | XOPT_CTX_POSIXMEHARDER | XOPT_CTX_STRICT, options, &args, argc, (const char**)argv, &extra_count, &extras, &err, stderr,
"run [options] [paths] ...", "options:", NULL, 15);
bool show_usage = false;
if (err)
while (!show_usage)
{
fprintf(stderr, "Error: %s\n", err);
return 2;
static const struct option k_options[] =
{
{ "script", required_argument, NULL, 's' },
{ "ssb-port", required_argument, NULL, 'b' },
{ "http-port", required_argument, NULL, 'p' },
{ "https-port", required_argument, NULL, 'q' },
{ "db-path", required_argument, NULL, 'd' },
{ "count", required_argument, NULL, 'n' },
{ "args", required_argument, NULL, 'a' },
{ "one-proc", no_argument, NULL, 'o' },
{ "zip", required_argument, NULL, 'z' },
{ "verbose", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
};
int c = getopt_long(argc, argv, "s:b:p:q:d:n:a:oz:vh", k_options, NULL);
if (c == -1)
{
break;
}
switch (c)
{
case '?':
case 'h':
default:
show_usage = true;
break;
case 's':
args.script = optarg;
break;
case 'b':
args.ssb_port = atoi(optarg);
break;
case 'p':
args.http_port = atoi(optarg);
break;
case 'q':
args.https_port = atoi(optarg);
break;
case 'd':
args.db_path = optarg;
break;
case 'n':
args.count = atoi(optarg);
break;
case 'a':
args.args = optarg;
break;
case 'o':
args.one_proc = true;
break;
case 'z':
args.zip = optarg;
break;
case 'v':
args.verbose = true;
break;
}
}
if (show_usage)
{
tf_printf("\n%s run [options]\n\n", file);
tf_printf("options\n");
tf_printf(" -s, --script script Script to run (default: core/core.js).\n");
tf_printf(" -b, --ssb-port port Port on which to run SSB (default: 8008, 0 disables).\n");
tf_printf(" -p, --http-port port Port on which to run Tilde Friends web server (default: 12345).\n");
tf_printf(" -q, --https-port port Port on which to run secure Tilde Friends web server (default: 12346).\n");
tf_printf(" -d, --db-path path SQLite database path (default: %s).\n", k_db_path_default);
tf_printf(" -n, --count count Number of instances to run.\n");
tf_printf(" -a, --args args Arguments of the format key=value,foo=bar,verbose=true.\n");
tf_printf(" -o, --one-proc Run everything in one process (unsafely!).\n");
tf_printf(" -z, --zip path Zip archive from which to load files.\n");
tf_printf(" -v, --verbose Log raw messages.\n");
tf_printf(" -h, --help Show this usage information.\n");
return EXIT_FAILURE;
}
int result = 0;
@ -479,44 +530,41 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
tf_free(data);
tf_free(threads);
}
if (extras)
{
free((void*)extras);
}
return result;
xopt_help:
if (extras)
{
free((void*)extras);
}
return 1;
}
static int _tf_command_sandbox(const char* file, int argc, char* argv[])
{
typedef struct args_t
{
const char* script;
bool help;
} args_t;
bool show_usage = false;
xoptOption options[] = {
{ "help", 'h', offsetof(args_t, help), NULL, XOPT_TYPE_BOOL, NULL, "Shows this help message." },
XOPT_NULLOPTION,
};
args_t args = { 0 };
const char** extras = NULL;
int extra_count = 0;
const char* err = NULL;
XOPT_PARSE(file, XOPT_CTX_KEEPFIRST | XOPT_CTX_POSIXMEHARDER | XOPT_CTX_STRICT, options, &args, argc, (const char**)argv, &extra_count, &extras, &err, stderr,
"sandbox [options]", "options:", NULL, 15);
if (err)
while (!show_usage)
{
fprintf(stderr, "Error: %s\n", err);
return 2;
static const struct option k_options[] =
{
{ "help", no_argument, NULL, 'h' },
{ 0 },
};
int c = getopt_long(argc, argv, "h", k_options, NULL);
if (c == -1)
{
break;
}
switch (c)
{
case '?':
case 'h':
default:
show_usage = true;
break;
}
}
if (show_usage)
{
tf_printf("\nUsage: %s sandbox [options]\n\n", file);
tf_printf("options:\n");
tf_printf(" -h, --help Show this usage information.\n");
return EXIT_FAILURE;
}
#if defined(__linux__)
@ -528,22 +576,11 @@ static int _tf_command_sandbox(const char* file, int argc, char* argv[])
/* The caller will trigger tf_task_activate with a message. */
tf_task_run(task);
tf_task_destroy(task);
if (extras)
{
free((void*)extras);
}
return 0;
xopt_help:
if (extras)
{
free((void*)extras);
}
return 1;
return EXIT_SUCCESS;
}
#if !defined(__ANDROID__)
static int _tf_command_usage(const char* file, int argc, char* argv[])
static int _tf_command_usage(const char* file)
{
tf_printf("Usage: %s command [command-options]\n", file);
tf_printf("commands:\n");
@ -675,15 +712,15 @@ int main(int argc, char* argv[])
const command_t* command = &k_commands[i];
if (strcmp(argv[1], command->name) == 0)
{
result = command->callback(argv[0], argc - 2, argv + 2);
result = command->callback(argv[0], argc - 1, argv + 1);
goto done;
}
}
result = _tf_command_usage(argv[0], argc, argv);
result = _tf_command_usage(argv[0]);
}
else
{
result = _tf_command_run(argv[0], argc - 1, argv + 1);
result = _tf_command_run(argv[0], argc, argv);
}
done:
tf_mem_shutdown();