From 65473abf07416fcf0311b3df9213cacdd1a964b8 Mon Sep 17 00:00:00 2001 From: jantje Date: Sat, 13 Jun 2020 13:14:47 -0400 Subject: [PATCH] Bug 564123 refactor terminal Env to use same code as EnvironmentReader Env had a very old way of reading environment variables from before Java supported environemnt variables from System. This commit makes the code the same as core CDT's EnvironmentReader. Env holds a copy of the EnvironmentReader code to avoid dependency of terminal to other CDT. Change-Id: I8929bf427af99d3d62cca28ee8090aa423c9aded Signed-off-by: jantje --- .../META-INF/MANIFEST.MF | 2 +- .../tm/terminal/view/core/utils/Env.java | 214 ++++-------------- 2 files changed, 43 insertions(+), 173 deletions(-) diff --git a/terminal/plugins/org.eclipse.tm.terminal.view.core/META-INF/MANIFEST.MF b/terminal/plugins/org.eclipse.tm.terminal.view.core/META-INF/MANIFEST.MF index 2ab84b66046..53ea1127f92 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.view.core/META-INF/MANIFEST.MF +++ b/terminal/plugins/org.eclipse.tm.terminal.view.core/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.tm.terminal.view.core;singleton:=true -Bundle-Version: 4.6.0.qualifier +Bundle-Version: 4.6.100.qualifier Bundle-Activator: org.eclipse.tm.terminal.view.core.activator.CoreBundleActivator Bundle-Vendor: %providerName Require-Bundle: org.eclipse.core.expressions;bundle-version="3.4.400", diff --git a/terminal/plugins/org.eclipse.tm.terminal.view.core/src/org/eclipse/tm/terminal/view/core/utils/Env.java b/terminal/plugins/org.eclipse.tm.terminal.view.core/src/org/eclipse/tm/terminal/view/core/utils/Env.java index 0aa8449beb6..21c599b0072 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.view.core/src/org/eclipse/tm/terminal/view/core/utils/Env.java +++ b/terminal/plugins/org.eclipse.tm.terminal.view.core/src/org/eclipse/tm/terminal/view/core/utils/Env.java @@ -11,33 +11,20 @@ *******************************************************************************/ package org.eclipse.tm.terminal.view.core.utils; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Properties; -import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.Platform; -import org.eclipse.osgi.service.environment.Constants; /** * Environment handling utility methods. */ public class Env { - // Reference to the monitor to lock if determining the native environment - private final static Object ENV_GET_MONITOR = new Object(); - - // Reference to the native environment with the case of the variable names preserved - private static Map nativeEnvironmentCasePreserved = null; - /** * Returns the merged environment of the native environment and the passed * in environment. Passed in variables will overwrite the native environment @@ -54,187 +41,70 @@ public class Env { * @return The merged environment. */ public static String[] getEnvironment(String[] envp, boolean terminal) { - // Get the cached native environment - Map nativeEnv = getNativeEnvironmentCasePreserved(); - // Make a copy of the native environment so it can be manipulated without changing - // the cached environment - Map env = new LinkedHashMap(nativeEnv); - // Set the TERM environment variable if in terminal mode - if (terminal) - env.put("TERM", "xterm"); //$NON-NLS-1$ //$NON-NLS-2$ - - // On Windows, the environment variable names are not case-sensitive. However, - // we desire to preserve the original case. Build up a translation map between - // an all lowercase name and the original environment name - Map k2n = null; - if (Platform.OS_WIN32.equals(Platform.getOS())) { - k2n = new HashMap(); - for (String name : env.keySet()) { - k2n.put(name.toLowerCase(), name); - } - } + // Get a copy of the native environment variables + Properties environmentVariables = getEnvVars(); // If a "local" environment is provided, merge it with the native // environment. if (envp != null) { - for (int i = 0; i < envp.length; i++) { - // The full provided variable in form "name=value" - String envpPart = envp[i]; - // Split the variable - int eqIdx = envpPart.indexOf('='); - if (eqIdx < 1) - continue; - String name = envpPart.substring(0, eqIdx); - // Map the variable name to the real environment name (Windows only) - if (Platform.OS_WIN32.equals(Platform.getOS())) { - if (k2n.containsKey(name.toLowerCase())) { - String candidate = k2n.get(name.toLowerCase()); - Assert.isNotNull(candidate); - name = candidate; - } - // Filter out environment variables with bad names - if ("".equals(name.trim()) || name.contains(":")) { //$NON-NLS-1$ //$NON-NLS-2$ - continue; - } - } - // Get the variable value - String value = envpPart.substring(eqIdx + 1); - // Don't overwrite the TERM variable if in terminal mode - if (terminal && "TERM".equals(name)) //$NON-NLS-1$ - continue; - // If a variable with the name does not exist, just append it - if (!env.containsKey(name) && !"".equals(value)) { //$NON-NLS-1$ - env.put(name, value); - } else if (env.containsKey(name)) { - // If the value contains the special placeholder "", remove the variable from the environment - if ("".equals(value)) {//$NON-NLS-1$ - env.remove(name); + for (String keyValue : envp) { + // keyValue is the full provided variable in form "name=value" + String[] parts = keyValue.split("=", 2); //$NON-NLS-1$ + if (parts.length == 2) { + String name = parts[0]; + String value = parts[1]; + + if ("".equals(value)) { //$NON-NLS-1$ + environmentVariables.remove(name); } else { - // A variable with the name already exist, check if the value is different - String oldValue = env.get(name); - if (oldValue != null && !oldValue.equals(value) || oldValue == null && value != null) { - env.put(name, value); - } + environmentVariables.put(name, value); } } } } + // Set the TERM environment variable if in terminal mode + if (terminal) + environmentVariables.put("TERM", "xterm");//$NON-NLS-1$ //$NON-NLS-2$ // Convert into an array of strings - List keys = new ArrayList(env.keySet()); + List keys = new ArrayList<>(environmentVariables.stringPropertyNames()); // On Windows hosts, sort the environment keys if (Platform.OS_WIN32.equals(Platform.getOS())) Collections.sort(keys); - Iterator iter = keys.iterator(); - List strings = new ArrayList(env.size()); - StringBuilder buffer = null; - while (iter.hasNext()) { - String key = iter.next(); - buffer = new StringBuilder(key); - buffer.append('=').append(env.get(key)); - strings.add(buffer.toString()); + List strings = new ArrayList<>(keys.size()); + for (String key : keys) { + String value = environmentVariables.getProperty(key); + strings.add(key + "=" + value); //$NON-NLS-1$ } return strings.toArray(new String[strings.size()]); } - /** - * Determine the native environment. - * - * @return The native environment, or an empty map. - */ - private static Map getNativeEnvironmentCasePreserved() { - synchronized (ENV_GET_MONITOR) { - if (nativeEnvironmentCasePreserved == null) { - nativeEnvironmentCasePreserved = new LinkedHashMap(); - cacheNativeEnvironment(nativeEnvironmentCasePreserved); + //WARNING + //Below is a copy of org.eclipse.cdt.utils.spawner.EnvironmentReader to make the terminal independent from CDT + //This is supposed to be a straight copy (no modifications) + //except for making getEnvVars private to avoid errors/warnings + private static Properties envVars; + private static List toUppercaseEnvironmentVars = Arrays.asList("PATH"); //$NON-NLS-1$ + + static { + boolean isWindows = Platform.OS_WIN32.equals(Platform.getOS()); + envVars = new Properties(); + Map envMap = System.getenv(); + for (Map.Entry curEnvVar : envMap.entrySet()) { + String key = curEnvVar.getKey(); + String value = curEnvVar.getValue(); + if (isWindows && toUppercaseEnvironmentVars.contains(key.toUpperCase())) { + key = key.toUpperCase(); } - return new LinkedHashMap(nativeEnvironmentCasePreserved); + envVars.setProperty(key, value); } } /** - * Query the native environment and store it to the specified cache. - * - * @param cache The environment cache. Must not be null. + * @return a clone of the list of environment variables. */ - private static void cacheNativeEnvironment(Map cache) { - Assert.isNotNull(cache); - - try { - String nativeCommand = null; - if (Platform.getOS().equals(Constants.OS_WIN32)) { - nativeCommand = "cmd.exe /C set"; //$NON-NLS-1$ - } else if (!Platform.getOS().equals(Constants.OS_UNKNOWN)) { - nativeCommand = "env"; //$NON-NLS-1$ - } - if (nativeCommand == null) { - return; - } - Process process = Runtime.getRuntime().exec(nativeCommand); - - // read process directly on other platforms - // we need to parse out matching '{' and '}' for function declarations in .bash environments - // pattern is [function name]=() { and we must find the '}' on its own line with no trailing ';' - InputStream stream = process.getInputStream(); - InputStreamReader isreader = new InputStreamReader(stream); - BufferedReader reader = new BufferedReader(isreader); - try { - String line = reader.readLine(); - while (line != null) { - String key = null; - String value = null; - int func = line.indexOf("=()"); //$NON-NLS-1$ - if (func > 0) { - key = line.substring(0, func); - // scan until we find the closing '}' with no following chars - value = line.substring(func + 1); - do { - line = reader.readLine(); - if (line == null) - break; - if (line.equals("}")) //$NON-NLS-1$ - value += ';'; - value += ' ' + line; - } while (!line.equals("}")); //$NON-NLS-1$ - line = reader.readLine(); - } else { - int separator = line.indexOf('='); - if (separator > 0) { - key = line.substring(0, separator); - value = line.substring(separator + 1); - StringBuilder bufValue = new StringBuilder(value); - line = reader.readLine(); - if (line != null) { - // this line has a '=' read ahead to check next line for '=', might be broken on more - // than one line - separator = line.indexOf('='); - while (separator < 0) { - bufValue.append(line.trim()); - line = reader.readLine(); - if (line == null) { - // if next line read is the end of the file quit the loop - break; - } - separator = line.indexOf('='); - } - } - value = bufValue.toString(); - } - } - if (key != null) { - cache.put(key, value); - } else { - line = reader.readLine(); - } - } - } finally { - reader.close(); - } - } catch (IOException e) { - // Native environment-fetching code failed. - // This can easily happen and is not useful to log. - } + private static Properties getEnvVars() { + return (Properties) envVars.clone(); } - }