1 /**
2  * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: Jan 9, 2011
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module dvm.dvm.ShellScript;
8 
9 import std.conv : text;
10 import std.exception : assumeUnique;
11 import std.format : format;
12 
13 import tango.io.device.File;
14 
15 class ShellScript
16 {
17     string path;
18     private string content_;
19 
20     string content ()
21     {
22         return content_;
23     }
24 
25     private string content (string content)
26     {
27         return content_ = content;
28     }
29 
30     this (string path = "")
31     {
32         this.path = path;
33     }
34 
35     ShellScript allArgs (bool quote = true)
36     {
37         append(Sh.allArgs);
38         return this;
39     }
40 
41     ShellScript comment (string c)
42     {
43         append(Sh.comment(c));
44         return this;
45     }
46 
47     ShellScript declareVariable (string name, string value = "", bool local = false)
48     {
49         append(Sh.declareVariable(name, value, local)).nl;
50         return this;
51     }
52 
53     ShellScript echoOff ()
54     {
55         version (Windows)
56             append(Sh.echoOff).nl;
57 
58         return this;
59     }
60 
61     ShellScript exec (string name, string args = "")
62     {
63         append(Sh.exec(name, args));
64         return this;
65     }
66 
67     ShellScript export_ (string name, string content, bool quote = true)
68     {
69         append(Sh.export_(name, content, quote));
70         return this;
71     }
72 
73     ShellScript exportPath (string name, string[] args ...)
74     {
75         string content ;
76 
77         foreach (i, arg ; args)
78         {
79             if (i != args.length - 1)
80                 content ~= arg ~ Sh.separator;
81 
82             else
83                 content ~=  arg;
84         }
85 
86         return export_(name, content);
87     }
88 
89     ShellScript ifFileIsNotEmpty (string path, void delegate () ifBlock, void delegate () elseBlock = null)
90     {
91         version (Posix)
92             ifStatement("-s " ~ path, ifBlock, elseBlock);
93 
94         else
95             ifStatement("exist " ~ path, ifBlock, elseBlock);
96 
97         return this;
98     }
99 
100     ShellScript ifStatement (string condition, void delegate () ifBlock, void delegate () elseBlock = null)
101     {
102         version (Posix)
103         {
104             append(format("if [ %s ] ; then", condition)).nl.indent;
105             ifBlock();
106             nl;
107 
108             if (elseBlock)
109             {
110                 append("else").nl.indent;
111                 elseBlock();
112                 nl;
113             }
114 
115             append("fi");
116         }
117 
118         else
119         {
120             append(format("if %s (", condition)).nl.indent;
121             ifBlock();
122             nl;
123             append(")");
124 
125             if (elseBlock)
126             {
127                 append(" else (").nl.indent;
128                 elseBlock();
129                 nl;
130                 append(")");
131             }
132         }
133 
134         return this;
135     }
136 
137     ShellScript printError (string message, bool singleQuote = false)
138     {
139         version (Posix)
140         {
141             auto quote = singleQuote ? "'" : `"`;
142             append(format(`echo %sError: %s%s >&2`, quote, message, quote));
143         }
144 
145         else
146             append(format(`echo Error: %s >&2`, message));
147 
148         return this;
149     }
150 
151     ShellScript shebang ()
152     {
153         version (Posix)
154         {
155             if (Sh.shebang != "")
156                 append(Sh.shebang).nl;
157         }
158 
159         return this;
160     }
161 
162     ShellScript source (string path)
163     {
164         append(Sh.source(path));
165         return this;
166     }
167 
168     ShellScript variable (string name, bool quote = true)
169     {
170         append(Sh.variable(name, quote));
171         return this;
172     }
173 
174     ShellScript write ()
175     {
176         File.set(path, content);
177         return this;
178     }
179 
180     ShellScript append(T)(T value)
181     {
182         content_ ~= value.text;
183         return this;
184     }
185 
186 
187     ShellScript nl ()
188     {
189         version (Posix)
190             append('\n');
191 
192         else
193             append("\r\n");
194 
195         return this;
196     }
197 
198     ShellScript indent ()
199     {
200         append('\t');
201         return this;
202     }
203 }
204 
205 struct Sh
206 {
207     static:
208 
209     string quote (string str)
210     {
211         return format(`"%s"`, str);
212     }
213 
214     version (Posix)
215     {
216         enum shebang = "#!/bin/sh";
217         enum separator = ":";
218 
219         string allArgs (bool quote = true)
220         {
221             return quote ? `"$@"` : "$@";
222         }
223 
224         string comment (string c)
225         {
226             return "# " ~ c;
227         }
228 
229         string declareVariable (string name, string value = "", bool local = false)
230         {
231             string loc = local ? "local " : "";
232 
233             if (value == "")
234                 return loc ~ name;
235 
236             return format("%s%s=%s", loc, name, value);
237         }
238 
239         string exec (string name, string args = "")
240         {
241             args = args == "" ? "" : ' ' ~ args;
242 
243             return format("exec %s%s", name, args);
244         }
245 
246         string export_ (string name, string value, bool quote = true)
247         {
248             return format("%s=\"%s\"\nexport %s", name, value, name);
249         }
250 
251         string source (string path, bool quote = true)
252         {
253             return format(". %s", path);
254         }
255 
256         string exec (string command)
257         {
258             return format("exec %s", command);
259         }
260 
261         string variable (string name, bool quote = true)
262         {
263             return quote ? format(`"$%s"`, name) : '$' ~ name;
264         }
265     }
266 
267     else version (Windows)
268     {
269         // DMD 1.068 and up optimizes this out causing a linker error
270         //enum shebang = "";
271         enum echoOff = "@echo off";
272         enum separator = ";";
273 
274         string allArgs (bool quote = true)
275         {
276             return "%*";
277         }
278 
279         string comment (string c)
280         {
281             return "rem " ~ c;
282         }
283 
284         string declareVariable (string name, string value = "", bool local = false)
285         {
286             return format("set %s=%s", name, value);
287         }
288 
289         string export_ (string name, string content, bool quote = true)
290         {
291             return format("set %s=%s", name, content);
292         }
293 
294         string source (string path)
295         {
296             return format("call %s", path);
297         }
298 
299         string exec (string name, string args = "")
300         {
301             return format("%s %s", name, args);
302         }
303 
304         string variable (string name, bool quote = true)
305         {
306             return quote ? format(`"%%s%"`, name) : '%' ~ name ~ '%';
307         }
308     }
309 }
310 
311 string slashSafeSubstitute (string haystack, string needle, string replacement)
312 {
313     import tango.text.Util;
314 
315     version (Windows)
316     {
317         needle = needle.substitute("/", "\\").assumeUnique;
318         replacement = replacement.substitute("/", "\\").assumeUnique;
319     }
320 
321     return haystack.substitute(needle, replacement).assumeUnique;
322 }