1 /* 2 * Copyright 2003-2004 Sun Microsystems, Inc. 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. Sun designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 22 * CA 95054 USA or visit www.sun.com if you need additional information or 23 * have any questions. 24 */ 25 26 /* We use APIs that access the standard Unix environ array, which 27 * is defined by UNIX98 to look like: 28 * 29 * char **environ; 30 * 31 * These are unsorted, case-sensitive, null-terminated arrays of bytes 32 * of the form FOO=BAR\000 which are usually encoded in the user's 33 * default encoding (file.encoding is an excellent choice for 34 * encoding/decoding these). However, even though the user cannot 35 * directly access the underlying byte representation, we take pains 36 * to pass on the child the exact byte representation we inherit from 37 * the parent process for any environment name or value not created by 38 * Javaland. So we keep track of all the byte representations. 39 * 40 * Internally, we define the types Variable and Value that exhibit 41 * String/byteArray duality. The internal representation of the 42 * environment then looks like a Map<Variable,Value>. But we don't 43 * expose this to the user -- we only provide a Map<String,String> 44 * view, although we could also provide a Map<byte[],byte[]> view. 45 * 46 * The non-private methods in this class are not for general use even 47 * within this package. Instead, they are the system-dependent parts 48 * of the system-independent method of the same name. Don't even 49 * think of using this class unless your method's name appears below. 50 * 51 * @author Martin Buchholz 52 * @since 1.5 53 */ 54 55 package java.lang; 56 57 import java.io.*; 58 import java.util.*; 59 60 61 final class ProcessEnvironment 62 { 63 private static final HashMap<Variable,Value> theEnvironment; 64 private static final Map<String,String> theUnmodifiableEnvironment; 65 static final int MIN_NAME_LENGTH = 0; 66 67 static { 68 // We cache the C environment. This means that subsequent calls 69 // to putenv/setenv from C will not be visible from Java code. 70 byte[][] environ = environ(); 71 theEnvironment = new HashMap<Variable,Value>(environ.length/2 + 3); 72 // Read environment variables back to front, 73 // so that earlier variables override later ones. 74 for (int i = environ.length-1; i > 0; i-=2) { 75 final Variable variable = Variable.valueOf(environ[i-1]); 76 if (!variable.toString().equals("DESKTOP_STARTUP_ID")) { 77 theEnvironment.put(variable, 78 Value.valueOf(environ[i])); 79 } 80 } 81 82 theUnmodifiableEnvironment 83 = Collections.unmodifiableMap 84 (new StringEnvironment(theEnvironment)); 85 } 86 87 /* Only for use by System.getenv(String) */ 88 static String getenv(String name) { 89 return theUnmodifiableEnvironment.get(name); 90 } 91 92 /* Only for use by System.getenv() */ 93 static Map<String,String> getenv() { 94 return theUnmodifiableEnvironment; 95 } 96 97 /* Only for use by ProcessBuilder.environment() */ 98 static Map<String,String> environment() { 99 return new StringEnvironment 100 ((Map<Variable,Value>)(theEnvironment.clone())); 101 } 102 103 /* Only for use by Runtime.exec(...String[]envp...) */ 104 static Map<String,String> emptyEnvironment(int capacity) { 105 return new StringEnvironment(new HashMap<Variable,Value>(capacity)); 106 } 107 108 private static native byte[][] environ(); 109 110 // This class is not instantiable. 111 private ProcessEnvironment() {} 112 113 // Check that name is suitable for insertion into Environment map 114 private static void validateVariable(String name) { 115 if (name.indexOf('=') != -1 || 116 name.indexOf('\u0000') != -1) 117 throw new IllegalArgumentException 118 ("Invalid environment variable name: \"" + name + "\""); 119 } 120 121 // Check that value is suitable for insertion into Environment map 122 private static void validateValue(String value) { 123 if (value.indexOf('\u0000') != -1) 124 throw new IllegalArgumentException 125 ("Invalid environment variable value: \"" + value + "\""); 126 } 127 128 // A class hiding the byteArray-String duality of 129 // text data on Unixoid operating systems. 130 private static abstract class ExternalData { 131 protected final String str; 132 protected final byte[] bytes; 133 134 protected ExternalData(String str, byte[] bytes) { 135 this.str = str; 136 this.bytes = bytes; 137 } 138 139 public byte[] getBytes() { 140 return bytes; 141 } 142 143 public String toString() { 144 return str; 145 } 146 147 public boolean equals(Object o) { 148 return o instanceof ExternalData 149 && arrayEquals(getBytes(), ((ExternalData) o).getBytes()); 150 } 151 152 public int hashCode() { 153 return arrayHash(getBytes()); 154 } 155 } 156 157 private static class Variable 158 extends ExternalData implements Comparable<Variable> 159 { 160 protected Variable(String str, byte[] bytes) { 161 super(str, bytes); 162 } 163 164 public static Variable valueOfQueryOnly(Object str) { 165 return valueOfQueryOnly((String) str); 166 } 167 168 public static Variable valueOfQueryOnly(String str) { 169 return new Variable(str, str.getBytes()); 170 } 171 172 public static Variable valueOf(String str) { 173 validateVariable(str); 174 return valueOfQueryOnly(str); 175 } 176 177 public static Variable valueOf(byte[] bytes) { 178 return new Variable(new String(bytes), bytes); 179 } 180 181 public int compareTo(Variable variable) { 182 return arrayCompare(getBytes(), variable.getBytes()); 183 } 184 185 public boolean equals(Object o) { 186 return o instanceof Variable && super.equals(o); 187 } 188 } 189 190 private static class Value 191 extends ExternalData implements Comparable<Value> 192 { 193 protected Value(String str, byte[] bytes) { 194 super(str, bytes); 195 } 196 197 public static Value valueOfQueryOnly(Object str) { 198 return valueOfQueryOnly((String) str); 199 } 200 201 public static Value valueOfQueryOnly(String str) { 202 return new Value(str, str.getBytes()); 203 } 204 205 public static Value valueOf(String str) { 206 validateValue(str); 207 return valueOfQueryOnly(str); 208 } 209 210 public static Value valueOf(byte[] bytes) { 211 return new Value(new String(bytes), bytes); 212 } 213 214 public int compareTo(Value value) { 215 return arrayCompare(getBytes(), value.getBytes()); 216 } 217 218 public boolean equals(Object o) { 219 return o instanceof Value && super.equals(o); 220 } 221 } 222 223 // This implements the String map view the user sees. 224 private static class StringEnvironment 225 extends AbstractMap<String,String> 226 { 227 private Map<Variable,Value> m; 228 private static String toString(Value v) { 229 return v == null ? null : v.toString(); 230 } 231 public StringEnvironment(Map<Variable,Value> m) {this.m = m;} 232 public int size() {return m.size();} 233 public boolean isEmpty() {return m.isEmpty();} 234 public void clear() { m.clear();} 235 public boolean containsKey(Object key) { 236 return m.containsKey(Variable.valueOfQueryOnly(key)); 237 } 238 public boolean containsValue(Object value) { 239 return m.containsValue(Value.valueOfQueryOnly(value)); 240 } 241 public String get(Object key) { 242 return toString(m.get(Variable.valueOfQueryOnly(key))); 243 } 244 public String put(String key, String value) { 245 return toString(m.put(Variable.valueOf(key), 246 Value.valueOf(value))); 247 } 248 public String remove(Object key) { 249 return toString(m.remove(Variable.valueOfQueryOnly(key))); 250 } 251 public Set<String> keySet() { 252 return new StringKeySet(m.keySet()); 253 } 254 public Set<Map.Entry<String,String>> entrySet() { 255 return new StringEntrySet(m.entrySet()); 256 } 257 public Collection<String> values() { 258 return new StringValues(m.values()); 259 } 260 261 // It is technically feasible to provide a byte-oriented view 262 // as follows: 263 // public Map<byte[],byte[]> asByteArrayMap() { 264 // return new ByteArrayEnvironment(m); 265 // } 266 267 268 // Convert to Unix style environ as a monolithic byte array 269 // inspired by the Windows Environment Block, except we work 270 // exclusively with bytes instead of chars, and we need only 271 // one trailing NUL on Unix. 272 // This keeps the JNI as simple and efficient as possible. 273 public byte[] toEnvironmentBlock(int[]envc) { 274 int count = m.size() * 2; // For added '=' and NUL 275 for (Map.Entry<Variable,Value> entry : m.entrySet()) { 276 count += entry.getKey().getBytes().length; 277 count += entry.getValue().getBytes().length; 278 } 279 280 byte[] block = new byte[count]; 281 282 int i = 0; 283 for (Map.Entry<Variable,Value> entry : m.entrySet()) { 284 byte[] key = entry.getKey ().getBytes(); 285 byte[] value = entry.getValue().getBytes(); 286 System.arraycopy(key, 0, block, i, key.length); 287 i+=key.length; 288 block[i++] = (byte) '='; 289 System.arraycopy(value, 0, block, i, value.length); 290 i+=value.length + 1; 291 // No need to write NUL byte explicitly 292 //block[i++] = (byte) '\u0000'; 293 } 294 envc[0] = m.size(); 295 return block; 296 } 297 } 298 299 static byte[] toEnvironmentBlock(Map<String,String> map, int[]envc) { 300 return map == null ? null : 301 ((StringEnvironment)map).toEnvironmentBlock(envc); 302 } 303 304 305 private static class StringEntry 306 implements Map.Entry<String,String> 307 { 308 private final Map.Entry<Variable,Value> e; 309 public StringEntry(Map.Entry<Variable,Value> e) {this.e = e;} 310 public String getKey() {return e.getKey().toString();} 311 public String getValue() {return e.getValue().toString();} 312 public String setValue(String newValue) { 313 return e.setValue(Value.valueOf(newValue)).toString(); 314 } 315 public String toString() {return getKey() + "=" + getValue();} 316 public boolean equals(Object o) { 317 return o instanceof StringEntry 318 && e.equals(((StringEntry)o).e); 319 } 320 public int hashCode() {return e.hashCode();} 321 } 322 323 private static class StringEntrySet 324 extends AbstractSet<Map.Entry<String,String>> 325 { 326 private final Set<Map.Entry<Variable,Value>> s; 327 public StringEntrySet(Set<Map.Entry<Variable,Value>> s) {this.s = s;} 328 public int size() {return s.size();} 329 public boolean isEmpty() {return s.isEmpty();} 330 public void clear() { s.clear();} 331 public Iterator<Map.Entry<String,String>> iterator() { 332 return new Iterator<Map.Entry<String,String>>() { 333 Iterator<Map.Entry<Variable,Value>> i = s.iterator(); 334 public boolean hasNext() {return i.hasNext();} 335 public Map.Entry<String,String> next() { 336 return new StringEntry(i.next()); 337 } 338 public void remove() {i.remove();} 339 }; 340 } 341 private static Map.Entry<Variable,Value> vvEntry(final Object o) { 342 if (o instanceof StringEntry) 343 return ((StringEntry)o).e; 344 return new Map.Entry<Variable,Value>() { 345 public Variable getKey() { 346 return Variable.valueOfQueryOnly(((Map.Entry)o).getKey()); 347 } 348 public Value getValue() { 349 return Value.valueOfQueryOnly(((Map.Entry)o).getValue()); 350 } 351 public Value setValue(Value value) { 352 throw new UnsupportedOperationException(); 353 } 354 }; 355 } 356 public boolean contains(Object o) { return s.contains(vvEntry(o)); } 357 public boolean remove(Object o) { return s.remove(vvEntry(o)); } 358 public boolean equals(Object o) { 359 return o instanceof StringEntrySet 360 && s.equals(((StringEntrySet) o).s); 361 } 362 public int hashCode() {return s.hashCode();} 363 } 364 365 private static class StringValues 366 extends AbstractCollection<String> 367 { 368 private final Collection<Value> c; 369 public StringValues(Collection<Value> c) {this.c = c;} 370 public int size() {return c.size();} 371 public boolean isEmpty() {return c.isEmpty();} 372 public void clear() { c.clear();} 373 public Iterator<String> iterator() { 374 return new Iterator<String>() { 375 Iterator<Value> i = c.iterator(); 376 public boolean hasNext() {return i.hasNext();} 377 public String next() {return i.next().toString();} 378 public void remove() {i.remove();} 379 }; 380 } 381 public boolean contains(Object o) { 382 return c.contains(Value.valueOfQueryOnly(o)); 383 } 384 public boolean remove(Object o) { 385 return c.remove(Value.valueOfQueryOnly(o)); 386 } 387 public boolean equals(Object o) { 388 return o instanceof StringValues 389 && c.equals(((StringValues)o).c); 390 } 391 public int hashCode() {return c.hashCode();} 392 } 393 394 private static class StringKeySet extends AbstractSet<String> { 395 private final Set<Variable> s; 396 public StringKeySet(Set<Variable> s) {this.s = s;} 397 public int size() {return s.size();} 398 public boolean isEmpty() {return s.isEmpty();} 399 public void clear() { s.clear();} 400 public Iterator<String> iterator() { 401 return new Iterator<String>() { 402 Iterator<Variable> i = s.iterator(); 403 public boolean hasNext() {return i.hasNext();} 404 public String next() {return i.next().toString();} 405 public void remove() { i.remove();} 406 }; 407 } 408 public boolean contains(Object o) { 409 return s.contains(Variable.valueOfQueryOnly(o)); 410 } 411 public boolean remove(Object o) { 412 return s.remove(Variable.valueOfQueryOnly(o)); 413 } 414 } 415 416 // Replace with general purpose method someday 417 private static int arrayCompare(byte[]x, byte[] y) { 418 int min = x.length < y.length ? x.length : y.length; 419 for (int i = 0; i < min; i++) 420 if (x[i] != y[i]) 421 return x[i] - y[i]; 422 return x.length - y.length; 423 } 424 425 // Replace with general purpose method someday 426 private static boolean arrayEquals(byte[] x, byte[] y) { 427 if (x.length != y.length) 428 return false; 429 for (int i = 0; i < x.length; i++) 430 if (x[i] != y[i]) 431 return false; 432 return true; 433 } 434 435 // Replace with general purpose method someday 436 private static int arrayHash(byte[] x) { 437 int hash = 0; 438 for (int i = 0; i < x.length; i++) 439 hash = 31 * hash + x[i]; 440 return hash; 441 } 442 443 }