1 // SemiTwist D Tools: Library 2 // Written in the D programming language. 3 4 module semitwist.cmd.plain; 5 6 import std.stdio; 7 import std.file; 8 import std.path; 9 import std.string; 10 11 import semitwist.util.all; 12 13 Cmd cmd; 14 static this() 15 { 16 cmd = new Cmd(); 17 } 18 19 //TODO: Make promptChar 20 //TODO: Change prompt to "Press a key to continue..." 21 //TODO: Make a standard yes/no prompt 22 //TODO? Rename echoing to exececho or echoexec 23 //TODO: Handle env var stuff 24 //TODO: Handle exec stream redirecting 25 //TODO: Do piping 26 //TODO: Wrap Vfs 27 //TODO? Make dir something folder-specific instead of FileFolder? 28 //TODO: Dup Cmd 29 //TODO: Add echoerr (or just "err") 30 //TODO? Make "cmd.dir" (but not copies) affect Environment.cwd 31 class Cmd 32 { 33 private string _dir; // Working directory 34 //private static Layout!(char) layout; // Used by echo 35 36 invariant() 37 { 38 /* 39 //Shit, can't seem to do this stuff in here 40 mixin(deferAssert!("_dir.exists()")); 41 mixin(deferAssert!("_dir.isFolder()")); 42 mixin(deferAssert!("_dir.isAbsolute()")); 43 44 scope _dirStandard = _dir.dup.standard(); 45 mixin(deferAssert!("_dir.toString() == _dirStandard.toString()", "_dir is not in standard form")); 46 47 flushAsserts(); 48 */ 49 assert(_dir.exists()); 50 assert(_dir.isDir()); 51 assert(_dir.isAbsolute()); 52 53 //scope _dirStandard = _dir.dup.standard(); 54 //assert(_dir.toString() == _dirStandard.toString(), "_dir is not in standard form"); 55 } 56 57 this() 58 { 59 _dir = getcwd(); 60 //_dir.standard(); 61 } 62 63 /+ static this() 64 { 65 layout = new Layout!(char)(); 66 } 67 +/ 68 bool echoing = true; 69 70 // Property 71 /+ FileFolder dir() 72 { 73 //TODO: Don't create a new instance if dir hasn't changed 74 return new FileFolder(_dir.toString()); 75 } 76 FileFolder dir(VfsFolder value) 77 { 78 return dir(value.toString()); 79 } 80 FileFolder dir(string value) 81 { 82 auto cwdSave = Environment.cwd; 83 Environment.cwd = _dir.toString(); 84 value = Environment.toAbsolute(value); 85 Environment.cwd = cwdSave; 86 87 scope newDir = new FilePath(value); 88 if(newDir.isFolder() && newDir.exists()) 89 _dir.set( normalize(newDir.toString()), true ); 90 return dir(); 91 }+/ 92 93 //TODO: Abstract the interface so that the same exec calls work on both win and lin. 94 // Plain Versions 95 //TODO*** 96 /+ void exec(string cmd) 97 { 98 int errLevel; 99 exec(cmd, errLevel); 100 } 101 void exec(string app, string[] params) 102 { 103 int errLevel; 104 exec(app, params, errLevel); 105 }+/ 106 107 // With errLevel 108 //TODO*** 109 /+ void exec(string cmd, out int errLevel) 110 { 111 auto p = new Process(cmd); 112 execProcess(p, errLevel); 113 } 114 void exec(string app, string[] params, out int errLevel) 115 { 116 auto p = new Process(app ~ params); 117 execProcess(p, errLevel); 118 }+/ 119 120 // Implementation 121 /+ private void execProcess(Process p, out int errLevel) 122 { 123 //TODO: Find out what happens if wait() is called after the process finishes 124 p.workDir = _dir.toString(); 125 p.copyEnv = true; 126 p.redirect = echoing? Redirect.None : Redirect.All; 127 p.execute(); 128 auto r = p.wait(); 129 errLevel = (r.reason==Process.Result.Exit)? r.status : -1; 130 } 131 +/ 132 void echo(T...)(T args) 133 { 134 foreach(int i, arg; args) 135 { 136 if(i > 0) write(" "); 137 write(arg); 138 } 139 writeln(); 140 stdout.flush(); 141 } 142 143 private T _prompt(T, TChar)(TChar[] promptMsg, bool delegate(T) accept, 144 TChar[] rejectedMsg, T delegate() reader) 145 { 146 T input; 147 while(true) 148 { 149 Stdout(promptMsg).flush; 150 input = reader(); 151 152 if(accept is null) 153 break; 154 else 155 { 156 if(accept(input)) 157 break; 158 else 159 { 160 Stdout.newline; 161 Stdout.formatln(rejectedMsg, input); 162 } 163 } 164 } 165 166 return input; 167 } 168 169 // Input is Utf8-only for now because Cin is used which is Utf8-only 170 //TODO*** 171 /+ string prompt(T)(T[] promptMsg, bool delegate(string) accept=null, T[] rejectedMsg="") 172 { 173 string reader() 174 { 175 string input; 176 Cin.readln(input); 177 return strip(input); 178 } 179 180 return _prompt!(string,T)(promptMsg, accept, rejectedMsg, &reader); 181 } 182 +/ 183 /+ 184 // Doesn't work right now because I need to find a way to wait for and capture 185 // exactly one keypress. Cin.readln and getchar both wait for a newline. 186 187 // dchar not currently supported, because getchar/getwchar are used which 188 // don't have a Utf32 equivilent. 189 T promptChar(T)(T[] promptMsg, bool delegate(T) accept=null, T[] rejectedMsg="") 190 { 191 T reader() 192 { 193 static if(is(T==char)) 194 return cast(T)getchar(); 195 else static if(is(T==wchar)) 196 return cast(T)getwchar(); 197 else 198 static assert(false, "T must be char or wchar for promptChar"); 199 } 200 201 return _prompt!(T,T)(promptMsg, accept, rejectedMsg, &reader); 202 } 203 +/ 204 205 void pause() 206 { 207 /+ 208 // Can't do it this way until I get promptChar working 209 promptChar("Press a key to continue..."); 210 cmd.echo(); // The prompt doesn't output a newline 211 +/ 212 prompt("Press Enter to continue..."); 213 } 214 215 216 string prompt(string msg, bool delegate(string) accept=null, string msgRejected="") 217 { 218 string input; 219 while(true) 220 { 221 write(msg); 222 stdout.flush(); 223 input = stdin.readln(); 224 input = strip(input); 225 226 if(accept is null) 227 break; 228 else 229 { 230 if(accept(input)) 231 break; 232 else 233 { 234 writeln(); 235 writefln(msgRejected, input); 236 } 237 } 238 } 239 240 return input; 241 } 242 243 /+ 244 bool isHelpSwitch(string str) 245 { 246 if(str.length < 3) 247 return false; 248 249 if(str[0..2] == "--") 250 str = str[2..$]; 251 else if("-/".contains(str[0])) 252 str = str[1..$]; 253 else 254 return false; 255 256 return ["h"[], "help", "?"].contains(str.toLower()); 257 } 258 +/ 259 }