# HG changeset patch # User Artem Tikhomirov # Date 1328802437 -3600 # Node ID fa2be7a05af6f7e0976f136bdf091a40b268cefa # Parent 9fb990c8a7248ae42577acdcac0dc346cb9bce31 Implement discovery of mercurial installation, use it to read/write configuration files diff -r 9fb990c8a724 -r fa2be7a05af6 src/org/tmatesoft/hg/internal/Internals.java --- a/src/org/tmatesoft/hg/internal/Internals.java Thu Feb 09 04:15:17 2012 +0100 +++ b/src/org/tmatesoft/hg/internal/Internals.java Thu Feb 09 16:47:17 2012 +0100 @@ -22,7 +22,11 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; +import java.util.StringTokenizer; import org.tmatesoft.hg.repo.HgInternals; import org.tmatesoft.hg.repo.HgRepoConfig.ExtensionsSection; @@ -37,6 +41,8 @@ */ public class Internals { + public static final String CFG_PROPERTY_HG_INSTALL_ROOT = "hg4j.hg.install_root"; + private int requiresFlags = 0; private List filterFactories; @@ -114,63 +120,159 @@ public static boolean runningOnWindows() { return System.getProperty("os.name").indexOf("Windows") != -1; } - + + /** + * For Unix, returns installation root, which is the parent directory of the hg executable (or symlink) being run. + * For Windows, it's Mercurial installation directory itself + */ + private static File findHgInstallRoot() { + // let clients to override Hg install location + String p = System.getProperty(CFG_PROPERTY_HG_INSTALL_ROOT); + if (p != null) { + return new File(p); + } + StringTokenizer st = new StringTokenizer(System.getenv("PATH"), System.getProperty("path.separator"), false); + final boolean runsOnWin = runningOnWindows(); + while (st.hasMoreTokens()) { + String pe = st.nextToken(); + File execCandidate = new File(pe, runsOnWin ? "hg.exe" : "hg"); + if (execCandidate.exists() && execCandidate.isFile()) { + File execDir = execCandidate.getParentFile(); + // e.g. on Unix runs "/shared/tools/bin/hg", directory of interest is "/shared/tools/" + return runsOnWin ? execDir : execDir.getParentFile(); + } + } + return null; + } + + /** + * @see http://www.selenic.com/mercurial/hgrc.5.html + */ public ConfigFile readConfiguration(HgRepository hgRepo, File repoRoot) throws IOException { ConfigFile configFile = new ConfigFile(); + File hgInstallRoot = findHgInstallRoot(); // may be null + // if (runningOnWindows()) { - // FIXME read install-dir - // - // XXX perhaps, makes sense to compare getenv(USERPROFILE) and getenv(HOME) and use - // them if set (and use both if their values do not match). Only if both not set, rely to user.home? - // Also respect #getUserConfigurationFileToWrite() below - configFile.addLocation(new File(System.getProperty("user.home"), "Mercurial.ini")); + if (hgInstallRoot != null) { + for (File f : getWindowsConfigFilesPerInstall(hgInstallRoot)) { + configFile.addLocation(f); + } + } + LinkedHashSet locations = new LinkedHashSet(); + locations.add(System.getenv("USERPROFILE")); + locations.add(System.getenv("HOME")); + locations.remove(null); + for (String loc : locations) { + File location = new File(loc); + configFile.addLocation(new File(location, "Mercurial.ini")); + configFile.addLocation(new File(location, ".hgrc")); + } } else { - // FIXME read from install-root - // - File d = new File("/etc/mercurial/hgrc.d/"); - if (d.isDirectory()) { - for (File f : d.listFiles()) { - // XXX in fact, need to sort in alphabetical order - if (f.getName().endsWith(".rc")) { + if (hgInstallRoot != null) { + File d = new File(hgInstallRoot, "etc/mercurial/hgrc.d/"); + if (d.isDirectory() && d.canRead()) { + for (File f : listConfigFiles(d)) { configFile.addLocation(f); } } + configFile.addLocation(new File(hgInstallRoot, "etc/mercurial/hgrc")); + } + // same, but with absolute paths + File d = new File("/etc/mercurial/hgrc.d/"); + if (d.isDirectory() && d.canRead()) { + for (File f : listConfigFiles(d)) { + configFile.addLocation(f); + } } configFile.addLocation(new File("/etc/mercurial/hgrc")); + configFile.addLocation(new File(System.getenv("HOME"), ".hgrc")); } - configFile.addLocation(new File(System.getProperty("user.home"), ".hgrc")); // last one, overrides anything else // /.hg/hgrc configFile.addLocation(new File(repoRoot, "hgrc")); return configFile; } - - /** - * @return - */ + + private static List getWindowsConfigFilesPerInstall(File hgInstallDir) { + File f = new File(hgInstallDir, "Mercurial.ini"); + if (f.exists()) { + return Collections.singletonList(f); + } + f = new File(hgInstallDir, "hgrc.d/"); + if (f.canRead() && f.isDirectory()) { + return listConfigFiles(f); + } + // FIXME query registry, e.g. with + // Runtime.exec("reg query HKLM\Software\Mercurial") + // + f = new File("C:\\Mercurial\\Mercurial.ini"); + if (f.exists()) { + return Collections.singletonList(f); + } + return Collections.emptyList(); + } + + private static List listConfigFiles(File dir) { + assert dir.canRead(); + assert dir.isDirectory(); + final File[] allFiles = dir.listFiles(); + // File is Comparable, lexicographically by default + Arrays.sort(allFiles); + ArrayList rv = new ArrayList(allFiles.length); + for (File f : allFiles) { + if (f.getName().endsWith(".rc")) { + rv.add(f); + } + } + return rv; + } + public static File getInstallationConfigurationFileToWrite() { - // TODO Auto-generated method stub - // FIXME find out install-root - throw new UnsupportedOperationException(); + File hgInstallRoot = findHgInstallRoot(); // may be null + // choice of which hgrc to pick here is according to my own pure discretion + if (hgInstallRoot != null) { + // use this location only if it's writable + File cfg = new File(hgInstallRoot, runningOnWindows() ? "Mercurial.ini" : "etc/mercurial/hgrc"); + if (cfg.canWrite() || cfg.getParentFile().canWrite()) { + return cfg; + } + } + // fallback + if (runningOnWindows()) { + if (hgInstallRoot == null) { + return new File("C:\\Mercurial\\Mercurial.ini"); + } else { + // yes, we tried this file already (above) and found it non-writable + // let caller fail with can't write + return new File(hgInstallRoot, "Mercurial.ini"); + } + } else { + return new File("/etc/mercurial/hgrc"); + } } - /** - * @return - */ public static File getUserConfigurationFileToWrite() { - final File rv = new File(System.getProperty("user.home"), ".hgrc"); - if (rv.exists() && rv.canWrite()) { - return rv; + LinkedHashSet locations = new LinkedHashSet(); + final boolean runsOnWindows = runningOnWindows(); + if (runsOnWindows) { + locations.add(System.getenv("USERPROFILE")); } - if (runningOnWindows()) { - // try another well-known location - // TODO comment above regarding USERPROFILE and HOME variables applies here as well - File f = new File(System.getProperty("user.home"), "Mercurial.ini"); - if (f.exists() && f.canWrite()) { - return f; + locations.add(System.getenv("HOME")); + locations.remove(null); + for (String loc : locations) { + File location = new File(loc); + File rv = new File(location, ".hgrc"); + if (rv.exists() && rv.canWrite()) { + return rv; + } + if (runsOnWindows) { + rv = new File(location, "Mercurial.ini"); + if (rv.exists() && rv.canWrite()) { + return rv; + } } } - // fallback to default value - return rv; + // fallback to default, let calling code fail with Exception if can't write + return new File(System.getProperty("user.home"), ".hgrc"); } }