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 }