1 /**
2  * Argument-processing helper structure
3  */
4 module libgit2_d.example.args;
5 
6 
7 private static import core.stdc.stdlib;
8 private static import core.stdc.string;
9 private static import libgit2_d.example.common;
10 private static import libgit2_d.strarray;
11 private static import std.bitmanip;
12 
13 package:
14 
15 struct args_info
16 {
17 	int argc;
18 	char** argv;
19 	int pos;
20 
21 	/**
22 	 * < Did we see a -- separator
23 	 */
24 	mixin
25 	(
26 		std.bitmanip.bitfields!
27 		(
28 			int, "opts_done", 1,
29 			int, "not_used", 7
30 		)
31 	);
32 }
33 
34 pragma(inline, true)
35 pure nothrow @safe @nogc
36 .args_info ARGS_INFO_INIT(int argc, char** argv)
37 
38 	do
39 	{
40 		.args_info OUTPUT =
41 		{
42 			argc: argc,
43 			argv: argv,
44 			pos: 0,
45 		};
46 
47 		OUTPUT.opts_done = 0;
48 
49 		return OUTPUT;
50 	}
51 
52 //#define ARGS_CURRENT(args) args.argv[args.pos]
53 
54 /**
55  * Check if a string has the given prefix.  Returns 0 if not prefixed
56  * or the length of the prefix if it is.
57  */
58 nothrow @nogc
59 size_t is_prefixed(const (char)* str, const (char)* pfx)
60 
61 	in
62 	{
63 	}
64 
65 	do
66 	{
67 		size_t len = core.stdc..string.strlen(pfx);
68 
69 		return (core.stdc..string.strncmp(str, pfx, len)) ? (0) : (len);
70 	}
71 
72 /**
73  * Check current `args` entry against `opt` string.  If it matches
74  * exactly, take the next arg as a string; if it matches as a prefix with
75  * an equal sign, take the remainder as a string; if value not supplied,
76  * default value `def` will be given. otherwise return 0.
77  */
78 nothrow @nogc
79 int optional_str_arg(const (char)** out_, libgit2_d.example.args.args_info* args, const (char)* opt, const (char)* def)
80 
81 	in
82 	{
83 	}
84 
85 	do
86 	{
87 		const (char)* found = args.argv[args.pos];
88 		size_t len = .is_prefixed(found, opt);
89 
90 		if (!len) {
91 			return 0;
92 		}
93 
94 		if (!found[len]) {
95 			if ((args.pos + 1) == args.argc) {
96 				*out_ = def;
97 
98 				return 1;
99 			}
100 
101 			args.pos += 1;
102 			*out_ = args.argv[args.pos];
103 
104 			return 1;
105 		}
106 
107 		if (found[len] == '=') {
108 			*out_ = found + len + 1;
109 
110 			return 1;
111 		}
112 
113 		return 0;
114 	}
115 
116 /**
117  * Check current `args` entry against `opt` string.  If it matches
118  * exactly, take the next arg as a string; if it matches as a prefix with
119  * an equal sign, take the remainder as a string; otherwise return 0.
120  */
121 nothrow @nogc
122 int match_str_arg(const (char)** out_, libgit2_d.example.args.args_info* args, const (char)* opt)
123 
124 	in
125 	{
126 	}
127 
128 	do
129 	{
130 		const (char)* found = args.argv[args.pos];
131 		size_t len = .is_prefixed(found, opt);
132 
133 		if (!len) {
134 			return 0;
135 		}
136 
137 		if (!found[len]) {
138 			if ((args.pos + 1) == args.argc) {
139 				libgit2_d.example.common.fatal("expected value following argument", opt);
140 			}
141 
142 			args.pos += 1;
143 			*out_ = args.argv[args.pos];
144 
145 			return 1;
146 		}
147 
148 		if (found[len] == '=') {
149 			*out_ = found + len + 1;
150 
151 			return 1;
152 		}
153 
154 		return 0;
155 	}
156 
157 nothrow @nogc
158 private const (char)* match_numeric_arg(libgit2_d.example.args.args_info* args, const (char)* opt)
159 
160 	in
161 	{
162 	}
163 
164 	do
165 	{
166 		const (char)* found = args.argv[args.pos];
167 		size_t len = .is_prefixed(found, opt);
168 
169 		if (!len) {
170 			return null;
171 		}
172 
173 		if (!found[len]) {
174 			if ((args.pos + 1) == args.argc) {
175 				libgit2_d.example.common.fatal("expected numeric value following argument", opt);
176 			}
177 
178 			args.pos += 1;
179 			found = args.argv[args.pos];
180 		} else {
181 			found = found + len;
182 
183 			if (*found == '=') {
184 				found++;
185 			}
186 		}
187 
188 		return found;
189 	}
190 
191 /**
192  * Check current `args` entry against `opt` string parsing as uint16.  If
193  * `opt` matches exactly, take the next arg as a uint16_t value; if `opt`
194  * is a prefix (equal sign optional), take the remainder of the arg as a
195  * uint16_t value; otherwise return 0.
196  */
197 nothrow @nogc
198 int match_uint16_arg(ushort* out_, libgit2_d.example.args.args_info* args, const (char)* opt)
199 
200 	in
201 	{
202 	}
203 
204 	do
205 	{
206 		const (char)* found = .match_numeric_arg(args, opt);
207 
208 		if (found == null) {
209 			return 0;
210 		}
211 
212 		const (char)* endptr = null;
213 		ushort val = cast(ushort)(core.stdc.stdlib.strtoul(found, &endptr, 0));
214 
215 		if ((endptr == null) || (*endptr != '\0')) {
216 			libgit2_d.example.common.fatal("expected number after argument", opt);
217 		}
218 
219 		if (out_ != null) {
220 			*out_ = val;
221 		}
222 
223 		return 1;
224 	}
225 
226 /**
227  * Check current `args` entry against `opt` string parsing as uint32.  If
228  * `opt` matches exactly, take the next arg as a uint16_t value; if `opt`
229  * is a prefix (equal sign optional), take the remainder of the arg as a
230  * uint32_t value; otherwise return 0.
231  */
232 nothrow @nogc
233 int match_uint32_arg(uint* out_, libgit2_d.example.args.args_info* args, const (char)* opt)
234 
235 	in
236 	{
237 	}
238 
239 	do
240 	{
241 		const (char)* found = .match_numeric_arg(args, opt);
242 
243 		if (found == null) {
244 			return 0;
245 		}
246 
247 		const (char)* endptr = null;
248 
249 		ushort val = cast(ushort)(core.stdc.stdlib.strtoul(found, &endptr, 0));
250 
251 		if ((endptr == null) || (*endptr != '\0')) {
252 			libgit2_d.example.common.fatal("expected number after argument", opt);
253 		}
254 
255 		if (out_ != null) {
256 			*out_ = val;
257 		}
258 
259 		return 1;
260 	}
261 
262 nothrow @nogc
263 private int match_int_internal(int* out_, const (char)* str, int allow_negative, const (char)* opt)
264 
265 	in
266 	{
267 	}
268 
269 	do
270 	{
271 		const (char)* endptr = null;
272 		int val = cast(int)(core.stdc.stdlib.strtol(str, &endptr, 10));
273 
274 		if ((endptr == null) || (*endptr != '\0')) {
275 			libgit2_d.example.common.fatal("expected number", opt);
276 		} else if ((val < 0) && (!allow_negative)) {
277 			libgit2_d.example.common.fatal("negative values are not allowed", opt);
278 		}
279 
280 		if (out_ != null) {
281 			*out_ = val;
282 		}
283 
284 		return 1;
285 	}
286 
287 /**
288  * Check current `args` entry against a "bool" `opt` (ie. --[no-]progress).
289  * If `opt` matches positively, out will be set to 1, or if `opt` matches
290  * negatively, out will be set to 0, and in both cases 1 will be returned.
291  * If neither the positive or the negative form of opt matched, out will be -1,
292  * and 0 will be returned.
293  */
294 nothrow @nogc
295 int match_bool_arg(int* out_, libgit2_d.example.args.args_info* args, const (char)* opt)
296 
297 	in
298 	{
299 	}
300 
301 	do
302 	{
303 		const (char)* found = args.argv[args.pos];
304 
305 		if (!core.stdc..string.strcmp(found, opt)) {
306 			*out_ = 1;
307 
308 			return 1;
309 		}
310 
311 		if ((!core.stdc..string.strncmp(found, "--no-", core.stdc..string.strlen("--no-"))) && (!core.stdc..string.strcmp(found + core.stdc..string.strlen("--no-"), opt + 2))) {
312 			*out_ = 0;
313 
314 			return 1;
315 		}
316 
317 		*out_ = -1;
318 
319 		return 0;
320 	}
321 
322 /**
323  * Match an integer string, returning 1 if matched, 0 if not.
324  */
325 nothrow @nogc
326 int is_integer(int* out_, const (char)* str, int allow_negative)
327 
328 	in
329 	{
330 	}
331 
332 	do
333 	{
334 		return .match_int_internal(out_, str, allow_negative, null);
335 	}
336 
337 /**
338  * Check current `args` entry against `opt` string parsing as int.  If
339  * `opt` matches exactly, take the next arg as an int value; if it matches
340  * as a prefix (equal sign optional), take the remainder of the arg as a
341  * int value; otherwise return 0.
342  */
343 nothrow @nogc
344 int match_int_arg(int* out_, libgit2_d.example.args.args_info* args, const (char)* opt, int allow_negative)
345 
346 	in
347 	{
348 	}
349 
350 	do
351 	{
352 		const (char)* found = .match_numeric_arg(args, opt);
353 
354 		if (found == null) {
355 			return 0;
356 		}
357 
358 		return .match_int_internal(out_, found, allow_negative, opt);
359 	}
360 
361 /**
362  * Check if we're processing past the single -- separator
363  */
364 nothrow @nogc
365 int match_arg_separator(libgit2_d.example.args.args_info* args)
366 
367 	in
368 	{
369 	}
370 
371 	do
372 	{
373 		if (args.opts_done) {
374 			return 1;
375 		}
376 
377 		if (core.stdc..string.strcmp(args.argv[args.pos], "--") != 0) {
378 			return 0;
379 		}
380 
381 		args.opts_done = 1;
382 		args.pos++;
383 
384 		return 1;
385 	}
386 
387 /**
388  * Consume all remaining arguments in a git_strarray
389  */
390 nothrow @nogc
391 void strarray_from_args(libgit2_d.strarray.git_strarray* array, libgit2_d.example.args.args_info* args)
392 
393 	in
394 	{
395 	}
396 
397 	do
398 	{
399 		array.count = args.argc - args.pos;
400 		array.strings = cast(char**)(core.stdc.stdlib.calloc(array.count, (char*).sizeof));
401 		assert(array.strings != null);
402 
403 		for (size_t i = 0; args.pos < args.argc; ++args.pos) {
404 			array.strings[i++] = args.argv[args.pos];
405 		}
406 
407 		args.pos = args.argc;
408 	}