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