001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 *  contributor license agreements.  See the NOTICE file distributed with
004 *  this work for additional information regarding copyright ownership.
005 *  The ASF licenses this file to You under the Apache License, Version 2.0
006 *  (the "License"); you may not use this file except in compliance with
007 *  the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017
018package org.apache.commons.exec.launcher;
019
020import java.io.File;
021import java.io.IOException;
022import java.io.PrintWriter;
023import java.nio.charset.Charset;
024import java.nio.file.Files;
025import java.nio.file.Path;
026import java.util.Map;
027import java.util.Map.Entry;
028import java.util.Set;
029
030import org.apache.commons.exec.CommandLine;
031import org.apache.commons.exec.util.StringUtils;
032
033/**
034 * A command launcher for VMS that writes the command to a temporary DCL script before launching commands. This is due to limitations of both the DCL
035 * interpreter and the Java VM implementation.
036 */
037public class VmsCommandLauncher extends Java13CommandLauncher {
038
039    /**
040     * Writes the command into a temporary DCL script and returns the corresponding File object. The script will be deleted on exit.
041     */
042    private File createCommandFile(final CommandLine cmd, final Map<String, String> env) throws IOException {
043        final Path path = Files.createTempFile("EXEC", ".TMP");
044        final File script = path.toFile();
045        script.deleteOnExit();
046        try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(path, Charset.defaultCharset()))) {
047            // add the environment as global symbols for the DCL script
048            if (env != null) {
049                final Set<Entry<String, String>> entries = env.entrySet();
050                for (final Entry<String, String> entry : entries) {
051                    writer.print("$ ");
052                    writer.print(entry.getKey());
053                    writer.print(" == "); // define as global symbol
054                    writer.println('\"');
055                    String value = entry.getValue();
056                    // Any embedded " values need to be doubled
057                    if (value.indexOf('\"') > 0) {
058                        final StringBuilder sb = new StringBuilder();
059                        for (int i = 0; i < value.length(); i++) {
060                            final char c = value.charAt(i);
061                            if (c == '\"') {
062                                sb.append('\"');
063                            }
064                            sb.append(c);
065                        }
066                        value = sb.toString();
067                    }
068                    writer.print(value);
069                    writer.println('\"');
070                }
071            }
072
073            final String command = cmd.getExecutable();
074            if (cmd.isFile()) {// We assume it is it a script file
075                writer.print("$ @");
076                // This is a bit crude, but seems to work
077                final String[] parts = StringUtils.split(command, "/");
078                writer.print(parts[0]); // device
079                writer.print(":[");
080                writer.print(parts[1]); // top level directory
081                final int lastPart = parts.length - 1;
082                for (int i = 2; i < lastPart; i++) {
083                    writer.print(".");
084                    writer.print(parts[i]);
085                }
086                writer.print("]");
087                writer.print(parts[lastPart]);
088            } else {
089                writer.print("$ ");
090                writer.print(command);
091            }
092            final String[] args = cmd.getArguments();
093            for (final String arg : args) {
094                writer.println(" -");
095                writer.print(arg);
096            }
097            writer.println();
098        }
099        return script;
100    }
101
102    /**
103     * Launches the given command in a new process.
104     */
105    @Override
106    public Process exec(final CommandLine cmd, final Map<String, String> env) throws IOException {
107        return super.exec(new CommandLine(createCommandFile(cmd, env).getPath()), env);
108    }
109
110    /**
111     * Launches the given command in a new process, in the given working directory. Note that under Java 1.3.1, 1.4.0 and 1.4.1 on VMS this method only works if
112     * {@code workingDir} is null or the logical JAVA$FORK_SUPPORT_CHDIR needs to be set to TRUE.
113     */
114    @Override
115    public Process exec(final CommandLine cmd, final Map<String, String> env, final File workingDir) throws IOException {
116        return super.exec(new CommandLine(createCommandFile(cmd, env).getPath()), env, workingDir);
117    }
118
119    /** @see org.apache.commons.exec.launcher.CommandLauncher#isFailure(int) */
120    @Override
121    public boolean isFailure(final int exitValue) {
122        // even exit value signals failure
123        return exitValue % 2 == 0;
124    }
125}