1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 *
17 */
18
19package org.apache.tools.ant.taskdefs.condition;
20
21import org.apache.tools.ant.BuildException;
22
23import java.util.Locale;
24
25/**
26 * Condition that tests the OS type.
27 *
28 * @since Ant 1.4
29 */
30public class Os implements Condition {
31    private static final String OS_NAME =
32        System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
33    private static final String OS_ARCH =
34        System.getProperty("os.arch").toLowerCase(Locale.ENGLISH);
35    private static final String OS_VERSION =
36        System.getProperty("os.version").toLowerCase(Locale.ENGLISH);
37    private static final String PATH_SEP =
38        System.getProperty("path.separator");
39
40    /**
41     * OS family to look for
42     */
43    private String family;
44    /**
45     * Name of OS
46     */
47    private String name;
48    /**
49     * version of OS
50     */
51    private String version;
52    /**
53     * OS architecture
54     */
55    private String arch;
56    /**
57     * OS family that can be tested for. {@value}
58     */
59    public static final String FAMILY_WINDOWS = "windows";
60    /**
61     * OS family that can be tested for. {@value}
62     */
63    public static final String FAMILY_9X = "win9x";
64    /**
65     * OS family that can be tested for. {@value}
66     */
67    public static final String FAMILY_NT = "winnt";
68    /**
69     * OS family that can be tested for. {@value}
70     */
71    public static final String FAMILY_OS2 = "os/2";
72    /**
73     * OS family that can be tested for. {@value}
74     */
75    public static final String FAMILY_NETWARE = "netware";
76    /**
77     * OS family that can be tested for. {@value}
78     */
79    public static final String FAMILY_DOS = "dos";
80    /**
81     * OS family that can be tested for. {@value}
82     */
83    public static final String FAMILY_MAC = "mac";
84    /**
85     * OS family that can be tested for. {@value}
86     */
87    public static final String FAMILY_TANDEM = "tandem";
88    /**
89     * OS family that can be tested for. {@value}
90     */
91    public static final String FAMILY_UNIX = "unix";
92    /**
93     * OS family that can be tested for. {@value}
94     */
95    public static final String FAMILY_VMS = "openvms";
96    /**
97     * OS family that can be tested for. {@value}
98     */
99    public static final String FAMILY_ZOS = "z/os";
100    /** OS family that can be tested for. {@value} */
101    public static final String FAMILY_OS400 = "os/400";
102
103    /**
104     * OpenJDK is reported to call MacOS X "Darwin"
105     * @see https://issues.apache.org/bugzilla/show_bug.cgi?id=44889
106     * @see https://issues.apache.org/jira/browse/HADOOP-3318
107     */
108    private static final String DARWIN = "darwin";
109
110    /**
111     * Default constructor
112     *
113     */
114    public Os() {
115        //default
116    }
117
118    /**
119     * Constructor that sets the family attribute
120     * @param family a String value
121     */
122    public Os(String family) {
123        setFamily(family);
124    }
125
126    /**
127     * Sets the desired OS family type
128     *
129     * @param f      The OS family type desired<br />
130     *               Possible values:<br />
131     *               <ul>
132     *               <li>dos</li>
133     *               <li>mac</li>
134     *               <li>netware</li>
135     *               <li>os/2</li>
136     *               <li>tandem</li>
137     *               <li>unix</li>
138     *               <li>windows</li>
139     *               <li>win9x</li>
140     *               <li>z/os</li>
141     *               <li>os/400</li>
142     *               </ul>
143     */
144    public void setFamily(String f) {
145        family = f.toLowerCase(Locale.ENGLISH);
146    }
147
148    /**
149     * Sets the desired OS name
150     *
151     * @param name   The OS name
152     */
153    public void setName(String name) {
154        this.name = name.toLowerCase(Locale.ENGLISH);
155    }
156
157    /**
158     * Sets the desired OS architecture
159     *
160     * @param arch   The OS architecture
161     */
162    public void setArch(String arch) {
163        this.arch = arch.toLowerCase(Locale.ENGLISH);
164    }
165
166    /**
167     * Sets the desired OS version
168     *
169     * @param version   The OS version
170     */
171    public void setVersion(String version) {
172        this.version = version.toLowerCase(Locale.ENGLISH);
173    }
174
175    /**
176     * Determines if the OS on which Ant is executing matches the type of
177     * that set in setFamily.
178     * @return true if the os matches.
179     * @throws BuildException if there is an error.
180     * @see Os#setFamily(String)
181     */
182    @Override
183    public boolean eval() throws BuildException {
184        return isOs(family, name, arch, version);
185    }
186
187    /**
188     * Determines if the OS on which Ant is executing matches the
189     * given OS family.
190     * @param family the family to check for
191     * @return true if the OS matches
192     * @since 1.5
193     */
194    public static boolean isFamily(String family) {
195        return isOs(family, null, null, null);
196    }
197
198    /**
199     * Determines if the OS on which Ant is executing matches the
200     * given OS name.
201     *
202     * @param name the OS name to check for
203     * @return true if the OS matches
204     * @since 1.7
205     */
206    public static boolean isName(String name) {
207        return isOs(null, name, null, null);
208    }
209
210    /**
211     * Determines if the OS on which Ant is executing matches the
212     * given OS architecture.
213     *
214     * @param arch the OS architecture to check for
215     * @return true if the OS matches
216     * @since 1.7
217     */
218    public static boolean isArch(String arch) {
219        return isOs(null, null, arch, null);
220    }
221
222    /**
223     * Determines if the OS on which Ant is executing matches the
224     * given OS version.
225     *
226     * @param version the OS version to check for
227     * @return true if the OS matches
228     * @since 1.7
229     */
230    public static boolean isVersion(String version) {
231        return isOs(null, null, null, version);
232    }
233
234    /**
235     * Determines if the OS on which Ant is executing matches the
236     * given OS family, name, architecture and version
237     *
238     * @param family   The OS family
239     * @param name   The OS name
240     * @param arch   The OS architecture
241     * @param version   The OS version
242     * @return true if the OS matches
243     * @since 1.7
244     */
245    public static boolean isOs(String family, String name, String arch,
246                               String version) {
247        boolean retValue = false;
248
249        if (family != null || name != null || arch != null
250            || version != null) {
251
252            boolean isFamily = true;
253            boolean isName = true;
254            boolean isArch = true;
255            boolean isVersion = true;
256
257            if (family != null) {
258
259                //windows probing logic relies on the word 'windows' in
260                //the OS
261                boolean isWindows = OS_NAME.indexOf(FAMILY_WINDOWS) > -1;
262                boolean is9x = false;
263                boolean isNT = false;
264                if (isWindows) {
265                    //there are only four 9x platforms that we look for
266                    is9x = (OS_NAME.indexOf("95") >= 0
267                            || OS_NAME.indexOf("98") >= 0
268                            || OS_NAME.indexOf("me") >= 0
269                            //wince isn't really 9x, but crippled enough to
270                            //be a muchness. Ant doesnt run on CE, anyway.
271                            || OS_NAME.indexOf("ce") >= 0);
272                    isNT = !is9x;
273                }
274                if (family.equals(FAMILY_WINDOWS)) {
275                    isFamily = isWindows;
276                } else if (family.equals(FAMILY_9X)) {
277                    isFamily = isWindows && is9x;
278                } else if (family.equals(FAMILY_NT)) {
279                    isFamily = isWindows && isNT;
280                } else if (family.equals(FAMILY_OS2)) {
281                    isFamily = OS_NAME.indexOf(FAMILY_OS2) > -1;
282                } else if (family.equals(FAMILY_NETWARE)) {
283                    isFamily = OS_NAME.indexOf(FAMILY_NETWARE) > -1;
284                } else if (family.equals(FAMILY_DOS)) {
285                    isFamily = PATH_SEP.equals(";") && !isFamily(FAMILY_NETWARE);
286                } else if (family.equals(FAMILY_MAC)) {
287                    isFamily = OS_NAME.indexOf(FAMILY_MAC) > -1
288                        || OS_NAME.indexOf(DARWIN) > -1;
289                } else if (family.equals(FAMILY_TANDEM)) {
290                    isFamily = OS_NAME.indexOf("nonstop_kernel") > -1;
291                } else if (family.equals(FAMILY_UNIX)) {
292                    isFamily = PATH_SEP.equals(":")
293                        && !isFamily(FAMILY_VMS)
294                        && (!isFamily(FAMILY_MAC) || OS_NAME.endsWith("x")
295                            || OS_NAME.indexOf(DARWIN) > -1);
296                } else if (family.equals(FAMILY_ZOS)) {
297                    isFamily = OS_NAME.indexOf(FAMILY_ZOS) > -1
298                        || OS_NAME.indexOf("os/390") > -1;
299                } else if (family.equals(FAMILY_OS400)) {
300                    isFamily = OS_NAME.indexOf(FAMILY_OS400) > -1;
301                } else if (family.equals(FAMILY_VMS)) {
302                    isFamily = OS_NAME.indexOf(FAMILY_VMS) > -1;
303                } else {
304                    throw new BuildException(
305                        "Don\'t know how to detect os family \""
306                        + family + "\"");
307                }
308            }
309            if (name != null) {
310                isName = name.equals(OS_NAME);
311            }
312            if (arch != null) {
313                isArch = arch.equals(OS_ARCH);
314            }
315            if (version != null) {
316                isVersion = version.equals(OS_VERSION);
317            }
318            retValue = isFamily && isName && isArch && isVersion;
319        }
320        return retValue;
321    }
322}
323