# HG changeset patch # User Artem Tikhomirov # Date 1344359693 -7200 # Node ID e31e85cf4d4c5e135e4b02f9be9f35c4d43e01d4 # Parent 6c67debed07e6858b27fbc0fa59b7b268ee6762e Handle include and unset directives in config files diff -r 6c67debed07e -r e31e85cf4d4c cmdline/org/tmatesoft/hg/console/Main.java --- a/cmdline/org/tmatesoft/hg/console/Main.java Tue Aug 07 14:27:13 2012 +0200 +++ b/cmdline/org/tmatesoft/hg/console/Main.java Tue Aug 07 19:14:53 2012 +0200 @@ -44,8 +44,10 @@ import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.BasicSessionContext; import org.tmatesoft.hg.internal.ByteArrayChannel; +import org.tmatesoft.hg.internal.ConfigFile; import org.tmatesoft.hg.internal.DigestHelper; import org.tmatesoft.hg.internal.IntMap; +import org.tmatesoft.hg.internal.Internals; import org.tmatesoft.hg.internal.PathGlobMatcher; import org.tmatesoft.hg.internal.PhasesHelper; import org.tmatesoft.hg.internal.RelativePathRewrite; @@ -109,7 +111,8 @@ public static void main(String[] args) throws Exception { Main m = new Main(args); - m.dumpCommitLastMessage(); + m.readConfigFile(); +// m.dumpCommitLastMessage(); // m.buildFileLog(); // m.testConsoleLog(); // m.testTreeTraversal(); @@ -130,7 +133,20 @@ // m.dumpCompleteManifestHigh(); // m.bunchOfTests(); } - + + // TODO as test + private void readConfigFile() throws Exception { + ConfigFile configFile = new ConfigFile(HgInternals.getContext(hgRepo)); + configFile.addLocation(new File(System.getProperty("user.home"), "test-cfg/aaa/config1")); + for (String s : configFile.getSectionNames()) { + System.out.printf("[%s]\n", s); + for (Map.Entry e : configFile.getSection(s).entrySet()) { + System.out.printf("%s = %s\n", e.getKey(), e.getValue()); + } + } + + } + private void dumpCommitLastMessage() throws Exception { System.out.println(hgRepo.getCommitLastMessage()); } diff -r 6c67debed07e -r e31e85cf4d4c cmdline/org/tmatesoft/hg/console/Remote.java --- a/cmdline/org/tmatesoft/hg/console/Remote.java Tue Aug 07 14:27:13 2012 +0200 +++ b/cmdline/org/tmatesoft/hg/console/Remote.java Tue Aug 07 19:14:53 2012 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 TMate Software Ltd + * Copyright (c) 2011-2012 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,6 +35,7 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; +import org.tmatesoft.hg.internal.BasicSessionContext; import org.tmatesoft.hg.internal.ConfigFile; /** @@ -90,7 +91,7 @@ empty result */ public static void main(String[] args) throws Exception { - ConfigFile cfg = new ConfigFile(); + ConfigFile cfg = new ConfigFile(new BasicSessionContext(null)); cfg.addLocation(new File(System.getProperty("user.home"), ".hgrc")); String svnkitServer = cfg.getSection("paths").get("svnkit"); // URL url = new URL(svnkitServer + "?cmd=branches&nodes=30bd389788464287cee22ccff54c330a4b715de5"); diff -r 6c67debed07e -r e31e85cf4d4c src/org/tmatesoft/hg/core/HgUpdateConfigCommand.java --- a/src/org/tmatesoft/hg/core/HgUpdateConfigCommand.java Tue Aug 07 14:27:13 2012 +0200 +++ b/src/org/tmatesoft/hg/core/HgUpdateConfigCommand.java Tue Aug 07 19:14:53 2012 +0200 @@ -43,21 +43,24 @@ private Map> toRemove; private Map> toSet; - private HgUpdateConfigCommand(File configurationFile) { + private final SessionContext sessionCtx; + + private HgUpdateConfigCommand(SessionContext sessionContext, File configurationFile) { + sessionCtx = sessionContext; configFile = configurationFile; } public static HgUpdateConfigCommand forRepository(HgRepository hgRepo) { // XXX HgRepository to implement SessionContextProvider (with getContext())? - return new HgUpdateConfigCommand(new File(HgInternals.getRepositoryDir(hgRepo), "hgrc")); + return new HgUpdateConfigCommand(HgInternals.getContext(hgRepo), new File(HgInternals.getRepositoryDir(hgRepo), "hgrc")); } public static HgUpdateConfigCommand forUser(SessionContext ctx) { - return new HgUpdateConfigCommand(Internals.getUserConfigurationFileToWrite(ctx)); + return new HgUpdateConfigCommand(ctx, Internals.getUserConfigurationFileToWrite(ctx)); } public static HgUpdateConfigCommand forInstallation(SessionContext ctx) { - return new HgUpdateConfigCommand(Internals.getInstallationConfigurationFileToWrite(ctx)); + return new HgUpdateConfigCommand(ctx, Internals.getInstallationConfigurationFileToWrite(ctx)); } /** @@ -118,7 +121,7 @@ */ public void execute() throws HgException { try { - ConfigFile cfg = new ConfigFile(); + ConfigFile cfg = new ConfigFile(sessionCtx); cfg.addLocation(configFile); if (toRemove != null) { for (Map.Entry> s : toRemove.entrySet()) { diff -r 6c67debed07e -r e31e85cf4d4c src/org/tmatesoft/hg/internal/ConfigFile.java --- a/src/org/tmatesoft/hg/internal/ConfigFile.java Tue Aug 07 14:27:13 2012 +0200 +++ b/src/org/tmatesoft/hg/internal/ConfigFile.java Tue Aug 07 19:14:53 2012 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 TMate Software Ltd + * Copyright (c) 2011-2012 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,11 +16,9 @@ */ package org.tmatesoft.hg.internal; -import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; -import java.io.FileReader; import java.io.IOException; import java.io.PrintStream; import java.nio.ByteBuffer; @@ -33,6 +31,10 @@ import java.util.List; import java.util.Map; +import org.tmatesoft.hg.core.SessionContext; +import org.tmatesoft.hg.repo.HgInvalidFileException; +import org.tmatesoft.hg.util.LogFacility; + /** * .ini / .rc file reader * @author Artem Tikhomirov @@ -40,13 +42,15 @@ */ public class ConfigFile { + private final SessionContext sessionContext; private List sections; private List> content; - public ConfigFile() { + public ConfigFile(SessionContext ctx) { + sessionContext = ctx; } - public void addLocation(File path) throws IOException { + public void addLocation(File path) throws HgInvalidFileException { read(path); } @@ -111,10 +115,7 @@ } } - // TODO handle %include and %unset directives - // TODO "" and lists - // XXX perhaps, single string to keep whole section with substrings for keys/values to minimize number of arrays (String.value) - private void read(File f) throws IOException { + private void read(File f) throws HgInvalidFileException { if (f == null || !f.canRead()) { return; } @@ -122,54 +123,9 @@ sections = new ArrayList(); content = new ArrayList>(); } - BufferedReader br = null; - try { - br = new BufferedReader(new FileReader(f)); - String line; - String sectionName = ""; - Map section = new LinkedHashMap(); - while ((line = br.readLine()) != null) { - line = line.trim(); - int x; - if ((x = line.indexOf('#')) != -1) { - // do not keep comments in memory, get new, shorter string - line = new String(line.substring(0, x).trim()); - } - if (line.length() <= 2) { // a=b or [a] are at least of length 3 - continue; - } - if (line.charAt(0) == '[' && line.charAt(line.length() - 1) == ']') { - sectionName = line.substring(1, line.length() - 1); - if (sections.indexOf(sectionName) == -1) { - sections.add(sectionName); - content.add(section = new LinkedHashMap()); - } else { - section = null; // drop cached value - } - } else if ((x = line.indexOf('=')) != -1) { - // share char[] of the original string - String key = line.substring(0, x).trim(); - String value = line.substring(x+1).trim(); - if (section == null) { - int i = sections.indexOf(sectionName); - assert i >= 0; - section = content.get(i); - } - if (sectionName.length() == 0) { - // add fake section only if there are any values - sections.add(sectionName); - content.add(section); - } - section.put(key, value); - } - } - } finally { - ((ArrayList) sections).trimToSize(); - ((ArrayList) content).trimToSize(); - if (br != null) { - br.close(); - } - } + new Parser().go(f, this); + ((ArrayList) sections).trimToSize(); + ((ArrayList) content).trimToSize(); assert sections.size() == content.size(); } @@ -210,4 +166,84 @@ ps.flush(); return bos.toByteArray(); } + + private static class Parser implements LineReader.LineConsumer { + + private String sectionName = ""; + private Map section = new LinkedHashMap(); + private File contextFile; + + // TODO "" and lists + // XXX perhaps, single string to keep whole section with substrings for keys/values to minimize number of arrays (String.value) + public boolean consume(String line, ConfigFile cfg) throws IOException { + int x; + if ((x = line.indexOf('#')) != -1) { + // do not keep comments in memory, get new, shorter string + line = new String(line.substring(0, x).trim()); + } + if (line.length() <= 2) { // a=b or [a] are at least of length 3 + return true; + } + if (line.charAt(0) == '[' && line.charAt(line.length() - 1) == ']') { + sectionName = line.substring(1, line.length() - 1); + if (cfg.sections.indexOf(sectionName) == -1) { + cfg.sections.add(sectionName); + cfg.content.add(section = new LinkedHashMap()); + } else { + section = null; // drop cached value + } + } else if (line.startsWith("%unset")) { + if (section != null) { + section.remove(line.substring(7).trim()); + } + } else if (line.startsWith("%include")) { + processInclude(line.substring(9).trim(), cfg); + } else if ((x = line.indexOf('=')) != -1) { + // share char[] of the original string + String key = line.substring(0, x).trim(); + String value = line.substring(x+1).trim(); + if (section == null) { + int i = cfg.sections.indexOf(sectionName); + assert i >= 0; + section = cfg.content.get(i); + } + if (sectionName.length() == 0) { + // add fake section only if there are any values + cfg.sections.add(sectionName); + cfg.content.add(section); + } + section.put(key, value); + } + return true; + } + + public void go(File f, ConfigFile cfg) throws HgInvalidFileException { + contextFile = f; + LineReader lr = new LineReader(f, cfg.sessionContext.getLog()); + lr.ignoreLineComments("#"); + lr.read(this, cfg); + } + + // include failure doesn't propagate + private void processInclude(String includeValue, ConfigFile cfg) { + File f; + // TODO handle environment variable expansion + if (includeValue.startsWith("~/")) { + f = new File(System.getProperty("user.home"), includeValue.substring(2)); + } else { + f = new File(contextFile.getParentFile(), includeValue); + } + try { + if (f.canRead()) { + new Parser().go(f, cfg); + } else { + LogFacility lf = cfg.sessionContext.getLog(); + lf.dump(ConfigFile.class, LogFacility.Severity.Debug, "Can't read file to include: %s", f); + } + } catch (HgInvalidFileException ex) { + LogFacility lf = cfg.sessionContext.getLog(); + lf.dump(ConfigFile.class, LogFacility.Severity.Warn, "Can't include %s (%s)", f, includeValue); + } + } + } } diff -r 6c67debed07e -r e31e85cf4d4c src/org/tmatesoft/hg/internal/Internals.java --- a/src/org/tmatesoft/hg/internal/Internals.java Tue Aug 07 14:27:13 2012 +0200 +++ b/src/org/tmatesoft/hg/internal/Internals.java Tue Aug 07 19:14:53 2012 +0200 @@ -246,8 +246,10 @@ * @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(HgInternals.getContext(hgRepo)); // may be null + // XXX Internals now have sessionContext field, is there real need to extract one from the repo? + SessionContext sessionCtx = HgInternals.getContext(hgRepo); + ConfigFile configFile = new ConfigFile(sessionCtx); + File hgInstallRoot = findHgInstallRoot(sessionCtx); // may be null // if (runningOnWindows()) { if (hgInstallRoot != null) { diff -r 6c67debed07e -r e31e85cf4d4c src/org/tmatesoft/hg/internal/LineReader.java --- a/src/org/tmatesoft/hg/internal/LineReader.java Tue Aug 07 14:27:13 2012 +0200 +++ b/src/org/tmatesoft/hg/internal/LineReader.java Tue Aug 07 19:14:53 2012 +0200 @@ -109,10 +109,12 @@ } catch (IOException ex) { throw new HgInvalidFileException(ex.getMessage(), ex, file); } finally { - try { - statusFileReader.close(); - } catch (IOException ex) { - log.dump(MqManager.class, Warn, ex, null); + if (statusFileReader != null) { + try { + statusFileReader.close(); + } catch (IOException ex) { + log.dump(MqManager.class, Warn, ex, null); + } } // try { // consumer.end(file, paramObj); diff -r 6c67debed07e -r e31e85cf4d4c src/org/tmatesoft/hg/internal/NewlineFilter.java --- a/src/org/tmatesoft/hg/internal/NewlineFilter.java Tue Aug 07 14:27:13 2012 +0200 +++ b/src/org/tmatesoft/hg/internal/NewlineFilter.java Tue Aug 07 19:14:53 2012 +0200 @@ -24,12 +24,12 @@ import static org.tmatesoft.hg.util.LogFacility.Severity.Warn; import java.io.File; -import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Map; import org.tmatesoft.hg.repo.HgInternals; +import org.tmatesoft.hg.repo.HgInvalidFileException; import org.tmatesoft.hg.repo.HgInvalidStateException; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.util.Adaptable; @@ -312,10 +312,10 @@ // return; // } // XXX perhaps, add HgDataFile.hasWorkingCopy and workingCopyContent()? - ConfigFile hgeol = new ConfigFile(); + ConfigFile hgeol = new ConfigFile(HgInternals.getContext(hgRepo)); try { hgeol.addLocation(cfgFile); - } catch (IOException ex) { + } catch (HgInvalidFileException ex) { HgInternals.getContext(hgRepo).getLog().dump(getClass(), Warn, ex, null); } nativeRepoFormat = hgeol.getSection("repository").get("native"); diff -r 6c67debed07e -r e31e85cf4d4c src/org/tmatesoft/hg/repo/HgInternals.java --- a/src/org/tmatesoft/hg/repo/HgInternals.java Tue Aug 07 14:27:13 2012 +0200 +++ b/src/org/tmatesoft/hg/repo/HgInternals.java Tue Aug 07 19:14:53 2012 +0200 @@ -150,6 +150,7 @@ // expose otherwise package-local information primarily to use in our own o.t.hg.core package public static SessionContext getContext(HgRepository repo) { + // TODO SessionContext.Source and HgRepo to implement it return repo.getContext(); } diff -r 6c67debed07e -r e31e85cf4d4c src/org/tmatesoft/hg/repo/HgLookup.java --- a/src/org/tmatesoft/hg/repo/HgLookup.java Tue Aug 07 14:27:13 2012 +0200 +++ b/src/org/tmatesoft/hg/repo/HgLookup.java Tue Aug 07 19:14:53 2012 +0200 @@ -132,10 +132,10 @@ private ConfigFile getGlobalConfig() { if (globalCfg == null) { - globalCfg = new ConfigFile(); + globalCfg = new ConfigFile(getContext()); try { globalCfg.addLocation(new File(System.getProperty("user.home"), ".hgrc")); - } catch (IOException ex) { + } catch (HgInvalidFileException ex) { // XXX perhaps, makes sense to let caller/client know that we've failed to read global config? getContext().getLog().dump(getClass(), Warn, ex, null); } diff -r 6c67debed07e -r e31e85cf4d4c src/org/tmatesoft/hg/repo/HgRepository.java --- a/src/org/tmatesoft/hg/repo/HgRepository.java Tue Aug 07 14:27:13 2012 +0200 +++ b/src/org/tmatesoft/hg/repo/HgRepository.java Tue Aug 07 19:14:53 2012 +0200 @@ -325,7 +325,7 @@ } catch (IOException ex) { String m = "Errors while reading user configuration file"; getContext().getLog().dump(getClass(), Warn, ex, m); - return new HgRepoConfig(new ConfigFile()); // empty config, do not cache, allow to try once again + return new HgRepoConfig(new ConfigFile(getContext())); // empty config, do not cache, allow to try once again //throw new HgInvalidControlFileException(m, ex, null); } }