aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Voss <mail@thomasvoss.com> 2024-03-10 17:25:09 +0200
committerThomas Voss <mail@thomasvoss.com> 2024-03-10 17:25:09 +0200
commitc3d40a0dba2d4cff85ef78d06973aeacc778cc74 (patch)
treef94238037c1550d8d53df7092a8883ed780d0c99
parent8abb53d6786b822ba777dd5ada337e6772da309f (diff)
Make optparse() use u8view’s for longopts
-rw-r--r--include/mbstring.h2
-rw-r--r--include/optparse.h2
-rw-r--r--lib/optparse/optparse.c118
3 files changed, 69 insertions, 53 deletions
diff --git a/include/mbstring.h b/include/mbstring.h
index 3ac9277..9a95836 100644
--- a/include/mbstring.h
+++ b/include/mbstring.h
@@ -8,6 +8,8 @@
#include "__rune.h"
#include "__u8view.h"
+#define U8V(s) ((struct u8view){.p = (s), .len = sizeof(s) - 1})
+
/* clang-format off */
#define U8_BYTE_1(x) (((x) & 0x80) == 0x00)
#define U8_BYTE_2(x) (((x) & 0xE0) == 0xC0)
diff --git a/include/optparse.h b/include/optparse.h
index 70e4318..5d16e67 100644
--- a/include/optparse.h
+++ b/include/optparse.h
@@ -25,7 +25,7 @@ enum op_argkind {
struct op_option {
rune shortopt;
- const char8_t *longopt;
+ struct u8view longopt;
enum op_argkind argtype;
};
diff --git a/lib/optparse/optparse.c b/lib/optparse/optparse.c
index 6f9e4bf..ec2e470 100644
--- a/lib/optparse/optparse.c
+++ b/lib/optparse/optparse.c
@@ -9,15 +9,14 @@
#define OPT_MSG_MISSING "option requires an argument"
#define OPT_MSG_TOOMANY "option takes no arguments"
-#define IS_SHORTOPT(s) ((s)[0] == '-' && (s)[1] != '-' && (s)[1] != '\0')
-#define IS_LONGOPT(s) ((s)[0] == '-' && (s)[1] == '-' && (s)[2] != '\0')
+#define IS_SHORTOPT(s) ((s).len > 1 && (s).p[0] == '-' && (s).p[1] != '-')
+#define IS_LONGOPT(s) ((s).len > 2 && (s).p[0] == '-' && (s).p[1] == '-')
#define error(st, msg, x) \
- _Generic((x), const char *: error_s, rune: error_r)((st), (msg), (x))
+ _Generic((x), struct u8view: error_s, rune: error_r)((st), (msg), (x))
-static bool is_a_match(const char *, const char *);
static rune error_r(struct optparse *, const char *, rune);
-static rune error_s(struct optparse *, const char *, const char *);
+static rune error_s(struct optparse *, const char *, struct u8view);
static rune shortopt(struct optparse *, const struct op_option *, size_t);
struct optparse
@@ -35,10 +34,12 @@ optparse(struct optparse *st, const struct op_option *opts, size_t nopts)
st->errmsg[0] = '\0';
st->optarg = (struct u8view){};
- const char *opt = st->_argv[st->optind];
- if (opt == nullptr)
+ struct u8view opt = {.p = st->_argv[st->optind]};
+ if (opt.p == nullptr)
return 0;
- if (streq(opt, "--")) {
+ opt.len = strlen(opt.p);
+
+ if (u8eq(opt, U8V("--"))) {
st->optind++;
return 0;
}
@@ -49,41 +50,61 @@ optparse(struct optparse *st, const struct op_option *opts, size_t nopts)
st->_subopt = 0;
st->optind++;
- opt += 2; /* Skip ‘--’ */
+
+ /* Skip ‘--’ */
+ opt.p += 2;
+ opt.len -= 2;
+
+ const struct op_option *o = nullptr;
+ const char8_t *eq_p = u8chr(opt.p, '=', opt.len);
+ struct u8view opt_no_eq = {
+ .p = opt.p,
+ .len = eq_p == nullptr ? opt.len : (size_t)(eq_p - opt.p),
+ };
for (size_t i = 0; i < nopts; i++) {
- struct op_option o = opts[i];
- if (!is_a_match(opt, o.longopt))
+ if (!u8haspfx(opts[i].longopt.p, opts[i].longopt.len, opt_no_eq.p,
+ opt_no_eq.len))
+ {
continue;
- switch (o.argtype) {
- case OPT_NONE:
- if (strchr(opt, '=') != nullptr)
- return error(st, OPT_MSG_TOOMANY, opt);
- break;
- case OPT_REQ: {
- const char *p = strchr(opt, '=');
- if (p == nullptr) {
- if (st->_argv[st->optind] == nullptr)
- return error(st, OPT_MSG_MISSING, opt);
- st->optarg.p = st->_argv[st->optind++];
- } else
- st->optarg.p = p + 1;
- st->optarg.len = strlen(st->optarg.p);
- } break;
- case OPT_OPT: {
- const char *p = strchr(opt, '=');
- if (p == nullptr)
- st->optarg = (struct u8view){};
- else {
- st->optarg.p = p + 1;
- st->optarg.len = strlen(st->optarg.p);
- }
- } break;
}
- return o.shortopt;
+
+ if (o)
+ return error(st, OPT_MSG_INVALID, opt_no_eq);
+ o = opts + i;
}
- return error(st, OPT_MSG_INVALID, opt);
+ if (o == nullptr)
+ return error(st, OPT_MSG_INVALID, opt_no_eq);
+
+ switch (o->argtype) {
+ case OPT_NONE:
+ if (eq_p != nullptr)
+ return error(st, OPT_MSG_TOOMANY, opt);
+ break;
+ case OPT_OPT:
+ if (eq_p == nullptr)
+ st->optarg = (struct u8view){};
+ else {
+ ASSUME(opt.len > opt_no_eq.len);
+ st->optarg.p = eq_p + 1;
+ st->optarg.len = opt.len - opt_no_eq.len + 1;
+ }
+ break;
+ case OPT_REQ:
+ if (eq_p == nullptr) {
+ if (st->_argv[st->optind] == nullptr)
+ return error(st, OPT_MSG_MISSING, opt);
+ st->optarg.p = st->_argv[st->optind++];
+ st->optarg.len = strlen(st->optarg.p);
+ } else {
+ ASSUME(opt.len > opt_no_eq.len);
+ st->optarg.p = eq_p + 1;
+ st->optarg.len = opt.len - opt_no_eq.len + 1;
+ }
+ break;
+ }
+ return o->shortopt;
}
rune
@@ -127,29 +148,22 @@ out:
return ch;
}
-bool
-is_a_match(const char *u, const char *t)
-{
- for (; *u && *t && *u != '='; u++, t++) {
- if (*u != *t)
- return false;
- }
- return *t == '\0' && (*u == '\0' || *u == '=');
-}
-
rune
-error_s(struct optparse *st, const char *msg, const char *s)
+error_s(struct optparse *st, const char *msg, struct u8view s)
{
- const char *p = strchr(s, '=');
snprintf(st->errmsg, sizeof(st->errmsg), u8"%s — ‘%.*s’", msg,
- (int)(p ? (size_t)(p - s) : strlen(s)), s);
+ U8_PRI_ARGS(s));
return -1;
}
+/* clang-format off */
+
rune
error_r(struct optparse *st, const char *msg, rune ch)
{
char buf[U8_LEN_MAX + 1] = {};
- rtou8(buf, ch, sizeof(buf));
- return error_s(st, msg, buf);
+ return error_s(st, msg, (struct u8view){
+ .p = buf,
+ .len = rtou8(buf, ch, sizeof(buf)),
+ });
}