1 /// Author: Aziz Köksal 2 /// License: GPL3 3 /// $(Maturity average) 4 module util.OptParser; 5 6 import common; 7 8 /// A command line option parser. 9 class OptParser 10 { 11 cstring[] argv; /// The argument vector. 12 bool delegate()[] parseDgs; /// Option parsing delegates. 13 cstring error; /// Holds the error message if an error occurred. 14 /// Missing argument error message. 15 cstring missingArgMessage = "missing argument for option ‘{}’"; 16 /// Usage error message. 17 cstring usageErrorMessage = "Usage error:\n {}"; 18 19 /// Constructs an OptParser object. 20 this(cstring[] argv) 21 { 22 this.argv = argv; 23 } 24 /// ditto 25 this(string[] argv) 26 { 27 this.argv = cast(cstring[])argv; 28 } 29 30 /// Parses all arguments. 31 bool parseArgs() 32 { 33 cstring[] remArgs; // Remaining arguments. 34 while (hasArgs) 35 { 36 auto n = argv.length; // Remember number of args. 37 foreach (parseOption; parseDgs) 38 if (!hasArgs || parseOption()) 39 break; 40 else if (error !is null) 41 goto Lerror; 42 if (argv.length == n) // No arguments consumed? 43 remArgs ~= popArg(); // Append to remaining args. 44 } 45 argv = remArgs; 46 return true; 47 Lerror: 48 argv = remArgs ~ argv; 49 return false; 50 } 51 52 /// Adds a parser delegate. 53 void add()(bool delegate() parseDg) 54 { 55 parseDgs ~= parseDg; 56 } 57 58 /// A dummy variable used to force the compiler to create a closure. 59 private bool delegate() closureDelegate; 60 61 /// Adds a delegate for parsing an option. 62 void add(T)(cstring param, ref T out_arg, void delegate() cb = null) 63 { // Have to assign to outer variable first to create a closure. 64 add(closureDelegate = 65 { return parse(param, out_arg) && (cb && cb(), true); }); 66 } 67 68 /// Adds a delegate accepting any option. 69 void addDefault(void delegate() defaultDg) 70 { 71 add(closureDelegate = { defaultDg(); return true; }); 72 } 73 74 /// Parses a parameter. 75 bool parse(cstring param, ref cstring out_arg) 76 { 77 if (!hasArgs) return false; 78 auto arg0 = argv[0]; 79 auto n = param.length; 80 if (arg0.startsWith(param)) 81 { 82 if (arg0.length == n) // arg0 == param 83 { // Eg: -I /include/path 84 if (argv.length <= 1) 85 goto Lerror; 86 out_arg = argv[1]; 87 n = 2; 88 } 89 else 90 { // Eg: -I/include/path 91 auto skipEqualSign = arg0[n] == '='; 92 out_arg = arg0[n + skipEqualSign .. $]; 93 n = 1; 94 } 95 consume(n); // Consume n arguments. 96 return true; 97 } 98 return false; 99 Lerror: 100 error = Format(missingArgMessage, param); 101 return false; 102 } 103 104 /// Parses a flag. 105 bool parse(cstring flag, ref bool out_arg) 106 { 107 if (hasArgs && argv[0] == flag) { 108 out_arg = true; 109 consume(1); 110 return true; 111 } 112 return false; 113 } 114 115 /// Slices off n elements from argv. 116 void consume(size_t n) 117 { 118 argv = argv[n..$]; 119 } 120 121 /// Returns true if arguments are available. 122 bool hasArgs() @property 123 { 124 return argv.length != 0; 125 } 126 127 /// Returns the first argument and removes it from the list. 128 cstring popArg() 129 { 130 auto arg = argv[0]; 131 consume(1); 132 return arg; 133 } 134 135 /// Prints the usage error message. 136 void printUsageError() 137 { 138 Printfln(usageErrorMessage, error); 139 } 140 } 141 142 /// Returns true if str starts with s. 143 bool startsWith(cstring str, cstring s) 144 { 145 return str.length >= s.length && str[0 .. s.length] == s; 146 }