    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 *      https://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     * Constructs a new instance.
    041     */
    042    public VmsCommandLauncher() {
    043        // empty
    044    }
    045
    046    /**
    047     * Writes the command into a temporary DCL script and returns the corresponding File object. The script will be deleted on exit.
    048     */
    049    File createCommandFile(final CommandLine cmd, final Map<String, String> env) throws IOException {
    050        final Path path = Files.createTempFile("EXEC", ".TMP");
    051        final File script = path.toFile();
    052        script.deleteOnExit();
    053        try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(path, Charset.defaultCharset()))) {
    054            // add the environment as global symbols for the DCL script
    055            if (env != null) {
    056                final Set<Entry<String, String>> entries = env.entrySet();
    057                for (final Entry<String, String> entry : entries) {
    058                    writer.print("$ ");
    059                    writer.print(entry.getKey());
    060                    writer.print(" == "); // define as global symbol
    061                    writer.println('\"');
    062                    String value = entry.getValue();
    063                    // Any embedded " values need to be doubled
    064                    if (value.indexOf('\"') > 0) {
    065                        final StringBuilder sb = new StringBuilder();
    066                        for (int i = 0; i < value.length(); i++) {
    067                            final char c = value.charAt(i);
    068                            if (c == '\"') {
    069                                sb.append('\"');
    070                            }
    071                            sb.append(c);
    072                        }
    073                        value = sb.toString();
    074                    }
    075                    writer.print(value);
    076                    writer.println('\"');
    077                }
    078            }
    079
    080            final String command = cmd.getExecutable();
    081            if (cmd.isFile()) {// We assume it is it a script file
    082                writer.print("$ @");
    083                // This is a bit crude, but seems to work
    084                final String[] parts = StringUtils.split(command, "/");
    085                writer.print(parts[0]); // device
    086                writer.print(":[");
    087                writer.print(parts[1]); // top level directory
    088                final int lastPart = parts.length - 1;
    089                for (int i = 2; i < lastPart; i++) {
    090                    writer.print(".");
    091                    writer.print(parts[i]);
    092                }
    093                writer.print("]");
    094                writer.print(parts[lastPart]);
    095            } else {
    096                writer.print("$ ");
    097                writer.print(command);
    098            }
    099            final String[] args = cmd.getArguments();
    100            for (final String arg : args) {
    101                writer.println(" -");
    102                writer.print(arg);
    103            }
    104            writer.println();
    105        }
    106        return script;
    107    }
    108
    109    /**
    110     * Launches the given command in a new process.
    111     */
    112    @Override
    113    public Process exec(final CommandLine cmd, final Map<String, String> env) throws IOException {
    114        return super.exec(new CommandLine(createCommandFile(cmd, env).getPath()), env);
    115    }
    116
    117    /**
    118     * 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
    119     * {@code workingDir} is null or the logical JAVA$FORK_SUPPORT_CHDIR needs to be set to TRUE.
    120     */
    121    @Override
    122    public Process exec(final CommandLine cmd, final Map<String, String> env, final File workingDir) throws IOException {
    123        return super.exec(new CommandLine(createCommandFile(cmd, env).getPath()), env, workingDir);
    124    }
    125
    126    /** @see org.apache.commons.exec.launcher.CommandLauncher#isFailure(int) */
    127    @Override
    128    public boolean isFailure(final int exitValue) {
    129        // even exit value signals failure
    130        return exitValue % 2 == 0;
    131    }
    132}