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 }