1 /**
2  * Simple execution of shell commands,
3  * and wrappers for common utilities.
4  *
5  * License:
6  *   This Source Code Form is subject to the terms of
7  *   the Mozilla Public License, v. 2.0. If a copy of
8  *   the MPL was not distributed with this file, You
9  *   can obtain one at http://mozilla.org/MPL/2.0/.
10  *
11  * Authors:
12  *   Vladimir Panteleev <vladimir@thecybershadow.net>
13  */
14 
15 module ae.sys.cmd;
16 
17 import core.thread;
18 
19 import std.exception;
20 import std.process;
21 import std.stdio;
22 import std.string;
23 
24 import ae.sys.file;
25 
26 string getTempFileName(string extension)
27 {
28 	import std.random;
29 	import std.file;
30 	import std.path : buildPath;
31 
32 	static int counter;
33 	return buildPath(tempDir(), format("run-%d-%d-%d.%s",
34 		getpid(),
35 		uniform!uint(),
36 		counter++,
37 		extension
38 	));
39 }
40 
41 // ************************************************************************
42 
43 private void invoke(alias runner)(string[] args)
44 {
45 	//debug scope(failure) std.stdio.writeln("[CWD] ", getcwd());
46 	auto status = runner();
47 	enforce(status == 0,
48 		"Command %s failed with status %d".format(args, status));
49 }
50 
51 /// std.process helper.
52 /// Run a command, and throw if it exited with a non-zero status.
53 void run(string[] args)
54 {
55 	invoke!({ return spawnProcess(args).wait(); })(args);
56 }
57 
58 /// std.process helper.
59 /// Run a command and collect its output.
60 /// Throw if it exited with a non-zero status.
61 string query(string[] args)
62 {
63 	string output;
64 	invoke!({
65 		auto result = execute(args);
66 		output = result.output.stripRight();
67 		return result.status;
68 	})(args);
69 	return output;
70 }
71 
72 /// std.process helper.
73 /// Run a command, feed it the given input, and collect its output.
74 /// Throw if it exited with non-zero status. Return output.
75 T[] pipe(T)(string[] args, in T[] input)
76 {
77 	T[] output;
78 	invoke!({
79 		auto pipes = pipeProcess(args);
80 		auto f = pipes.stdin;
81 		auto writer = writeFileAsync(f, input);
82 		scope(exit) writer.join();
83 		output = cast(T[])readFile(pipes.stdout);
84 		return pipes.pid.wait();
85 	})(args);
86 	return output;
87 }
88 
89 // ************************************************************************
90 
91 ubyte[] iconv(in void[] data, string inputEncoding, string outputEncoding)
92 {
93 	auto args = ["iconv", "-f", inputEncoding, "-t", outputEncoding];
94 	auto result = pipe(args, data);
95 	return cast(ubyte[])result;
96 }
97 
98 string iconv(in void[] data, string inputEncoding)
99 {
100 	import std.utf;
101 	auto result = cast(string)iconv(data, inputEncoding, "UTF-8");
102 	validate(result);
103 	return result;
104 }
105 
106 version (HAVE_UNIX)
107 unittest
108 {
109 	assert(iconv("Hello"w, "UTF-16LE") == "Hello");
110 }
111 
112 string sha1sum(in void[] data)
113 {
114 	auto output = cast(string)pipe(["sha1sum", "-b", "-"], data);
115 	return output[0..40];
116 }
117 
118 version (HAVE_UNIX)
119 unittest
120 {
121 	assert(sha1sum("") == "da39a3ee5e6b4b0d3255bfef95601890afd80709");
122 	assert(sha1sum("a  b\nc\r\nd") == "667c71ffe2ac8a4fe500e3b96435417e4c5ec13b");
123 }
124 
125 // ************************************************************************
126 
127 version (Windows)
128 	enum NULL_FILE = "nul";
129 else
130 	enum NULL_FILE = "/dev/null";
131 
132 // ************************************************************************
133 
134 /// Reverse of std.process.environment.toAA
135 void setEnvironment(string[string] env)
136 {
137 	foreach (k, v; env)
138 		if (k.length)
139 			environment[k] = v;
140 	foreach (k, v; environment.toAA())
141 		if (k.length && k !in env)
142 			environment.remove(k);
143 }