
java.lang.ProcessEnvironment Maven / Gradle / Ivy
/*
* Copyright 2016 Carlos Ballesteros Velasco
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.lang;
import java.util.*;
final class ProcessEnvironment extends HashMap {
private static String validateName(String name) {
// An initial `=' indicates a magic Windows variable name -- OK
if (name.indexOf('=', 1) != -1 || name.indexOf('\u0000') != -1) {
throw new IllegalArgumentException("Invalid environment variable name: \"" + name + "\"");
}
return name;
}
private static String validateValue(String value) {
if (value.indexOf('\u0000') != -1) {
throw new IllegalArgumentException("Invalid environment variable value: \"" + value + "\"");
}
return value;
}
private static String nonNullString(Object o) {
if (o == null) throw new NullPointerException();
return (String) o;
}
public String put(String key, String value) {
return super.put(validateName(key), validateValue(value));
}
public String get(Object key) {
return super.get(nonNullString(key));
}
public boolean containsKey(Object key) {
return super.containsKey(nonNullString(key));
}
public boolean containsValue(Object value) {
return super.containsValue(nonNullString(value));
}
public String remove(Object key) {
return super.remove(nonNullString(key));
}
private static class CheckedEntry
implements Entry {
private final Entry e;
public CheckedEntry(Entry e) {
this.e = e;
}
public String getKey() {
return e.getKey();
}
public String getValue() {
return e.getValue();
}
public String setValue(String value) {
return e.setValue(validateValue(value));
}
public String toString() {
return getKey() + "=" + getValue();
}
public boolean equals(Object o) {
return e.equals(o);
}
public int hashCode() {
return e.hashCode();
}
}
private static class CheckedEntrySet
extends AbstractSet> {
private final Set> s;
public CheckedEntrySet(Set> s) {
this.s = s;
}
public int size() {
return s.size();
}
public boolean isEmpty() {
return s.isEmpty();
}
public void clear() {
s.clear();
}
public Iterator> iterator() {
return new Iterator>() {
Iterator> i = s.iterator();
public boolean hasNext() {
return i.hasNext();
}
public Entry next() {
return new CheckedEntry(i.next());
}
public void remove() {
i.remove();
}
};
}
private static Entry checkedEntry(Object o) {
@SuppressWarnings("unchecked")
Entry e = (Entry) o;
nonNullString(e.getKey());
nonNullString(e.getValue());
return e;
}
public boolean contains(Object o) {
return s.contains(checkedEntry(o));
}
public boolean remove(Object o) {
return s.remove(checkedEntry(o));
}
}
private static class CheckedValues extends AbstractCollection {
private final Collection c;
public CheckedValues(Collection c) {
this.c = c;
}
public int size() {
return c.size();
}
public boolean isEmpty() {
return c.isEmpty();
}
public void clear() {
c.clear();
}
public Iterator iterator() {
return c.iterator();
}
public boolean contains(Object o) {
return c.contains(nonNullString(o));
}
public boolean remove(Object o) {
return c.remove(nonNullString(o));
}
}
private static class CheckedKeySet extends AbstractSet {
private final Set s;
public CheckedKeySet(Set s) {
this.s = s;
}
public int size() {
return s.size();
}
public boolean isEmpty() {
return s.isEmpty();
}
public void clear() {
s.clear();
}
public Iterator iterator() {
return s.iterator();
}
public boolean contains(Object o) {
return s.contains(nonNullString(o));
}
public boolean remove(Object o) {
return s.remove(nonNullString(o));
}
}
public Set keySet() {
return new CheckedKeySet(super.keySet());
}
public Collection values() {
return new CheckedValues(super.values());
}
public Set> entrySet() {
return new CheckedEntrySet(super.entrySet());
}
private static final class NameComparator
implements Comparator {
public int compare(String s1, String s2) {
// We can't use String.compareToIgnoreCase since it
// canonicalizes to lower case, while Windows
// canonicalizes to upper case! For example, "_" should
// sort *after* "Z", not before.
int n1 = s1.length();
int n2 = s2.length();
int min = Math.min(n1, n2);
for (int i = 0; i < min; i++) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2)
// No overflow because of numeric promotion
return c1 - c2;
}
}
return n1 - n2;
}
}
private static final class EntryComparator
implements Comparator> {
public int compare(Entry e1,
Entry e2) {
return nameComparator.compare(e1.getKey(), e2.getKey());
}
}
// Allow `=' as first char in name, e.g. =C:=C:\DIR
static final int MIN_NAME_LENGTH = 1;
private static final NameComparator nameComparator;
private static final EntryComparator entryComparator;
private static final ProcessEnvironment theEnvironment;
private static final Map theUnmodifiableEnvironment;
private static final Map theCaseInsensitiveEnvironment;
static {
nameComparator = new NameComparator();
entryComparator = new EntryComparator();
theEnvironment = new ProcessEnvironment();
theUnmodifiableEnvironment
= Collections.unmodifiableMap(theEnvironment);
String envblock = environmentBlock();
int beg, end, eql;
for (beg = 0;
((end = envblock.indexOf('\u0000', beg)) != -1 &&
// An initial `=' indicates a magic Windows variable name -- OK
(eql = envblock.indexOf('=', beg + 1)) != -1);
beg = end + 1) {
// Ignore corrupted environment strings.
if (eql < end)
theEnvironment.put(envblock.substring(beg, eql),
envblock.substring(eql + 1, end));
}
theCaseInsensitiveEnvironment = new TreeMap(nameComparator);
theCaseInsensitiveEnvironment.putAll(theEnvironment);
}
private ProcessEnvironment() {
super();
}
private ProcessEnvironment(int capacity) {
super(capacity);
}
// Only for use by System.getenv(String)
static String getenv(String name) {
// The original implementation used a native call to _wgetenv,
// but it turns out that _wgetenv is only consistent with
// GetEnvironmentStringsW (for non-ASCII) if `wmain' is used
// instead of `main', even in a process created using
// CREATE_UNICODE_ENVIRONMENT. Instead we perform the
// case-insensitive comparison ourselves. At least this
// guarantees that System.getenv().get(String) will be
// consistent with System.getenv(String).
return theCaseInsensitiveEnvironment.get(name);
}
// Only for use by System.getenv()
static Map getenv() {
return theUnmodifiableEnvironment;
}
// Only for use by ProcessBuilder.environment()
@SuppressWarnings("unchecked")
static Map environment() {
return (Map) theEnvironment.clone();
}
// Only for use by ProcessBuilder.environment(String[] envp)
static Map emptyEnvironment(int capacity) {
return new ProcessEnvironment(capacity);
}
private static native String environmentBlock();
// Only for use by ProcessImpl.start()
String toEnvironmentBlock() {
// Sort Unicode-case-insensitively by name
List> list = new ArrayList>(entrySet());
Collections.sort(list, entryComparator);
StringBuilder sb = new StringBuilder(size() * 30);
int cmp = -1;
// Some versions of MSVCRT.DLL require SystemRoot to be set.
// So, we make sure that it is always set, even if not provided
// by the caller.
final String SYSTEMROOT = "SystemRoot";
for (Entry e : list) {
String key = e.getKey();
String value = e.getValue();
if (cmp < 0 && (cmp = nameComparator.compare(key, SYSTEMROOT)) > 0) {
// Not set, so add it here
addToEnvIfSet(sb, SYSTEMROOT);
}
addToEnv(sb, key, value);
}
if (cmp < 0) {
// Got to end of list and still not found
addToEnvIfSet(sb, SYSTEMROOT);
}
if (sb.length() == 0) {
// Environment was empty and SystemRoot not set in parent
sb.append('\u0000');
}
// Block is double NUL terminated
sb.append('\u0000');
return sb.toString();
}
// add the environment variable to the child, if it exists in parent
private static void addToEnvIfSet(StringBuilder sb, String name) {
String s = getenv(name);
if (s != null)
addToEnv(sb, name, s);
}
private static void addToEnv(StringBuilder sb, String name, String val) {
sb.append(name).append('=').append(val).append('\u0000');
}
static String toEnvironmentBlock(Map map) {
return map == null ? null :
((ProcessEnvironment) map).toEnvironmentBlock();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy