rage-iodine 1.7.58

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. data/.github/workflows/ruby.yml +42 -0
  4. data/.gitignore +20 -0
  5. data/.rspec +2 -0
  6. data/.yardopts +8 -0
  7. data/CHANGELOG.md +1098 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +21 -0
  10. data/LIMITS.md +41 -0
  11. data/README.md +782 -0
  12. data/Rakefile +23 -0
  13. data/SPEC-PubSub-Draft.md +159 -0
  14. data/SPEC-WebSocket-Draft.md +239 -0
  15. data/bin/console +22 -0
  16. data/bin/info.md +353 -0
  17. data/bin/mustache_bench.rb +100 -0
  18. data/bin/poc/Gemfile.lock +23 -0
  19. data/bin/poc/README.md +37 -0
  20. data/bin/poc/config.ru +66 -0
  21. data/bin/poc/gemfile +1 -0
  22. data/bin/poc/www/index.html +57 -0
  23. data/examples/async_task.ru +92 -0
  24. data/examples/bates/README.md +3 -0
  25. data/examples/bates/config.ru +342 -0
  26. data/examples/bates/david+bold.pdf +0 -0
  27. data/examples/bates/public/drop-pdf.png +0 -0
  28. data/examples/bates/public/index.html +600 -0
  29. data/examples/config.ru +59 -0
  30. data/examples/echo.ru +59 -0
  31. data/examples/etag.ru +16 -0
  32. data/examples/hello.ru +29 -0
  33. data/examples/pubsub_engine.ru +81 -0
  34. data/examples/rack3.ru +12 -0
  35. data/examples/redis.ru +70 -0
  36. data/examples/shootout.ru +73 -0
  37. data/examples/sub-protocols.ru +90 -0
  38. data/examples/tcp_client.rb +66 -0
  39. data/examples/x-sendfile.ru +14 -0
  40. data/exe/iodine +280 -0
  41. data/ext/iodine/extconf.rb +110 -0
  42. data/ext/iodine/fio.c +12096 -0
  43. data/ext/iodine/fio.h +6390 -0
  44. data/ext/iodine/fio_cli.c +431 -0
  45. data/ext/iodine/fio_cli.h +189 -0
  46. data/ext/iodine/fio_json_parser.h +687 -0
  47. data/ext/iodine/fio_siphash.c +157 -0
  48. data/ext/iodine/fio_siphash.h +37 -0
  49. data/ext/iodine/fio_tls.h +129 -0
  50. data/ext/iodine/fio_tls_missing.c +649 -0
  51. data/ext/iodine/fio_tls_openssl.c +1056 -0
  52. data/ext/iodine/fio_tmpfile.h +50 -0
  53. data/ext/iodine/fiobj.h +44 -0
  54. data/ext/iodine/fiobj4fio.h +21 -0
  55. data/ext/iodine/fiobj_ary.c +333 -0
  56. data/ext/iodine/fiobj_ary.h +139 -0
  57. data/ext/iodine/fiobj_data.c +1185 -0
  58. data/ext/iodine/fiobj_data.h +167 -0
  59. data/ext/iodine/fiobj_hash.c +409 -0
  60. data/ext/iodine/fiobj_hash.h +176 -0
  61. data/ext/iodine/fiobj_json.c +622 -0
  62. data/ext/iodine/fiobj_json.h +68 -0
  63. data/ext/iodine/fiobj_mem.h +71 -0
  64. data/ext/iodine/fiobj_mustache.c +317 -0
  65. data/ext/iodine/fiobj_mustache.h +62 -0
  66. data/ext/iodine/fiobj_numbers.c +344 -0
  67. data/ext/iodine/fiobj_numbers.h +127 -0
  68. data/ext/iodine/fiobj_str.c +433 -0
  69. data/ext/iodine/fiobj_str.h +172 -0
  70. data/ext/iodine/fiobject.c +620 -0
  71. data/ext/iodine/fiobject.h +654 -0
  72. data/ext/iodine/hpack.h +1923 -0
  73. data/ext/iodine/http.c +2736 -0
  74. data/ext/iodine/http.h +1019 -0
  75. data/ext/iodine/http1.c +825 -0
  76. data/ext/iodine/http1.h +29 -0
  77. data/ext/iodine/http1_parser.h +1835 -0
  78. data/ext/iodine/http_internal.c +1279 -0
  79. data/ext/iodine/http_internal.h +248 -0
  80. data/ext/iodine/http_mime_parser.h +350 -0
  81. data/ext/iodine/iodine.c +1433 -0
  82. data/ext/iodine/iodine.h +64 -0
  83. data/ext/iodine/iodine_caller.c +218 -0
  84. data/ext/iodine/iodine_caller.h +27 -0
  85. data/ext/iodine/iodine_connection.c +941 -0
  86. data/ext/iodine/iodine_connection.h +55 -0
  87. data/ext/iodine/iodine_defer.c +420 -0
  88. data/ext/iodine/iodine_defer.h +6 -0
  89. data/ext/iodine/iodine_fiobj2rb.h +120 -0
  90. data/ext/iodine/iodine_helpers.c +282 -0
  91. data/ext/iodine/iodine_helpers.h +12 -0
  92. data/ext/iodine/iodine_http.c +1280 -0
  93. data/ext/iodine/iodine_http.h +23 -0
  94. data/ext/iodine/iodine_json.c +302 -0
  95. data/ext/iodine/iodine_json.h +6 -0
  96. data/ext/iodine/iodine_mustache.c +567 -0
  97. data/ext/iodine/iodine_mustache.h +6 -0
  98. data/ext/iodine/iodine_pubsub.c +580 -0
  99. data/ext/iodine/iodine_pubsub.h +26 -0
  100. data/ext/iodine/iodine_rack_io.c +273 -0
  101. data/ext/iodine/iodine_rack_io.h +20 -0
  102. data/ext/iodine/iodine_store.c +142 -0
  103. data/ext/iodine/iodine_store.h +20 -0
  104. data/ext/iodine/iodine_tcp.c +346 -0
  105. data/ext/iodine/iodine_tcp.h +13 -0
  106. data/ext/iodine/iodine_tls.c +261 -0
  107. data/ext/iodine/iodine_tls.h +13 -0
  108. data/ext/iodine/mustache_parser.h +1546 -0
  109. data/ext/iodine/redis_engine.c +957 -0
  110. data/ext/iodine/redis_engine.h +79 -0
  111. data/ext/iodine/resp_parser.h +317 -0
  112. data/ext/iodine/scheduler.c +173 -0
  113. data/ext/iodine/scheduler.h +6 -0
  114. data/ext/iodine/websocket_parser.h +506 -0
  115. data/ext/iodine/websockets.c +752 -0
  116. data/ext/iodine/websockets.h +185 -0
  117. data/iodine.gemspec +50 -0
  118. data/lib/iodine/connection.rb +61 -0
  119. data/lib/iodine/json.rb +42 -0
  120. data/lib/iodine/mustache.rb +113 -0
  121. data/lib/iodine/pubsub.rb +55 -0
  122. data/lib/iodine/rack_utils.rb +43 -0
  123. data/lib/iodine/tls.rb +16 -0
  124. data/lib/iodine/version.rb +3 -0
  125. data/lib/iodine.rb +274 -0
  126. data/lib/rack/handler/iodine.rb +33 -0
  127. data/logo.png +0 -0
  128. 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