/*
* $Id$
*
* Copyright (c) 2004-2005 by the TeXlapse Team.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package net.sourceforge.texlipse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import net.sourceforge.texlipse.properties.StringListFieldEditor;
/**
* Helper methods for environment variable handling.
*
* @author Kimmo Karlsson
*/
public class PathUtils {
/**
* This class needs not to be instantiated.
*/
private PathUtils() {}
/**
* Merge the given environment variables to the additional environment variables
* that are defined in the preferences. The variables defined in the preferences
* override the variables given as a parameter.
*
* @param env current environment variables
* @param prefName preference name where to read additional environment variable map
* @return merged environment variables as a suitable array for the Process.exec() -method
*/
public static String[] mergeEnvFromPrefs(Properties envProp, String prefName) {
HashMap<String, String> environment = new HashMap<String, String>();
Enumeration<Object> e = envProp.keys();
while (e.hasMoreElements()){
String key = (String)e.nextElement();
String v = envProp.getProperty(key);
environment.put(key, v);
}
Map<String, String> prefsMap = getPreferenceMap(prefName);
String[] keys = prefsMap.keySet().toArray(new String[0]);
for (int i = 0; i < keys.length; i++) {
String value = prefsMap.get(keys[i]);
value = replaceVar(envProp, value);
environment.put(keys[i], value);
}
return getStrings(environment);
}
/**
* Replaces environment variable names with their values in the given string.
*
* @param envProp environment variables
* @param str value for a new environment variable
* @return the given value with variables expanded
*/
private static String replaceVar(Properties envProp, String str) {
// TODO: perform variable expansion
return str;
}
/**
* Convert a Map object to an array of "key=value" -Strings.
*
* @param map key/value mappings as a Map
* @return array of "key=value" Strings
*/
public static String[] getStrings(Map<String, String> map) {
String[] array = new String[map.size()];
String[] keys = map.keySet().toArray(new String[0]);
for (int i = 0; i < keys.length; i++) {
array[i] = keys[i] + '=' + map.get(keys[i]);
}
return array;
}
/**
* Load a Map from preferences. The map is encoded into a String object
* by separating keys from values with a '=' and key/value-pairs with a ','.
* E.g.: "key1=val1,key2=val2"
*
* @param name preference name
* @return always non-null Map of String to String -mappings
*/
public static Map<String, String> getPreferenceMap(String name) {
Map<String, String> map = new HashMap<String, String>();
String str = TexlipsePlugin.getPreference(name);
if (str == null) {
return map;
}
String[] binds = str.split(StringListFieldEditor.SEPARATOR);
if (binds == null) {
return map;
}
for (int i = 0; i < binds.length; i++) {
int index = binds[i].indexOf('=');
if (index <= 0) {
continue;
}
map.put(binds[i].substring(0, index),
binds[i].substring(index+1));
}
return map;
}
/**
* Convert a Properties object to an array of "key=value" -Strings.
*
* @param prop key/value -mappings as properties
* @return an array of "key=value" -Strings.
*/
public static String[] getStrings(Properties prop) {
String[] env = new String[prop.size()];
int i = 0;
Enumeration<Object> enumeration = prop.keys();
while (enumeration.hasMoreElements()) {
String key = (String) enumeration.nextElement();
env[i++] = key + '=' + prop.getProperty(key);
}
return env;
}
/**
* Splits the given string into tokens so that
* sections of the string that are enclosed into quotes will
* form one token (without the quotes).
*
* E.g. string = "-editor \"echo %f:%l\" -q"
* tokens = { "-editor", "echo %f:%l", "-q" }
*
* @param args the string
* @param list tokens will be added to the end of this list
* in the order they are extracted
*/
public static void tokenizeEscapedString(String args, List<String> list) {
StringTokenizer st = new StringTokenizer(args, " ");
while (st.hasMoreTokens()) {
String token = st.nextToken();
if (token.charAt(0) == '"' && token.charAt(token.length() - 1) == '"') {
list.add(token.substring(1, token.length() - 1));
}
else if (token.charAt(0) == '"') {
StringBuffer sb = new StringBuffer();
sb.append(token.substring(1));
token = st.nextToken();
while (!token.endsWith("\"") && st.hasMoreTokens()) {
sb.append(' ');
sb.append(token);
token = st.nextToken();
}
sb.append(' ');
sb.append(token.substring(0, token.length()-1));
list.add(sb.toString());
} else {
list.add(token);
}
}
}
/**
* Get the path containing the given binary from environment variables.
*
* @param bin relative path and file name to search for
* in the directories listed in the "path" environment variable
* @return the directory that contains the given file or null if file was not found
*/
public static String getEnvPath(String bin) {
Properties prop = getEnv();
String path = prop.getProperty(findPathKey(prop));
if (path == null) {
return null;
}
StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
while (st.hasMoreTokens()) {
String latexPath = st.nextToken();
File latex = new File(latexPath + bin);
if (latex.exists() && latex.isFile()) {
return latexPath;
}
}
return null;
}
/**
*
* @param file file to search for
* @param path default path to use if the environment variable "path" doesn't contain the searched file
* @param winFile file to search for in windows
* @param winPath default path in windows
* @return the complete filename and path of the searched file
*/
public static String findEnvFile(String file, String path, String winFile, String winPath) {
// default binary file to search for in the path
String defaultFile = file;
String defaultPath = path;
// windows equivalents for the above
if (System.getProperty("os.name").indexOf("indow") > 0) {
defaultPath = winPath;
defaultFile = winFile;
}
// try to find the file in the path
String envPath = getEnvPath(File.separator + defaultFile);
if (envPath == null) {
envPath = defaultPath;
}
File envFile = new File(envPath, defaultFile);
if (envFile.exists()) {
return envFile.getAbsolutePath();
}
return "";
}
/**
*
* @param file file to search for
* @param path default path to use if the environment variable "path" doesn't contain the searched file
* @param winFile file to search for in windows
* @param winPath default path in windows
* @return the path containing the file or default path
*/
public static String findInEnvPath(String file, String path, String winFile, String winPath) {
// default binary file to search for in the path
String defaultFile = file;
String defaultPath = path;
// windows equivalents for the above
if (System.getProperty("os.name").indexOf("indow") > 0) {
defaultPath = winPath;
defaultFile = winFile;
}
// try to find the file in the path
String envPath = getEnvPath(File.separator + defaultFile);
if (envPath == null) {
envPath = defaultPath;
}
return envPath;
}
/**
* Read the operating system environment variables.
*
* @see http://www.rgagnon.com/javadetails/java-0150.html
* @return operating system environment variables and their values in a properties object
* or empty properties object if an error occurs
*/
public static Properties getEnv() {
Properties envVars = new Properties();
try {
String os = System.getProperty("os.name").toLowerCase();
Process p = null;
Runtime r = Runtime.getRuntime();
if (os.indexOf("windows 9") > -1) {
p = r.exec("command.com /c set");
PathUtils.loadEscaping(envVars, p.getInputStream());
} else if ((os.indexOf("windows") > -1)) {
p = r.exec("cmd.exe /c set");
PathUtils.loadEscaping(envVars, p.getInputStream());
} else {
p = r.exec("env");
envVars.load(p.getInputStream());
}
p.waitFor();
} catch(Exception e) {
}
return envVars;
}
/**
* Loads the given properties from the given input stream.
* @param prop properties object
* @param stream the stream
* @throws IOException if a read error occurs
*/
private static void loadEscaping(Properties prop, InputStream stream) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while (true) {
int b = stream.read();
if (b == -1) {
break;
} else if (b == '\\') {
// write extra backslash to escape them
//bos.write('\\');
}
bos.write(b);
}
StringTokenizer st = new StringTokenizer(bos.toString(), "\r\n");
while (st.hasMoreTokens()) {
String token = st.nextToken();
int index = token.indexOf('=');
if (index > 0) {
String key = token.substring(0, index);
String value = token.substring(index+1);
prop.setProperty(key, value);
}
}
}
/**
* Finds the property that represents the "path" environment variable.
* This has to be done as a search, because environment variables are
* case insensitive in Windows and property keys are case sensitive.
*
* @param prop the environment variables
* @return the key of path environment variable
*/
public static String findPathKey(Properties prop) {
Enumeration<Object> enumeration = prop.keys();
while (enumeration.hasMoreElements()) {
String key = (String) enumeration.nextElement();
if (key.toLowerCase().equals("path")) {
return key;
}
}
return "PATH";
}
}