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