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 }