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