rage-iodine 1.7.58
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- data/.github/workflows/ruby.yml +42 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.yardopts +8 -0
- data/CHANGELOG.md +1098 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/LIMITS.md +41 -0
- data/README.md +782 -0
- data/Rakefile +23 -0
- data/SPEC-PubSub-Draft.md +159 -0
- data/SPEC-WebSocket-Draft.md +239 -0
- data/bin/console +22 -0
- data/bin/info.md +353 -0
- data/bin/mustache_bench.rb +100 -0
- data/bin/poc/Gemfile.lock +23 -0
- data/bin/poc/README.md +37 -0
- data/bin/poc/config.ru +66 -0
- data/bin/poc/gemfile +1 -0
- data/bin/poc/www/index.html +57 -0
- data/examples/async_task.ru +92 -0
- data/examples/bates/README.md +3 -0
- data/examples/bates/config.ru +342 -0
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +600 -0
- data/examples/config.ru +59 -0
- data/examples/echo.ru +59 -0
- data/examples/etag.ru +16 -0
- data/examples/hello.ru +29 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/rack3.ru +12 -0
- data/examples/redis.ru +70 -0
- data/examples/shootout.ru +73 -0
- data/examples/sub-protocols.ru +90 -0
- data/examples/tcp_client.rb +66 -0
- data/examples/x-sendfile.ru +14 -0
- data/exe/iodine +280 -0
- data/ext/iodine/extconf.rb +110 -0
- data/ext/iodine/fio.c +12096 -0
- data/ext/iodine/fio.h +6390 -0
- data/ext/iodine/fio_cli.c +431 -0
- data/ext/iodine/fio_cli.h +189 -0
- data/ext/iodine/fio_json_parser.h +687 -0
- data/ext/iodine/fio_siphash.c +157 -0
- data/ext/iodine/fio_siphash.h +37 -0
- data/ext/iodine/fio_tls.h +129 -0
- data/ext/iodine/fio_tls_missing.c +649 -0
- data/ext/iodine/fio_tls_openssl.c +1056 -0
- data/ext/iodine/fio_tmpfile.h +50 -0
- data/ext/iodine/fiobj.h +44 -0
- data/ext/iodine/fiobj4fio.h +21 -0
- data/ext/iodine/fiobj_ary.c +333 -0
- data/ext/iodine/fiobj_ary.h +139 -0
- data/ext/iodine/fiobj_data.c +1185 -0
- data/ext/iodine/fiobj_data.h +167 -0
- data/ext/iodine/fiobj_hash.c +409 -0
- data/ext/iodine/fiobj_hash.h +176 -0
- data/ext/iodine/fiobj_json.c +622 -0
- data/ext/iodine/fiobj_json.h +68 -0
- data/ext/iodine/fiobj_mem.h +71 -0
- data/ext/iodine/fiobj_mustache.c +317 -0
- data/ext/iodine/fiobj_mustache.h +62 -0
- data/ext/iodine/fiobj_numbers.c +344 -0
- data/ext/iodine/fiobj_numbers.h +127 -0
- data/ext/iodine/fiobj_str.c +433 -0
- data/ext/iodine/fiobj_str.h +172 -0
- data/ext/iodine/fiobject.c +620 -0
- data/ext/iodine/fiobject.h +654 -0
- data/ext/iodine/hpack.h +1923 -0
- data/ext/iodine/http.c +2736 -0
- data/ext/iodine/http.h +1019 -0
- data/ext/iodine/http1.c +825 -0
- data/ext/iodine/http1.h +29 -0
- data/ext/iodine/http1_parser.h +1835 -0
- data/ext/iodine/http_internal.c +1279 -0
- data/ext/iodine/http_internal.h +248 -0
- data/ext/iodine/http_mime_parser.h +350 -0
- data/ext/iodine/iodine.c +1433 -0
- data/ext/iodine/iodine.h +64 -0
- data/ext/iodine/iodine_caller.c +218 -0
- data/ext/iodine/iodine_caller.h +27 -0
- data/ext/iodine/iodine_connection.c +941 -0
- data/ext/iodine/iodine_connection.h +55 -0
- data/ext/iodine/iodine_defer.c +420 -0
- data/ext/iodine/iodine_defer.h +6 -0
- data/ext/iodine/iodine_fiobj2rb.h +120 -0
- data/ext/iodine/iodine_helpers.c +282 -0
- data/ext/iodine/iodine_helpers.h +12 -0
- data/ext/iodine/iodine_http.c +1280 -0
- data/ext/iodine/iodine_http.h +23 -0
- data/ext/iodine/iodine_json.c +302 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_mustache.c +567 -0
- data/ext/iodine/iodine_mustache.h +6 -0
- data/ext/iodine/iodine_pubsub.c +580 -0
- data/ext/iodine/iodine_pubsub.h +26 -0
- data/ext/iodine/iodine_rack_io.c +273 -0
- data/ext/iodine/iodine_rack_io.h +20 -0
- data/ext/iodine/iodine_store.c +142 -0
- data/ext/iodine/iodine_store.h +20 -0
- data/ext/iodine/iodine_tcp.c +346 -0
- data/ext/iodine/iodine_tcp.h +13 -0
- data/ext/iodine/iodine_tls.c +261 -0
- data/ext/iodine/iodine_tls.h +13 -0
- data/ext/iodine/mustache_parser.h +1546 -0
- data/ext/iodine/redis_engine.c +957 -0
- data/ext/iodine/redis_engine.h +79 -0
- data/ext/iodine/resp_parser.h +317 -0
- data/ext/iodine/scheduler.c +173 -0
- data/ext/iodine/scheduler.h +6 -0
- data/ext/iodine/websocket_parser.h +506 -0
- data/ext/iodine/websockets.c +752 -0
- data/ext/iodine/websockets.h +185 -0
- data/iodine.gemspec +50 -0
- data/lib/iodine/connection.rb +61 -0
- data/lib/iodine/json.rb +42 -0
- data/lib/iodine/mustache.rb +113 -0
- data/lib/iodine/pubsub.rb +55 -0
- data/lib/iodine/rack_utils.rb +43 -0
- data/lib/iodine/tls.rb +16 -0
- data/lib/iodine/version.rb +3 -0
- data/lib/iodine.rb +274 -0
- data/lib/rack/handler/iodine.rb +33 -0
- data/logo.png +0 -0
- metadata +284 -0
@@ -0,0 +1,431 @@
|
|
1
|
+
/*
|
2
|
+
Copyright: Boaz segev, 2017
|
3
|
+
License: MIT
|
4
|
+
|
5
|
+
Feel free to copy, use and enjoy according to the license provided.
|
6
|
+
*/
|
7
|
+
#include <fio.h>
|
8
|
+
|
9
|
+
#include <fio_cli.h>
|
10
|
+
|
11
|
+
#include <stdint.h>
|
12
|
+
#include <stdio.h>
|
13
|
+
#include <stdlib.h>
|
14
|
+
#include <string.h>
|
15
|
+
#include <strings.h>
|
16
|
+
|
17
|
+
/* *****************************************************************************
|
18
|
+
CLI Data Stores
|
19
|
+
***************************************************************************** */
|
20
|
+
|
21
|
+
typedef struct {
|
22
|
+
size_t len;
|
23
|
+
const char *data;
|
24
|
+
} cstr_s;
|
25
|
+
|
26
|
+
#define FIO_SET_OBJ_TYPE const char *
|
27
|
+
#define FIO_SET_KEY_TYPE cstr_s
|
28
|
+
#define FIO_SET_KEY_COMPARE(o1, o2) \
|
29
|
+
(o1.len == o2.len && \
|
30
|
+
(o1.data == o2.data || !memcmp(o1.data, o2.data, o1.len)))
|
31
|
+
#define FIO_SET_NAME fio_cli_hash
|
32
|
+
#include <fio.h>
|
33
|
+
|
34
|
+
static fio_cli_hash_s fio_aliases = FIO_SET_INIT;
|
35
|
+
static fio_cli_hash_s fio_values = FIO_SET_INIT;
|
36
|
+
static size_t fio_unnamed_count = 0;
|
37
|
+
|
38
|
+
typedef struct {
|
39
|
+
int unnamed_min;
|
40
|
+
int unnamed_max;
|
41
|
+
int pos;
|
42
|
+
int unnamed_count;
|
43
|
+
int argc;
|
44
|
+
char const **argv;
|
45
|
+
char const *description;
|
46
|
+
char const **names;
|
47
|
+
} fio_cli_parser_data_s;
|
48
|
+
|
49
|
+
/** this will allow the function definition fio_cli_start to avoid the MACRO */
|
50
|
+
#define AVOID_MACRO
|
51
|
+
|
52
|
+
#define FIO_CLI_HASH_VAL(s) \
|
53
|
+
fio_risky_hash((s).data, (s).len, (uintptr_t)fio_cli_start)
|
54
|
+
|
55
|
+
/* *****************************************************************************
|
56
|
+
CLI Parsing
|
57
|
+
***************************************************************************** */
|
58
|
+
|
59
|
+
/* *****************************************************************************
|
60
|
+
CLI Parsing
|
61
|
+
***************************************************************************** */
|
62
|
+
|
63
|
+
static void fio_cli_map_line2alias(char const *line) {
|
64
|
+
cstr_s n = {.data = line};
|
65
|
+
while (n.data[0] == '-') {
|
66
|
+
while (n.data[n.len] && n.data[n.len] != ' ' && n.data[n.len] != ',') {
|
67
|
+
++n.len;
|
68
|
+
}
|
69
|
+
const char *old = NULL;
|
70
|
+
fio_cli_hash_insert(&fio_aliases, FIO_CLI_HASH_VAL(n), n, (void *)line,
|
71
|
+
&old);
|
72
|
+
if (old) {
|
73
|
+
FIO_LOG_WARNING("CLI argument name conflict detected\n"
|
74
|
+
" The following two directives conflict:\n"
|
75
|
+
"\t%s\n\t%s\n",
|
76
|
+
old, line);
|
77
|
+
}
|
78
|
+
|
79
|
+
while (n.data[n.len] && (n.data[n.len] == ' ' || n.data[n.len] == ',')) {
|
80
|
+
++n.len;
|
81
|
+
}
|
82
|
+
n.data += n.len;
|
83
|
+
n.len = 0;
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
static char const *fio_cli_get_line_type(fio_cli_parser_data_s *parser,
|
88
|
+
const char *line) {
|
89
|
+
if (!line) {
|
90
|
+
return NULL;
|
91
|
+
}
|
92
|
+
char const **pos = parser->names;
|
93
|
+
while (*pos) {
|
94
|
+
switch ((intptr_t)*pos) {
|
95
|
+
case FIO_CLI_STRING__TYPE_I: /* fallthrough */
|
96
|
+
case FIO_CLI_BOOL__TYPE_I: /* fallthrough */
|
97
|
+
case FIO_CLI_INT__TYPE_I: /* fallthrough */
|
98
|
+
case FIO_CLI_PRINT__TYPE_I: /* fallthrough */
|
99
|
+
case FIO_CLI_PRINT_HEADER__TYPE_I: /* fallthrough */
|
100
|
+
++pos;
|
101
|
+
continue;
|
102
|
+
}
|
103
|
+
if (line == *pos) {
|
104
|
+
goto found;
|
105
|
+
}
|
106
|
+
++pos;
|
107
|
+
}
|
108
|
+
return NULL;
|
109
|
+
found:
|
110
|
+
switch ((size_t)pos[1]) {
|
111
|
+
case FIO_CLI_STRING__TYPE_I: /* fallthrough */
|
112
|
+
case FIO_CLI_BOOL__TYPE_I: /* fallthrough */
|
113
|
+
case FIO_CLI_INT__TYPE_I: /* fallthrough */
|
114
|
+
case FIO_CLI_PRINT__TYPE_I: /* fallthrough */
|
115
|
+
case FIO_CLI_PRINT_HEADER__TYPE_I: /* fallthrough */
|
116
|
+
return pos[1];
|
117
|
+
}
|
118
|
+
return NULL;
|
119
|
+
}
|
120
|
+
|
121
|
+
static void fio_cli_set_arg(cstr_s arg, char const *value, char const *line,
|
122
|
+
fio_cli_parser_data_s *parser) {
|
123
|
+
/* handle unnamed argument */
|
124
|
+
if (!line || !arg.len) {
|
125
|
+
if (!value) {
|
126
|
+
goto print_help;
|
127
|
+
}
|
128
|
+
if (!strcmp(value, "-?") || !strcasecmp(value, "-h") ||
|
129
|
+
!strcasecmp(value, "-help") || !strcasecmp(value, "--help")) {
|
130
|
+
goto print_help;
|
131
|
+
}
|
132
|
+
cstr_s n = {.len = ++parser->unnamed_count};
|
133
|
+
fio_cli_hash_insert(&fio_values, n.len, n, value, NULL);
|
134
|
+
if (parser->unnamed_max >= 0 &&
|
135
|
+
parser->unnamed_count > parser->unnamed_max) {
|
136
|
+
arg.len = 0;
|
137
|
+
goto error;
|
138
|
+
}
|
139
|
+
return;
|
140
|
+
}
|
141
|
+
|
142
|
+
/* validate data types */
|
143
|
+
char const *type = fio_cli_get_line_type(parser, line);
|
144
|
+
switch ((size_t)type) {
|
145
|
+
case FIO_CLI_BOOL__TYPE_I:
|
146
|
+
if (value && value != parser->argv[parser->pos + 1]) {
|
147
|
+
goto error;
|
148
|
+
}
|
149
|
+
value = "1";
|
150
|
+
break;
|
151
|
+
case FIO_CLI_INT__TYPE_I:
|
152
|
+
if (value) {
|
153
|
+
char const *tmp = value;
|
154
|
+
fio_atol((char **)&tmp);
|
155
|
+
if (*tmp) {
|
156
|
+
goto error;
|
157
|
+
}
|
158
|
+
}
|
159
|
+
/* fallthrough */
|
160
|
+
case FIO_CLI_STRING__TYPE_I:
|
161
|
+
if (!value)
|
162
|
+
goto error;
|
163
|
+
if (!value[0])
|
164
|
+
goto finish;
|
165
|
+
break;
|
166
|
+
}
|
167
|
+
|
168
|
+
/* add values using all aliases possible */
|
169
|
+
{
|
170
|
+
cstr_s n = {.data = line};
|
171
|
+
while (n.data[0] == '-') {
|
172
|
+
while (n.data[n.len] && n.data[n.len] != ' ' && n.data[n.len] != ',') {
|
173
|
+
++n.len;
|
174
|
+
}
|
175
|
+
fio_cli_hash_insert(&fio_values, FIO_CLI_HASH_VAL(n), n, value, NULL);
|
176
|
+
while (n.data[n.len] && (n.data[n.len] == ' ' || n.data[n.len] == ',')) {
|
177
|
+
++n.len;
|
178
|
+
}
|
179
|
+
n.data += n.len;
|
180
|
+
n.len = 0;
|
181
|
+
}
|
182
|
+
}
|
183
|
+
|
184
|
+
finish:
|
185
|
+
|
186
|
+
/* handle additional argv progress (if value is on separate argv) */
|
187
|
+
if (value && parser->pos < parser->argc &&
|
188
|
+
value == parser->argv[parser->pos + 1])
|
189
|
+
++parser->pos;
|
190
|
+
return;
|
191
|
+
|
192
|
+
error: /* handle errors*/
|
193
|
+
/* TODO! */
|
194
|
+
fprintf(stderr, "\n\r\x1B[31mError:\x1B[0m unknown argument %.*s %s %s\n\n",
|
195
|
+
(int)arg.len, arg.data, arg.len ? "with value" : "",
|
196
|
+
value ? (value[0] ? value : "(empty)") : "(null)");
|
197
|
+
print_help:
|
198
|
+
fprintf(stderr, "\n%s\n",
|
199
|
+
parser->description ? parser->description
|
200
|
+
: "This application accepts any of the following "
|
201
|
+
"possible arguments:");
|
202
|
+
/* print out each line's arguments */
|
203
|
+
char const **pos = parser->names;
|
204
|
+
while (*pos) {
|
205
|
+
switch ((intptr_t)*pos) {
|
206
|
+
case FIO_CLI_STRING__TYPE_I: /* fallthrough */
|
207
|
+
case FIO_CLI_BOOL__TYPE_I: /* fallthrough */
|
208
|
+
case FIO_CLI_INT__TYPE_I: /* fallthrough */
|
209
|
+
case FIO_CLI_PRINT__TYPE_I: /* fallthrough */
|
210
|
+
case FIO_CLI_PRINT_HEADER__TYPE_I:
|
211
|
+
++pos;
|
212
|
+
continue;
|
213
|
+
}
|
214
|
+
type = (char *)FIO_CLI_STRING__TYPE_I;
|
215
|
+
switch ((intptr_t)pos[1]) {
|
216
|
+
case FIO_CLI_PRINT__TYPE_I:
|
217
|
+
fprintf(stderr, "%s\n", pos[0]);
|
218
|
+
pos += 2;
|
219
|
+
continue;
|
220
|
+
case FIO_CLI_PRINT_HEADER__TYPE_I:
|
221
|
+
fprintf(stderr, "\n\x1B[4m%s\x1B[0m\n", pos[0]);
|
222
|
+
pos += 2;
|
223
|
+
continue;
|
224
|
+
|
225
|
+
case FIO_CLI_STRING__TYPE_I: /* fallthrough */
|
226
|
+
case FIO_CLI_BOOL__TYPE_I: /* fallthrough */
|
227
|
+
case FIO_CLI_INT__TYPE_I: /* fallthrough */
|
228
|
+
type = pos[1];
|
229
|
+
}
|
230
|
+
/* print line @ pos, starting with main argument name */
|
231
|
+
int alias_count = 0;
|
232
|
+
int first_len = 0;
|
233
|
+
size_t tmp = 0;
|
234
|
+
char const *const p = *pos;
|
235
|
+
while (p[tmp] == '-') {
|
236
|
+
while (p[tmp] && p[tmp] != ' ' && p[tmp] != ',') {
|
237
|
+
if (!alias_count)
|
238
|
+
++first_len;
|
239
|
+
++tmp;
|
240
|
+
}
|
241
|
+
++alias_count;
|
242
|
+
while (p[tmp] && (p[tmp] == ' ' || p[tmp] == ',')) {
|
243
|
+
++tmp;
|
244
|
+
}
|
245
|
+
}
|
246
|
+
switch ((size_t)type) {
|
247
|
+
case FIO_CLI_STRING__TYPE_I:
|
248
|
+
fprintf(stderr, " \x1B[1m%.*s\x1B[0m\x1B[2m <>\x1B[0m\t%s\n", first_len,
|
249
|
+
p, p + tmp);
|
250
|
+
break;
|
251
|
+
case FIO_CLI_BOOL__TYPE_I:
|
252
|
+
fprintf(stderr, " \x1B[1m%.*s\x1B[0m \t%s\n", first_len, p, p + tmp);
|
253
|
+
break;
|
254
|
+
case FIO_CLI_INT__TYPE_I:
|
255
|
+
fprintf(stderr, " \x1B[1m%.*s\x1B[0m\x1B[2m ##\x1B[0m\t%s\n", first_len,
|
256
|
+
p, p + tmp);
|
257
|
+
break;
|
258
|
+
}
|
259
|
+
/* print aliase information */
|
260
|
+
tmp = first_len;
|
261
|
+
while (p[tmp] && (p[tmp] == ' ' || p[tmp] == ',')) {
|
262
|
+
++tmp;
|
263
|
+
}
|
264
|
+
while (p[tmp] == '-') {
|
265
|
+
const size_t start = tmp;
|
266
|
+
while (p[tmp] && p[tmp] != ' ' && p[tmp] != ',') {
|
267
|
+
++tmp;
|
268
|
+
}
|
269
|
+
int padding = first_len - (tmp - start);
|
270
|
+
if (padding < 0)
|
271
|
+
padding = 0;
|
272
|
+
switch ((size_t)type) {
|
273
|
+
case FIO_CLI_STRING__TYPE_I:
|
274
|
+
fprintf(stderr,
|
275
|
+
" \x1B[1m%.*s\x1B[0m\x1B[2m <>\x1B[0m%*s\t(same as "
|
276
|
+
"\x1B[1m%.*s\x1B[0m)\n",
|
277
|
+
(int)(tmp - start), p + start, padding, "", first_len, p);
|
278
|
+
break;
|
279
|
+
case FIO_CLI_BOOL__TYPE_I:
|
280
|
+
fprintf(stderr,
|
281
|
+
" \x1B[1m%.*s\x1B[0m %*s\t(same as \x1B[1m%.*s\x1B[0m)\n",
|
282
|
+
(int)(tmp - start), p + start, padding, "", first_len, p);
|
283
|
+
break;
|
284
|
+
case FIO_CLI_INT__TYPE_I:
|
285
|
+
fprintf(stderr,
|
286
|
+
" \x1B[1m%.*s\x1B[0m\x1B[2m ##\x1B[0m%*s\t(same as "
|
287
|
+
"\x1B[1m%.*s\x1B[0m)\n",
|
288
|
+
(int)(tmp - start), p + start, padding, "", first_len, p);
|
289
|
+
break;
|
290
|
+
}
|
291
|
+
}
|
292
|
+
|
293
|
+
++pos;
|
294
|
+
}
|
295
|
+
fprintf(stderr, "\nUse any of the following input formats:\n"
|
296
|
+
"\t-arg <value>\t-arg=<value>\t-arg<value>\n"
|
297
|
+
"\n"
|
298
|
+
"Use the -h, -help or -? to get this information again.\n"
|
299
|
+
"\n");
|
300
|
+
fio_cli_end();
|
301
|
+
exit(0);
|
302
|
+
}
|
303
|
+
|
304
|
+
static void fio_cli_end_promise(void *ignr_) {
|
305
|
+
/* make sure fio_cli_end is called before facil.io exists. */
|
306
|
+
fio_cli_end();
|
307
|
+
(void)ignr_;
|
308
|
+
}
|
309
|
+
|
310
|
+
void fio_cli_start AVOID_MACRO(int argc, char const *argv[], int unnamed_min,
|
311
|
+
int unnamed_max, char const *description,
|
312
|
+
char const **names) {
|
313
|
+
static fio_lock_i run_once = FIO_LOCK_INIT;
|
314
|
+
if (!fio_trylock(&run_once))
|
315
|
+
fio_state_callback_add(FIO_CALL_AT_EXIT, fio_cli_end_promise, NULL);
|
316
|
+
if (unnamed_max >= 0 && unnamed_max < unnamed_min)
|
317
|
+
unnamed_max = unnamed_min;
|
318
|
+
fio_cli_parser_data_s parser = {
|
319
|
+
.unnamed_min = unnamed_min,
|
320
|
+
.unnamed_max = unnamed_max,
|
321
|
+
.description = description,
|
322
|
+
.argc = argc,
|
323
|
+
.argv = argv,
|
324
|
+
.names = names,
|
325
|
+
.pos = 0,
|
326
|
+
};
|
327
|
+
|
328
|
+
if (fio_cli_hash_count(&fio_values)) {
|
329
|
+
fio_cli_end();
|
330
|
+
}
|
331
|
+
|
332
|
+
/* prepare aliases hash map */
|
333
|
+
|
334
|
+
char const **line = names;
|
335
|
+
while (*line) {
|
336
|
+
switch ((intptr_t)*line) {
|
337
|
+
case FIO_CLI_STRING__TYPE_I: /* fallthrough */
|
338
|
+
case FIO_CLI_BOOL__TYPE_I: /* fallthrough */
|
339
|
+
case FIO_CLI_INT__TYPE_I: /* fallthrough */
|
340
|
+
case FIO_CLI_PRINT__TYPE_I: /* fallthrough */
|
341
|
+
case FIO_CLI_PRINT_HEADER__TYPE_I: /* fallthrough */
|
342
|
+
++line;
|
343
|
+
continue;
|
344
|
+
}
|
345
|
+
if (line[1] != (char *)FIO_CLI_PRINT__TYPE_I &&
|
346
|
+
line[1] != (char *)FIO_CLI_PRINT_HEADER__TYPE_I)
|
347
|
+
fio_cli_map_line2alias(*line);
|
348
|
+
++line;
|
349
|
+
}
|
350
|
+
|
351
|
+
/* parse existing arguments */
|
352
|
+
|
353
|
+
while ((++parser.pos) < argc) {
|
354
|
+
char const *value = NULL;
|
355
|
+
cstr_s n = {.data = argv[parser.pos], .len = strlen(argv[parser.pos])};
|
356
|
+
if (parser.pos + 1 < argc) {
|
357
|
+
value = argv[parser.pos + 1];
|
358
|
+
}
|
359
|
+
const char *l = NULL;
|
360
|
+
while (n.len &&
|
361
|
+
!(l = fio_cli_hash_find(&fio_aliases, FIO_CLI_HASH_VAL(n), n))) {
|
362
|
+
--n.len;
|
363
|
+
value = n.data + n.len;
|
364
|
+
}
|
365
|
+
if (n.len && value && value[0] == '=') {
|
366
|
+
++value;
|
367
|
+
}
|
368
|
+
// fprintf(stderr, "Setting %.*s to %s\n", (int)n.len, n.data, value);
|
369
|
+
fio_cli_set_arg(n, value, l, &parser);
|
370
|
+
}
|
371
|
+
|
372
|
+
/* Cleanup and save state for API */
|
373
|
+
fio_cli_hash_free(&fio_aliases);
|
374
|
+
fio_unnamed_count = parser.unnamed_count;
|
375
|
+
/* test for required unnamed arguments */
|
376
|
+
if (parser.unnamed_count < parser.unnamed_min)
|
377
|
+
fio_cli_set_arg((cstr_s){.len = 0}, NULL, NULL, &parser);
|
378
|
+
}
|
379
|
+
|
380
|
+
void fio_cli_end(void) {
|
381
|
+
fio_cli_hash_free(&fio_values);
|
382
|
+
fio_cli_hash_free(&fio_aliases);
|
383
|
+
fio_unnamed_count = 0;
|
384
|
+
}
|
385
|
+
/* *****************************************************************************
|
386
|
+
CLI Data Access
|
387
|
+
***************************************************************************** */
|
388
|
+
|
389
|
+
/** Returns the argument's value as a NUL terminated C String. */
|
390
|
+
char const *fio_cli_get(char const *name) {
|
391
|
+
cstr_s n = {.data = name, .len = strlen(name)};
|
392
|
+
if (!fio_cli_hash_count(&fio_values)) {
|
393
|
+
return NULL;
|
394
|
+
}
|
395
|
+
char const *val = fio_cli_hash_find(&fio_values, FIO_CLI_HASH_VAL(n), n);
|
396
|
+
return val;
|
397
|
+
}
|
398
|
+
|
399
|
+
/** Returns the argument's value as an integer. */
|
400
|
+
int fio_cli_get_i(char const *name) {
|
401
|
+
char const *val = fio_cli_get(name);
|
402
|
+
if (!val)
|
403
|
+
return 0;
|
404
|
+
int i = (int)fio_atol((char **)&val);
|
405
|
+
return i;
|
406
|
+
}
|
407
|
+
|
408
|
+
/** Returns the number of unrecognized argument. */
|
409
|
+
unsigned int fio_cli_unnamed_count(void) {
|
410
|
+
return (unsigned int)fio_unnamed_count;
|
411
|
+
}
|
412
|
+
|
413
|
+
/** Returns the unrecognized argument using a 0 based `index`. */
|
414
|
+
char const *fio_cli_unnamed(unsigned int index) {
|
415
|
+
if (!fio_cli_hash_count(&fio_values) || !fio_unnamed_count) {
|
416
|
+
return NULL;
|
417
|
+
}
|
418
|
+
cstr_s n = {.data = NULL, .len = index + 1};
|
419
|
+
return fio_cli_hash_find(&fio_values, index + 1, n);
|
420
|
+
}
|
421
|
+
|
422
|
+
/**
|
423
|
+
* Sets the argument's value as a NUL terminated C String (no copy!).
|
424
|
+
*
|
425
|
+
* Note: this does NOT copy the C strings to memory. Memory should be kept
|
426
|
+
* alive until `fio_cli_end` is called.
|
427
|
+
*/
|
428
|
+
void fio_cli_set(char const *name, char const *value) {
|
429
|
+
cstr_s n = (cstr_s){.data = name, .len = strlen(name)};
|
430
|
+
fio_cli_hash_insert(&fio_values, FIO_CLI_HASH_VAL(n), n, value, NULL);
|
431
|
+
}
|
@@ -0,0 +1,189 @@
|
|
1
|
+
/*
|
2
|
+
Copyright: Boaz segev, 2017
|
3
|
+
License: MIT
|
4
|
+
|
5
|
+
Feel free to copy, use and enjoy according to the license provided.
|
6
|
+
*/
|
7
|
+
#ifndef H_FIO_CLI_HELPER_H
|
8
|
+
#define H_FIO_CLI_HELPER_H
|
9
|
+
|
10
|
+
/* support C++ */
|
11
|
+
#ifdef __cplusplus
|
12
|
+
extern "C" {
|
13
|
+
#endif
|
14
|
+
|
15
|
+
/* *****************************************************************************
|
16
|
+
CLI API
|
17
|
+
***************************************************************************** */
|
18
|
+
|
19
|
+
/** Indicates the CLI argument should be a String (default). */
|
20
|
+
#define FIO_CLI_STRING(line)
|
21
|
+
/** Indicates the CLI argument is a Boolean value. */
|
22
|
+
#define FIO_CLI_BOOL(line)
|
23
|
+
/** Indicates the CLI argument should be an Integer (numerical). */
|
24
|
+
#define FIO_CLI_INT(line)
|
25
|
+
/** Indicates the CLI string should be printed as is. */
|
26
|
+
#define FIO_CLI_PRINT(line)
|
27
|
+
/** Indicates the CLI string should be printed as a header. */
|
28
|
+
#define FIO_CLI_PRINT_HEADER(line)
|
29
|
+
|
30
|
+
/**
|
31
|
+
* This function parses the Command Line Interface (CLI), creating a temporary
|
32
|
+
* "dictionary" that allows easy access to the CLI using their names or aliases.
|
33
|
+
*
|
34
|
+
* Command line arguments may be typed. If an optional type requirement is
|
35
|
+
* provided and the provided arument fails to match the required type, execution
|
36
|
+
* will end and an error message will be printed along with a short "help".
|
37
|
+
*
|
38
|
+
* The function / macro accepts the following arguments:
|
39
|
+
* - `argc`: command line argument count.
|
40
|
+
* - `argv`: command line argument list (array).
|
41
|
+
* - `unnamed_min`: the required minimum of un-named arguments.
|
42
|
+
* - `unnamed_max`: the maximum limit of un-named arguments.
|
43
|
+
* - `description`: a C string containing the program's description.
|
44
|
+
* - named arguments list: a list of C strings describing named arguments.
|
45
|
+
*
|
46
|
+
* The following optional type requirements are:
|
47
|
+
*
|
48
|
+
* * FIO_CLI_STRING(desc_line) - (default) string argument.
|
49
|
+
* * FIO_CLI_BOOL(desc_line) - boolean argument (no value).
|
50
|
+
* * FIO_CLI_INT(desc_line) - integer argument.
|
51
|
+
* * FIO_CLI_PRINT_HEADER(desc_line) - extra header for output.
|
52
|
+
* * FIO_CLI_PRINT(desc_line) - extra information for output.
|
53
|
+
*
|
54
|
+
* Argument names MUST start with the '-' character. The first word starting
|
55
|
+
* without the '-' character will begin the description for the CLI argument.
|
56
|
+
*
|
57
|
+
* The arguments "-?", "-h", "-help" and "--help" are automatically handled
|
58
|
+
* unless overridden.
|
59
|
+
*
|
60
|
+
* Un-named arguments shouldn't be listed in the named arguments list.
|
61
|
+
*
|
62
|
+
* Example use:
|
63
|
+
*
|
64
|
+
* fio_cli_start(argc, argv, 0, 0, "this example accepts the following:",
|
65
|
+
* FIO_CLI_PRINT_HREADER("Concurrency:"),
|
66
|
+
* FIO_CLI_INT("-t -thread number of threads to run."),
|
67
|
+
* FIO_CLI_INT("-w -workers number of workers to run."),
|
68
|
+
* FIO_CLI_PRINT_HREADER("Address Binding:"),
|
69
|
+
* "-b, -address the address to bind to.",
|
70
|
+
* FIO_CLI_INT("-p,-port the port to bind to."),
|
71
|
+
* FIO_CLI_PRINT("\t\tset port to zero (0) for Unix s."),
|
72
|
+
* FIO_CLI_PRINT_HREADER("Logging:"),
|
73
|
+
* FIO_CLI_BOOL("-v -log enable logging."));
|
74
|
+
*
|
75
|
+
*
|
76
|
+
* This would allow access to the named arguments:
|
77
|
+
*
|
78
|
+
* fio_cli_get("-b") == fio_cli_get("-address");
|
79
|
+
*
|
80
|
+
*
|
81
|
+
* Once all the data was accessed, free the parsed data dictionary using:
|
82
|
+
*
|
83
|
+
* fio_cli_end();
|
84
|
+
*
|
85
|
+
* It should be noted, arguments will be recognized in a number of forms, i.e.:
|
86
|
+
*
|
87
|
+
* app -t=1 -p3000 -a localhost
|
88
|
+
*
|
89
|
+
* This function is NOT thread safe.
|
90
|
+
*/
|
91
|
+
#define fio_cli_start(argc, argv, unnamed_min, unnamed_max, description, ...) \
|
92
|
+
fio_cli_start((argc), (argv), (unnamed_min), (unnamed_max), (description), \
|
93
|
+
(char const *[]){__VA_ARGS__, NULL})
|
94
|
+
#define FIO_CLI_IGNORE
|
95
|
+
/**
|
96
|
+
* Never use the function directly, always use the MACRO, because the macro
|
97
|
+
* attaches a NULL marker at the end of the `names` argument collection.
|
98
|
+
*/
|
99
|
+
void fio_cli_start FIO_CLI_IGNORE(int argc, char const *argv[], int unnamed_min,
|
100
|
+
int unnamed_max, char const *description,
|
101
|
+
char const **names);
|
102
|
+
/**
|
103
|
+
* Clears the memory used by the CLI dictionary, removing all parsed data.
|
104
|
+
*
|
105
|
+
* This function is NOT thread safe.
|
106
|
+
*/
|
107
|
+
void fio_cli_end(void);
|
108
|
+
|
109
|
+
/** Returns the argument's value as a NUL terminated C String. */
|
110
|
+
char const *fio_cli_get(char const *name);
|
111
|
+
|
112
|
+
/** Returns the argument's value as an integer. */
|
113
|
+
int fio_cli_get_i(char const *name);
|
114
|
+
|
115
|
+
/** This MACRO returns the argument's value as a boolean. */
|
116
|
+
#define fio_cli_get_bool(name) (fio_cli_get((name)) != NULL)
|
117
|
+
|
118
|
+
/** Returns the number of unnamed argument. */
|
119
|
+
unsigned int fio_cli_unnamed_count(void);
|
120
|
+
|
121
|
+
/** Returns the unnamed argument using a 0 based `index`. */
|
122
|
+
char const *fio_cli_unnamed(unsigned int index);
|
123
|
+
|
124
|
+
/**
|
125
|
+
* Sets the argument's value as a NUL terminated C String (no copy!).
|
126
|
+
*
|
127
|
+
* CAREFUL: This does not automatically detect aliases or type violations! it
|
128
|
+
* will only effect the specific name given, even if invalid. i.e.:
|
129
|
+
*
|
130
|
+
* fio_cli_start(argc, argv,
|
131
|
+
* "this is example accepts the following options:",
|
132
|
+
* "-p -port the port to bind to", FIO_CLI_INT;
|
133
|
+
*
|
134
|
+
* fio_cli_set("-p", "hello"); // fio_cli_get("-p") != fio_cli_get("-port");
|
135
|
+
*
|
136
|
+
* Note: this does NOT copy the C strings to memory. Memory should be kept alive
|
137
|
+
* until `fio_cli_end` is called.
|
138
|
+
*
|
139
|
+
* This function is NOT thread safe.
|
140
|
+
*/
|
141
|
+
void fio_cli_set(char const *name, char const *value);
|
142
|
+
|
143
|
+
/**
|
144
|
+
* This MACRO is the same as:
|
145
|
+
*
|
146
|
+
* if(!fio_cli_get(name)) {
|
147
|
+
* fio_cli_set(name, value)
|
148
|
+
* }
|
149
|
+
*
|
150
|
+
* See fio_cli_set for notes and restrictions.
|
151
|
+
*/
|
152
|
+
#define fio_cli_set_default(name, value) \
|
153
|
+
if (!fio_cli_get((name))) \
|
154
|
+
fio_cli_set(name, value);
|
155
|
+
|
156
|
+
#ifdef __cplusplus
|
157
|
+
} /* extern "C" */
|
158
|
+
#endif
|
159
|
+
|
160
|
+
/* *****************************************************************************
|
161
|
+
Internal Macro Implementation
|
162
|
+
***************************************************************************** */
|
163
|
+
|
164
|
+
/** Used internally. */
|
165
|
+
#define FIO_CLI_STRING__TYPE_I 0x1
|
166
|
+
#define FIO_CLI_BOOL__TYPE_I 0x2
|
167
|
+
#define FIO_CLI_INT__TYPE_I 0x3
|
168
|
+
#define FIO_CLI_PRINT__TYPE_I 0x4
|
169
|
+
#define FIO_CLI_PRINT_HEADER__TYPE_I 0x5
|
170
|
+
|
171
|
+
#undef FIO_CLI_STRING
|
172
|
+
#undef FIO_CLI_BOOL
|
173
|
+
#undef FIO_CLI_INT
|
174
|
+
#undef FIO_CLI_PRINT
|
175
|
+
#undef FIO_CLI_PRINT_HEADER
|
176
|
+
|
177
|
+
/** Indicates the CLI argument should be a String (default). */
|
178
|
+
#define FIO_CLI_STRING(line) (line), ((char *)FIO_CLI_STRING__TYPE_I)
|
179
|
+
/** Indicates the CLI argument is a Boolean value. */
|
180
|
+
#define FIO_CLI_BOOL(line) (line), ((char *)FIO_CLI_BOOL__TYPE_I)
|
181
|
+
/** Indicates the CLI argument should be an Integer (numerical). */
|
182
|
+
#define FIO_CLI_INT(line) (line), ((char *)FIO_CLI_INT__TYPE_I)
|
183
|
+
/** Indicates the CLI string should be printed as is. */
|
184
|
+
#define FIO_CLI_PRINT(line) (line), ((char *)FIO_CLI_PRINT__TYPE_I)
|
185
|
+
/** Indicates the CLI string should be printed as a header. */
|
186
|
+
#define FIO_CLI_PRINT_HEADER(line) \
|
187
|
+
(line), ((char *)FIO_CLI_PRINT_HEADER__TYPE_I)
|
188
|
+
|
189
|
+
#endif
|