Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/windows/classes/java/lang/ProcessEnvironment.java
32287 views
/*1* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/* We use APIs that access a so-called Windows "Environment Block",26* which looks like an array of jchars like this:27*28* FOO=BAR\u0000 ... GORP=QUUX\u0000\u000029*30* This data structure has a number of peculiarities we must contend with:31* (see: http://windowssdk.msdn.microsoft.com/en-us/library/ms682009.aspx)32* - The NUL jchar separators, and a double NUL jchar terminator.33* It appears that the Windows implementation requires double NUL34* termination even if the environment is empty. We should always35* generate environments with double NUL termination, while accepting36* empty environments consisting of a single NUL.37* - on Windows9x, this is actually an array of 8-bit chars, not jchars,38* encoded in the system default encoding.39* - The block must be sorted by Unicode value, case-insensitively,40* as if folded to upper case.41* - There are magic environment variables maintained by Windows42* that start with a `=' (!) character. These are used for43* Windows drive current directory (e.g. "=C:=C:\WINNT") or the44* exit code of the last command (e.g. "=ExitCode=0000001").45*46* Since Java and non-9x Windows speak the same character set, and47* even the same encoding, we don't have to deal with unreliable48* conversion to byte streams. Just add a few NUL terminators.49*50* System.getenv(String) is case-insensitive, while System.getenv()51* returns a map that is case-sensitive, which is consistent with52* native Windows APIs.53*54* The non-private methods in this class are not for general use even55* within this package. Instead, they are the system-dependent parts56* of the system-independent method of the same name. Don't even57* think of using this class unless your method's name appears below.58*59* @author Martin Buchholz60* @since 1.561*/6263package java.lang;6465import java.io.*;66import java.util.*;6768final class ProcessEnvironment extends HashMap<String,String>69{7071private static final long serialVersionUID = -8017839552603542824L;7273private static String validateName(String name) {74// An initial `=' indicates a magic Windows variable name -- OK75if (name.indexOf('=', 1) != -1 ||76name.indexOf('\u0000') != -1)77throw new IllegalArgumentException78("Invalid environment variable name: \"" + name + "\"");79return name;80}8182private static String validateValue(String value) {83if (value.indexOf('\u0000') != -1)84throw new IllegalArgumentException85("Invalid environment variable value: \"" + value + "\"");86return value;87}8889private static String nonNullString(Object o) {90if (o == null)91throw new NullPointerException();92return (String) o;93}9495public String put(String key, String value) {96return super.put(validateName(key), validateValue(value));97}9899public String get(Object key) {100return super.get(nonNullString(key));101}102103public boolean containsKey(Object key) {104return super.containsKey(nonNullString(key));105}106107public boolean containsValue(Object value) {108return super.containsValue(nonNullString(value));109}110111public String remove(Object key) {112return super.remove(nonNullString(key));113}114115private static class CheckedEntry116implements Map.Entry<String,String>117{118private final Map.Entry<String,String> e;119public CheckedEntry(Map.Entry<String,String> e) {this.e = e;}120public String getKey() { return e.getKey();}121public String getValue() { return e.getValue();}122public String setValue(String value) {123return e.setValue(validateValue(value));124}125public String toString() { return getKey() + "=" + getValue();}126public boolean equals(Object o) {return e.equals(o);}127public int hashCode() {return e.hashCode();}128}129130private static class CheckedEntrySet131extends AbstractSet<Map.Entry<String,String>>132{133private final Set<Map.Entry<String,String>> s;134public CheckedEntrySet(Set<Map.Entry<String,String>> s) {this.s = s;}135public int size() {return s.size();}136public boolean isEmpty() {return s.isEmpty();}137public void clear() { s.clear();}138public Iterator<Map.Entry<String,String>> iterator() {139return new Iterator<Map.Entry<String,String>>() {140Iterator<Map.Entry<String,String>> i = s.iterator();141public boolean hasNext() { return i.hasNext();}142public Map.Entry<String,String> next() {143return new CheckedEntry(i.next());144}145public void remove() { i.remove();}146};147}148private static Map.Entry<String,String> checkedEntry(Object o) {149@SuppressWarnings("unchecked")150Map.Entry<String,String> e = (Map.Entry<String,String>) o;151nonNullString(e.getKey());152nonNullString(e.getValue());153return e;154}155public boolean contains(Object o) {return s.contains(checkedEntry(o));}156public boolean remove(Object o) {return s.remove(checkedEntry(o));}157}158159private static class CheckedValues extends AbstractCollection<String> {160private final Collection<String> c;161public CheckedValues(Collection<String> c) {this.c = c;}162public int size() {return c.size();}163public boolean isEmpty() {return c.isEmpty();}164public void clear() { c.clear();}165public Iterator<String> iterator() {return c.iterator();}166public boolean contains(Object o) {return c.contains(nonNullString(o));}167public boolean remove(Object o) {return c.remove(nonNullString(o));}168}169170private static class CheckedKeySet extends AbstractSet<String> {171private final Set<String> s;172public CheckedKeySet(Set<String> s) {this.s = s;}173public int size() {return s.size();}174public boolean isEmpty() {return s.isEmpty();}175public void clear() { s.clear();}176public Iterator<String> iterator() {return s.iterator();}177public boolean contains(Object o) {return s.contains(nonNullString(o));}178public boolean remove(Object o) {return s.remove(nonNullString(o));}179}180181public Set<String> keySet() {182return new CheckedKeySet(super.keySet());183}184185public Collection<String> values() {186return new CheckedValues(super.values());187}188189public Set<Map.Entry<String,String>> entrySet() {190return new CheckedEntrySet(super.entrySet());191}192193194private static final class NameComparator195implements Comparator<String> {196public int compare(String s1, String s2) {197// We can't use String.compareToIgnoreCase since it198// canonicalizes to lower case, while Windows199// canonicalizes to upper case! For example, "_" should200// sort *after* "Z", not before.201int n1 = s1.length();202int n2 = s2.length();203int min = Math.min(n1, n2);204for (int i = 0; i < min; i++) {205char c1 = s1.charAt(i);206char c2 = s2.charAt(i);207if (c1 != c2) {208c1 = Character.toUpperCase(c1);209c2 = Character.toUpperCase(c2);210if (c1 != c2)211// No overflow because of numeric promotion212return c1 - c2;213}214}215return n1 - n2;216}217}218219private static final class EntryComparator220implements Comparator<Map.Entry<String,String>> {221public int compare(Map.Entry<String,String> e1,222Map.Entry<String,String> e2) {223return nameComparator.compare(e1.getKey(), e2.getKey());224}225}226227// Allow `=' as first char in name, e.g. =C:=C:\DIR228static final int MIN_NAME_LENGTH = 1;229230private static final NameComparator nameComparator;231private static final EntryComparator entryComparator;232private static final ProcessEnvironment theEnvironment;233private static final Map<String,String> theUnmodifiableEnvironment;234private static final Map<String,String> theCaseInsensitiveEnvironment;235236static {237nameComparator = new NameComparator();238entryComparator = new EntryComparator();239theEnvironment = new ProcessEnvironment();240theUnmodifiableEnvironment241= Collections.unmodifiableMap(theEnvironment);242243String envblock = environmentBlock();244int beg, end, eql;245for (beg = 0;246((end = envblock.indexOf('\u0000', beg )) != -1 &&247// An initial `=' indicates a magic Windows variable name -- OK248(eql = envblock.indexOf('=' , beg+1)) != -1);249beg = end + 1) {250// Ignore corrupted environment strings.251if (eql < end)252theEnvironment.put(envblock.substring(beg, eql),253envblock.substring(eql+1,end));254}255256theCaseInsensitiveEnvironment = new TreeMap<>(nameComparator);257theCaseInsensitiveEnvironment.putAll(theEnvironment);258}259260private ProcessEnvironment() {261super();262}263264private ProcessEnvironment(int capacity) {265super(capacity);266}267268// Only for use by System.getenv(String)269static String getenv(String name) {270// The original implementation used a native call to _wgetenv,271// but it turns out that _wgetenv is only consistent with272// GetEnvironmentStringsW (for non-ASCII) if `wmain' is used273// instead of `main', even in a process created using274// CREATE_UNICODE_ENVIRONMENT. Instead we perform the275// case-insensitive comparison ourselves. At least this276// guarantees that System.getenv().get(String) will be277// consistent with System.getenv(String).278return theCaseInsensitiveEnvironment.get(name);279}280281// Only for use by System.getenv()282static Map<String,String> getenv() {283return theUnmodifiableEnvironment;284}285286// Only for use by ProcessBuilder.environment()287@SuppressWarnings("unchecked")288static Map<String,String> environment() {289return (Map<String,String>) theEnvironment.clone();290}291292// Only for use by ProcessBuilder.environment(String[] envp)293static Map<String,String> emptyEnvironment(int capacity) {294return new ProcessEnvironment(capacity);295}296297private static native String environmentBlock();298299// Only for use by ProcessImpl.start()300String toEnvironmentBlock() {301// Sort Unicode-case-insensitively by name302List<Map.Entry<String,String>> list = new ArrayList<>(entrySet());303Collections.sort(list, entryComparator);304305StringBuilder sb = new StringBuilder(size()*30);306int cmp = -1;307308// Some versions of MSVCRT.DLL require SystemRoot to be set.309// So, we make sure that it is always set, even if not provided310// by the caller.311final String SYSTEMROOT = "SystemRoot";312313for (Map.Entry<String,String> e : list) {314String key = e.getKey();315String value = e.getValue();316if (cmp < 0 && (cmp = nameComparator.compare(key, SYSTEMROOT)) > 0) {317// Not set, so add it here318addToEnvIfSet(sb, SYSTEMROOT);319}320addToEnv(sb, key, value);321}322if (cmp < 0) {323// Got to end of list and still not found324addToEnvIfSet(sb, SYSTEMROOT);325}326if (sb.length() == 0) {327// Environment was empty and SystemRoot not set in parent328sb.append('\u0000');329}330// Block is double NUL terminated331sb.append('\u0000');332return sb.toString();333}334335// add the environment variable to the child, if it exists in parent336private static void addToEnvIfSet(StringBuilder sb, String name) {337String s = getenv(name);338if (s != null)339addToEnv(sb, name, s);340}341342private static void addToEnv(StringBuilder sb, String name, String val) {343sb.append(name).append('=').append(val).append('\u0000');344}345346static String toEnvironmentBlock(Map<String,String> map) {347return map == null ? null :348((ProcessEnvironment)map).toEnvironmentBlock();349}350}351352353