aboutsummaryrefslogtreecommitdiff
path: root/src/Args.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/Args.c')
-rw-r--r--src/Args.c219
1 files changed, 219 insertions, 0 deletions
diff --git a/src/Args.c b/src/Args.c
new file mode 100644
index 0000000..e780fed
--- /dev/null
+++ b/src/Args.c
@@ -0,0 +1,219 @@
+#include <BH/Args.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <memory.h>
+
+
+static int BH_ArgsExtractArg(int argc,
+ char **argv,
+ BH_ArgsOption *option,
+ BH_ArgsCallback callback,
+ void *data,
+ int *i,
+ int isLong,
+ char *next)
+{
+ if (isLong && *next == '=')
+ return callback(option->key, next + 1, data);
+
+ if (!isLong && *next)
+ return callback(option->key, next, data);
+
+ if ((*i) + 1 < argc && !(argv[(*i) + 1][0] == '-' && argv[(*i) + 1][1]))
+ return callback(option->key, argv[++(*i)], data);
+
+ if (option->flags & BH_ARGS_OPTIONAL)
+ return callback(option->key, NULL, data);
+
+ return BH_ERROR;
+}
+
+
+static int BH_ArgsParseShort(int argc,
+ char **argv,
+ BH_ArgsOption *options,
+ BH_ArgsCallback callback,
+ void *data,
+ int *i)
+{
+ char *symbol;
+ BH_ArgsOption *option;
+
+ for (symbol = argv[*i] + 1; *symbol; symbol++)
+ {
+ /* Find and process option */
+ for (option = options; option->key || option->name; option++)
+ {
+ if (option->key > 128 || *symbol != option->key)
+ continue;
+
+ if (option->flags & BH_ARGS_VALUE)
+ return BH_ArgsExtractArg(argc, argv, option, callback, data, i, 0, symbol + 1);
+
+ if (callback(option->key, NULL, data))
+ return BH_ERROR;
+
+ break;
+ }
+
+ /* Unknown option */
+ if (!option->key && !option->name)
+ {
+ callback(BH_ARGS_UNKNOWN, argv[*i], data);
+ return BH_ERROR;
+ }
+ }
+
+ return BH_OK;
+}
+
+
+static int BH_ArgsParseLong(int argc,
+ char **argv,
+ BH_ArgsOption *options,
+ BH_ArgsCallback callback,
+ void *data,
+ int *i)
+{
+ char *start, *end;
+ BH_ArgsOption *option;
+
+ /* Extract option name from argv */
+ start = argv[*i] + 2;
+ end = argv[*i] + 2;
+ while (isgraph(*end) && *end != '=')
+ end++;
+
+ if (end == start)
+ return BH_ERROR;
+
+ /* Find and process option */
+ for (option = options; option->key || option->name; option++)
+ {
+ if (!option->name || strlen(option->name) != (size_t)(end - start))
+ continue;
+
+ if (memcmp(option->name, start, end - start))
+ continue;
+
+ if (option->flags & BH_ARGS_VALUE)
+ return BH_ArgsExtractArg(argc, argv, option, callback, data, i, 1, end);
+
+ return callback(option->key, NULL, data);
+ }
+
+ /* Unknown long option */
+ callback(BH_ARGS_UNKNOWN, argv[*i], data);
+ return BH_ERROR;
+}
+
+
+int BH_ArgsParse(int argc,
+ char **argv,
+ BH_ArgsOption *options,
+ BH_ArgsCallback callback,
+ void *data)
+{
+ int i, j, ignoreRest, size;
+ char *arg;
+
+ ignoreRest = 0; i = 1; size = argc;
+ while (i < argc)
+ {
+ arg = argv[i];
+
+ if (arg[0] == '-' && arg[1] && !ignoreRest)
+ {
+ /* Parse ingore, short or long option */
+ if (arg[1] == '-')
+ {
+ if (arg[2] && BH_ArgsParseLong(argc, argv, options, callback, data, &i))
+ return BH_ERROR;
+ else if (!arg[2])
+ ignoreRest = 1;
+
+ }
+ else if (arg[1] && BH_ArgsParseShort(argc, argv, options, callback, data, &i))
+ return BH_ERROR;
+
+ i++;
+ }
+ else
+ {
+ /* Shift arguments */
+ for (j = i; j < size - 1; j++)
+ argv[j] = argv[j + 1];
+ argv[size - 1] = arg;
+
+ if (callback(BH_ARGS_ARGUMENT, arg, data))
+ return BH_ERROR;
+
+ argc--;
+ }
+ }
+ return BH_OK;
+}
+
+
+void BH_ArgsHelp(BH_ArgsOption *options,
+ int padding)
+{
+ int width = 0;
+ BH_ArgsOption *option;
+
+ if (padding == 0)
+ padding = 30;
+
+ /* Calculate option column width */
+ for (option = options; option->key || option->name; option++)
+ {
+ const char *symbol;
+ width = 5;
+
+ /* Print option column with padding */
+ if (isgraph(option->key))
+ {
+ fputs(" -", stdout);
+ fputc(option->key, stdout);
+ }
+ else
+ fputs(" ", stdout);
+
+ if (isgraph(option->key) && option->name)
+ fputs(", ", stdout);
+ else
+ fputs(" ", stdout);
+
+ if (option->name)
+ {
+ width += 3 + strlen(option->name);
+ fputs("--", stdout);
+ fputs(option->name, stdout);
+ fputc(' ', stdout);
+ }
+
+ if (option->flags & BH_ARGS_VALUE)
+ {
+ width += 8;
+ if (option->flags & BH_ARGS_OPTIONAL)
+ {
+ width += 2;
+ fputs("[<value>] ", stdout);
+ }
+ else
+ fputs("<value> ", stdout);
+ }
+ for (; width++ < padding; fputc(' ', stdout));
+
+ /* Print description */
+ for (symbol = option->description; symbol && *symbol; symbol++)
+ {
+ fputc(*symbol, stdout);
+
+ if (*symbol == '\n')
+ for (width = 0; width++ < padding; fputc(' ', stdout));
+ }
+ fputc('\n', stdout);
+ }
+}