-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmcli.h
More file actions
241 lines (222 loc) · 7.57 KB
/
mcli.h
File metadata and controls
241 lines (222 loc) · 7.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/*
* mcli was brought to you by matyz
* licensed under MIT
*/
#ifndef MCLI_H
#define MCLI_H
/* We include <string.h> header for strcmp
* You can provide your own strcmp implementation if you want :) */
#include <string.h>
/* For free(), malloc() and realloc() functions */
#include <stdlib.h>
#include <stdio.h>
/* ---------------------------
* STRUCTS
* ---------------------------*/
struct argdef {
struct {
char* long_name;
unsigned char accepts_value;
char short_name;
} hdr;
union {
int found;
char* str;
} value;
};
enum mcli_error_type {
/* for example: `program --param` where `struct argdef param = argdef_long("param", 1);`*/
MCLI_ERR_NO_VALUE,
/* for example: `program -param` (we don't support flags like this) */
MCLI_ERR_SHORT_ARG_TOO_LONG,
/* for example: `program -` */
MCLI_ERR_ONLY_HYPHEN_MINUS,
MCLI_ERR_UNKNOWN_OPTION,
MCLI_ERR_VALUE_WITHOUT_OPTION
};
struct mcli_error {
enum mcli_error_type error_code;
/*
* for MCLI_ERR_NO_VALUE: it's argument's name
* for MCLI_ERR_SHORT_ARG_TOO_LONG: it's argument's name
* for MCLI_ERR_ONLY_HYPHEN_MINUS: it's NULL
* for MCLI_ERR_UNKNOWN_OPTION: it's argument's name
* for MCLI_ERR_VALUE_WITHOUT_OPTION: it's the value
*/
char* value;
};
struct mcli_errbuf {
struct mcli_error* ptr;
unsigned int len;
};
/* ---------------------------
* IMPLEMENTATION
* ---------------------------*/
int mcli_error_print(struct mcli_error* err, FILE* file) {
switch (err->error_code) {
case MCLI_ERR_NO_VALUE:
return fprintf(file, "%s: no value found\n", err->value);
case MCLI_ERR_SHORT_ARG_TOO_LONG:
return fprintf(file, "%s: more than 1 characters are not allowed with only 1 '-'\n", err->value);
case MCLI_ERR_ONLY_HYPHEN_MINUS:
return fprintf(file, "error: argument only consists of '-'\n");
case MCLI_ERR_UNKNOWN_OPTION:
return fprintf(file, "%s: unknown option\n", err->value);
case MCLI_ERR_VALUE_WITHOUT_OPTION:
return fprintf(file, "%s: value without option\n", err->value);
default:
return 1;
}
}
int mcli_errbuf_print(struct mcli_errbuf* errbuf, FILE* file) {
int i, r;
for (i = 0; i < errbuf->len; i++) {
r = mcli_error_print(&errbuf->ptr[i], file);
if (r) return r;
}
return 0;
}
void mcli_errbuf_push(struct mcli_errbuf* errbuf, struct mcli_error err) {
struct argdef a = {0};
long size = sizeof(a);
if (!errbuf->ptr) {
errbuf->ptr = (struct mcli_error*)malloc(1 * size);
errbuf->ptr[0] = err;
errbuf->len = 1;
} else {
errbuf->len += 1;
errbuf->ptr = (struct mcli_error*)realloc(errbuf->ptr, errbuf->len * size);
errbuf->ptr[errbuf->len-1] = err;
}
}
void mcli_errbuf_free(struct mcli_errbuf* errbuf) {
if (errbuf->ptr) free(errbuf->ptr);
}
struct argdef argdef_full(char short_name, char* long_name, int accepts_value) {
struct argdef argdef = {0};
argdef.hdr.short_name = short_name;
argdef.hdr.long_name = long_name;
argdef.hdr.accepts_value = accepts_value;
return argdef;
}
struct argdef argdef_long(char* long_name, int accepts_value) {
struct argdef argdef = {0};
argdef.hdr.long_name = long_name;
argdef.hdr.accepts_value = accepts_value;
return argdef;
}
struct argdef argdef_short(char short_name, int accepts_value) {
struct argdef argdef = {0};
argdef.hdr.short_name = short_name;
argdef.hdr.accepts_value = accepts_value;
return argdef;
}
struct argdef argdef_value() {
struct argdef argdef = {0};
argdef.hdr.accepts_value = 1;
return argdef;
}
/* 0 if it's not a flag
* 1 if it's a short flag
* 2 if it's a long flag
* -1 if it only consists of '-'
* -2 if it's something like '-param'
* we assert that flag is not NULL
*/
static int _mcli_flag_type(char *flag) {
if (flag[0] == '-') {
return flag[1] == '-' ? (flag[2] == 0 ? -1 : 2) : (flag[1] != 0 ? (flag[2] == 0 ? 1 : -2) : -1);
} else {
return 0;
}
}
struct mcli_errbuf parse_args(struct argdef* arg_array[], unsigned int len, unsigned int argv, char** argc) {
struct mcli_errbuf errbuf = {0};
struct mcli_error e;
int i, j, found;
/* we start from index 1, because index 0 is program name */
for (i = 1; i < argv; i++) {
switch (_mcli_flag_type(argc[i])) {
case -2:
e.error_code = MCLI_ERR_SHORT_ARG_TOO_LONG;
e.value = argc[i];
mcli_errbuf_push(&errbuf, e);
break;
case -1:
e.error_code = MCLI_ERR_ONLY_HYPHEN_MINUS;
e.value = NULL;
mcli_errbuf_push(&errbuf, e);
break;
case 0:
found = 0;
for (j = 0; j < len; j++) {
if (!arg_array[j]->hdr.short_name &&
!arg_array[j]->hdr.long_name &&
arg_array[j]->hdr.accepts_value &&
!arg_array[j]->value.found)
{
arg_array[j]->value.str = argc[i];
found = 1;
}
}
if (!found) {
e.error_code = MCLI_ERR_VALUE_WITHOUT_OPTION;
e.value = argc[i];
mcli_errbuf_push(&errbuf, e);
}
break;
case 1:
found = 0;
for (j = 0; j < len; j++) {
if (arg_array[j]->hdr.short_name == argc[i][1]) {
found = 1;
if (arg_array[j]->hdr.accepts_value) {
if (++i < argv) {
arg_array[j]->value.str = argc[i];
} else {
e.error_code = MCLI_ERR_NO_VALUE;
e.value = argc[i - 1];
mcli_errbuf_push(&errbuf, e);
}
} else {
arg_array[j]->value.found++;
}
break;
}
}
if (!found) {
e.error_code = MCLI_ERR_UNKNOWN_OPTION;
e.value = argc[i];
mcli_errbuf_push(&errbuf, e);
}
break;
case 2:
found = 0;
for (j = 0; j < len; j++) {
if (arg_array[j]->hdr.long_name && strcmp(arg_array[j]->hdr.long_name, argc[i] + 2) == 0) {
found = 1;
if (arg_array[j]->hdr.accepts_value) {
if (++i < argv) {
arg_array[j]->value.str = argc[i];
} else {
e.error_code = MCLI_ERR_NO_VALUE;
e.value = argc[i - 1];
mcli_errbuf_push(&errbuf, e);
}
} else {
arg_array[j]->value.found++;
}
break;
}
}
if (!found) {
e.error_code = MCLI_ERR_UNKNOWN_OPTION;
e.value = argc[i];
mcli_errbuf_push(&errbuf, e);
}
break;
}
}
return errbuf;
}
#endif