1 // SemiTwist D Tools 2 // Samples: semitwist.cmd sample 3 // Written in the D programming language. 4 5 /++ 6 Author: 7 $(WEB www.semitwist.com, Nick Sabalausky) 8 9 This has been tested to work with DMD 2.058 through 2.062 10 +/ 11 12 module semitwist.apps.samples.cmdSample.main; 13 14 import semitwist.cmd.all; 15 16 void showSectionHeader(string str) 17 { 18 cmd.echo(); 19 cmd.echo("--------", str, "--------"); 20 } 21 22 //TODO: Find those functions in tango to read and maybe write a file in one line. 23 // And add the import to semitwist.cmd.all, and add samples for it here. 24 // -> tango.io.device.File: string blah = cast(string)File.get("blah.txt"); 25 26 void main(string[] args) 27 { 28 // ----- semitwist.cmd: cmd.pause ----- 29 // - Prompt and wait for the user to press Enter 30 cmd.pause(); 31 32 // ----- semitwist.cmd: cmd.echo ----- 33 // - Script-style alternative to Stdout, largely here for completeness 34 // - May be extended later to support a limited form of 35 // "blah $myvar blah"-style string expansion. 36 // - Note that a newline is automatically appended, 37 // which is useful for shell-script-style apps. 38 showSectionHeader("semitwist.cmd: cmd.echo"); 39 40 cmd.echo("Hello"); 41 cmd.echo("This is on a separate line"); 42 cmd.echo("Multiple mixed-types:"c, 2, "+"w, 3.1, "="d, 5.1); 43 cmd.echo("No worry about forgetting spaces:"); 44 cmd.echo(32, 64, 128, 256); 45 class Foo { 46 override string toString() { 47 return "Any type that Stdout can handle is ok."; 48 } 49 } 50 cmd.echo(new Foo(), "See?"); 51 write("Of course, "); 52 writefln("ordinary writeln %s available too.", "is"); 53 54 cmd.pause(); 55 56 // ----- semitwist.util.text: sformat/sformatln ----- 57 // - Wraps tango's Layout seamlessly for char, wchar and dchar. 58 // - Note that you don't need to manually instantiate it. 59 // - Using D's array-method calling syntax is, of course, optional. 60 // (But I like using it.) 61 showSectionHeader("semitwist.util.text: sformat/sformatln"); 62 63 auto myStr8 = "Hello %s".format("Joe"); 64 cmd.echo(myStr8); 65 auto myStr16 = "This %s wstr ends in a newline, %s"w.formatln("happy", "whee"); 66 cmd.echo(myStr16); 67 cmd.echo("See? There was an extra newline up there."); 68 69 cmd.pause(); 70 71 // ----- semitwist.util.mixins: trace ----- 72 // - Useful for debugging. 73 // - Outputs file and line information 74 showSectionHeader("semitwist.util.mixins: trace"); 75 76 trace(); 77 trace(); 78 trace("======== this is easy to spot ========"); 79 80 cmd.pause(); 81 82 // ----- semitwist.util.mixins: traceVal ----- 83 // - Useful for debugging. 84 // - DRY way to output both an expression and the expression's value 85 showSectionHeader("semitwist.util.mixins: traceVal"); 86 87 double myDouble = 5.55; 88 int myInt = 7; 89 mixin(traceVal!("myDouble", "myInt ", " myInt", "4*7")); 90 mixin(traceVal!(`"Any expression %s".format("is ok")`)); 91 92 cmd.pause(); 93 94 // ----- semitwist.cmd: cmd.exec ----- 95 // - Easy tango.sys.Process wrapper for running apps 96 // 97 // We'll test this with two small sample apps: 98 // - showargs: Lists the args passed into it 99 // - seterrorlevel: Sets the error level to a desired value 100 /+showSectionHeader("semitwist.cmd: cmd.exec"); 101 102 cmd.exec("semitwist-showargs Hello from D!"); // Three args 103 cmd.exec(`semitwist-showargs "Hello from D!"`); // One arg with spaces 104 cmd.exec("semitwist-showargs", ["arg 1"[], "arg 2"]); // Two args, each with spaces 105 int errLevel; 106 mixin(traceVal!("errLevel")); 107 cmd.exec("seterrorlevel 42", errLevel); 108 mixin(traceVal!("errLevel")); 109 110 cmd.pause();+/ 111 112 // ----- semitwist.cmd: cmd.echoing ----- 113 // - Determines whether an exec'd program's stdout/stderr are actually 114 // sent to this program's stdout/stderr or are hidden. 115 // - Not to be confused with the functionality of Win's "echo on"/"echo off" 116 // 117 // We'll test this with a small sample app: 118 // - myecho: Like ordinary echo, but needed on Win because Win's echo 119 // is not an actual executable and therefore can't be launched 120 // by the tango.sys.Process used by exec. 121 /+showSectionHeader("semitwist.cmd: cmd.echoing"); 122 123 cmd.exec("semitwist-echo You can see this"); 124 cmd.echoing = false; 125 cmd.exec("semitwist-echo You cannot see this"); 126 cmd.echoing = true; 127 cmd.exec("semitwist-echo You can see this again"); 128 129 cmd.pause();+/ 130 131 // ----- semitwist.cmd: cmd.prompt ----- 132 // - Easy way to prompt for information interactively 133 showSectionHeader("semitwist.cmd: cmd.prompt"); 134 135 string input; 136 input = cmd.prompt("Type some stuff: "); 137 cmd.echo("You entered:", input); 138 139 // Easy prompt-with-validation 140 string promptMsg = "Do you want 'coffee' or 'tea'? "; 141 string failureMsg = "Please enter 'coffee' or 'tea', not '{0}'"; 142 bool accept(string input) 143 { 144 return ["coffee", "tea"].contains(toLower(input)); 145 } 146 // This will *not* return until the user enters a valid choice, 147 // so we don't need to do any more validation. 148 input = cmd.prompt(promptMsg, &accept, failureMsg); 149 cmd.echo("Tough luck! No", input, "for you!"); 150 /+ 151 // Single-char prompts 152 auto letter = cmd.promptChar("Enter any letter: "); 153 cmd.echo("You entered:", letter); 154 // Validation supported on single-char prompts, too. 155 promptMsg = "Do you like D (y/n)? "; 156 failureMsg = "Please enter 'y' or 'n', not '{0}'"; 157 bool acceptChar(char input) 158 { 159 return "YNyn".contains(input); 160 } 161 letter = cmd.promptChar(promptMsg, &acceptChar, failureMsg); 162 cmd.echo("Yy".contains(letter)? "Yay!" : "Dang..."); 163 +/ 164 165 cmd.pause(); 166 167 // ----- semitwist.cmd: cmd.dir ----- 168 // - Get/Set cmd's working directory 169 // - Note that setting dir does NOT affect the app's working directory, 170 // just the directory the cmd object operates on. 171 /+ showSectionHeader("semitwist.cmd: cmd.dir"); 172 173 cmd.echo("cmd is in", cmd.dir); // Starts in the app's working dir 174 cmd.dir.folder("myNewDir").create; // dir exposes Tango's VfsFolder 175 cmd.dir = "myNewDir"; // Enter newly created directory 176 cmd.dir = ".."; // Back up 177 cmd.dir = cmd.dir.folder("new2").create; // Create/enter new dir in one line 178 // (Note that cmd.dir can be assigned either string or Tango's VfsFolder) 179 180 // Create/open/write/close a new file in this new "new2" directory. 181 auto filename = "file.txt"; 182 auto fout = cmd.dir.file(filename).create.output; 183 fout.write("Content of {0}".sformat(filename)); // Easy templated content! 184 fout.close; 185 186 // Look ma! Independent simultaneous cmd interfaces! 187 auto cmd2 = new Cmd(); 188 // cmd2's dir is unaffected by cmd's dir 189 mixin(traceVal!("cmd.dir", "cmd2.dir")); // Note the usefullness of traceVal 190 191 // Changing dir doesn't change the app's working dir, 192 // but we can still change it if we want to. 193 // Remember that cmd is still in the newly-created "new2", 194 // so let's go there: 195 mixin(traceVal!("Environment.cwd")); // Original working dir 196 Environment.cwd = cmd.dir.toString; 197 mixin(traceVal!("Environment.cwd")); // New working dir 198 199 // Now a newly created Cmd is in "new2", 200 // because we changed the app's working directory. 201 // (But ordinarily, you wouldn't need to do this. It's just to illustrate.) 202 auto cmd3 = new Cmd(); 203 mixin(traceVal!("cmd3.dir")); 204 205 // Let's undo all the directory changing now: 206 cmd.dir = ".."; 207 // (note we never changed cmd2's dir) 208 cmd3.dir = ".."; 209 Environment.cwd = cmd.dir.toString; 210 // Now all the working dirs are back to their original state. 211 // We also could have done it like this: 212 // 213 // auto saveOriginalDir = cmd.dir; 214 // ...do all our messing around... 215 // cmd.dir = saveOriginalDir; 216 // cmd3.dir = saveOriginalDir; 217 // Environment.cwd = saveOriginalDir.toString; 218 219 // ----- tango.io.vfs.FileFolder ----- 220 // - More explanation and examples of this are available at: 221 // http://www.dsource.org/projects/tango/wiki/ChapterVFS 222 showSectionHeader("tango.io.vfs.FileFolder"); 223 224 cmd.echo("Current '.' dir is "~cmd.dir.toString); 225 cmd.echo("cmd.dir contains:"); 226 foreach(VfsFolder folder; cmd.dir) 227 { 228 // Remember, in D, mixins can generate multiple statements 229 mixin(traceVal!("folder", "folder.name")); 230 } 231 232 auto entireParentTree = cmd.dir.folder("..").open.tree; 233 auto entireParentDir = cmd.dir.folder("..").open.self; 234 235 cmd.echo("Total bytes in '..' dir: %s".format(entireParentDir.bytes)); 236 cmd.echo("Total bytes in entire '..' tree: %s".format(entireParentTree.bytes)); 237 238 cmd.echo("Total num folders in '..' dir: %s".format(entireParentDir.folders)); 239 foreach(VfsFolder folder; cmd.dir.folder("..").open) 240 { 241 cmd.echo(" -", folder); 242 cmd.echo(" (", (new FilePath(normalize(folder.toString))).native, ")"); 243 cmd.echo(); 244 } 245 246 cmd.echo("Total num folders in entire '..' tree: %s".format(entireParentTree.folders)); 247 foreach(VfsFolder folder; cmd.dir) 248 cmd.echo(" -"~folder.toString); 249 250 cmd.echo("Total num files in '..' dir: %s".format(entireParentDir.files)); 251 foreach(VfsFile file; entireParentDir.catalog) 252 cmd.echo(" -"~file.toString); 253 254 cmd.echo("Total num files in entire '..' tree: %s".format(entireParentTree.files)); 255 foreach(VfsFile file; entireParentTree.catalog) 256 cmd.echo(" -"~file.toString); 257 258 cmd.echo("Total num *.txt files in entire '..' tree: %s".format(entireParentTree.catalog("*.txt").files)); 259 foreach(VfsFile file; entireParentTree.catalog("*.txt")) 260 cmd.echo(" -"~file.toString); 261 262 cmd.pause(); 263 264 +/ 265 //TODO: Add deferAssert stuff 266 }