~ubuntu-branches/ubuntu/jaunty/ant/jaunty-proposed

« back to all changes in this revision

Viewing changes to src/main/org/apache/tools/ant/types/Commandline.java

  • Committer: Bazaar Package Importer
  • Author(s): Stefan Gybas
  • Date: 2002-02-14 14:28:48 UTC
  • Revision ID: james.westby@ubuntu.com-20020214142848-2ww7ynmqkj31vlmn
Tags: upstream-1.4.1
ImportĀ upstreamĀ versionĀ 1.4.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * The Apache Software License, Version 1.1
 
3
 *
 
4
 * Copyright (c) 2000 The Apache Software Foundation.  All rights
 
5
 * reserved.
 
6
 *
 
7
 * Redistribution and use in source and binary forms, with or without
 
8
 * modification, are permitted provided that the following conditions
 
9
 * are met:
 
10
 *
 
11
 * 1. Redistributions of source code must retain the above copyright
 
12
 *    notice, this list of conditions and the following disclaimer.
 
13
 *
 
14
 * 2. Redistributions in binary form must reproduce the above copyright
 
15
 *    notice, this list of conditions and the following disclaimer in
 
16
 *    the documentation and/or other materials provided with the
 
17
 *    distribution.
 
18
 *
 
19
 * 3. The end-user documentation included with the redistribution, if
 
20
 *    any, must include the following acknowlegement:
 
21
 *       "This product includes software developed by the
 
22
 *        Apache Software Foundation (http://www.apache.org/)."
 
23
 *    Alternately, this acknowlegement may appear in the software itself,
 
24
 *    if and wherever such third-party acknowlegements normally appear.
 
25
 *
 
26
 * 4. The names "The Jakarta Project", "Ant", and "Apache Software
 
27
 *    Foundation" must not be used to endorse or promote products derived
 
28
 *    from this software without prior written permission. For written
 
29
 *    permission, please contact apache@apache.org.
 
30
 *
 
31
 * 5. Products derived from this software may not be called "Apache"
 
32
 *    nor may "Apache" appear in their names without prior written
 
33
 *    permission of the Apache Group.
 
34
 *
 
35
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 
36
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
37
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 
38
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 
39
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
40
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
41
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 
42
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 
43
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 
44
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 
45
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 
46
 * SUCH DAMAGE.
 
47
 * ====================================================================
 
48
 *
 
49
 * This software consists of voluntary contributions made by many
 
50
 * individuals on behalf of the Apache Software Foundation.  For more
 
51
 * information on the Apache Software Foundation, please see
 
52
 * <http://www.apache.org/>.
 
53
 */
 
54
 
 
55
package org.apache.tools.ant.types;
 
56
 
 
57
import org.apache.tools.ant.BuildException;
 
58
import java.io.File;
 
59
import java.util.Vector;
 
60
import java.util.StringTokenizer;
 
61
 
 
62
 
 
63
/**
 
64
 * Commandline objects help handling command lines specifying processes to
 
65
 * execute.
 
66
 *
 
67
 * The class can be used to define a command line as nested elements or as a
 
68
 * helper to define a command line by an application.
 
69
 * <p>
 
70
 * <code>
 
71
 * &lt;someelement&gt;<br>
 
72
 * &nbsp;&nbsp;&lt;acommandline executable="/executable/to/run"&gt;<br>
 
73
 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 1" /&gt;<br>
 
74
 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument line="argument_1 argument_2 argument_3" /&gt;<br>
 
75
 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 4" /&gt;<br>
 
76
 * &nbsp;&nbsp;&lt;/acommandline&gt;<br>
 
77
 * &lt;/someelement&gt;<br>
 
78
 * </code>
 
79
 * The element <code>someelement</code> must provide a method
 
80
 * <code>createAcommandline</code> which returns an instance of this class.
 
81
 *
 
82
 * @author thomas.haas@softwired-inc.com
 
83
 * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> 
 
84
 */
 
85
public class Commandline implements Cloneable {
 
86
 
 
87
    private Vector arguments = new Vector();
 
88
    private String executable = null;
 
89
 
 
90
    public Commandline(String to_process) {
 
91
        super();
 
92
        String[] tmp = translateCommandline(to_process);
 
93
        if (tmp != null && tmp.length > 0) {
 
94
            setExecutable(tmp[0]);
 
95
            for (int i=1; i<tmp.length; i++) {
 
96
                createArgument().setValue(tmp[i]);
 
97
            }
 
98
        }
 
99
    }
 
100
 
 
101
    public Commandline() {
 
102
        super();
 
103
    }
 
104
 
 
105
    /**
 
106
     * Used for nested xml command line definitions.
 
107
     */
 
108
    public class Argument {
 
109
 
 
110
        private String[] parts;
 
111
 
 
112
        /**
 
113
         * Sets a single commandline argument.
 
114
         *
 
115
         * @param value a single commandline argument.
 
116
         */
 
117
        public void setValue(String value) {
 
118
            parts = new String[] {value};
 
119
        }
 
120
 
 
121
        /**
 
122
         * Line to split into several commandline arguments.
 
123
         *
 
124
         * @param line line to split into several commandline arguments
 
125
         */
 
126
        public void setLine(String line) {
 
127
            parts = translateCommandline(line);
 
128
        }
 
129
 
 
130
        /**
 
131
         * Sets a single commandline argument and treats it like a
 
132
         * PATH - ensures the right separator for the local platform
 
133
         * is used.
 
134
         *
 
135
         * @param value a single commandline argument.  
 
136
         */
 
137
        public void setPath(Path value) {
 
138
            parts = new String[] {value.toString()};
 
139
        }
 
140
 
 
141
        /**
 
142
         * Sets a single commandline argument to the absolute filename
 
143
         * of the given file.  
 
144
         *
 
145
         * @param value a single commandline argument.  
 
146
         */
 
147
        public void setFile(File value) {
 
148
            parts = new String[] {value.getAbsolutePath()};
 
149
        }
 
150
 
 
151
        /**
 
152
         * Returns the parts this Argument consists of.
 
153
         */
 
154
        public String[] getParts() {
 
155
            return parts;
 
156
        }
 
157
    }
 
158
 
 
159
    /**
 
160
     * Class to keep track of the position of an Argument.
 
161
     */
 
162
    // <p>This class is there to support the srcfile and targetfile
 
163
    // elements of &lt;execon&gt; and &lt;transform&gt; - don't know
 
164
    // whether there might be additional use cases.</p> --SB
 
165
    public class Marker {
 
166
 
 
167
        private int position;
 
168
        private int realPos = -1;
 
169
 
 
170
        Marker(int position) {
 
171
            this.position = position;
 
172
        }
 
173
 
 
174
        /**
 
175
         * Return the number of arguments that preceeded this marker.
 
176
         *
 
177
         * <p>The name of the executable - if set - is counted as the
 
178
         * very first argument.</p>
 
179
         */
 
180
        public int getPosition() {
 
181
            if (realPos == -1) {
 
182
                realPos = (executable == null ? 0 : 1);
 
183
                for (int i=0; i<position; i++) {
 
184
                    Argument arg = (Argument) arguments.elementAt(i);
 
185
                    realPos += arg.getParts().length;
 
186
                }
 
187
            }
 
188
            return realPos;
 
189
        }
 
190
    }
 
191
 
 
192
    /**
 
193
     * Creates an argument object.
 
194
     * Each commandline object has at most one instance of the argument class.
 
195
     * @return the argument object.
 
196
     */
 
197
    public Argument createArgument() {
 
198
        Argument argument = new Argument();
 
199
        arguments.addElement(argument);
 
200
        return argument;
 
201
    }
 
202
 
 
203
 
 
204
    /**
 
205
     * Sets the executable to run.
 
206
     */
 
207
    public void setExecutable(String executable) {
 
208
        if (executable == null || executable.length() == 0) return;
 
209
        this.executable = executable.replace('/', File.separatorChar)
 
210
            .replace('\\', File.separatorChar);
 
211
    }
 
212
 
 
213
 
 
214
    public String getExecutable() {
 
215
        return executable;
 
216
    }
 
217
 
 
218
 
 
219
    public void addArguments(String[] line) {
 
220
        for (int i=0; i < line.length; i++) {
 
221
            createArgument().setValue(line[i]);
 
222
        }
 
223
    }
 
224
 
 
225
    /**
 
226
     * Returns the executable and all defined arguments.
 
227
     */
 
228
    public String[] getCommandline() {
 
229
        final String[] args = getArguments();
 
230
        if (executable == null) return args;
 
231
        final String[] result = new String[args.length+1];
 
232
        result[0] = executable;
 
233
        System.arraycopy(args, 0, result, 1, args.length);
 
234
        return result;
 
235
    }
 
236
 
 
237
 
 
238
    /**
 
239
     * Returns all arguments defined by <code>addLine</code>,
 
240
     * <code>addValue</code> or the argument object.
 
241
     */
 
242
    public String[] getArguments() {
 
243
        Vector result = new Vector(arguments.size()*2);
 
244
        for (int i=0; i<arguments.size(); i++) {
 
245
            Argument arg = (Argument) arguments.elementAt(i);
 
246
            String[] s = arg.getParts();
 
247
            for (int j=0; j<s.length; j++) {
 
248
                result.addElement(s[j]);
 
249
            }
 
250
        }
 
251
        
 
252
        String [] res = new String[result.size()];
 
253
        result.copyInto(res);
 
254
        return res;
 
255
    }
 
256
 
 
257
 
 
258
    public String toString() {
 
259
        return toString(getCommandline());
 
260
    }
 
261
 
 
262
    /**
 
263
     * Put quotes around the given String if necessary.
 
264
     *
 
265
     * <p>If the argument doesn't include spaces or quotes, return it
 
266
     * as is. If it contains double quotes, use single quotes - else
 
267
     * surround the argument by double quotes.</p>
 
268
     *
 
269
     * @exception BuildException if the argument contains both, single
 
270
     *                           and double quotes.  
 
271
     */
 
272
    public static String quoteArgument(String argument) {
 
273
        if (argument.indexOf("\"") > -1) {
 
274
            if (argument.indexOf("\'") > -1) {
 
275
                throw new BuildException("Can\'t handle single and double quotes in same argument");
 
276
            } else {
 
277
                return '\''+argument+'\'';
 
278
            }
 
279
        } else if (argument.indexOf("\'") > -1 || argument.indexOf(" ") > -1) {
 
280
            return '\"'+argument+'\"';
 
281
        } else {
 
282
            return argument;
 
283
        }
 
284
    }
 
285
 
 
286
    public static String toString(String [] line) {
 
287
        // empty path return empty string
 
288
        if (line == null || line.length == 0) return "";
 
289
 
 
290
        // path containing one or more elements
 
291
        final StringBuffer result = new StringBuffer();
 
292
        for (int i=0; i < line.length; i++) {
 
293
            if (i > 0) {
 
294
                result.append(' ');
 
295
            }
 
296
            result.append(quoteArgument(line[i]));
 
297
        }
 
298
        return result.toString();
 
299
    }
 
300
 
 
301
    public static String[] translateCommandline(String to_process) {
 
302
        if (to_process == null || to_process.length() == 0) {
 
303
            return new String[0];
 
304
        }
 
305
 
 
306
        // parse with a simple finite state machine
 
307
        
 
308
        final int normal = 0;
 
309
        final int inQuote = 1;
 
310
        final int inDoubleQuote = 2;
 
311
        int state = normal;
 
312
        StringTokenizer tok = new StringTokenizer(to_process, "\"\' ", true);
 
313
        Vector v = new Vector();
 
314
        StringBuffer current = new StringBuffer();
 
315
 
 
316
        while (tok.hasMoreTokens()) {
 
317
            String nextTok = tok.nextToken();
 
318
            switch (state) {
 
319
            case inQuote:
 
320
                if ("\'".equals(nextTok)) {
 
321
                    state = normal;
 
322
                } else {
 
323
                    current.append(nextTok);
 
324
                }
 
325
                break;
 
326
            case inDoubleQuote:
 
327
                if ("\"".equals(nextTok)) {
 
328
                    state = normal;
 
329
                } else {
 
330
                    current.append(nextTok);
 
331
                }
 
332
                break;
 
333
            default:
 
334
                if ("\'".equals(nextTok)) {
 
335
                    state = inQuote;
 
336
                } else if ("\"".equals(nextTok)) {
 
337
                    state = inDoubleQuote;
 
338
                } else if (" ".equals(nextTok)) {
 
339
                    if (current.length() != 0) {
 
340
                        v.addElement(current.toString());
 
341
                        current.setLength(0);
 
342
                    }
 
343
                } else {
 
344
                    current.append(nextTok);
 
345
                }
 
346
                break;
 
347
            }
 
348
        }
 
349
 
 
350
        if (current.length() != 0) {
 
351
            v.addElement(current.toString());
 
352
        }
 
353
 
 
354
        if (state == inQuote || state == inDoubleQuote) {
 
355
            throw new BuildException("unbalanced quotes in " + to_process);
 
356
        }
 
357
 
 
358
        String[] args = new String[v.size()];
 
359
        v.copyInto(args);
 
360
        return args;
 
361
    }
 
362
 
 
363
    public int size() {
 
364
        return getCommandline().length;
 
365
    }
 
366
 
 
367
    public Object clone() {
 
368
        Commandline c = new Commandline();
 
369
        c.setExecutable(executable);
 
370
        c.addArguments(getArguments());
 
371
        return c;
 
372
    }
 
373
 
 
374
    /**
 
375
     * Clear out the whole command line.  */
 
376
    public void clear() {
 
377
        executable = null;
 
378
        arguments.removeAllElements();
 
379
    }
 
380
 
 
381
    /**
 
382
     * Clear out the arguments but leave the executable in place for another operation.
 
383
     */
 
384
    public void clearArgs() {
 
385
        arguments.removeAllElements();
 
386
    }
 
387
        
 
388
    /**
 
389
     * Return a marker.
 
390
     *
 
391
     * <p>This marker can be used to locate a position on the
 
392
     * commandline - to insert something for example - when all
 
393
     * parameters have been set.</p>
 
394
     */
 
395
    public Marker createMarker() {
 
396
        return new Marker(arguments.size());
 
397
    }
 
398
}