Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/windows/classes/java/lang/ProcessEnvironment.java
32287 views
1
/*
2
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
/* We use APIs that access a so-called Windows "Environment Block",
27
* which looks like an array of jchars like this:
28
*
29
* FOO=BAR\u0000 ... GORP=QUUX\u0000\u0000
30
*
31
* This data structure has a number of peculiarities we must contend with:
32
* (see: http://windowssdk.msdn.microsoft.com/en-us/library/ms682009.aspx)
33
* - The NUL jchar separators, and a double NUL jchar terminator.
34
* It appears that the Windows implementation requires double NUL
35
* termination even if the environment is empty. We should always
36
* generate environments with double NUL termination, while accepting
37
* empty environments consisting of a single NUL.
38
* - on Windows9x, this is actually an array of 8-bit chars, not jchars,
39
* encoded in the system default encoding.
40
* - The block must be sorted by Unicode value, case-insensitively,
41
* as if folded to upper case.
42
* - There are magic environment variables maintained by Windows
43
* that start with a `=' (!) character. These are used for
44
* Windows drive current directory (e.g. "=C:=C:\WINNT") or the
45
* exit code of the last command (e.g. "=ExitCode=0000001").
46
*
47
* Since Java and non-9x Windows speak the same character set, and
48
* even the same encoding, we don't have to deal with unreliable
49
* conversion to byte streams. Just add a few NUL terminators.
50
*
51
* System.getenv(String) is case-insensitive, while System.getenv()
52
* returns a map that is case-sensitive, which is consistent with
53
* native Windows APIs.
54
*
55
* The non-private methods in this class are not for general use even
56
* within this package. Instead, they are the system-dependent parts
57
* of the system-independent method of the same name. Don't even
58
* think of using this class unless your method's name appears below.
59
*
60
* @author Martin Buchholz
61
* @since 1.5
62
*/
63
64
package java.lang;
65
66
import java.io.*;
67
import java.util.*;
68
69
final class ProcessEnvironment extends HashMap<String,String>
70
{
71
72
private static final long serialVersionUID = -8017839552603542824L;
73
74
private static String validateName(String name) {
75
// An initial `=' indicates a magic Windows variable name -- OK
76
if (name.indexOf('=', 1) != -1 ||
77
name.indexOf('\u0000') != -1)
78
throw new IllegalArgumentException
79
("Invalid environment variable name: \"" + name + "\"");
80
return name;
81
}
82
83
private static String validateValue(String value) {
84
if (value.indexOf('\u0000') != -1)
85
throw new IllegalArgumentException
86
("Invalid environment variable value: \"" + value + "\"");
87
return value;
88
}
89
90
private static String nonNullString(Object o) {
91
if (o == null)
92
throw new NullPointerException();
93
return (String) o;
94
}
95
96
public String put(String key, String value) {
97
return super.put(validateName(key), validateValue(value));
98
}
99
100
public String get(Object key) {
101
return super.get(nonNullString(key));
102
}
103
104
public boolean containsKey(Object key) {
105
return super.containsKey(nonNullString(key));
106
}
107
108
public boolean containsValue(Object value) {
109
return super.containsValue(nonNullString(value));
110
}
111
112
public String remove(Object key) {
113
return super.remove(nonNullString(key));
114
}
115
116
private static class CheckedEntry
117
implements Map.Entry<String,String>
118
{
119
private final Map.Entry<String,String> e;
120
public CheckedEntry(Map.Entry<String,String> e) {this.e = e;}
121
public String getKey() { return e.getKey();}
122
public String getValue() { return e.getValue();}
123
public String setValue(String value) {
124
return e.setValue(validateValue(value));
125
}
126
public String toString() { return getKey() + "=" + getValue();}
127
public boolean equals(Object o) {return e.equals(o);}
128
public int hashCode() {return e.hashCode();}
129
}
130
131
private static class CheckedEntrySet
132
extends AbstractSet<Map.Entry<String,String>>
133
{
134
private final Set<Map.Entry<String,String>> s;
135
public CheckedEntrySet(Set<Map.Entry<String,String>> s) {this.s = s;}
136
public int size() {return s.size();}
137
public boolean isEmpty() {return s.isEmpty();}
138
public void clear() { s.clear();}
139
public Iterator<Map.Entry<String,String>> iterator() {
140
return new Iterator<Map.Entry<String,String>>() {
141
Iterator<Map.Entry<String,String>> i = s.iterator();
142
public boolean hasNext() { return i.hasNext();}
143
public Map.Entry<String,String> next() {
144
return new CheckedEntry(i.next());
145
}
146
public void remove() { i.remove();}
147
};
148
}
149
private static Map.Entry<String,String> checkedEntry(Object o) {
150
@SuppressWarnings("unchecked")
151
Map.Entry<String,String> e = (Map.Entry<String,String>) o;
152
nonNullString(e.getKey());
153
nonNullString(e.getValue());
154
return e;
155
}
156
public boolean contains(Object o) {return s.contains(checkedEntry(o));}
157
public boolean remove(Object o) {return s.remove(checkedEntry(o));}
158
}
159
160
private static class CheckedValues extends AbstractCollection<String> {
161
private final Collection<String> c;
162
public CheckedValues(Collection<String> c) {this.c = c;}
163
public int size() {return c.size();}
164
public boolean isEmpty() {return c.isEmpty();}
165
public void clear() { c.clear();}
166
public Iterator<String> iterator() {return c.iterator();}
167
public boolean contains(Object o) {return c.contains(nonNullString(o));}
168
public boolean remove(Object o) {return c.remove(nonNullString(o));}
169
}
170
171
private static class CheckedKeySet extends AbstractSet<String> {
172
private final Set<String> s;
173
public CheckedKeySet(Set<String> s) {this.s = s;}
174
public int size() {return s.size();}
175
public boolean isEmpty() {return s.isEmpty();}
176
public void clear() { s.clear();}
177
public Iterator<String> iterator() {return s.iterator();}
178
public boolean contains(Object o) {return s.contains(nonNullString(o));}
179
public boolean remove(Object o) {return s.remove(nonNullString(o));}
180
}
181
182
public Set<String> keySet() {
183
return new CheckedKeySet(super.keySet());
184
}
185
186
public Collection<String> values() {
187
return new CheckedValues(super.values());
188
}
189
190
public Set<Map.Entry<String,String>> entrySet() {
191
return new CheckedEntrySet(super.entrySet());
192
}
193
194
195
private static final class NameComparator
196
implements Comparator<String> {
197
public int compare(String s1, String s2) {
198
// We can't use String.compareToIgnoreCase since it
199
// canonicalizes to lower case, while Windows
200
// canonicalizes to upper case! For example, "_" should
201
// sort *after* "Z", not before.
202
int n1 = s1.length();
203
int n2 = s2.length();
204
int min = Math.min(n1, n2);
205
for (int i = 0; i < min; i++) {
206
char c1 = s1.charAt(i);
207
char c2 = s2.charAt(i);
208
if (c1 != c2) {
209
c1 = Character.toUpperCase(c1);
210
c2 = Character.toUpperCase(c2);
211
if (c1 != c2)
212
// No overflow because of numeric promotion
213
return c1 - c2;
214
}
215
}
216
return n1 - n2;
217
}
218
}
219
220
private static final class EntryComparator
221
implements Comparator<Map.Entry<String,String>> {
222
public int compare(Map.Entry<String,String> e1,
223
Map.Entry<String,String> e2) {
224
return nameComparator.compare(e1.getKey(), e2.getKey());
225
}
226
}
227
228
// Allow `=' as first char in name, e.g. =C:=C:\DIR
229
static final int MIN_NAME_LENGTH = 1;
230
231
private static final NameComparator nameComparator;
232
private static final EntryComparator entryComparator;
233
private static final ProcessEnvironment theEnvironment;
234
private static final Map<String,String> theUnmodifiableEnvironment;
235
private static final Map<String,String> theCaseInsensitiveEnvironment;
236
237
static {
238
nameComparator = new NameComparator();
239
entryComparator = new EntryComparator();
240
theEnvironment = new ProcessEnvironment();
241
theUnmodifiableEnvironment
242
= Collections.unmodifiableMap(theEnvironment);
243
244
String envblock = environmentBlock();
245
int beg, end, eql;
246
for (beg = 0;
247
((end = envblock.indexOf('\u0000', beg )) != -1 &&
248
// An initial `=' indicates a magic Windows variable name -- OK
249
(eql = envblock.indexOf('=' , beg+1)) != -1);
250
beg = end + 1) {
251
// Ignore corrupted environment strings.
252
if (eql < end)
253
theEnvironment.put(envblock.substring(beg, eql),
254
envblock.substring(eql+1,end));
255
}
256
257
theCaseInsensitiveEnvironment = new TreeMap<>(nameComparator);
258
theCaseInsensitiveEnvironment.putAll(theEnvironment);
259
}
260
261
private ProcessEnvironment() {
262
super();
263
}
264
265
private ProcessEnvironment(int capacity) {
266
super(capacity);
267
}
268
269
// Only for use by System.getenv(String)
270
static String getenv(String name) {
271
// The original implementation used a native call to _wgetenv,
272
// but it turns out that _wgetenv is only consistent with
273
// GetEnvironmentStringsW (for non-ASCII) if `wmain' is used
274
// instead of `main', even in a process created using
275
// CREATE_UNICODE_ENVIRONMENT. Instead we perform the
276
// case-insensitive comparison ourselves. At least this
277
// guarantees that System.getenv().get(String) will be
278
// consistent with System.getenv(String).
279
return theCaseInsensitiveEnvironment.get(name);
280
}
281
282
// Only for use by System.getenv()
283
static Map<String,String> getenv() {
284
return theUnmodifiableEnvironment;
285
}
286
287
// Only for use by ProcessBuilder.environment()
288
@SuppressWarnings("unchecked")
289
static Map<String,String> environment() {
290
return (Map<String,String>) theEnvironment.clone();
291
}
292
293
// Only for use by ProcessBuilder.environment(String[] envp)
294
static Map<String,String> emptyEnvironment(int capacity) {
295
return new ProcessEnvironment(capacity);
296
}
297
298
private static native String environmentBlock();
299
300
// Only for use by ProcessImpl.start()
301
String toEnvironmentBlock() {
302
// Sort Unicode-case-insensitively by name
303
List<Map.Entry<String,String>> list = new ArrayList<>(entrySet());
304
Collections.sort(list, entryComparator);
305
306
StringBuilder sb = new StringBuilder(size()*30);
307
int cmp = -1;
308
309
// Some versions of MSVCRT.DLL require SystemRoot to be set.
310
// So, we make sure that it is always set, even if not provided
311
// by the caller.
312
final String SYSTEMROOT = "SystemRoot";
313
314
for (Map.Entry<String,String> e : list) {
315
String key = e.getKey();
316
String value = e.getValue();
317
if (cmp < 0 && (cmp = nameComparator.compare(key, SYSTEMROOT)) > 0) {
318
// Not set, so add it here
319
addToEnvIfSet(sb, SYSTEMROOT);
320
}
321
addToEnv(sb, key, value);
322
}
323
if (cmp < 0) {
324
// Got to end of list and still not found
325
addToEnvIfSet(sb, SYSTEMROOT);
326
}
327
if (sb.length() == 0) {
328
// Environment was empty and SystemRoot not set in parent
329
sb.append('\u0000');
330
}
331
// Block is double NUL terminated
332
sb.append('\u0000');
333
return sb.toString();
334
}
335
336
// add the environment variable to the child, if it exists in parent
337
private static void addToEnvIfSet(StringBuilder sb, String name) {
338
String s = getenv(name);
339
if (s != null)
340
addToEnv(sb, name, s);
341
}
342
343
private static void addToEnv(StringBuilder sb, String name, String val) {
344
sb.append(name).append('=').append(val).append('\u0000');
345
}
346
347
static String toEnvironmentBlock(Map<String,String> map) {
348
return map == null ? null :
349
((ProcessEnvironment)map).toEnvironmentBlock();
350
}
351
}
352
353