0N/A/*
3909N/A * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A/* We use APIs that access the standard Unix environ array, which
0N/A * is defined by UNIX98 to look like:
0N/A *
0N/A * char **environ;
0N/A *
0N/A * These are unsorted, case-sensitive, null-terminated arrays of bytes
0N/A * of the form FOO=BAR\000 which are usually encoded in the user's
0N/A * default encoding (file.encoding is an excellent choice for
0N/A * encoding/decoding these). However, even though the user cannot
0N/A * directly access the underlying byte representation, we take pains
0N/A * to pass on the child the exact byte representation we inherit from
0N/A * the parent process for any environment name or value not created by
0N/A * Javaland. So we keep track of all the byte representations.
0N/A *
0N/A * Internally, we define the types Variable and Value that exhibit
0N/A * String/byteArray duality. The internal representation of the
0N/A * environment then looks like a Map<Variable,Value>. But we don't
0N/A * expose this to the user -- we only provide a Map<String,String>
0N/A * view, although we could also provide a Map<byte[],byte[]> view.
0N/A *
0N/A * The non-private methods in this class are not for general use even
0N/A * within this package. Instead, they are the system-dependent parts
0N/A * of the system-independent method of the same name. Don't even
0N/A * think of using this class unless your method's name appears below.
0N/A *
0N/A * @author Martin Buchholz
0N/A * @since 1.5
0N/A */
0N/A
0N/Apackage java.lang;
0N/A
0N/Aimport java.io.*;
0N/Aimport java.util.*;
0N/A
0N/A
0N/Afinal class ProcessEnvironment
0N/A{
0N/A private static final HashMap<Variable,Value> theEnvironment;
0N/A private static final Map<String,String> theUnmodifiableEnvironment;
0N/A static final int MIN_NAME_LENGTH = 0;
0N/A
0N/A static {
0N/A // We cache the C environment. This means that subsequent calls
0N/A // to putenv/setenv from C will not be visible from Java code.
0N/A byte[][] environ = environ();
3323N/A theEnvironment = new HashMap<>(environ.length/2 + 3);
0N/A // Read environment variables back to front,
0N/A // so that earlier variables override later ones.
0N/A for (int i = environ.length-1; i > 0; i-=2)
0N/A theEnvironment.put(Variable.valueOf(environ[i-1]),
0N/A Value.valueOf(environ[i]));
0N/A
0N/A theUnmodifiableEnvironment
0N/A = Collections.unmodifiableMap
0N/A (new StringEnvironment(theEnvironment));
0N/A }
0N/A
0N/A /* Only for use by System.getenv(String) */
0N/A static String getenv(String name) {
0N/A return theUnmodifiableEnvironment.get(name);
0N/A }
0N/A
0N/A /* Only for use by System.getenv() */
0N/A static Map<String,String> getenv() {
0N/A return theUnmodifiableEnvironment;
0N/A }
0N/A
0N/A /* Only for use by ProcessBuilder.environment() */
0N/A static Map<String,String> environment() {
0N/A return new StringEnvironment
0N/A ((Map<Variable,Value>)(theEnvironment.clone()));
0N/A }
0N/A
0N/A /* Only for use by Runtime.exec(...String[]envp...) */
0N/A static Map<String,String> emptyEnvironment(int capacity) {
0N/A return new StringEnvironment(new HashMap<Variable,Value>(capacity));
0N/A }
0N/A
0N/A private static native byte[][] environ();
0N/A
0N/A // This class is not instantiable.
0N/A private ProcessEnvironment() {}
0N/A
0N/A // Check that name is suitable for insertion into Environment map
0N/A private static void validateVariable(String name) {
0N/A if (name.indexOf('=') != -1 ||
0N/A name.indexOf('\u0000') != -1)
0N/A throw new IllegalArgumentException
0N/A ("Invalid environment variable name: \"" + name + "\"");
0N/A }
0N/A
0N/A // Check that value is suitable for insertion into Environment map
0N/A private static void validateValue(String value) {
0N/A if (value.indexOf('\u0000') != -1)
0N/A throw new IllegalArgumentException
0N/A ("Invalid environment variable value: \"" + value + "\"");
0N/A }
0N/A
0N/A // A class hiding the byteArray-String duality of
0N/A // text data on Unixoid operating systems.
0N/A private static abstract class ExternalData {
0N/A protected final String str;
0N/A protected final byte[] bytes;
0N/A
0N/A protected ExternalData(String str, byte[] bytes) {
0N/A this.str = str;
0N/A this.bytes = bytes;
0N/A }
0N/A
0N/A public byte[] getBytes() {
0N/A return bytes;
0N/A }
0N/A
0N/A public String toString() {
0N/A return str;
0N/A }
0N/A
0N/A public boolean equals(Object o) {
0N/A return o instanceof ExternalData
0N/A && arrayEquals(getBytes(), ((ExternalData) o).getBytes());
0N/A }
0N/A
0N/A public int hashCode() {
0N/A return arrayHash(getBytes());
0N/A }
0N/A }
0N/A
0N/A private static class Variable
0N/A extends ExternalData implements Comparable<Variable>
0N/A {
0N/A protected Variable(String str, byte[] bytes) {
0N/A super(str, bytes);
0N/A }
0N/A
0N/A public static Variable valueOfQueryOnly(Object str) {
0N/A return valueOfQueryOnly((String) str);
0N/A }
0N/A
0N/A public static Variable valueOfQueryOnly(String str) {
0N/A return new Variable(str, str.getBytes());
0N/A }
0N/A
0N/A public static Variable valueOf(String str) {
0N/A validateVariable(str);
0N/A return valueOfQueryOnly(str);
0N/A }
0N/A
0N/A public static Variable valueOf(byte[] bytes) {
0N/A return new Variable(new String(bytes), bytes);
0N/A }
0N/A
0N/A public int compareTo(Variable variable) {
0N/A return arrayCompare(getBytes(), variable.getBytes());
0N/A }
0N/A
0N/A public boolean equals(Object o) {
0N/A return o instanceof Variable && super.equals(o);
0N/A }
0N/A }
0N/A
0N/A private static class Value
0N/A extends ExternalData implements Comparable<Value>
0N/A {
0N/A protected Value(String str, byte[] bytes) {
0N/A super(str, bytes);
0N/A }
0N/A
0N/A public static Value valueOfQueryOnly(Object str) {
0N/A return valueOfQueryOnly((String) str);
0N/A }
0N/A
0N/A public static Value valueOfQueryOnly(String str) {
0N/A return new Value(str, str.getBytes());
0N/A }
0N/A
0N/A public static Value valueOf(String str) {
0N/A validateValue(str);
0N/A return valueOfQueryOnly(str);
0N/A }
0N/A
0N/A public static Value valueOf(byte[] bytes) {
0N/A return new Value(new String(bytes), bytes);
0N/A }
0N/A
0N/A public int compareTo(Value value) {
0N/A return arrayCompare(getBytes(), value.getBytes());
0N/A }
0N/A
0N/A public boolean equals(Object o) {
0N/A return o instanceof Value && super.equals(o);
0N/A }
0N/A }
0N/A
0N/A // This implements the String map view the user sees.
0N/A private static class StringEnvironment
0N/A extends AbstractMap<String,String>
0N/A {
0N/A private Map<Variable,Value> m;
0N/A private static String toString(Value v) {
0N/A return v == null ? null : v.toString();
0N/A }
0N/A public StringEnvironment(Map<Variable,Value> m) {this.m = m;}
0N/A public int size() {return m.size();}
0N/A public boolean isEmpty() {return m.isEmpty();}
0N/A public void clear() { m.clear();}
0N/A public boolean containsKey(Object key) {
0N/A return m.containsKey(Variable.valueOfQueryOnly(key));
0N/A }
0N/A public boolean containsValue(Object value) {
0N/A return m.containsValue(Value.valueOfQueryOnly(value));
0N/A }
0N/A public String get(Object key) {
0N/A return toString(m.get(Variable.valueOfQueryOnly(key)));
0N/A }
0N/A public String put(String key, String value) {
0N/A return toString(m.put(Variable.valueOf(key),
0N/A Value.valueOf(value)));
0N/A }
0N/A public String remove(Object key) {
0N/A return toString(m.remove(Variable.valueOfQueryOnly(key)));
0N/A }
0N/A public Set<String> keySet() {
0N/A return new StringKeySet(m.keySet());
0N/A }
0N/A public Set<Map.Entry<String,String>> entrySet() {
0N/A return new StringEntrySet(m.entrySet());
0N/A }
0N/A public Collection<String> values() {
0N/A return new StringValues(m.values());
0N/A }
0N/A
0N/A // It is technically feasible to provide a byte-oriented view
0N/A // as follows:
0N/A // public Map<byte[],byte[]> asByteArrayMap() {
0N/A // return new ByteArrayEnvironment(m);
0N/A // }
0N/A
0N/A
0N/A // Convert to Unix style environ as a monolithic byte array
0N/A // inspired by the Windows Environment Block, except we work
0N/A // exclusively with bytes instead of chars, and we need only
0N/A // one trailing NUL on Unix.
0N/A // This keeps the JNI as simple and efficient as possible.
0N/A public byte[] toEnvironmentBlock(int[]envc) {
0N/A int count = m.size() * 2; // For added '=' and NUL
0N/A for (Map.Entry<Variable,Value> entry : m.entrySet()) {
0N/A count += entry.getKey().getBytes().length;
0N/A count += entry.getValue().getBytes().length;
0N/A }
0N/A
0N/A byte[] block = new byte[count];
0N/A
0N/A int i = 0;
0N/A for (Map.Entry<Variable,Value> entry : m.entrySet()) {
0N/A byte[] key = entry.getKey ().getBytes();
0N/A byte[] value = entry.getValue().getBytes();
0N/A System.arraycopy(key, 0, block, i, key.length);
0N/A i+=key.length;
0N/A block[i++] = (byte) '=';
0N/A System.arraycopy(value, 0, block, i, value.length);
0N/A i+=value.length + 1;
0N/A // No need to write NUL byte explicitly
0N/A //block[i++] = (byte) '\u0000';
0N/A }
0N/A envc[0] = m.size();
0N/A return block;
0N/A }
0N/A }
0N/A
0N/A static byte[] toEnvironmentBlock(Map<String,String> map, int[]envc) {
0N/A return map == null ? null :
0N/A ((StringEnvironment)map).toEnvironmentBlock(envc);
0N/A }
0N/A
0N/A
0N/A private static class StringEntry
0N/A implements Map.Entry<String,String>
0N/A {
0N/A private final Map.Entry<Variable,Value> e;
0N/A public StringEntry(Map.Entry<Variable,Value> e) {this.e = e;}
0N/A public String getKey() {return e.getKey().toString();}
0N/A public String getValue() {return e.getValue().toString();}
0N/A public String setValue(String newValue) {
0N/A return e.setValue(Value.valueOf(newValue)).toString();
0N/A }
0N/A public String toString() {return getKey() + "=" + getValue();}
0N/A public boolean equals(Object o) {
0N/A return o instanceof StringEntry
0N/A && e.equals(((StringEntry)o).e);
0N/A }
0N/A public int hashCode() {return e.hashCode();}
0N/A }
0N/A
0N/A private static class StringEntrySet
0N/A extends AbstractSet<Map.Entry<String,String>>
0N/A {
0N/A private final Set<Map.Entry<Variable,Value>> s;
0N/A public StringEntrySet(Set<Map.Entry<Variable,Value>> s) {this.s = s;}
0N/A public int size() {return s.size();}
0N/A public boolean isEmpty() {return s.isEmpty();}
0N/A public void clear() { s.clear();}
0N/A public Iterator<Map.Entry<String,String>> iterator() {
0N/A return new Iterator<Map.Entry<String,String>>() {
0N/A Iterator<Map.Entry<Variable,Value>> i = s.iterator();
0N/A public boolean hasNext() {return i.hasNext();}
0N/A public Map.Entry<String,String> next() {
0N/A return new StringEntry(i.next());
0N/A }
0N/A public void remove() {i.remove();}
0N/A };
0N/A }
0N/A private static Map.Entry<Variable,Value> vvEntry(final Object o) {
0N/A if (o instanceof StringEntry)
0N/A return ((StringEntry)o).e;
0N/A return new Map.Entry<Variable,Value>() {
0N/A public Variable getKey() {
0N/A return Variable.valueOfQueryOnly(((Map.Entry)o).getKey());
0N/A }
0N/A public Value getValue() {
0N/A return Value.valueOfQueryOnly(((Map.Entry)o).getValue());
0N/A }
0N/A public Value setValue(Value value) {
0N/A throw new UnsupportedOperationException();
0N/A }
0N/A };
0N/A }
0N/A public boolean contains(Object o) { return s.contains(vvEntry(o)); }
0N/A public boolean remove(Object o) { return s.remove(vvEntry(o)); }
0N/A public boolean equals(Object o) {
0N/A return o instanceof StringEntrySet
0N/A && s.equals(((StringEntrySet) o).s);
0N/A }
0N/A public int hashCode() {return s.hashCode();}
0N/A }
0N/A
0N/A private static class StringValues
0N/A extends AbstractCollection<String>
0N/A {
0N/A private final Collection<Value> c;
0N/A public StringValues(Collection<Value> c) {this.c = c;}
0N/A public int size() {return c.size();}
0N/A public boolean isEmpty() {return c.isEmpty();}
0N/A public void clear() { c.clear();}
0N/A public Iterator<String> iterator() {
0N/A return new Iterator<String>() {
0N/A Iterator<Value> i = c.iterator();
0N/A public boolean hasNext() {return i.hasNext();}
0N/A public String next() {return i.next().toString();}
0N/A public void remove() {i.remove();}
0N/A };
0N/A }
0N/A public boolean contains(Object o) {
0N/A return c.contains(Value.valueOfQueryOnly(o));
0N/A }
0N/A public boolean remove(Object o) {
0N/A return c.remove(Value.valueOfQueryOnly(o));
0N/A }
0N/A public boolean equals(Object o) {
0N/A return o instanceof StringValues
0N/A && c.equals(((StringValues)o).c);
0N/A }
0N/A public int hashCode() {return c.hashCode();}
0N/A }
0N/A
0N/A private static class StringKeySet extends AbstractSet<String> {
0N/A private final Set<Variable> s;
0N/A public StringKeySet(Set<Variable> s) {this.s = s;}
0N/A public int size() {return s.size();}
0N/A public boolean isEmpty() {return s.isEmpty();}
0N/A public void clear() { s.clear();}
0N/A public Iterator<String> iterator() {
0N/A return new Iterator<String>() {
0N/A Iterator<Variable> i = s.iterator();
0N/A public boolean hasNext() {return i.hasNext();}
0N/A public String next() {return i.next().toString();}
0N/A public void remove() { i.remove();}
0N/A };
0N/A }
0N/A public boolean contains(Object o) {
0N/A return s.contains(Variable.valueOfQueryOnly(o));
0N/A }
0N/A public boolean remove(Object o) {
0N/A return s.remove(Variable.valueOfQueryOnly(o));
0N/A }
0N/A }
0N/A
0N/A // Replace with general purpose method someday
0N/A private static int arrayCompare(byte[]x, byte[] y) {
0N/A int min = x.length < y.length ? x.length : y.length;
0N/A for (int i = 0; i < min; i++)
0N/A if (x[i] != y[i])
0N/A return x[i] - y[i];
0N/A return x.length - y.length;
0N/A }
0N/A
0N/A // Replace with general purpose method someday
0N/A private static boolean arrayEquals(byte[] x, byte[] y) {
0N/A if (x.length != y.length)
0N/A return false;
0N/A for (int i = 0; i < x.length; i++)
0N/A if (x[i] != y[i])
0N/A return false;
0N/A return true;
0N/A }
0N/A
0N/A // Replace with general purpose method someday
0N/A private static int arrayHash(byte[] x) {
0N/A int hash = 0;
0N/A for (int i = 0; i < x.length; i++)
0N/A hash = 31 * hash + x[i];
0N/A return hash;
0N/A }
0N/A
0N/A}