iodine 0.6.5 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +4 -4
  4. data/SPEC-Websocket-Draft.md +3 -6
  5. data/bin/mustache.rb +128 -0
  6. data/examples/test_template.mustache +16 -0
  7. data/ext/iodine/fio.c +9397 -0
  8. data/ext/iodine/fio.h +4723 -0
  9. data/ext/iodine/fio_ary.h +353 -54
  10. data/ext/iodine/fio_cli.c +351 -361
  11. data/ext/iodine/fio_cli.h +84 -105
  12. data/ext/iodine/fio_hashmap.h +70 -16
  13. data/ext/iodine/fio_json_parser.h +35 -24
  14. data/ext/iodine/fio_siphash.c +104 -4
  15. data/ext/iodine/fio_siphash.h +18 -2
  16. data/ext/iodine/fio_str.h +1218 -0
  17. data/ext/iodine/fio_tmpfile.h +1 -1
  18. data/ext/iodine/fiobj.h +13 -8
  19. data/ext/iodine/fiobj4sock.h +6 -8
  20. data/ext/iodine/fiobj_ary.c +107 -17
  21. data/ext/iodine/fiobj_ary.h +36 -4
  22. data/ext/iodine/fiobj_data.c +146 -127
  23. data/ext/iodine/fiobj_data.h +25 -23
  24. data/ext/iodine/fiobj_hash.c +7 -7
  25. data/ext/iodine/fiobj_hash.h +6 -5
  26. data/ext/iodine/fiobj_json.c +20 -17
  27. data/ext/iodine/fiobj_json.h +5 -5
  28. data/ext/iodine/fiobj_mem.h +71 -0
  29. data/ext/iodine/fiobj_mustache.c +310 -0
  30. data/ext/iodine/fiobj_mustache.h +40 -0
  31. data/ext/iodine/fiobj_numbers.c +199 -94
  32. data/ext/iodine/fiobj_numbers.h +7 -7
  33. data/ext/iodine/fiobj_str.c +142 -333
  34. data/ext/iodine/fiobj_str.h +65 -55
  35. data/ext/iodine/fiobject.c +49 -11
  36. data/ext/iodine/fiobject.h +40 -39
  37. data/ext/iodine/http.c +382 -190
  38. data/ext/iodine/http.h +124 -80
  39. data/ext/iodine/http1.c +99 -127
  40. data/ext/iodine/http1.h +5 -5
  41. data/ext/iodine/http1_parser.c +3 -2
  42. data/ext/iodine/http1_parser.h +2 -2
  43. data/ext/iodine/http_internal.c +14 -12
  44. data/ext/iodine/http_internal.h +25 -19
  45. data/ext/iodine/iodine.c +37 -18
  46. data/ext/iodine/iodine.h +4 -0
  47. data/ext/iodine/iodine_caller.c +9 -2
  48. data/ext/iodine/iodine_caller.h +2 -0
  49. data/ext/iodine/iodine_connection.c +82 -117
  50. data/ext/iodine/iodine_defer.c +57 -50
  51. data/ext/iodine/iodine_defer.h +0 -1
  52. data/ext/iodine/iodine_fiobj2rb.h +4 -2
  53. data/ext/iodine/iodine_helpers.c +4 -4
  54. data/ext/iodine/iodine_http.c +25 -32
  55. data/ext/iodine/iodine_json.c +2 -1
  56. data/ext/iodine/iodine_mustache.c +423 -0
  57. data/ext/iodine/iodine_mustache.h +6 -0
  58. data/ext/iodine/iodine_pubsub.c +48 -153
  59. data/ext/iodine/iodine_pubsub.h +5 -4
  60. data/ext/iodine/iodine_rack_io.c +7 -5
  61. data/ext/iodine/iodine_store.c +16 -13
  62. data/ext/iodine/iodine_tcp.c +26 -34
  63. data/ext/iodine/mustache_parser.h +1085 -0
  64. data/ext/iodine/redis_engine.c +740 -646
  65. data/ext/iodine/redis_engine.h +13 -15
  66. data/ext/iodine/resp_parser.h +11 -5
  67. data/ext/iodine/websocket_parser.h +13 -13
  68. data/ext/iodine/websockets.c +240 -393
  69. data/ext/iodine/websockets.h +52 -113
  70. data/lib/iodine.rb +1 -1
  71. data/lib/iodine/mustache.rb +140 -0
  72. data/lib/iodine/version.rb +1 -1
  73. metadata +15 -28
  74. data/ext/iodine/defer.c +0 -566
  75. data/ext/iodine/defer.h +0 -148
  76. data/ext/iodine/evio.c +0 -26
  77. data/ext/iodine/evio.h +0 -161
  78. data/ext/iodine/evio_callbacks.c +0 -26
  79. data/ext/iodine/evio_epoll.c +0 -251
  80. data/ext/iodine/evio_kqueue.c +0 -194
  81. data/ext/iodine/facil.c +0 -2325
  82. data/ext/iodine/facil.h +0 -616
  83. data/ext/iodine/fio_base64.c +0 -277
  84. data/ext/iodine/fio_base64.h +0 -71
  85. data/ext/iodine/fio_llist.h +0 -257
  86. data/ext/iodine/fio_mem.c +0 -675
  87. data/ext/iodine/fio_mem.h +0 -143
  88. data/ext/iodine/fio_random.c +0 -248
  89. data/ext/iodine/fio_random.h +0 -45
  90. data/ext/iodine/fio_sha1.c +0 -362
  91. data/ext/iodine/fio_sha1.h +0 -107
  92. data/ext/iodine/fio_sha2.c +0 -842
  93. data/ext/iodine/fio_sha2.h +0 -169
  94. data/ext/iodine/pubsub.c +0 -867
  95. data/ext/iodine/pubsub.h +0 -221
  96. data/ext/iodine/sock.c +0 -1366
  97. data/ext/iodine/sock.h +0 -566
  98. data/ext/iodine/spnlock.inc +0 -111
@@ -4,420 +4,410 @@ License: MIT
4
4
 
5
5
  Feel free to copy, use and enjoy according to the license provided.
6
6
  */
7
- #include "fio_cli.h"
8
- #include "fiobj.h"
7
+ #include <fio.h>
9
8
 
9
+ #include <fio_cli.h>
10
+
11
+ #include <stdint.h>
12
+ #include <stdio.h>
13
+ #include <stdlib.h>
10
14
  #include <string.h>
11
- /* *****************************************************************************
12
- State (static data)
13
- ***************************************************************************** */
15
+ #include <strings.h>
14
16
 
15
- /* static variables are automatically initialized to 0, which is what we need.*/
16
- static int FIO_CLI_ARGC;
17
- static const char **FIO_CLI_ARGV;
18
- static FIOBJ arg_aliases; /* a hash for translating aliases */
19
- static FIOBJ arg_type; /* a with information about each argument */
20
- static FIOBJ parsed; /* a with information about each argument */
21
- static FIOBJ help_str; /* The CLI help string */
22
- static FIOBJ info_str; /* The CLI information string */
23
- static int is_parsed;
24
- static int ignore_unknown;
25
-
26
- const char DEFAULT_CLI_INFO[] =
27
- "This application accepts any of the following possible arguments:";
28
17
  /* *****************************************************************************
29
- Error / Help handling - printing the information and exiting.
18
+ CLI Data Stores
30
19
  ***************************************************************************** */
31
20
 
32
- /** Tells the CLI helper to ignore invalid command line arguments. */
33
- void fio_cli_ignore_unknown(void) { ignore_unknown = 1; }
21
+ typedef struct {
22
+ size_t len;
23
+ const char *data;
24
+ } cstr_s;
34
25
 
35
- static void fio_cli_handle_error(void) {
36
- if (ignore_unknown)
37
- return;
38
- fio_cstr_s info = fiobj_obj2cstr(info_str);
39
- fio_cstr_s args = fiobj_obj2cstr(help_str);
40
- fprintf(stdout,
41
- "\n"
42
- "%s\n"
43
- "%s\n"
44
- "Use any of the following input formats:\n"
45
- "\t-arg <value>\t-arg=<value>\t-arg<value>\n"
46
- "\n"
47
- "Use the -h, -help or -? to get this information again.\n"
48
- "\n",
49
- info.data, args.data);
50
- fio_cli_end();
51
- exit(0);
52
- }
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_hash
32
+ #include <fio.h>
53
33
 
54
- /* *****************************************************************************
55
- Initializing the CLI data
56
- ***************************************************************************** */
34
+ static fio_hash_s fio_aliases = FIO_SET_INIT;
35
+ static fio_hash_s fio_values = FIO_SET_INIT;
36
+ static size_t fio_unknown_count = 0;
57
37
 
58
- static void fio_cli_init(void) {
59
- /* if init is called after parsing, discard previous result */
60
- if (parsed) {
61
- fiobj_free(parsed);
62
- parsed = FIOBJ_INVALID;
63
- }
64
- /* avoid overwriting existing data */
65
- if (arg_aliases)
66
- return;
67
- arg_aliases = fiobj_hash_new();
68
- arg_type = fiobj_hash_new();
69
- help_str = fiobj_str_buf(1024);
70
- if (!info_str) /* might exist through `fio_cli_start` */
71
- info_str = fiobj_str_new(DEFAULT_CLI_INFO, sizeof(DEFAULT_CLI_INFO) - 1);
72
- }
38
+ typedef struct {
39
+ int allow_unknown;
40
+ int pos;
41
+ int unknown_count;
42
+ char const *description;
43
+ char const **names;
44
+ } fio_cli_parser_data_s;
45
+
46
+ /** this will allow the function definition fio_cli_start to avoid the MACRO */
47
+ #define AVOID_MACRO
48
+
49
+ #define FIO_CLI_HASH_VAL(s) fio_siphash13((s).data, (s).len)
73
50
 
74
51
  /* *****************************************************************************
75
- Matching arguments to C string
52
+ CLI Parsing
76
53
  ***************************************************************************** */
77
54
 
78
- /* returns the primamry symbol for the argument, of NULL (if none) */
79
- static inline FIOBJ fio_cli_get_name(const char *str, size_t len) {
80
- const uint64_t key = fio_siphash(str, len);
81
- return fiobj_hash_get2(arg_aliases, key);
82
- }
83
-
84
55
  /* *****************************************************************************
85
- Setting an argument's type and alias.
56
+ CLI Parsing
86
57
  ***************************************************************************** */
87
- typedef enum { CLI_BOOL, CLI_NUM, CLI_STR } cli_type;
88
- static void fio_cli_set(const char *aliases, const char *desc, cli_type type) {
89
- fio_cli_init();
90
- const char *start = aliases;
91
- FIOBJ arg_name = FIOBJ_INVALID;
92
-
93
- while (1) {
94
- /* get rid of any white space or commas */
95
- while (start[0] == ' ' || start[0] == ',')
96
- start++;
97
- /* we're done */
98
- if (!start[0])
99
- return;
100
- size_t len = 0;
101
- /* find the length of the argument name */
102
- while (start[len] != 0 && start[len] != ' ' && start[len] != ',')
103
- len++;
104
-
105
- if (!arg_name) {
106
- /* this is the main identifier */
107
- arg_name = fiobj_str_new(start, len);
108
- /* add to aliases hash */
109
- fiobj_hash_set(arg_aliases, arg_name, arg_name);
110
- /* add the help section and set type*/
111
- switch (type) {
112
- case CLI_BOOL:
113
- fiobj_str_write2(help_str, "\t\x1B[1m-%s\x1B[0m\t\t%s\n",
114
- fiobj_obj2cstr(arg_name).data, desc);
115
- fiobj_hash_set(arg_type, arg_name, fiobj_null());
116
- break;
117
- case CLI_NUM:
118
- fiobj_str_write2(help_str,
119
- "\t\x1B[1m-%s\x1B[0m\x1B[2m ###\x1B[0m\t%s\n",
120
- fiobj_obj2cstr(arg_name).data, desc);
121
- fiobj_hash_set(arg_type, arg_name, fiobj_true());
122
- break;
123
- case CLI_STR:
124
- fiobj_str_write2(help_str,
125
- "\t\x1B[1m-%s\x1B[0m\x1B[2m <val>\x1B[0m\t%s\n",
126
- fiobj_obj2cstr(arg_name).data, desc);
127
- fiobj_hash_set(arg_type, arg_name, fiobj_false());
128
- break;
129
- }
130
- } else {
131
- /* this is an alias */
132
- FIOBJ tmp = fiobj_str_new(start, len);
133
- /* add to aliases hash */
134
- fiobj_hash_set(arg_aliases, tmp, fiobj_dup(arg_name));
135
- /* add to description + free it*/
136
- fiobj_str_write2(help_str, "\t\t\x1B[1m-%s\x1B[0m\tsame as -%s\n",
137
- fiobj_obj2cstr(tmp).data, fiobj_obj2cstr(arg_name).data);
138
- fiobj_free(tmp);
58
+
59
+ static void fio_cli_map_line2alias(char const *line) {
60
+ cstr_s n = {.data = line};
61
+ while (n.data[0] == '-') {
62
+ while (n.data[n.len] && n.data[n.len] != ' ' && n.data[n.len] != ',') {
63
+ ++n.len;
139
64
  }
140
- start += len;
65
+ const char **old = fio_hash_find(&fio_aliases, FIO_CLI_HASH_VAL(n), n);
66
+ if (old) {
67
+ fprintf(stderr,
68
+ "WARNING: CLI argument name conflict detected\n"
69
+ " The following two directives conflict:\n"
70
+ "\t%s\n\t%s\n",
71
+ *old, line);
72
+ }
73
+ fio_hash_insert(&fio_aliases, FIO_CLI_HASH_VAL(n), n, (void *)line);
74
+ while (n.data[n.len] && (n.data[n.len] == ' ' || n.data[n.len] == ',')) {
75
+ ++n.len;
76
+ }
77
+ n.data += n.len;
78
+ n.len = 0;
141
79
  }
142
80
  }
143
81
 
144
- /* *****************************************************************************
145
- parsing the arguments
146
- ***************************************************************************** */
147
-
148
- static void fio_cli_parse(void) {
149
- if (!FIO_CLI_ARGC || !FIO_CLI_ARGV) {
150
- fprintf(
151
- stderr,
152
- "ERROR: (fio_cli) fio_cli_get_* "
153
- "can only be called after `fio_cli_start` and before `fio_cli_end`\n");
154
- exit(-1);
82
+ char const *fio_cli_get_line_type(fio_cli_parser_data_s *parser,
83
+ const char *line) {
84
+ if (!line) {
85
+ return NULL;
155
86
  }
156
- if (!arg_aliases) {
157
- fprintf(stderr, "WARNING: (fio_cli) fio_cli_get_* "
158
- "should only be called after `fio_cli_accept_*`\n");
159
- return;
87
+ char const **pos = parser->names;
88
+ while (*pos) {
89
+ switch ((intptr_t)*pos) {
90
+ case /* FIO_CLI_TYPE_STRING */ 0x1: /* fallthrough */
91
+ case /* FIO_CLI_TYPE_BOOL */ 0x2: /* fallthrough */
92
+ case /* FIO_CLI_TYPE_INT */ 0x3: /* fallthrough */
93
+ ++pos;
94
+ continue;
95
+ }
96
+ if (line == *pos) {
97
+ goto found;
98
+ }
99
+ ++pos;
160
100
  }
161
- if (parsed)
162
- return;
163
- parsed = fiobj_hash_new();
164
- // {
165
- // FIOBJ json = fiobj_obj2json(arg_aliases, 1);
166
- // fprintf(stderr, "%s\n", fiobj_obj2cstr(json).data);
167
- // fiobj_free(json);
168
- // json = fiobj_obj2json(arg_type, 1);
169
- // fprintf(stderr, "%s\n", fiobj_obj2cstr(json).data);
170
- // fiobj_free(json);
171
- // }
172
-
173
- const char *start;
174
- FIOBJ arg_name;
175
-
176
- /* ignore the first element, it's the program's name. */
177
- for (int i = 1; i < FIO_CLI_ARGC; i++) {
178
- /* test for errors or help requests */
179
- if (FIO_CLI_ARGV[i][0] != '-' || FIO_CLI_ARGV[i][1] == 0) {
180
- if (ignore_unknown)
181
- continue;
182
- start = FIO_CLI_ARGV[i];
101
+ return NULL;
102
+ found:
103
+ switch ((size_t)pos[1]) {
104
+ case /* FIO_CLI_TYPE_STRING */ 0x1: /* fallthrough */
105
+ case /* FIO_CLI_TYPE_BOOL */ 0x2: /* fallthrough */
106
+ case /* FIO_CLI_TYPE_INT */ 0x3: /* fallthrough */
107
+ return pos[1];
108
+ }
109
+ return NULL;
110
+ }
111
+
112
+ static void fio_cli_set_arg(cstr_s arg, char const *value, char const *line,
113
+ fio_cli_parser_data_s *parser) {
114
+ /* handle unknown argument */
115
+ if (!line || !arg.len) {
116
+ if (!value) {
117
+ /*wtf?*/
118
+ return;
119
+ }
120
+ if (!strcmp(value, "-?") || !strcasecmp(value, "-h") ||
121
+ !strcasecmp(value, "-help") || !strcasecmp(value, "--help")) {
122
+ goto print_help;
123
+ }
124
+ cstr_s n = {.len = ++parser->unknown_count};
125
+ fio_hash_insert(&fio_values, n.len, n, value);
126
+ if (!parser->allow_unknown) {
127
+ arg.len = 0;
183
128
  goto error;
184
129
  }
185
- if ((FIO_CLI_ARGV[i][1] == '?' && FIO_CLI_ARGV[i][2] == 0) ||
186
- (FIO_CLI_ARGV[i][1] == 'h' &&
187
- (FIO_CLI_ARGV[i][2] == 0 ||
188
- (FIO_CLI_ARGV[i][2] == 'e' && FIO_CLI_ARGV[i][3] == 'l' &&
189
- FIO_CLI_ARGV[i][4] == 'p' && FIO_CLI_ARGV[i][5] == 0)))) {
190
- fio_cli_handle_error();
191
- continue;
130
+ return;
131
+ }
132
+
133
+ /* validate data types */
134
+ char const *type = fio_cli_get_line_type(parser, line);
135
+ switch ((size_t)type) {
136
+ case /* FIO_CLI_TYPE_BOOL */ 0x2:
137
+ if (value &&
138
+ (value == arg.data + arg.len ||
139
+ (value == arg.data + arg.len + 1 && arg.data[arg.len] == '='))) {
140
+ goto error;
192
141
  }
193
- /* we walk the name backwards, so `name` is tested before `n` */
194
- start = FIO_CLI_ARGV[i] + 1;
195
- size_t len = strlen(start);
196
- while (len && !(arg_name = fio_cli_get_name(start, len))) {
197
- --len;
142
+ value = "1";
143
+ break;
144
+ case /* FIO_CLI_TYPE_INT */ 0x3: /* fallthrough */
145
+ {
146
+ if (!value)
147
+ goto error;
148
+ char const *tmp = value;
149
+ if (*tmp == '-' || *tmp == '+') {
150
+ ++tmp;
198
151
  }
199
- if (!len)
152
+ if (!*tmp) {
200
153
  goto error;
201
- /* at this point arg_name is a handle to the argument's Symbol */
202
- FIOBJ type = fiobj_hash_get(arg_type, arg_name);
203
- if (FIOBJ_TYPE_IS(type, FIOBJ_T_NULL)) {
204
- /* type is BOOL, no further processing required */
205
- start = "1";
206
- len = 1;
207
- goto set_arg;
208
154
  }
209
- if (start[len] == 0) {
210
- i++;
211
- if (i == FIO_CLI_ARGC)
212
- goto error;
213
- start = FIO_CLI_ARGV[i];
214
- } else if (start[len] == '=') {
215
- start = start + len + 1;
216
- } else
217
- start = start + len;
218
- len = 0;
219
- if (FIOBJ_TYPE_IS(type, FIOBJ_T_FALSE)) /* no restrictions on data */
220
- goto set_arg;
221
- /* test that the argument is numerical */
222
- if (start[len] == '-') /* negative number? */
223
- len++;
224
- while (start[len] >= '0' && start[len] <= '9')
225
- len++;
226
- if (start[len] == '.') { /* float number? */
227
- while (start[len] >= '0' && start[len] <= '9')
228
- len++;
155
+ while (*tmp && *tmp >= '0' && *tmp <= '9') {
156
+ ++tmp;
229
157
  }
230
- if (start[len]) /* if there's data left, this aint a number. */
158
+ if (*tmp) {
231
159
  goto error;
232
- set_arg:
233
- fiobj_hash_set(parsed, arg_name, fiobj_str_new(start, strlen(start)));
234
- continue;
235
- error:
236
- fprintf(stderr, "\n\t*** Argument Error: %s ***\n", start);
237
- fio_cli_handle_error();
160
+ }
161
+ }
162
+ case /* FIO_CLI_TYPE_STRING */ 0x1: /* fallthrough */
163
+ break;
238
164
  }
239
- }
240
165
 
241
- /* *****************************************************************************
242
- CLI API
243
- ***************************************************************************** */
166
+ /* add values using all aliases possible */
167
+ {
168
+ cstr_s n = {.data = line};
169
+ while (n.data[0] == '-') {
170
+ while (n.data[n.len] && n.data[n.len] != ' ' && n.data[n.len] != ',') {
171
+ ++n.len;
172
+ }
173
+ fio_hash_insert(&fio_values, FIO_CLI_HASH_VAL(n), n, value);
174
+ while (n.data[n.len] && (n.data[n.len] == ' ' || n.data[n.len] == ',')) {
175
+ ++n.len;
176
+ }
177
+ n.data += n.len;
178
+ n.len = 0;
179
+ }
180
+ }
244
181
 
245
- /** Initialize the CLI helper */
246
- void fio_cli_start(int argc, const char **argv, const char *info) {
247
- FIO_CLI_ARGV = argv;
248
- FIO_CLI_ARGC = argc;
249
- if (info_str)
250
- fiobj_free(info_str);
251
- if (info) {
252
- info_str = fiobj_str_new(info, strlen(info));
253
- } else {
254
- info_str = fiobj_str_new(DEFAULT_CLI_INFO, sizeof(DEFAULT_CLI_INFO) - 1);
182
+ /* handle additional argv progress (if value is on separate argv) */
183
+ if (type != FIO_CLI_TYPE_BOOL && value && value != arg.data + arg.len &&
184
+ !arg.data[arg.len]) {
185
+ /* advance the position marker more than once? */
186
+ ++parser->pos;
255
187
  }
256
- }
188
+ return;
189
+
190
+ error: /* handle errors*/
191
+ /* TODO! */
192
+ fprintf(stderr, "\n\r\x1B[31mError:\x1B[0m unknown argument %.*s %s %s\n\n",
193
+ (int)arg.len, arg.data, arg.len ? "with value" : "",
194
+ value ? value : "(null)");
195
+ print_help:
196
+ fprintf(stderr, "\n%s\n\n",
197
+ parser->description ? parser->description
198
+ : "This application accepts any of the following "
199
+ "possible arguments:");
200
+ /* print out each line's arguments */
201
+ char const **pos = parser->names;
202
+ while (*pos) {
203
+ switch ((intptr_t)*pos) {
204
+ case /* FIO_CLI_TYPE_STRING */ 0x1: /* fallthrough */
205
+ case /* FIO_CLI_TYPE_BOOL */ 0x2: /* fallthrough */
206
+ case /* FIO_CLI_TYPE_INT */ 0x3: /* fallthrough */
207
+ ++pos;
208
+ continue;
209
+ }
210
+ type = FIO_CLI_TYPE_STRING;
211
+ switch ((intptr_t)pos[1]) {
212
+ case /* FIO_CLI_TYPE_STRING */ 0x1: /* fallthrough */
213
+ case /* FIO_CLI_TYPE_BOOL */ 0x2: /* fallthrough */
214
+ case /* FIO_CLI_TYPE_INT */ 0x3: /* fallthrough */
215
+ type = pos[1];
216
+ }
217
+ /* print line @ pos, starting with main argument name */
218
+ int alias_count = 0;
219
+ int first_len = 0;
220
+ size_t tmp = 0;
221
+ char const *const p = *pos;
222
+ while (p[tmp] == '-') {
223
+ while (p[tmp] && p[tmp] != ' ' && p[tmp] != ',') {
224
+ if (!alias_count)
225
+ ++first_len;
226
+ ++tmp;
227
+ }
228
+ ++alias_count;
229
+ while (p[tmp] && (p[tmp] == ' ' || p[tmp] == ',')) {
230
+ ++tmp;
231
+ }
232
+ }
233
+ switch ((size_t)type) {
234
+ case /* FIO_CLI_TYPE_STRING */ 0x1:
235
+ fprintf(stderr, "\t\x1B[1m%.*s\x1B[0m\x1B[2m <val>\x1B[0m\t%s\n",
236
+ first_len, p, p + tmp);
237
+ break;
238
+ case /* FIO_CLI_TYPE_BOOL */ 0x2:
239
+ fprintf(stderr, "\t\x1B[1m%.*s\x1B[0m \t%s\n", first_len, p,
240
+ p + tmp);
241
+ break;
242
+ case /* FIO_CLI_TYPE_INT */ 0x3:
243
+ fprintf(stderr, "\t\x1B[1m%.*s\x1B[0m\x1B[2m ### \x1B[0m\t%s\n",
244
+ first_len, p, p + tmp);
245
+ break;
246
+ }
247
+ /* print aliase information */
248
+ tmp = first_len;
249
+ while (p[tmp] && (p[tmp] == ' ' || p[tmp] == ',')) {
250
+ ++tmp;
251
+ }
252
+ while (p[tmp] == '-') {
253
+ const size_t start = tmp;
254
+ while (p[tmp] && p[tmp] != ' ' && p[tmp] != ',') {
255
+ ++tmp;
256
+ }
257
+ switch ((size_t)type) {
258
+ case /* FIO_CLI_TYPE_STRING */ 0x1:
259
+ fprintf(stderr,
260
+ "\t\x1B[1m%.*s\x1B[0m\x1B[2m <val>\x1B[0m\t(same as "
261
+ "\x1B[1m%.*s\x1B[0m)\n",
262
+ (int)(tmp - start), p + start, first_len, p);
263
+ break;
264
+ case /* FIO_CLI_TYPE_BOOL */ 0x2:
265
+ fprintf(stderr,
266
+ "\t\x1B[1m%.*s\x1B[0m \t(same as \x1B[1m%.*s\x1B[0m)\n",
267
+ (int)(tmp - start), p + start, first_len, p);
268
+ break;
269
+ case /* FIO_CLI_TYPE_INT */ 0x3:
270
+ fprintf(stderr,
271
+ "\t\x1B[1m%.*s\x1B[0m\x1B[2m ### \x1B[0m\t(same as "
272
+ "\x1B[1m%.*s\x1B[0m)\n",
273
+ (int)(tmp - start), p + start, first_len, p);
274
+ break;
275
+ }
276
+ }
257
277
 
258
- /** Clears the memory and resources used by the CLI helper */
259
- void fio_cli_end(void) {
260
- #define free_and_reset(o) \
261
- do { \
262
- fiobj_free((o)); \
263
- o = FIOBJ_INVALID; \
264
- } while (0);
265
-
266
- free_and_reset(arg_aliases);
267
- free_and_reset(arg_type);
268
- free_and_reset(help_str);
269
- free_and_reset(info_str);
270
- if (parsed)
271
- free_and_reset(parsed);
272
-
273
- #undef free_and_reset
274
-
275
- FIO_CLI_ARGC = 0;
276
- FIO_CLI_ARGV = NULL;
277
- is_parsed = 0;
278
+ ++pos;
279
+ }
280
+ fprintf(stderr, "\nUse any of the following input formats:\n"
281
+ "\t-arg <value>\t-arg=<value>\t-arg<value>\n"
282
+ "\n"
283
+ "Use the -h, -help or -? to get this information again.\n"
284
+ "\n");
285
+ fio_cli_end();
286
+ exit(0);
278
287
  }
279
288
 
280
- /**
281
- * Sets a CLI acceptable argument of type Number (both `int` and `float`).
282
- *
283
- * The `aliases` string sets aliases for the same argument. i.e. "string
284
- * s".
285
- *
286
- * The first alias will be the name available for `fio_cli_get_*`
287
- * functions.
288
- *
289
- * The `desc` string will be printed if `-?`, `-h` of `-help` are used.
290
- *
291
- * The function will crash the application on failure, printing an error
292
- * message.
293
- */
294
- void fio_cli_accept_num(const char *aliases, const char *desc) {
295
- fio_cli_set(aliases, desc, CLI_NUM);
296
- }
289
+ void fio_cli_start AVOID_MACRO(int argc, char const *argv[], int allow_unknown,
290
+ char const *description, char const **names) {
291
+ fio_cli_parser_data_s parser = {
292
+ .allow_unknown = allow_unknown,
293
+ .description = description,
294
+ .names = names,
295
+ .pos = 0,
296
+ };
297
+
298
+ if (fio_hash_count(&fio_values)) {
299
+ fio_cli_end();
300
+ }
297
301
 
298
- /**
299
- * Sets a CLI acceptable argument of type String.
300
- *
301
- * The `aliases` string sets aliases for the same argument. i.e. "string s".
302
- *
303
- * The first alias will be the name used
304
- *
305
- * The `desc` string will be printed if `-?`, `-h` of `-help` are used.
306
- *
307
- * The function will crash the application on failure, printing an error
308
- * message.
309
- */
310
- void fio_cli_accept_str(const char *aliases, const char *desc) {
311
- fio_cli_set(aliases, desc, CLI_STR);
312
- }
302
+ /* prepare aliases hash map */
313
303
 
314
- /**
315
- * Sets a CLI acceptable argument of type Bool (true if exists).
316
- *
317
- * The `aliases` string sets aliases for the same argument. i.e. "string s".
318
- *
319
- * The first alias will be the name available for `fio_cli_get_*` functions.
320
- *
321
- * The `desc` string will be printed if `-?`, `-h` of `-help` are used.
322
- *
323
- * The function will crash the application on failure, printing an error
324
- * message.
325
- */
326
- void fio_cli_accept_bool(const char *aliases, const char *desc) {
327
- fio_cli_set(aliases, desc, CLI_BOOL);
304
+ char const **line = names;
305
+ while (*line) {
306
+ switch ((intptr_t)*line) {
307
+ case /* FIO_CLI_TYPE_STRING */ 0x1: /* fallthrough */
308
+ case /* FIO_CLI_TYPE_BOOL */ 0x2: /* fallthrough */
309
+ case /* FIO_CLI_TYPE_INT */ 0x3: /* fallthrough */
310
+ ++line;
311
+ continue;
312
+ }
313
+ fio_cli_map_line2alias(*line);
314
+ ++line;
315
+ }
316
+
317
+ /* parse existing arguments */
318
+
319
+ while ((++parser.pos) < argc) {
320
+ char const *value = NULL;
321
+ cstr_s n = {.data = argv[parser.pos], .len = strlen(argv[parser.pos])};
322
+ if (parser.pos + 1 < argc) {
323
+ value = argv[parser.pos + 1];
324
+ }
325
+ const char **l = NULL;
326
+ while (n.len &&
327
+ !(l = fio_hash_find(&fio_aliases, FIO_CLI_HASH_VAL(n), n))) {
328
+ --n.len;
329
+ value = n.data + n.len;
330
+ }
331
+ if (n.len && value && value[0] == '=') {
332
+ ++value;
333
+ }
334
+ // fprintf(stderr, "Setting %.*s to %s\n", (int)n.len, n.data, value);
335
+ fio_cli_set_arg(n, value, (l ? (*l) : NULL), &parser);
336
+ }
337
+
338
+ /* Cleanup and save state for API */
339
+ fio_hash_free(&fio_aliases);
340
+ fio_unknown_count = parser.unknown_count;
328
341
  }
329
342
 
330
- /**
331
- * Returns a C String containing the value of the received argument, or NULL
332
- * if none.
333
- */
334
- const char *fio_cli_get_str(const char *opt) {
335
- fio_cli_parse();
336
- FIOBJ name = fio_cli_get_name(opt, strlen(opt));
337
- if (!name)
338
- return NULL;
339
- FIOBJ result = fiobj_hash_get(parsed, name);
340
- if (!result)
341
- return NULL;
342
- return fiobj_obj2cstr(result).data;
343
+ void fio_cli_end(void) {
344
+ fio_hash_free(&fio_values);
345
+ fio_hash_free(&fio_aliases);
346
+ fio_unknown_count = 0;
343
347
  }
348
+ /* *****************************************************************************
349
+ CLI Data Access
350
+ ***************************************************************************** */
344
351
 
345
- /**
346
- * Returns an Integer containing the parsed value of the argument.
347
- *
348
- * For boolean values, the value will be 0 for FALSE and 1 for TRUE.
349
- */
350
- int fio_cli_get_int(const char *opt) {
351
- fio_cli_parse();
352
- FIOBJ name = fio_cli_get_name(opt, strlen(opt));
353
- if (!name)
354
- return 0;
355
- FIOBJ result = fiobj_hash_get(parsed, name);
356
- if (!result)
357
- return 0;
358
- return (int)fiobj_obj2num(result);
352
+ /** Returns the argument's value as a NUL terminated C String. */
353
+ char const *fio_cli_get(char const *name) {
354
+ cstr_s n = {.data = name, .len = strlen(name)};
355
+ if (!fio_hash_count(&fio_values)) {
356
+ return NULL;
357
+ }
358
+ const char **val = fio_hash_find(&fio_values, FIO_CLI_HASH_VAL(n), n);
359
+ if (val)
360
+ return *val;
361
+ return NULL;
359
362
  }
360
363
 
361
- /**
362
- * Returns a Float containing the parsed value of the argument.
363
- *
364
- * For boolean values, the value will be 0 for FALSE and 1 for TRUE.
365
- */
366
- double fio_cli_get_float(const char *opt) {
367
- fio_cli_parse();
368
- FIOBJ name = fio_cli_get_name(opt, strlen(opt));
369
- if (!name)
364
+ /** Returns the argument's value as an integer. */
365
+ int fio_cli_get_i(char const *name) {
366
+ char const *val = fio_cli_get(name);
367
+ if (!val)
370
368
  return 0;
371
- FIOBJ result = fiobj_hash_get(parsed, name);
372
- if (!result)
373
- return 0;
374
- return fiobj_obj2float(result);
369
+ int ret = 0;
370
+ int invert = 0;
371
+ while (*val == '-' || *val == '+') {
372
+ if (*val == '-') {
373
+ invert += 1;
374
+ }
375
+ ++val;
376
+ }
377
+ while (*val) {
378
+ ret = (ret * 10) + (*val - '0');
379
+ ++val;
380
+ }
381
+ if ((invert & 1)) {
382
+ ret = 0 - ret;
383
+ }
384
+ return ret;
375
385
  }
376
386
 
377
- /**
378
- * Overrides the existing value of the argument with the requested C String.
379
- *
380
- * Boolean that were set to TRUE have the string "1".
381
- */
382
- void fio_cli_set_str(const char *opt, const char *value) {
383
- fio_cli_parse();
384
- FIOBJ name = fio_cli_get_name(opt, strlen(opt));
385
- if (!name) {
386
- fprintf(stderr, "ERROR: facil.io's CLI helper can only override values for "
387
- "valid options\n");
388
- exit(-1);
389
- }
390
- fiobj_hash_set(parsed, name, fiobj_str_new(value, strlen(value)));
387
+ /** Returns the number of unrecognized argument. */
388
+ unsigned int fio_cli_unknown_count(void) {
389
+ return (unsigned int)fio_unknown_count;
391
390
  }
392
391
 
393
- /**
394
- * Overrides the existing value of the argument with the requested Integer.
395
- *
396
- * For boolean values, the value will be 0 for FALSE and 1 for TRUE.
397
- */
398
- void fio_cli_set_int(const char *opt, int value) {
399
- fio_cli_parse();
400
- FIOBJ name = fio_cli_get_name(opt, strlen(opt));
401
- if (!name) {
402
- fprintf(stderr, "ERROR: facil.io's CLI helper can only override values for "
403
- "valid options\n");
404
- exit(-1);
392
+ /** Returns the unrecognized argument using a 0 based `index`. */
393
+ char const *fio_cli_unknown(unsigned int index) {
394
+ if (!fio_hash_count(&fio_values) || !fio_unknown_count) {
395
+ return NULL;
405
396
  }
406
- fiobj_hash_set(parsed, name, fiobj_num_new(value));
397
+ cstr_s n = {.data = NULL, .len = index + 1};
398
+ const char **val = fio_hash_find(&fio_values, index + 1, n);
399
+ if (val)
400
+ return *val;
401
+ return NULL;
407
402
  }
408
403
 
409
404
  /**
410
- * Overrides the existing value of the argument with the requested Float.
405
+ * Sets the argument's value as a NUL terminated C String (no copy!).
411
406
  *
412
- * For boolean values, the value will be 0 for FALSE and 1 for TRUE.
407
+ * Note: this does NOT copy the C strings to memory. Memory should be kept
408
+ * alive until `fio_cli_end` is called.
413
409
  */
414
- void fio_cli_set_float(const char *opt, double value) {
415
- fio_cli_parse();
416
- FIOBJ name = fio_cli_get_name(opt, strlen(opt));
417
- if (!name) {
418
- fprintf(stderr, "ERROR: facil.io's CLI helper can only override values for "
419
- "valid options\n");
420
- exit(-1);
421
- }
422
- fiobj_hash_set(parsed, name, fiobj_float_new(value));
410
+ void fio_cli_set(char const *name, char const *value) {
411
+ cstr_s n = (cstr_s){.data = name, .len = strlen(name)};
412
+ fio_hash_insert(&fio_values, FIO_CLI_HASH_VAL(n), n, value);
423
413
  }