1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-23 17:05:26 +02:00

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 <eclipse@baeyens.it>
This commit is contained in:
jantje 2020-06-13 13:14:47 -04:00 committed by Jonah Graham
parent 3516a46a92
commit 65473abf07
2 changed files with 43 additions and 173 deletions

View file

@ -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",

View file

@ -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<String, String> 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<String, String> nativeEnv = getNativeEnvironmentCasePreserved();
// Make a copy of the native environment so it can be manipulated without changing
// the cached environment
Map<String, String> env = new LinkedHashMap<String, String>(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<String, String> k2n = null;
if (Platform.OS_WIN32.equals(Platform.getOS())) {
k2n = new HashMap<String, String>();
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) && !"<unset>".equals(value)) { //$NON-NLS-1$
env.put(name, value);
} else if (env.containsKey(name)) {
// If the value contains the special placeholder "<unset>", remove the variable from the environment
if ("<unset>".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 ("<unset>".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<String> keys = new ArrayList<String>(env.keySet());
List<String> keys = new ArrayList<>(environmentVariables.stringPropertyNames());
// On Windows hosts, sort the environment keys
if (Platform.OS_WIN32.equals(Platform.getOS()))
Collections.sort(keys);
Iterator<String> iter = keys.iterator();
List<String> strings = new ArrayList<String>(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<String> 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<String, String> getNativeEnvironmentCasePreserved() {
synchronized (ENV_GET_MONITOR) {
if (nativeEnvironmentCasePreserved == null) {
nativeEnvironmentCasePreserved = new LinkedHashMap<String, String>();
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<String> toUppercaseEnvironmentVars = Arrays.asList("PATH"); //$NON-NLS-1$
static {
boolean isWindows = Platform.OS_WIN32.equals(Platform.getOS());
envVars = new Properties();
Map<String, String> envMap = System.getenv();
for (Map.Entry<String, String> curEnvVar : envMap.entrySet()) {
String key = curEnvVar.getKey();
String value = curEnvVar.getValue();
if (isWindows && toUppercaseEnvironmentVars.contains(key.toUpperCase())) {
key = key.toUpperCase();
}
return new LinkedHashMap<String, String>(nativeEnvironmentCasePreserved);
envVars.setProperty(key, value);
}
}
/**
* Query the native environment and store it to the specified cache.
*
* @param cache The environment cache. Must not be <code>null</code>.
* @return a clone of the list of environment variables.
*/
private static void cacheNativeEnvironment(Map<String, String> 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();
}
}