BIND 10 trac346, updated. 319debfb957641f311102739a15059f8453c54ce Merge branch 'master' into trac346

BIND 10 source code commits bind10-changes at lists.isc.org
Sat Feb 19 02:31:31 UTC 2011


The branch, trac346 has been updated
       via  319debfb957641f311102739a15059f8453c54ce (commit)
       via  80a4c6e866a3f40462b26260c6de1ef05a334e53 (commit)
       via  7e90e3729b54ca18425c3b5a5223230aa335efff (commit)
       via  d58cbd26658afd381cb715d0ca976d11006a0445 (commit)
       via  9136696612968df28a925c910b54217ffe84d206 (commit)
       via  a2a0bf66d71c5b5adba4a1e0dca48496bfee0ce1 (commit)
       via  e5fb3bc1ed5f3c0aec6eb40a16c63f3d0fc6a7b2 (commit)
       via  c78659bd80e45ddb44b3cafb4937aa7b4e5f1b14 (commit)
       via  05f49a93714de9a09ad1c0a9d6e2a7365e890232 (commit)
       via  1c3d1954d25eed486f1fa97d4ae5493693b04945 (commit)
       via  0fac71e31d16f1cfe127d8d0341842aad762a642 (commit)
       via  6f031a09a248e7684723c000f3e8cc981dcdb349 (commit)
       via  b3d03749ba27521a93188402c18600360e44c497 (commit)
       via  500a785136096ec3a7c5259ec348a80256aed494 (commit)
       via  9c690982f24fef19c747a72f43c4298333a58f48 (commit)
       via  ce7bc899e76309c63b1ad270b9474fdc1407cf5e (commit)
       via  96c902f2ffb43d75fbd138a6da15dbbc0d80a71f (commit)
       via  aa32fd56bf7b4eb3f1ee13b502f2724af8f7ac4d (commit)
       via  d0a29d0aa3931ad8091c24665ceeee95b94d58eb (commit)
       via  6750a2bb8306b0c335f12f6d1bd97c1b0559bdd7 (commit)
       via  dab32ddc42b4707968fbc056261c50d4fa08e408 (commit)
       via  73b2eaec32ef4dbfa93bb5009229e9ca261c3676 (commit)
       via  72c4f5046949567927410bdb2ccb7ecb663d16dd (commit)
       via  8f777e093c960f76ff489f12684005eb48580323 (commit)
       via  689503220317cf2e86dae704a8e660ed646af67b (commit)
       via  130ba31d0829fa47a5e245490a9ae172d1c91b7c (commit)
       via  ae9a05a4c5d73b3d32303b622c6b7d0c04d061d3 (commit)
       via  57736fd520bda626de430e55d9f7f0b1c13e6353 (commit)
       via  9b9f126d3e4adc4ef7e6b61945245499745996df (commit)
       via  b6bce27baf454101b3755d46877ac84c5eef20f2 (commit)
       via  8661db658ebec93ba0e2e890ea7db2b9fa20df4d (commit)
       via  967fe3d95663240940e2c5e653fc3ecac7739037 (commit)
       via  6bbb42b7fa668d7a8fcc3cf3809365179705a3a2 (commit)
       via  1e3a2586abe597550611cca5697916f311ffd2a9 (commit)
       via  cad6c0a62b3c5cace703d9863d64dbdb1e047046 (commit)
       via  d7a9547198d61bc455d41ba45f6f14874f05c1d5 (commit)
       via  17832da870f12a8bfb6ec4e8f91fda964c1afcbf (commit)
       via  cf0dac64cd9e7f132ca58f0df3609f00d2cc4a07 (commit)
       via  a9944090a851bd3be8f459c7185058394d31fd55 (commit)
       via  310d15bf23ac533d70a20c0a881fc285cb7af3f7 (commit)
       via  c2a34db25f4b0c903a025514cdff758e7ea55e42 (commit)
       via  98908690535371b9128a1263c7a4b59150609526 (commit)
       via  a01b61f9915856af9c915beca5061867804e5a86 (commit)
       via  5ba0bcb7d12da8e28ca791fef5bdb82ee2619fa8 (commit)
       via  866ae82be92451173b08ff1135c2ffe9ce820271 (commit)
       via  634dd02746cd7d5cc6c702b9a3ea690825282c22 (commit)
       via  24afa6a4a2bc6d4fb4151a866cb12ff5728db277 (commit)
       via  00b804d7100f12f5ea261b4dccb01f1de4cd94cc (commit)
       via  75cd27c3b2d1bde61ffef18bb53198cc459910ed (commit)
       via  b099d028354adfd16fe3dd838f1f36f024c79f40 (commit)
       via  ccdb524e2fa0b9d2ce5570b73023a3f127756c76 (commit)
       via  dd59f2ff605abcdb0a1e8b284c219b8d788c0585 (commit)
       via  42cd95d56e1f9c71eeeb107284c4762cf1a9a726 (commit)
       via  1888b383084177cbfab6c408e15565a3bec17ee9 (commit)
       via  73837f513323e69175abfcfd04ca2f7b1fe22a4c (commit)
       via  f0324078a088345812c5397317b66445b02f82be (commit)
       via  297c145c01d18f73e2b453ef968426352815dda6 (commit)
       via  0d1efe0f87c1868ff5389c731dbfa7ead9416655 (commit)
       via  e935dae553418e90ef4ac06d598da285e8b20bea (commit)
       via  355e2efda4baba2827a9d95a648974ab3f5bcfa3 (commit)
       via  9cdddf2713e4e4fe103cb3cdd6e9af44e827c5d8 (commit)
       via  2cb4775feeb0be8174565e175c4f800899879c19 (commit)
       via  d74693d2cb99c839b5efdd7c1bc0188fe39bc86f (commit)
       via  e26d533eb62f553fcbae616d82e84e3784f750c6 (commit)
       via  d619eab67a0c2255f249dfabd7c9db0e5d14b833 (commit)
       via  6fbb0f720f6e6a5d58c0dfc6710f0e26e79816c4 (commit)
       via  76e1d3b208772073e664437788865a5e3d067a68 (commit)
       via  d9fda268d2fcecbb69f06f3480bc9dcd058c582e (commit)
       via  17f874bb877356f0eb3c777282e1a21172a241c9 (commit)
       via  deedd6f7ff3339f822f9ed8de77a5a7164bda9a1 (commit)
       via  de81dbd26e42616b7c56718ffe1ffc7c6861b9e9 (commit)
       via  406ff425ec228f5b99a61576d8a576fbc76b3122 (commit)
       via  2db67df75178a2bc407417540dde75e895eb619b (commit)
       via  0da13ea71e97d62ee8cffeac9613a59f6dd07335 (commit)
       via  c95b25359f139a816f114378872bc08ab7e6aef2 (commit)
       via  91a8eaa5b51c15b6d8f6cad3d75597678eeb03f1 (commit)
       via  7f5705228eb95dba445d0d1d8297c6de0306360b (commit)
       via  7abd3c8866212a9c29920ee82be7390ab2499c43 (commit)
       via  3c66bce7b2da45b00fa54358ef0728c0ab2e0a71 (commit)
       via  a7cbba998123e58d89a9d0da2d77afa184ea357e (commit)
       via  13a3b9f237b1e29489804ccb5a25612cbea511f4 (commit)
       via  0c25e92e3d59db2012909c9bb4c9663e220a9522 (commit)
       via  3cddfb8bd95782eba2fa6c5935a9298500dcdd6a (commit)
       via  f059f96f5c390ffe7a3835ada1e4c6be4502985b (commit)
       via  b786dfa77ef76b5486367a40dfcc5f8b8681db52 (commit)
       via  10b0c31c6762cfde1e466039d18e159a601f867f (commit)
       via  691d3b625c7299fc9b7d58cb855615b9b11fe393 (commit)
       via  8a2edd0d1909bfdb0f7ddbdfb953eac95c987f2f (commit)
       via  3337b581fafb8c7a747badfabb84b2ad01007ae6 (commit)
       via  f16f9856058ea2dca3b0be5a964fc3926ee6f395 (commit)
       via  7b51465130cede5f68994d4535b86ece6cc2f11d (commit)
       via  36fe3ea99b743875476fa2b229a5f60502b745a4 (commit)
       via  c02f78e1a0757e0590e81f6486007deed34f2d4d (commit)
       via  fcf3cdd41a35c9c96e95272bc5d817b0457ab8dc (commit)
       via  bf5f7b5f56ed649f78206fa2c3b4f9acdee1e310 (commit)
       via  a463f3d1b7b87d98f8c84d9d93a9fbd0285c53b9 (commit)
       via  5607fdf2add21db2a5e080fdc6b146dea58e15cc (commit)
       via  f3d4565f0bf0839c6b5d3cfd8ae8ee48dbc20732 (commit)
       via  5cd467d3209b8665c21fad038e79c5063ca95798 (commit)
       via  2ebf994dfcb704b90f9b1dcccbf56a4b38c84a36 (commit)
       via  2ad85af5e37a9e393903a8c0b3589b9c086c16ec (commit)
       via  d588ec8aafe64a586cc1bccab6be4a9fc50886d4 (commit)
       via  0afb9dde6cef8abfa38935e85030ab975002cf91 (commit)
       via  614ecf7cba243e94331171cc8d1761a99ebcab0f (commit)
       via  f7f035851da68ff926341eba4cad497ffa476115 (commit)
       via  90980c5054e54aa637969ad59e2dc16b351d4488 (commit)
       via  086b420bfba5b31f13e2828ca1be842a8faad4aa (commit)
       via  f33c6b79fa27d93bcf9be45aa6844e1c26f35a97 (commit)
       via  ef67acec41e9b83d4aacff8de12e6a3e37628227 (commit)
       via  184c6c7068ec90f2a85a859bfa56d779a4294382 (commit)
       via  7c7c776676860ba64571154faef440093799e996 (commit)
       via  db105a36beb8999996f9e4853a2cceadb074775f (commit)
       via  0061de28095c4ef166de784c12366adabc8a6636 (commit)
       via  f64a1962849e0773c33f95b541daf18a95911265 (commit)
       via  25530c6ca2483cf92aab7f240c7fa70bd3c5b3a8 (commit)
       via  33f0b8420f858e389d2fb0f27ddde1fa77287dc7 (commit)
       via  e8edbcf573f453fd1f72a3b899eaad9d195cc37c (commit)
       via  c31de72c76258d7444acc1f1ae1f30024b772af7 (commit)
       via  14824b3a8199caabbf76a5c7b933715a0f0c4fec (commit)
       via  cf32a344e6ed4eb7b5f2fcfb5c4d91a2d972d7d3 (commit)
       via  ff385d569e9a9b13c3c174b7d55f1916c75ce1df (commit)
       via  7099d4df871fb6186a64157c13b92b2d8b56de0e (commit)
       via  1b662b8dcab972fd07a7869b6b9108a8d128796f (commit)
       via  330e485343c4f3054146046dac4b98c937c35269 (commit)
       via  9851a189c6456b6ce5054cc4c9eea10a6866ce0f (commit)
       via  359efa4b2178e6929e9226fdf1bce782fce0c002 (commit)
       via  88effdc1b00be8636d40ef84f5556de60013347f (commit)
      from  ed89ba2b9f20a5aad5f213191f9b34981aa6a0b9 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                          |   37 ++-
 src/bin/auth/auth.spec.pre.in                      |   71 +++---
 src/bin/bindctl/bindcmd.py                         |  243 ++++++++++------
 src/bin/bindctl/bindctl-source.py.in               |   64 +++--
 src/bin/bindctl/cmdparse.py                        |   56 ++++-
 src/bin/bindctl/moduleinfo.py                      |   68 ++++-
 src/bin/bindctl/tests/Makefile.am                  |    2 +-
 src/bin/bindctl/tests/bindctl_test.py              |   96 ++++++-
 src/bin/bindctl/tests/cmdparse_test.py             |   88 ++++++
 src/bin/resolver/resolver.spec.pre.in              |    2 +-
 src/lib/asiolink/recursive_query.cc                |    7 +-
 src/lib/cache/TODO                                 |    2 +
 src/lib/cache/message_entry.cc                     |    2 +-
 src/lib/cache/message_entry.h                      |   11 +-
 src/lib/cache/resolver_cache.cc                    |    3 -
 src/lib/cache/rrset_cache.cc                       |    3 +-
 src/lib/cache/tests/Makefile.am                    |    9 +
 src/lib/cache/tests/cache_test_sectioncount.h      |    2 +-
 src/lib/cache/tests/rrset_entry_unittest.cc        |    3 +-
 src/lib/config/tests/testdata/spec22.spec          |    4 +-
 src/lib/datasrc/memory_datasrc.cc                  |  116 +++++++-
 src/lib/datasrc/tests/memory_datasrc_unittest.cc   |  322 ++++++++++++++++++--
 src/lib/dns/Makefile.am                            |    3 +
 src/lib/dns/rdata/generic/detail/nsec_bitmap.cc    |   78 +++++
 src/lib/dns/rdata/generic/detail/nsec_bitmap.h     |   51 +++
 src/lib/dns/rdata/generic/nsec3_50.cc              |  118 ++++---
 src/lib/dns/rdata/generic/nsec3_50.h               |    3 +-
 src/lib/dns/rdata/generic/nsec_47.cc               |   40 +---
 src/lib/dns/tests/Makefile.am                      |    1 +
 src/lib/dns/tests/rdata_mx_unittest.cc             |    5 +-
 src/lib/dns/tests/rdata_nsec3_unittest.cc          |  127 ++++++--
 src/lib/dns/tests/rdata_nsec_unittest.cc           |   41 +---
 ...ec_unittest.cc => rdata_nsecbitmap_unittest.cc} |   97 +++----
 src/lib/dns/tests/testdata/Makefile.am             |   25 ++-
 src/lib/dns/tests/testdata/gen-wiredata.py.in      |   81 ++++-
 .../{rdata_mx_toWire1 => rdata_mx_toWire2}         |    4 +-
 .../dns/tests/testdata/rdata_nsec3_fromWire1.spec  |    7 +
 .../dns/tests/testdata/rdata_nsec3_fromWire10.spec |    8 +
 .../dns/tests/testdata/rdata_nsec3_fromWire11.spec |    8 +
 .../dns/tests/testdata/rdata_nsec3_fromWire12.spec |    9 +
 .../dns/tests/testdata/rdata_nsec3_fromWire13.spec |    9 +
 .../dns/tests/testdata/rdata_nsec3_fromWire14.spec |    9 +
 .../dns/tests/testdata/rdata_nsec3_fromWire15.spec |   10 +
 src/lib/dns/tests/testdata/rdata_nsec3_fromWire2   |   12 -
 .../dns/tests/testdata/rdata_nsec3_fromWire2.spec  |    9 +
 .../dns/tests/testdata/rdata_nsec3_fromWire4.spec  |    9 +
 ...c_fromWire5.spec => rdata_nsec3_fromWire5.spec} |    8 +-
 .../dns/tests/testdata/rdata_nsec3_fromWire6.spec  |   11 +
 ...c_fromWire7.spec => rdata_nsec3_fromWire7.spec} |    6 +-
 ...c_fromWire8.spec => rdata_nsec3_fromWire8.spec} |    6 +-
 .../dns/tests/testdata/rdata_nsec3_fromWire9.spec  |   10 +
 src/lib/log/Makefile.am                            |   12 +-
 src/lib/log/compiler/message.cc                    |  270 +++++++++++------
 src/lib/log/{dbglevels.h => debug_levels.h}        |   10 +-
 src/lib/log/documentation.txt                      |  205 ++++++++-----
 src/lib/log/filename.cc                            |    8 +-
 src/lib/log/filename.h                             |   12 +-
 src/lib/log/logger.cc                              |  316 ++++++--------------
 src/lib/log/logger.h                               |  265 ++++++-----------
 src/lib/log/logger_impl.cc                         |  221 ++++++++++++++
 src/lib/log/logger_impl.h                          |  267 ++++++++++++++++
 src/lib/log/{logger.cc => logger_impl_log4cxx.cc}  |   98 +-----
 src/lib/log/{logger.h => logger_impl_log4cxx.h}    |  178 +++++------
 .../log/{message_exception.cc => logger_levels.h}  |   32 ++-
 src/lib/log/logger_support.cc                      |   58 +++--
 src/lib/log/logger_support.h                       |   21 +-
 src/lib/log/message_dictionary.cc                  |   51 ++--
 src/lib/log/message_dictionary.h                   |   70 ++++-
 src/lib/log/message_exception.cc                   |    2 -
 src/lib/log/message_exception.h                    |    4 +-
 src/lib/log/message_initializer.cc                 |   20 +-
 src/lib/log/message_initializer.h                  |   20 +-
 src/lib/log/message_reader.cc                      |   91 +++++--
 src/lib/log/message_reader.h                       |   43 +++-
 src/lib/log/message_types.h                        |   13 +-
 src/lib/log/messagedef.cc                          |   46 +++-
 src/lib/log/messagedef.h                           |   36 ++-
 src/lib/log/messagedef.mes                         |   61 +++-
 src/lib/log/root_logger_name.cc                    |   26 ++-
 src/lib/log/root_logger_name.h                     |   52 +---
 src/lib/log/strutil.cc                             |   11 +-
 src/lib/log/strutil.h                              |    8 +-
 src/lib/log/tests/Makefile.am                      |    6 +-
 src/lib/log/tests/filename_unittest.cc             |    4 +-
 src/lib/log/tests/localdef.mes                     |   23 --
 src/lib/log/tests/logger_impl_log4cxx_unittest.cc  |   91 ++++++
 src/lib/log/tests/logger_support_test.cc           |   45 ++--
 src/lib/log/tests/logger_unittest.cc               |  202 +++++--------
 src/lib/log/tests/message_dictionary_unittest.cc   |   58 +++-
 src/lib/log/tests/message_initializer_unittest.cc  |   18 +-
 .../log/tests/message_initializer_unittest_2.cc    |    4 +-
 src/lib/log/tests/message_reader_unittest.cc       |   62 +++-
 src/lib/log/tests/root_logger_name_unittest.cc     |   12 +-
 src/lib/log/tests/run_time_init_test.sh.in         |   34 ++-
 src/lib/log/tests/run_unittests.cc                 |    4 +-
 src/lib/log/tests/strutil_unittest.cc              |    4 +-
 src/lib/log/tests/xdebuglevel_unittest.cc          |    6 +-
 src/lib/log/xdebuglevel.cc                         |   28 +-
 src/lib/log/xdebuglevel.h                          |    6 +-
 src/lib/python/isc/cc/data.py                      |   10 +-
 src/lib/python/isc/config/ccsession.py             |   22 ++-
 src/lib/python/isc/config/config_data.py           |  181 ++++++++----
 .../python/isc/config/tests/config_data_test.py    |   76 ++++--
 103 files changed, 3613 insertions(+), 1826 deletions(-)
 create mode 100644 src/bin/bindctl/tests/cmdparse_test.py
 create mode 100644 src/lib/dns/rdata/generic/detail/nsec_bitmap.cc
 create mode 100644 src/lib/dns/rdata/generic/detail/nsec_bitmap.h
 copy src/lib/dns/tests/{rdata_nsec_unittest.cc => rdata_nsecbitmap_unittest.cc} (61%)
 copy src/lib/dns/tests/testdata/{rdata_mx_toWire1 => rdata_mx_toWire2} (76%)
 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire1.spec
 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.spec
 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.spec
 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.spec
 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.spec
 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.spec
 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.spec
 delete mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire2
 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.spec
 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.spec
 copy src/lib/dns/tests/testdata/{rdata_nsec_fromWire5.spec => rdata_nsec3_fromWire5.spec} (56%)
 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.spec
 copy src/lib/dns/tests/testdata/{rdata_nsec_fromWire7.spec => rdata_nsec3_fromWire7.spec} (54%)
 copy src/lib/dns/tests/testdata/{rdata_nsec_fromWire8.spec => rdata_nsec3_fromWire8.spec} (51%)
 create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.spec
 rename src/lib/log/{dbglevels.h => debug_levels.h} (89%)
 create mode 100644 src/lib/log/logger_impl.cc
 create mode 100644 src/lib/log/logger_impl.h
 copy src/lib/log/{logger.cc => logger_impl_log4cxx.cc} (76%)
 copy src/lib/log/{logger.h => logger_impl_log4cxx.h} (70%)
 copy src/lib/log/{message_exception.cc => logger_levels.h} (56%)
 delete mode 100644 src/lib/log/tests/localdef.mes
 create mode 100644 src/lib/log/tests/logger_impl_log4cxx_unittest.cc

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 9cf9b3b..7de9c19 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,15 +1,38 @@
+  172.  [func]      jelte
+	Improved the bindctl cli in various ways, mainly concerning
+	list and map item addressing, the correct display of actual values,
+	and internal help.
+	(Trac #384, git e5fb3bc1ed5f3c0aec6eb40a16c63f3d0fc6a7b2)
+
+  171.  [func]      feng, jerry, jinmei, vorner
+	b10-auth, src/lib/datasrc: in memory data source now works as a
+	complete data source for authoritative DNS servers and b10-auth
+	uses it.  It still misses major features, however, including
+	DNSSEC support and zone transfer.
+	(Last trac #552, but many more,
+	git 6f031a09a248e7684723c000f3e8cc981dcdb349)
+
+  170.	[bug]		jinmei
+	Tightened validity checks in the NSEC3 constructors, both "from
+	"text" and "from wire".  Specifically, wire data containing
+	invalid type bitmaps or invalid lengths of salt or hash is now
+	correctly rejected.
+	(Trac #117, git 9c690982f24fef19c747a72f43c4298333a58f48)
+
   169.  [func]      zhang likun, jelte
 	Added a basic implementation for a resolver cache (though not
 	used yet).
 	(Trac #449, git 8aa3b2246ae095bbe7f855fd11656ae3bdb98986)
 
   168.  [bug]       vorner
-	Boss no longer has the -f argument, which was undocumented and stayed as
-	a relict of previous versions, currently causing only strange behaviour.
+	Boss no longer has the -f argument, which was undocumented and
+	stayed as a relict of previous versions, currently causing only
+	strange behaviour.
 	(Trac #572, git 17f237478961005707d649a661cc72a4a0d612d4)
 
   167.  [bug]           naokikambe
-	Fixed failure of termination of msgq_test.py with python3 coverage(3.3.1)
+	Fixed failure of termination of msgq_test.py with python3
+	coverage(3.3.1)
 	(Trac #573, git 0e6a18e12f61cc482e07078776234f32605312e5)
 
   166.  [func]      jelte
@@ -30,10 +53,10 @@
 	(Trac #452, git c9f6acc81e24c4b8f0eb351123dc7b43f64e0914)
 
   163.  [func]      vorner
-	The pimpl design pattern is used in UDPServer, with a shared pointer. This
-	makes it smaller to copy (which is done a lot as a sideeffect of being
-	coroutine) and speeds applications of this class (notably b10-auth) up by
-	around 10%.
+	The pimpl design pattern is used in UDPServer, with a shared
+	pointer. This makes it smaller to copy (which is done a lot as a
+	sideeffect of being coroutine) and speeds applications of this
+	class (notably b10-auth) up by around 10%.
 	(Trac #537, git 94cb95b1d508541201fc064302ba836164d3cbe6)
 
   162.  [func]		stephen
diff --git a/src/bin/auth/auth.spec.pre.in b/src/bin/auth/auth.spec.pre.in
index 8a77455..7cb571c 100644
--- a/src/bin/auth/auth.spec.pre.in
+++ b/src/bin/auth/auth.spec.pre.in
@@ -12,45 +12,44 @@
         "item_type": "list",
         "item_optional": true,
         "item_default": [],
-	"list_item_spec": {
-          "item_name": "list_element",
+        "list_item_spec":
+        { "item_name": "list_element",
           "item_type": "map",
           "item_optional": false,
           "item_default": {},
-	  "map_item_spec": [
-	    { "item_name": "type",
-	      "item_type": "string",
-	      "item_optional": false,
-	      "item_default": ""
-	    },
-	    { "item_name": "class",
-	      "item_type": "string",
-	      "item_optional": false,
-	      "item_default": "IN"
-	    },
-	    { "item_name": "zones",
-	      "item_type": "list",
-	      "item_optional": false,
-	      "item_default": [],
-	      "list_item_spec": {
-	        "item_name": "list_element",
-	        "item_type": "map",
-	        "item_optional": true,
-	        "map_item_spec": [
-		  { "item_name": "origin",
-		    "item_type": "string",
-		    "item_optional": false,
-		    "item_default": ""
-		  },
-		  { "item_name": "file",
-		    "item_type": "string",
-		    "item_optional": false,
-		    "item_default": ""
-		  }
-		]
-	      }
-	    }
-	  ]
+          "map_item_spec": [
+          { "item_name": "type",
+            "item_type": "string",
+            "item_optional": false,
+            "item_default": ""
+          },
+          { "item_name": "class",
+            "item_type": "string",
+            "item_optional": false,
+            "item_default": "IN"
+          },
+          { "item_name": "zones",
+            "item_type": "list",
+            "item_optional": false,
+            "item_default": [],
+            "list_item_spec":
+            { "item_name": "list_element",
+              "item_type": "map",
+              "item_optional": true,
+              "item_default": { "origin": "", "file": "" },
+              "map_item_spec": [
+              { "item_name": "origin",
+                "item_type": "string",
+                "item_optional": false,
+                "item_default": ""
+              },
+              { "item_name": "file",
+                "item_type": "string",
+                "item_optional": false,
+                "item_default": ""
+              }]
+            }
+          }]
         }
       },
       { "item_name": "statistics-interval",
diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py
index fb6a892..683dda9 100644
--- a/src/bin/bindctl/bindcmd.py
+++ b/src/bin/bindctl/bindcmd.py
@@ -51,7 +51,6 @@ except ImportError:
     my_readline = sys.stdin.readline
 
 CSV_FILE_NAME = 'default_user.csv'
-FAIL_TO_CONNECT_WITH_CMDCTL = "Fail to connect with b10-cmdctl module, is it running?"
 CONFIG_MODULE_NAME = 'config'
 CONST_BINDCTL_HELP = """
 usage: <module name> <command name> [param1 = value1 [, param2 = value2]]
@@ -92,10 +91,13 @@ class BindCmdInterpreter(Cmd):
         Cmd.__init__(self)
         self.location = ""
         self.prompt_end = '> '
-        self.prompt = self.prompt_end
+        if sys.stdin.isatty():
+            self.prompt = self.prompt_end
+        else:
+            self.prompt = ""
         self.ruler = '-'
         self.modules = OrderedDict()
-        self.add_module_info(ModuleInfo("help", desc = "Get help for bindctl"))
+        self.add_module_info(ModuleInfo("help", desc = "Get help for bindctl."))
         self.server_port = server_port
         self.conn = ValidatedHTTPSConnection(self.server_port,
                                              ca_certs=pem_file)
@@ -119,8 +121,8 @@ class BindCmdInterpreter(Cmd):
 
             self.cmdloop()
         except FailToLogin as err:
-            print(err)
-            print(FAIL_TO_CONNECT_WITH_CMDCTL)
+            # error already printed when this was raised, ignoring
+            pass
         except KeyboardInterrupt:
             print('\nExit from bindctl')
 
@@ -270,8 +272,10 @@ class BindCmdInterpreter(Cmd):
         return line 
 
     def postcmd(self, stop, line):
-        '''Update the prompt after every command'''
-        self.prompt = self.location + self.prompt_end
+        '''Update the prompt after every command, but only if we
+           have a tty as output'''
+        if sys.stdin.isatty():
+            self.prompt = self.location + self.prompt_end
         return stop
 
     def _prepare_module_commands(self, module_spec):
@@ -375,7 +379,14 @@ class BindCmdInterpreter(Cmd):
         if cmd.command == "help" or ("help" in cmd.params.keys()):
             self._handle_help(cmd)
         elif cmd.module == CONFIG_MODULE_NAME:
-            self.apply_config_cmd(cmd)
+            try:
+                self.apply_config_cmd(cmd)
+            except isc.cc.data.DataTypeError as dte:
+                print("Error: " + str(dte))
+            except isc.cc.data.DataNotFoundError as dnfe:
+                print("Error: " + str(dnfe))
+            except KeyError as ke:
+                print("Error: missing " + str(ke))
         else:
             self.apply_cmd(cmd)
 
@@ -396,9 +407,24 @@ class BindCmdInterpreter(Cmd):
 
     def do_help(self, name):
         print(CONST_BINDCTL_HELP)
-        for k in self.modules.keys():
-            print("\t", self.modules[k])
-                
+        for k in self.modules.values():
+            n = k.get_name()
+            if len(n) >= CONST_BINDCTL_HELP_INDENT_WIDTH:
+                print("    %s" % n)
+                print(textwrap.fill(k.get_desc(),
+                      initial_indent="            ",
+                      subsequent_indent="    " +
+                      " " * CONST_BINDCTL_HELP_INDENT_WIDTH,
+                      width=70))
+            else:
+                print(textwrap.fill("%s%s%s" %
+                    (k.get_name(),
+                     " "*(CONST_BINDCTL_HELP_INDENT_WIDTH - len(k.get_name())),
+                     k.get_desc()),
+                    initial_indent="    ",
+                    subsequent_indent="    " +
+                    " " * CONST_BINDCTL_HELP_INDENT_WIDTH,
+                    width=70))
     
     def onecmd(self, line):
         if line == 'EOF' or line.lower() == "quit":
@@ -411,7 +437,19 @@ class BindCmdInterpreter(Cmd):
         Cmd.onecmd(self, line)
 
     def remove_prefix(self, list, prefix):
-        return [(val[len(prefix):]) for val in list]
+        """Removes the prefix already entered, and all elements from the
+           list that don't match it"""
+        if prefix.startswith('/'):
+            prefix = prefix[1:]
+
+        new_list = []
+        for val in list:
+            if val.startswith(prefix):
+                new_val = val[len(prefix):]
+                if new_val.startswith("/"):
+                    new_val = new_val[1:]
+                new_list.append(new_val)
+        return new_list
 
     def complete(self, text, state):
         if 0 == state:
@@ -502,8 +540,7 @@ class BindCmdInterpreter(Cmd):
             self._validate_cmd(cmd)
             self._handle_cmd(cmd)
         except (IOError, http.client.HTTPException) as err:
-            print('Error!', err)
-            print(FAIL_TO_CONNECT_WITH_CMDCTL)
+            print('Error: ', err)
         except BindCtlException as err:
             print("Error! ", err)
             self._print_correct_usage(err)
@@ -541,87 +578,115 @@ class BindCmdInterpreter(Cmd):
            Raises a KeyError if the command was not complete
         '''
         identifier = self.location
-        try:
-            if 'identifier' in cmd.params:
-                if not identifier.endswith("/"):
-                    identifier += "/"
-                if cmd.params['identifier'].startswith("/"):
-                    identifier = cmd.params['identifier']
-                else:
-                    identifier += cmd.params['identifier']
-
-                # Check if the module is known; for unknown modules
-                # we currently deny setting preferences, as we have
-                # no way yet to determine if they are ok.
-                module_name = identifier.split('/')[1]
-                if self.config_data is None or \
-                   not self.config_data.have_specification(module_name):
-                    print("Error: Module '" + module_name + "' unknown or not running")
-                    return
+        if 'identifier' in cmd.params:
+            if not identifier.endswith("/"):
+                identifier += "/"
+            if cmd.params['identifier'].startswith("/"):
+                identifier = cmd.params['identifier']
+            else:
+                if cmd.params['identifier'].startswith('['):
+                    identifier = identifier[:-1]
+                identifier += cmd.params['identifier']
+
+            # Check if the module is known; for unknown modules
+            # we currently deny setting preferences, as we have
+            # no way yet to determine if they are ok.
+            module_name = identifier.split('/')[1]
+            if module_name != "" and (self.config_data is None or \
+               not self.config_data.have_specification(module_name)):
+                print("Error: Module '" + module_name + "' unknown or not running")
+                return
 
-            if cmd.command == "show":
-                values = self.config_data.get_value_maps(identifier)
-                for value_map in values:
-                    line = value_map['name']
-                    if value_map['type'] in [ 'module', 'map', 'list' ]:
-                        line += "/"
-                    else:
-                        line += ":\t" + json.dumps(value_map['value'])
-                    line += "\t" + value_map['type']
-                    line += "\t"
-                    if value_map['default']:
-                        line += "(default)"
-                    if value_map['modified']:
-                        line += "(modified)"
-                    print(line)
-            elif cmd.command == "add":
-                self.config_data.add_value(identifier, cmd.params['value'])
-            elif cmd.command == "remove":
-                if 'value' in cmd.params:
-                    self.config_data.remove_value(identifier, cmd.params['value'])
+        if cmd.command == "show":
+            # check if we have the 'all' argument
+            show_all = False
+            if 'argument' in cmd.params:
+                if cmd.params['argument'] == 'all':
+                    show_all = True
+                elif 'identifier' not in cmd.params:
+                    # no 'all', no identifier, assume this is the
+                    #identifier
+                    identifier += cmd.params['argument']
                 else:
-                    self.config_data.remove_value(identifier, None)
-            elif cmd.command == "set":
-                if 'identifier' not in cmd.params:
-                    print("Error: missing identifier or value")
+                    print("Error: unknown argument " + cmd.params['argument'] + ", or multiple identifiers given")
+                    return
+            values = self.config_data.get_value_maps(identifier, show_all)
+            for value_map in values:
+                line = value_map['name']
+                if value_map['type'] in [ 'module', 'map' ]:
+                    line += "/"
+                elif value_map['type'] == 'list' \
+                     and value_map['value'] != []:
+                    # do not print content of non-empty lists if
+                    # we have more data to show
+                    line += "/"
                 else:
-                    parsed_value = None
-                    try:
-                        parsed_value = json.loads(cmd.params['value'])
-                    except Exception as exc:
-                        # ok could be an unquoted string, interpret as such
-                        parsed_value = cmd.params['value']
-                    self.config_data.set_value(identifier, parsed_value)
-            elif cmd.command == "unset":
-                self.config_data.unset(identifier)
-            elif cmd.command == "revert":
-                self.config_data.clear_local_changes()
-            elif cmd.command == "commit":
-                self.config_data.commit()
-            elif cmd.command == "diff":
-                print(self.config_data.get_local_changes());
-            elif cmd.command == "go":
-                self.go(identifier)
-        except isc.cc.data.DataTypeError as dte:
-            print("Error: " + str(dte))
-        except isc.cc.data.DataNotFoundError as dnfe:
-            print("Error: " + identifier + " not found")
-        except KeyError as ke:
-            print("Error: missing " + str(ke))
-            raise ke
+                    line += "\t" + json.dumps(value_map['value'])
+                line += "\t" + value_map['type']
+                line += "\t"
+                if value_map['default']:
+                    line += "(default)"
+                if value_map['modified']:
+                    line += "(modified)"
+                print(line)
+        elif cmd.command == "show_json":
+            if identifier == "":
+                print("Need at least the module to show the configuration in JSON format")
+            else:
+                data, default = self.config_data.get_value(identifier)
+                print(json.dumps(data))
+        elif cmd.command == "add":
+            if 'value' in cmd.params:
+                self.config_data.add_value(identifier, cmd.params['value'])
+            else:
+                self.config_data.add_value(identifier)
+        elif cmd.command == "remove":
+            if 'value' in cmd.params:
+                self.config_data.remove_value(identifier, cmd.params['value'])
+            else:
+                self.config_data.remove_value(identifier, None)
+        elif cmd.command == "set":
+            if 'identifier' not in cmd.params:
+                print("Error: missing identifier or value")
+            else:
+                parsed_value = None
+                try:
+                    parsed_value = json.loads(cmd.params['value'])
+                except Exception as exc:
+                    # ok could be an unquoted string, interpret as such
+                    parsed_value = cmd.params['value']
+                self.config_data.set_value(identifier, parsed_value)
+        elif cmd.command == "unset":
+            self.config_data.unset(identifier)
+        elif cmd.command == "revert":
+            self.config_data.clear_local_changes()
+        elif cmd.command == "commit":
+            self.config_data.commit()
+        elif cmd.command == "diff":
+            print(self.config_data.get_local_changes());
+        elif cmd.command == "go":
+            self.go(identifier)
 
     def go(self, identifier):
         '''Handles the config go command, change the 'current' location
-           within the configuration tree'''
-        # this is just to see if it exists
-        self.config_data.get_value(identifier)
-        # some sanitizing
-        identifier = identifier.replace("//", "/")
-        if not identifier.startswith("/"):
-            identifier = "/" + identifier
-        if identifier.endswith("/"):
-            identifier = identifier[:-1]
-        self.location = identifier
+           within the configuration tree. '..' will be interpreted as
+           'up one level'.'''
+        id_parts = isc.cc.data.split_identifier(identifier)
+
+        new_location = ""
+        for id_part in id_parts:
+            if (id_part == ".."):
+                # go 'up' one level
+                new_location, a, b = new_location.rpartition("/")
+            else:
+                new_location += "/" + id_part
+        # check if exists, if not, revert and error
+        v,d = self.config_data.get_value(new_location)
+        if v is None:
+            print("Error: " + identifier + " not found")
+            return
+
+        self.location = new_location
 
     def apply_cmd(self, cmd):
         '''Handles a general module command'''
diff --git a/src/bin/bindctl/bindctl-source.py.in b/src/bin/bindctl/bindctl-source.py.in
index 83059d2..2e9d513 100644
--- a/src/bin/bindctl/bindctl-source.py.in
+++ b/src/bin/bindctl/bindctl-source.py.in
@@ -33,51 +33,60 @@ isc.util.process.rename()
 # number, and the overall BIND 10 version number (set in configure.ac).
 VERSION = "bindctl 20101201 (BIND 10 @PACKAGE_VERSION@)"
 
+DEFAULT_IDENTIFIER_DESC = "The identifier specifies the config item. Child elements are separated with the '/' character. List indices can be specified with '[i]', where i is an integer specifying the index, starting with 0. Examples: 'Boss/start_auth', 'Recurse/listen_on[0]/address'. If no identifier is given, shows the item at the current location."
+
 def prepare_config_commands(tool):
     '''Prepare fixed commands for local configuration editing'''
-    module = ModuleInfo(name = CONFIG_MODULE_NAME, desc = "Configuration commands")
-    cmd = CommandInfo(name = "show", desc = "Show configuration")
-    param = ParamInfo(name = "identifier", type = "string", optional=True)
+    module = ModuleInfo(name = CONFIG_MODULE_NAME, desc = "Configuration commands.")
+    cmd = CommandInfo(name = "show", desc = "Show configuration.")
+    param = ParamInfo(name = "argument", type = "string", optional=True, desc = "If you specify the argument 'all' (before the identifier), recursively show all child elements for the given identifier.")
+    cmd.add_param(param)
+    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
+    cmd.add_param(param)
+    module.add_command(cmd)
+
+    cmd = CommandInfo(name = "show_json", desc = "Show full configuration in JSON format.")
+    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "add", desc = "Add entry to configuration list")
-    param = ParamInfo(name = "identifier", type = "string", optional=True)
+    cmd = CommandInfo(name = "add", desc = "Add an entry to configuration list. If no value is given, a default value is added.")
+    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
-    param = ParamInfo(name = "value", type = "string", optional=False)
+    param = ParamInfo(name = "value", type = "string", optional=True, desc = "Specifies a value to add to the list. It must be in correct JSON format and complete.")
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "remove", desc = "Remove entry from configuration list")
-    param = ParamInfo(name = "identifier", type = "string", optional=True)
+    cmd = CommandInfo(name = "remove", desc = "Remove entry from configuration list.")
+    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
-    param = ParamInfo(name = "value", type = "string", optional=True)
+    param = ParamInfo(name = "value", type = "string", optional=True, desc = "Specifies a value to remove from the list. It must be in correct JSON format and complete.")
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "set", desc = "Set a configuration value")
-    param = ParamInfo(name = "identifier", type = "string", optional=True)
+    cmd = CommandInfo(name = "set", desc = "Set a configuration value.")
+    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
-    param = ParamInfo(name = "value", type = "string", optional=False)
+    param = ParamInfo(name = "value", type = "string", optional=False, desc = "Specifies a value to set. It must be in correct JSON format and complete.")
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "unset", desc = "Unset a configuration value")
-    param = ParamInfo(name = "identifier", type = "string", optional=False)
+    cmd = CommandInfo(name = "unset", desc = "Unset a configuration value (i.e. revert to the default, if any).")
+    param = ParamInfo(name = "identifier", type = "string", optional=False, desc = DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "diff", desc = "Show all local changes")
+    cmd = CommandInfo(name = "diff", desc = "Show all local changes that have not been committed.")
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "revert", desc = "Revert all local changes")
+    cmd = CommandInfo(name = "revert", desc = "Revert all local changes.")
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "commit", desc = "Commit all local changes")
+    cmd = CommandInfo(name = "commit", desc = "Commit all local changes.")
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "go", desc = "Go to a specific configuration part")
-    param = ParamInfo(name = "identifier", type="string", optional=False)
+    cmd = CommandInfo(name = "go", desc = "Go to a specific configuration part.")
+    param = ParamInfo(name = "identifier", type="string", optional=False, desc = DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
     module.add_command(cmd)
 
@@ -115,15 +124,12 @@ def set_bindctl_options(parser):
                       help = 'PEM formatted server certificate validation chain file')
 
 if __name__ == '__main__':
-    try:
-        parser = OptionParser(version = VERSION)
-        set_bindctl_options(parser)
-        (options, args) = parser.parse_args()
-        server_addr = options.addr + ':' + str(options.port)
-        tool = BindCmdInterpreter(server_addr, pem_file=options.cert_chain)
-        prepare_config_commands(tool)
-        tool.run()
-    except Exception as e:
-        print(e, "\nFailed to connect with b10-cmdctl module, is it running?")
+    parser = OptionParser(version = VERSION)
+    set_bindctl_options(parser)
+    (options, args) = parser.parse_args()
+    server_addr = options.addr + ':' + str(options.port)
+    tool = BindCmdInterpreter(server_addr, pem_file=options.cert_chain)
+    prepare_config_commands(tool)
+    tool.run()
 
 
diff --git a/src/bin/bindctl/cmdparse.py b/src/bin/bindctl/cmdparse.py
index ab891d7..c624cba 100644
--- a/src/bin/bindctl/cmdparse.py
+++ b/src/bin/bindctl/cmdparse.py
@@ -33,6 +33,7 @@ param_value_str  = "(?P<param_value>[^\'\" ][^, ]+)"
 param_value_with_quota_str  = "[\"\'](?P<param_value>.+?)(?<!\\\)[\"\']"
 next_params_str = "(?P<blank>\s*)(?P<comma>,?)(?P<next_params>.*)$"
 
+
 PARAM_WITH_QUOTA_PATTERN = re.compile(param_name_str + 
                                       param_value_with_quota_str + 
                                       next_params_str)
@@ -40,8 +41,58 @@ PARAM_PATTERN = re.compile(param_name_str + param_value_str + next_params_str)
 # Used for module and command name
 NAME_PATTERN = re.compile("^\s*(?P<name>[\w]+)(?P<blank>\s*)(?P<others>.*)$")
 
+# this removes all whitespace in the given string, except when
+# between " quotes
+_remove_unquoted_whitespace = \
+    lambda text:'"'.join( it if i%2 else ''.join(it.split())
+        for i,it in enumerate(text.split('"'))  )
+
+
+def _remove_list_and_map_whitespace(text):
+    """Returns a string where the whitespace between matching [ and ]
+       is removed, unless quoted"""
+    # regular expression aren't really the right tool, since we may have
+    # nested structures
+    result = []
+    start_pos = 0
+    pos = 0
+    list_count = 0
+    map_count = 0
+    cur_start_list_pos = None
+    cur_start_map_pos = None
+    for i in text:
+        if i == '[' and map_count == 0:
+            if list_count == 0:
+                result.append(text[start_pos:pos + 1])
+                cur_start_list_pos = pos + 1
+            list_count = list_count + 1
+        elif i == ']' and map_count == 0:
+            if list_count > 0:
+                list_count = list_count - 1
+                if list_count == 0:
+                    result.append(_remove_unquoted_whitespace(text[cur_start_list_pos:pos + 1]))
+                    start_pos = pos + 1
+        if i == '{' and list_count == 0:
+            if map_count == 0:
+                result.append(text[start_pos:pos + 1])
+                cur_start_map_pos = pos + 1
+            map_count = map_count + 1
+        elif i == '}' and list_count == 0:
+            if map_count > 0:
+                map_count = map_count - 1
+                if map_count == 0:
+                    result.append(_remove_unquoted_whitespace(text[cur_start_map_pos:pos + 1]))
+                    start_pos = pos + 1
+        
+
+        pos = pos + 1
+    if start_pos <= len(text):
+        result.append(text[start_pos:len(text)])
+    return "".join(result)
+    
+    
 class BindCmdParse:
-    """ This class will parse the command line usr input into three part
+    """ This class will parse the command line user input into three parts:
     module name, command, parameters
     the first two parts are strings and parameter is one hash, 
     parameters part is optional
@@ -86,9 +137,12 @@ class BindCmdParse:
 
             self._parse_params(param_str)
 
+    def _remove_list_whitespace(self, text):
+        return ""
 
     def _parse_params(self, param_text):
         """convert a=b,c=d into one hash """
+        param_text = _remove_list_and_map_whitespace(param_text)
         
         # Check parameter name "help"
         param = NAME_PATTERN.match(param_text)
diff --git a/src/bin/bindctl/moduleinfo.py b/src/bin/bindctl/moduleinfo.py
index 015ef16..6e41dce 100644
--- a/src/bin/bindctl/moduleinfo.py
+++ b/src/bin/bindctl/moduleinfo.py
@@ -16,6 +16,8 @@
 """This module holds classes representing modules, commands and
    parameters for use in bindctl"""
 
+import textwrap
+
 try:
     from collections import OrderedDict
 except ImportError:
@@ -30,6 +32,9 @@ MODULE_NODE_NAME = 'module'
 COMMAND_NODE_NAME = 'command'
 PARAM_NODE_NAME = 'param'
 
+# this is used to align the descriptions in help output
+CONST_BINDCTL_HELP_INDENT_WIDTH=12
+
 
 class ParamInfo:
     """One parameter of one command.
@@ -52,6 +57,12 @@ class ParamInfo:
     def __str__(self):        
         return str("\t%s <type: %s> \t(%s)" % (self.name, self.type, self.desc))
 
+    def get_name(self):
+        return "%s <type: %s>" % (self.name, self.type)
+
+    def get_desc(self):
+        return self.desc
+
 class CommandInfo:
     """One command which is provided by one bind10 module, it has zero
        or more parameters
@@ -63,13 +74,18 @@ class CommandInfo:
         self.params = OrderedDict()        
         # Set default parameter "help"
         self.add_param(ParamInfo("help", 
-                                  desc = "Get help for command",
+                                  desc = "Get help for command.",
                                   optional = True))
                 
     def __str__(self):
         return str("%s \t(%s)" % (self.name, self.desc))
-        
 
+    def get_name(self):
+        return self.name
+
+    def get_desc(self):
+        return self.desc;
+    
     def add_param(self, paraminfo):
         """Add a ParamInfo object to this CommandInfo"""
         self.params[paraminfo.name] = paraminfo
@@ -144,22 +160,30 @@ class CommandInfo:
         del params["help"]
 
         if len(params) == 0:
-            print("\tNo parameters for the command")
+            print("No parameters for the command")
             return
         
-        print("\n\tMandatory parameters:")
+        print("\nMandatory parameters:")
         mandatory_infos = []
         for info in params.values():            
             if not info.is_optional:
-                print("\t", info)
+                print("    %s" % info.get_name())
+                print(textwrap.fill(info.get_desc(),
+                      initial_indent="        ",
+                      subsequent_indent="        ",
+                      width=70))
                 mandatory_infos.append(info)
 
         optional_infos = [info for info in params.values() 
                           if info not in mandatory_infos]
         if len(optional_infos) > 0:
-            print("\n\tOptional parameters:")      
+            print("\nOptional parameters:")      
             for info in optional_infos:
-                    print("\t", info)
+                print("    %s" % info.get_name())
+                print(textwrap.fill(info.get_desc(),
+                      initial_indent="        ",
+                      subsequent_indent="        ",
+                      width=70))
 
 
 class ModuleInfo:
@@ -172,11 +196,17 @@ class ModuleInfo:
         self.desc = desc
         self.commands = OrderedDict()         
         self.add_command(CommandInfo(name = "help", 
-                                     desc = "Get help for module"))
+                                     desc = "Get help for module."))
         
     def __str__(self):
         return str("%s \t%s" % (self.name, self.desc))
-        
+
+    def get_name(self):
+        return self.name
+
+    def get_desc(self):
+        return self.desc
+
     def add_command(self, command_info):
         """Add a CommandInfo to this ModuleInfo."""
         self.commands[command_info.name] = command_info
@@ -201,8 +231,24 @@ class ModuleInfo:
     def module_help(self):
         """Prints the help info for this module to stdout"""
         print("Module ", self, "\nAvailable commands:")
-        for k in self.commands.keys():
-            print("\t", self.commands[k])
+        for k in self.commands.values():
+            n = k.get_name()
+            if len(n) >= CONST_BINDCTL_HELP_INDENT_WIDTH:
+                print("    %s" % n)
+                print(textwrap.fill(k.get_desc(),
+                      initial_indent="            ",
+                      subsequent_indent="    " +
+                      " " * CONST_BINDCTL_HELP_INDENT_WIDTH,
+                      width=70))
+            else:
+                print(textwrap.fill("%s%s%s" %
+                    (k.get_name(),
+                     " "*(CONST_BINDCTL_HELP_INDENT_WIDTH - len(k.get_name())),
+                     k.get_desc()),
+                    initial_indent="    ",
+                    subsequent_indent="    " +
+                    " " * CONST_BINDCTL_HELP_INDENT_WIDTH,
+                    width=70))
             
     def command_help(self, command):
         """Prints the help info for the command with the given name.
diff --git a/src/bin/bindctl/tests/Makefile.am b/src/bin/bindctl/tests/Makefile.am
index 5f93644..8a7a623 100644
--- a/src/bin/bindctl/tests/Makefile.am
+++ b/src/bin/bindctl/tests/Makefile.am
@@ -1,5 +1,5 @@
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-PYTESTS = bindctl_test.py
+PYTESTS = bindctl_test.py cmdparse_test.py
 EXTRA_DIST = $(PYTESTS)
 
 # test using command-line arguments, so use check-local target instead of TESTS
diff --git a/src/bin/bindctl/tests/bindctl_test.py b/src/bin/bindctl/tests/bindctl_test.py
index 653c908..490dd7a 100644
--- a/src/bin/bindctl/tests/bindctl_test.py
+++ b/src/bin/bindctl/tests/bindctl_test.py
@@ -17,6 +17,8 @@
 import unittest
 import isc.cc.data
 import os
+from isc.config.config_data import ConfigData, MultiConfigData
+from isc.config.module_spec import ModuleSpec
 from bindctl import cmdparse
 from bindctl import bindcmd
 from bindctl.moduleinfo import *
@@ -238,11 +240,101 @@ class TestNameSequence(unittest.TestCase):
             assert self.random_names[i] == module_names[i+1]
             i = i + 1
 
-    def test_apply_cfg_command(self):
+# tine class to fake a UIModuleCCSession, but only the config data
+# parts for the next set of tests
+class FakeCCSession(MultiConfigData):
+    def __init__(self):
+        self._local_changes = {}
+        self._current_config = {}
+        self._specifications = {}
+        self.add_foo_spec()
+
+    def add_foo_spec(self):
+        spec = { "module_name": "foo",
+                 "config_data": [
+                 { "item_name": "an_int",
+                   "item_type": "integer",
+                   "item_optional": False,
+                   "item_default": 1
+                 },
+                 { "item_name": "a_list",
+                   "item_type": "list",
+                   "item_optional": False,
+                   "item_default": [],
+                   "list_item_spec":
+                   { "item_name": "a_string",
+                     "item_type": "string",
+                     "item_optional": False,
+                     "item_default": "bar"
+                   }
+                 }
+                 ]
+               }
+        self.set_specification(ModuleSpec(spec))
+    
+
+class TestConfigCommands(unittest.TestCase):
+    def setUp(self):
+        self.tool = bindcmd.BindCmdInterpreter()
+        mod_info = ModuleInfo(name = "foo")
+        self.tool.add_module_info(mod_info)
+        self.tool.config_data = FakeCCSession()
+        
+    def test_apply_cfg_command_int(self):
         self.tool.location = '/'
-        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/bar\" value=\"5\"")
+
+        self.assertEqual((1, MultiConfigData.DEFAULT),
+                         self.tool.config_data.get_value("/foo/an_int"))
+
+        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/an_int\" value=\"5\"")
         self.tool.apply_config_cmd(cmd)
+        self.assertEqual((5, MultiConfigData.LOCAL),
+                         self.tool.config_data.get_value("/foo/an_int"))
+
+        # this should raise a NotFoundError
+        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/bar\" value=\"[]\"")
+        self.assertRaises(isc.cc.data.DataNotFoundError, self.tool.apply_config_cmd, cmd)
+
+        # this should raise a TypeError
+        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/an_int\" value=\"[]\"")
+        self.assertRaises(isc.cc.data.DataTypeError, self.tool.apply_config_cmd, cmd)
+
+    # this is a very specific one for use with a set of list tests
+    # to try out the flexibility of the parser (only in the next test)
+    def clt(self, full_cmd_string, item_value):
+        cmd = cmdparse.BindCmdParse(full_cmd_string)
+        self.tool.apply_config_cmd(cmd)
+        self.assertEqual(([item_value], MultiConfigData.LOCAL),
+                         self.tool.config_data.get_value("/foo/a_list"))
+
+    def test_apply_cfg_command_list(self):
+        self.tool.location = '/'
+
+        self.assertEqual(([], MultiConfigData.DEFAULT),
+                         self.tool.config_data.get_value("/foo/a_list"))
+
+        self.clt("config set identifier=\"foo/a_list\" value=[\"a\"]", "a")
+        self.clt("config set identifier=\"foo/a_list\" value =[\"b\"]", "b")
+        self.clt("config set identifier=\"foo/a_list\" value= [\"c\"]", "c")
+        self.clt("config set identifier=\"foo/a_list\" value = [\"d\"]", "d")
+        self.clt("config set identifier =\"foo/a_list\" value=[\"e\"]", "e")
+        self.clt("config set identifier= \"foo/a_list\" value=[\"f\"]", "f")
+        self.clt("config set identifier = \"foo/a_list\" value=[\"g\"]", "g")
+        self.clt("config set identifier = \"foo/a_list\" value = [\"h\"]", "h")
+        self.clt("config set identifier = \"foo/a_list\" value=[\"i\" ]", "i")
+        self.clt("config set identifier = \"foo/a_list\" value=[ \"j\"]", "j")
+        self.clt("config set identifier = \"foo/a_list\" value=[ \"k\" ]", "k")
+
+        # this should raise a TypeError
+        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/a_list\" value=\"a\"")
+        self.assertRaises(isc.cc.data.DataTypeError, self.tool.apply_config_cmd, cmd)
+        
+        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/a_list\" value=[1]")
+        self.assertRaises(isc.cc.data.DataTypeError, self.tool.apply_config_cmd, cmd)
+
+
     
+
 class FakeBindCmdInterpreter(bindcmd.BindCmdInterpreter):
     def __init__(self):
         pass
diff --git a/src/bin/bindctl/tests/cmdparse_test.py b/src/bin/bindctl/tests/cmdparse_test.py
new file mode 100644
index 0000000..9150ed3
--- /dev/null
+++ b/src/bin/bindctl/tests/cmdparse_test.py
@@ -0,0 +1,88 @@
+# Copyright (C) 2009  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+import unittest
+from bindctl import cmdparse
+
+class TestCmdParse(unittest.TestCase):
+
+    def test_remove_unquoted_whitespace(self):
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("a"), "a")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace(" a"), "a")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("a "), "a")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace(" a "), "a")
+        self.assertNotEqual(cmdparse._remove_unquoted_whitespace("a"), "a ")
+        self.assertNotEqual(cmdparse._remove_unquoted_whitespace(" a"), " a")
+        self.assertNotEqual(cmdparse._remove_unquoted_whitespace("a "), "a ")
+        self.assertNotEqual(cmdparse._remove_unquoted_whitespace(" a "), " a ")
+        self.assertNotEqual(cmdparse._remove_unquoted_whitespace(" a "), "b")
+
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("\"abc\""), "\"abc\"")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace(" \"abc\""), "\"abc\"")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("\"abc\" "), "\"abc\"")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace(" \"abc\" "), "\"abc\"")
+        
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("\" abc\""), "\" abc\"")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace(" \"a bc\""), "\"a bc\"")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("\"ab c\" "), "\"ab c\"")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace(" \"abc \" "), "\"abc \"")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace(" \" a b c \" "), "\" a b c \"")
+        
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("a\" abc\"a"), "a\" abc\"a")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("a \"a bc\"a"), "a\"a bc\"a")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("a\"ab c\" a"), "a\"ab c\"a")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("a \"abc \" a"), "a\"abc \"a")
+        self.assertEqual(cmdparse._remove_unquoted_whitespace("a \" a b c \" a"), "a\" a b c \"a")
+
+    # short-hand function to make the set of tests more readable
+    def rws(self, a, b):
+        self.assertEqual(cmdparse._remove_list_and_map_whitespace(a), b)
+
+    def test_remove_list_whitespace(self):
+        self.rws("a", "a")
+        self.rws(" a ", " a ")
+        self.rws(" [a] ", " [a] ")
+        self.rws(" [ a] ", " [a] ")
+        self.rws(" [ a ] ", " [a] ")
+        self.rws(" [ a b c ] ", " [abc] ")
+        self.rws(" [ a \"b c\" ] ", " [a\"b c\"] ")
+        self.rws("a [ a \"b c\" ] a", "a [a\"b c\"] a")
+        self.rws("a] [ a \"b c\" ] a", "a] [a\"b c\"] a")
+        self.rws(" [ a [b c] ] ", " [a[bc]] ")
+        self.rws(" [ a b][ c d ] ", " [ab][cd] ")
+        self.rws(" [ a b] [ c d ] ", " [ab] [cd] ")
+        
+        self.rws("a", "a")
+        self.rws(" a ", " a ")
+        self.rws(" {a} ", " {a} ")
+        self.rws(" { a} ", " {a} ")
+        self.rws(" { a } ", " {a} ")
+        self.rws(" { a b c } ", " {abc} ")
+        self.rws(" { a \"b c\" } ", " {a\"b c\"} ")
+        self.rws("a { a \"b c\" } a", "a {a\"b c\"} a")
+        self.rws("a} { a \"b c\" } a", "a} {a\"b c\"} a")
+        self.rws(" { a {b c} } ", " {a{bc}} ")
+        self.rws(" { a b}{ c d } ", " {ab}{cd} ")
+        self.rws(" { a b} { c d } ", " {ab} {cd} ")
+
+        self.rws(" [ a b]{ c d } ", " [ab]{cd} ")
+        self.rws(" [ a b{ c d }] ", " [ab{cd}] ")
+        self.rws(" [ a b{ \"c d\" }] ", " [ab{\"c d\"}] ")
+        
+
+if __name__== "__main__":
+    unittest.main()
+    
diff --git a/src/bin/resolver/resolver.spec.pre.in b/src/bin/resolver/resolver.spec.pre.in
index a249009..48e1eb6 100644
--- a/src/bin/resolver/resolver.spec.pre.in
+++ b/src/bin/resolver/resolver.spec.pre.in
@@ -25,7 +25,7 @@
         "item_name": "retries",
         "item_type": "integer",
         "item_optional": False,
-        "item_default": 0
+        "item_default": 3
       },
       {
         "item_name": "forward_addresses",
diff --git a/src/lib/asiolink/recursive_query.cc b/src/lib/asiolink/recursive_query.cc
index 9f814b0..231be67 100644
--- a/src/lib/asiolink/recursive_query.cc
+++ b/src/lib/asiolink/recursive_query.cc
@@ -352,8 +352,10 @@ public:
         // we have an answer or timeout ourselves
         isc::resolve::makeErrorMessage(answer_message_,
                                        Rcode::SERVFAIL());
-        resolvercallback_->success(answer_message_);
-        answer_sent_ = true;
+        if (!answer_sent_) {
+            answer_sent_ = true;
+            resolvercallback_->success(answer_message_);
+        }
     }
 
     virtual void stop(bool resume) {
@@ -366,6 +368,7 @@ public:
         // until that one comes back to us)
         done_ = true;
         if (resume && !answer_sent_) {
+            answer_sent_ = true;
             resolvercallback_->success(answer_message_);
         } else {
             resolvercallback_->failure();
diff --git a/src/lib/cache/TODO b/src/lib/cache/TODO
index 0743ee4..a7d2458 100644
--- a/src/lib/cache/TODO
+++ b/src/lib/cache/TODO
@@ -9,4 +9,6 @@
   can removed first.
 * Make resolver cache be smart to refetch the messages that are about
   to expire.
+* When the rrset beging updated is an NS rrset, NSAS should be updated
+  together.
 
diff --git a/src/lib/cache/message_entry.cc b/src/lib/cache/message_entry.cc
index fe21ce9..2fdb07c 100644
--- a/src/lib/cache/message_entry.cc
+++ b/src/lib/cache/message_entry.cc
@@ -131,7 +131,7 @@ MessageEntry::getRRsetTrustLevel(const Message& message,
                     ++rrset_iter;
                 }
                 assert(rrset_iter != message.endSection(section));
-                
+
                 // According RFC2181 section 5.4.1, only the record
                 // describing that ailas is necessarily authoritative.
                 // If there is one or more CNAME records in answer section.
diff --git a/src/lib/cache/message_entry.h b/src/lib/cache/message_entry.h
index 0ed00f0..20b2472 100644
--- a/src/lib/cache/message_entry.h
+++ b/src/lib/cache/message_entry.h
@@ -89,16 +89,14 @@ public:
         return (*hash_key_ptr_);
     }
 
+    /// \short Protected memebers, so they can be accessed by tests.
+    //@{
 protected:
     /// \brief Initialize the message entry with dns message.
     ///
     /// \param message The Message to initialize the entry with
     void initMessageEntry(const isc::dns::Message& message);
 
-    /// \brief These two functions should be static functions
-    ///        placed in cc file. Put them here just for easy unit
-    ///        tests.
-    //@{
     /// \brief Parse the rrsets in specified section.
     ///
     /// \param msg The message to parse the RRsets from
@@ -148,10 +146,9 @@ protected:
     ///         otherwise.
     bool getRRsetEntries(std::vector<RRsetEntryPtr>& rrset_entry_vec,
                          const time_t time_now);
-    //@}
-protected:
-    /// \note Make the variable be protected for easy test.
+
     time_t expire_time_;  // Expiration time of the message.
+    //@}
 
 private:
     std::string entry_name_; // The name for this entry(name + type)
diff --git a/src/lib/cache/resolver_cache.cc b/src/lib/cache/resolver_cache.cc
index aa681cc..7ebdb4f 100644
--- a/src/lib/cache/resolver_cache.cc
+++ b/src/lib/cache/resolver_cache.cc
@@ -241,9 +241,6 @@ ResolverCache::getClassCache(const isc::dns::RRClass& cache_class) const {
     return NULL;
 }
 
-
-
-
 } // namespace cache
 } // namespace isc
 
diff --git a/src/lib/cache/rrset_cache.cc b/src/lib/cache/rrset_cache.cc
index 02a6a45..86ee867 100644
--- a/src/lib/cache/rrset_cache.cc
+++ b/src/lib/cache/rrset_cache.cc
@@ -76,11 +76,10 @@ RRsetCache::update(const isc::dns::RRset& rrset, const RRsetTrustLevel& level) {
             return (entry_ptr);
         } else {
             HashKey key = entry_ptr->hashKey();
-            rrset_table_.remove(key);
             entry_ptr.reset(new RRsetEntry(rrset, level));
             //TODO, lru list touch.
             // Replace the expired rrset entry if it exists.
-            rrset_table_.add(entry_ptr, key, true);
+            rrset_table_.add(entry_ptr, entry_ptr->hashKey(), true);
             return (entry_ptr);
         }
     }
diff --git a/src/lib/cache/tests/Makefile.am b/src/lib/cache/tests/Makefile.am
index 9bc3e3a..b93c9a7 100644
--- a/src/lib/cache/tests/Makefile.am
+++ b/src/lib/cache/tests/Makefile.am
@@ -38,6 +38,8 @@ run_unittests_SOURCES  += message_cache_unittest.cc
 run_unittests_SOURCES  += message_entry_unittest.cc
 run_unittests_SOURCES  += local_zone_data_unittest.cc
 run_unittests_SOURCES  += resolver_cache_unittest.cc
+run_unittests_SOURCES  += cache_test_messagefromfile.h
+run_unittests_SOURCES  += cache_test_sectioncount.h
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
@@ -55,3 +57,10 @@ run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 endif
 
 noinst_PROGRAMS = $(TESTS)
+
+EXTRA_DIST = testdata/message_fromWire1
+EXTRA_DIST += testdata/message_fromWire2
+EXTRA_DIST += testdata/message_fromWire3
+EXTRA_DIST += testdata/message_fromWire4
+EXTRA_DIST += testdata/message_fromWire5
+EXTRA_DIST += testdata/message_fromWire6
diff --git a/src/lib/cache/tests/cache_test_sectioncount.h b/src/lib/cache/tests/cache_test_sectioncount.h
index 9583805..a385133 100644
--- a/src/lib/cache/tests/cache_test_sectioncount.h
+++ b/src/lib/cache/tests/cache_test_sectioncount.h
@@ -33,7 +33,7 @@ int
 sectionRRsetCount(Message& msg, Message::Section section) {
     int count = 0;
     for (RRsetIterator rrset_iter = msg.beginSection(section);
-         rrset_iter != msg.endSection(section); 
+         rrset_iter != msg.endSection(section);
          ++rrset_iter) {
         ++count;
     }
diff --git a/src/lib/cache/tests/rrset_entry_unittest.cc b/src/lib/cache/tests/rrset_entry_unittest.cc
index ce6238f..29c05b5 100644
--- a/src/lib/cache/tests/rrset_entry_unittest.cc
+++ b/src/lib/cache/tests/rrset_entry_unittest.cc
@@ -49,9 +49,10 @@ TEST_F(GenCacheKeyTest, genCacheEntryKey2) {
 
 class DerivedRRsetEntry: public RRsetEntry {
 public:
+    DerivedRRsetEntry(const isc::dns::RRset& rrset, const RRsetTrustLevel& level) : RRsetEntry(rrset, level) {};
 
     void updateTTLForTest() {
-        
+
     }
 };
 
diff --git a/src/lib/config/tests/testdata/spec22.spec b/src/lib/config/tests/testdata/spec22.spec
index be6d51f..cccd77b 100644
--- a/src/lib/config/tests/testdata/spec22.spec
+++ b/src/lib/config/tests/testdata/spec22.spec
@@ -1,6 +1,6 @@
 {
   "module_spec": {
-    "module_name": "Spec2",
+    "module_name": "Spec22",
     "config_data": [
       { "item_name": "value1",
         "item_type": "integer",
@@ -81,7 +81,7 @@
       { "item_name": "value9",
         "item_type": "map",
         "item_optional": false,
-        "item_default": {},
+        "item_default": { "v91": "def", "v92": {} },
         "map_item_spec": [
           { "item_name": "v91",
             "item_type": "string",
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index fdef888..bbc5166 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -94,22 +94,22 @@ struct MemoryZone::MemoryZoneImpl {
              l > origin_labels;
              --l, wname = wname.split(1)) {
             if (wname.isWildcard()) {
-                // Ensure a separate level exists for the wildcard name.
-                // Note: for 'name' itself we do this later anyway, but the
-                // overhead should be marginal because wildcard names should
-                // be rare.
+                // Ensure a separate level exists for the "wildcarding" name,
+                // and mark the node as "wild".
                 DomainNode* node;
                 DomainTree::Result result(domains.insert(wname.split(1),
                                                          &node));
                 assert(result == DomainTree::SUCCESS ||
                        result == DomainTree::ALREADYEXISTS);
+                node->setFlag(DOMAINFLAG_WILD);
 
-                // Ensure a separate level exists for the "wildcarding" name,
-                // and mark the node as "wild".
+                // Ensure a separate level exists for the wildcard name.
+                // Note: for 'name' itself we do this later anyway, but the
+                // overhead should be marginal because wildcard names should
+                // be rare.
                 result = domains.insert(wname, &node);
                 assert(result == DomainTree::SUCCESS ||
                        result == DomainTree::ALREADYEXISTS);
-                node->setFlag(DOMAINFLAG_WILD);
             }
         }
     }
@@ -351,6 +351,35 @@ struct MemoryZone::MemoryZoneImpl {
         return (false);
     }
 
+    /*
+     * Prepares a rrset to be return as a result.
+     *
+     * If rename is false, it returns the one provided. If it is true, it
+     * creates a new rrset with the same data but with provided name.
+     * It is designed for wildcard case, where we create the rrsets
+     * dynamically.
+     */
+    static ConstRRsetPtr prepareRRset(const Name& name, const ConstRRsetPtr&
+        rrset, bool rename)
+    {
+        if (rename) {
+            /*
+             * We lose a signature here. But it would be wrong anyway, because
+             * the name changed. This might turn out to be unimportant in
+             * future, because wildcards will probably be handled somehow
+             * by DNSSEC.
+             */
+            RRsetPtr result(new RRset(name, rrset->getClass(),
+                rrset->getType(), rrset->getTTL()));
+            for (RdataIteratorPtr i(rrset->getRdataIterator()); !i->isLast();
+                i->next()) {
+                result->addRdata(i->getCurrent());
+            }
+            return (result);
+        } else {
+            return (rrset);
+        }
+    }
 
     // Implementation of MemoryZone::find
     FindResult find(const Name& name, RRType type,
@@ -360,6 +389,7 @@ struct MemoryZone::MemoryZoneImpl {
         DomainNode* node(NULL);
         FindState state(options);
         RBTreeNodeChain<Domain> node_path;
+        bool rename(false);
         switch (domains_.find(name, &node, node_path, cutCallback, &state)) {
             case DomainTree::PARTIALMATCH:
                 /*
@@ -383,10 +413,12 @@ struct MemoryZone::MemoryZoneImpl {
                 if (state.dname_node_ != NULL) {
                     // We were traversing a DNAME node (and wanted to go
                     // lower below it), so return the DNAME
-                    return (FindResult(DNAME, state.rrset_));
+                    return (FindResult(DNAME, prepareRRset(name, state.rrset_,
+                        rename)));
                 }
                 if (state.zonecut_node_ != NULL) {
-                    return (FindResult(DELEGATION, state.rrset_));
+                    return (FindResult(DELEGATION, prepareRRset(name,
+                        state.rrset_, rename)));
                 }
 
                 // If the RBTree search stopped at a node for a super domain
@@ -397,6 +429,60 @@ struct MemoryZone::MemoryZoneImpl {
                     return (FindResult(NXRRSET, ConstRRsetPtr()));
                 }
 
+                /*
+                 * No redirection anywhere. Let's try if it is a wildcard.
+                 *
+                 * The wildcard is checked after the empty non-terminal domain
+                 * case above, because if that one triggers, it means we should
+                 * not match according to 4.3.3 of RFC 1034 (the query name
+                 * is known to exist).
+                 */
+                if (node->getFlag(DOMAINFLAG_WILD)) {
+                    /* Should we cancel this match?
+                     *
+                     * If we compare with some node and get a common ancestor,
+                     * it might mean we are comparing with a non-wildcard node.
+                     * In that case, we check which part is common. If we have
+                     * something in common that lives below the node we got
+                     * (the one above *), then we should cancel the match
+                     * according to section 4.3.3 of RFC 1034 (as the name
+                     * between the wildcard domain and the query name is known
+                     * to exist).
+                     *
+                     * Because the way the tree stores relative names, we will
+                     * have exactly one common label (the ".") in case we have
+                     * nothing common under the node we got and we will get
+                     * more common labels otherwise (yes, this relies on the
+                     * internal RBTree structure, which leaks out through this
+                     * little bit).
+                     *
+                     * If the empty non-terminal node actually exists in the
+                     * tree, then this cancellation is not needed, because we
+                     * will not get here at all.
+                     */
+                    if (node_path.getLastComparisonResult().getRelation() ==
+                        NameComparisonResult::COMMONANCESTOR && node_path.
+                        getLastComparisonResult().getCommonLabels() > 1) {
+                        return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+                    }
+                    Name wildcard(Name("*").concatenate(
+                        node_path.getAbsoluteName()));
+                    DomainTree::Result result(domains_.find(wildcard, &node));
+                    /*
+                     * Otherwise, why would the DOMAINFLAG_WILD be there if
+                     * there was no wildcard under it?
+                     */
+                    assert(result = DomainTree::EXACTMATCH);
+                    /*
+                     * We have the wildcard node now. Jump below the switch,
+                     * where handling of the common (exact-match) case is.
+                     *
+                     * However, rename it to the searched name.
+                     */
+                    rename = true;
+                    break;
+                }
+
                 // fall through
             case DomainTree::NOTFOUND:
                 return (FindResult(NXDOMAIN, ConstRRsetPtr()));
@@ -420,7 +506,8 @@ struct MemoryZone::MemoryZoneImpl {
         if (node->getFlag(DomainNode::FLAG_CALLBACK) && node != origin_data_) {
             found = node->getData()->find(RRType::NS());
             if (found != node->getData()->end()) {
-                return (FindResult(DELEGATION, found->second));
+                return (FindResult(DELEGATION, prepareRRset(name,
+                    found->second, rename)));
             }
         }
 
@@ -431,7 +518,8 @@ struct MemoryZone::MemoryZoneImpl {
                  found != node->getData()->end(); found++)
             {
                 target->addRRset(
-                    boost::const_pointer_cast<RRset>(found->second));
+                    boost::const_pointer_cast<RRset>(prepareRRset(name,
+                    found->second, rename)));
             }
             return (FindResult(SUCCESS, ConstRRsetPtr()));
         }
@@ -439,12 +527,14 @@ struct MemoryZone::MemoryZoneImpl {
         found = node->getData()->find(type);
         if (found != node->getData()->end()) {
             // Good, it is here
-            return (FindResult(SUCCESS, found->second));
+            return (FindResult(SUCCESS, prepareRRset(name, found->second,
+                rename)));
         } else {
             // Next, try CNAME.
             found = node->getData()->find(RRType::CNAME());
             if (found != node->getData()->end()) {
-                return (FindResult(CNAME, found->second));
+                return (FindResult(CNAME, prepareRRset(name, found->second,
+                    rename)));
             }
         }
         // No exact match or CNAME.  Return NXRRSET.
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index 15b06b4..16d749c 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -197,12 +197,17 @@ public:
              &rr_child_dname_},
             {"example.com. 300 IN A 192.0.2.10", &rr_out_},
             {"*.wild.example.org. 300 IN A 192.0.2.1", &rr_wild_},
+            {"foo.wild.example.org. 300 IN A 192.0.2.3", &rr_under_wild_},
             {"wild.*.foo.example.org. 300 IN A 192.0.2.1", &rr_emptywild_},
             {"wild.*.foo.*.bar.example.org. 300 IN A 192.0.2.1",
              &rr_nested_emptywild_},
             {"*.nswild.example.org. 300 IN NS nswild.example.", &rr_nswild_},
             {"*.dnamewild.example.org. 300 IN DNAME dnamewild.example.",
              &rr_dnamewild_},
+            {"*.child.example.org. 300 IN A 192.0.2.1", &rr_child_wild_},
+            {"bar.foo.wild.example.org. 300 IN A 192.0.2.2", &rr_not_wild_},
+            {"baz.foo.wild.example.org. 300 IN A 192.0.2.3",
+             &rr_not_wild_another_},
             {NULL, NULL}
         };
 
@@ -252,6 +257,10 @@ public:
     RRsetPtr rr_emptywild_;
     RRsetPtr rr_nested_emptywild_;
     RRsetPtr rr_nswild_, rr_dnamewild_;
+    RRsetPtr rr_child_wild_;
+    RRsetPtr rr_under_wild_;
+    RRsetPtr rr_not_wild_;
+    RRsetPtr rr_not_wild_another_;
 
     /**
      * \brief Test one find query to the zone.
@@ -268,13 +277,18 @@ public:
      * \param answer The expected rrset, if any should be returned.
      * \param zone Check different MemoryZone object than zone_ (if NULL,
      *     uses zone_)
+     * \param check_wild_answer Checks that the answer has the same RRs, type
+     *     class and TTL as the eqxpected answer and that the name corresponds
+     *     to the one searched. It is meant for checking answers for wildcard
+     *     queries.
      */
     void findTest(const Name& name, const RRType& rrtype, Zone::Result result,
                   bool check_answer = true,
                   const ConstRRsetPtr& answer = ConstRRsetPtr(),
                   RRsetList* target = NULL,
                   MemoryZone* zone = NULL,
-                  Zone::FindOptions options = Zone::FIND_DEFAULT)
+                  Zone::FindOptions options = Zone::FIND_DEFAULT,
+                  bool check_wild_answer = false)
     {
         if (!zone) {
             zone = &zone_;
@@ -288,9 +302,39 @@ public:
                 EXPECT_EQ(result, find_result.code);
                 if (check_answer) {
                     EXPECT_EQ(answer, find_result.rrset);
+                } else if (check_wild_answer) {
+                    ASSERT_NE(ConstRRsetPtr(), answer) <<
+                        "Wrong test, don't check for wild names if you expect "
+                        "empty answer";
+                    ASSERT_NE(ConstRRsetPtr(), find_result.rrset) <<
+                        "No answer found";
+                    RdataIteratorPtr expectedIt(answer->getRdataIterator());
+                    RdataIteratorPtr actualIt(
+                        find_result.rrset->getRdataIterator());
+                    while (!expectedIt->isLast() && !actualIt->isLast()) {
+                        EXPECT_EQ(0, expectedIt->getCurrent().compare(
+                            actualIt->getCurrent())) << "The RRs differ ('" <<
+                            expectedIt->getCurrent().toText() << "', '" <<
+                            actualIt->getCurrent().toText() << "')";
+                        expectedIt->next();
+                        actualIt->next();
+                    }
+                    EXPECT_TRUE(expectedIt->isLast()) <<
+                        "Result has less RRs than expected";
+                    EXPECT_TRUE(actualIt->isLast()) <<
+                        "Result has more RRs than expected";
+                    EXPECT_EQ(answer->getClass(),
+                        find_result.rrset->getClass());
+                    EXPECT_EQ(answer->getType(),
+                        find_result.rrset->getType());
+                    EXPECT_EQ(answer->getTTL(),
+                        find_result.rrset->getTTL());
+                    EXPECT_EQ(name, find_result.rrset->getName());
                 }
             });
     }
+    // Internal part of the cancelWildcard test that is multiple times
+    void doCancelWildcardTest();
 };
 
 /**
@@ -542,11 +586,6 @@ TEST_F(MemoryZoneTest, glue) {
     findTest(Name("www.child.example.org"), RRType::A(), Zone::DELEGATION,
              true, rr_child_ns_, NULL, NULL, Zone::FIND_GLUE_OK);
 
-    // TODO:
-    // glue name would match a wildcard under a zone cut: wildcard match
-    // shouldn't happen under a cut and result must be PARTIALMATCH
-    // (This case cannot be tested yet)
-
     // nested cut case.  The glue should be found.
     findTest(rr_grandchild_glue_->getName(), RRType::AAAA(),
              Zone::SUCCESS,
@@ -656,11 +695,11 @@ TEST_F(MemoryZoneTest, load) {
         MasterLoadError);
 }
 
-// Note: once #507 is merged, findTest() would succeed whether or not
-// we load the wildcard correctly, so the test will become meaningless.
-// The plan is to clean them up when we complete #551 (then the effect of
-// load will be indirectly tested via find() tests).
-TEST_F(MemoryZoneTest, loadWildcard) {
+/*
+ * Test that puts a (simple) wildcard into the zone and checks we can
+ * correctly find the data.
+ */
+TEST_F(MemoryZoneTest, wildcard) {
     /*
      *            example.org.
      *                 |
@@ -669,11 +708,96 @@ TEST_F(MemoryZoneTest, loadWildcard) {
      *                 *
      */
     EXPECT_EQ(SUCCESS, zone_.add(rr_wild_));
-    findTest(Name("wild.example.org"), RRType::A(), Zone::NXRRSET);
+
+    // Search at the parent. The parent will not have the A, but it will
+    // be in the wildcard (so check the wildcard isn't matched at the parent)
+    {
+        SCOPED_TRACE("Search at parrent");
+        findTest(Name("wild.example.org"), RRType::A(), Zone::NXRRSET);
+    }
+
+    // Search the original name of wildcard
+    {
+        SCOPED_TRACE("Search directly at *");
+        findTest(Name("*.wild.example.org"), RRType::A(), Zone::SUCCESS, true,
+            rr_wild_);
+    }
+    // Search "created" name.
+    {
+        SCOPED_TRACE("Search at created child");
+        findTest(Name("a.wild.example.org"), RRType::A(), Zone::SUCCESS, false,
+            rr_wild_, NULL, NULL, Zone::FIND_DEFAULT, true);
+    }
+
+    // Search another created name, this time little bit lower
+    {
+        SCOPED_TRACE("Search at created grand-child");
+        findTest(Name("a.b.wild.example.org"), RRType::A(), Zone::SUCCESS,
+            false, rr_wild_, NULL, NULL, Zone::FIND_DEFAULT, true);
+    }
+
+    EXPECT_EQ(SUCCESS, zone_.add(rr_under_wild_));
+    {
+        SCOPED_TRACE("Search under non-wildcard");
+        findTest(Name("bar.foo.wild.example.org"), RRType::A(),
+            Zone::NXDOMAIN);
+    }
+}
+
+/*
+ * Test that we don't match a wildcard if we get under delegation.
+ * By 4.3.3 of RFC1034:
+ * "Wildcard RRs do not apply:
+ *   - When the query is in another zone.  That is, delegation cancels
+ *     the wildcard defaults."
+ */
+TEST_F(MemoryZoneTest, delegatedWildcard) {
+    EXPECT_EQ(SUCCESS, zone_.add(rr_child_wild_));
+    EXPECT_EQ(SUCCESS, zone_.add(rr_child_ns_));
+
+    {
+        SCOPED_TRACE("Looking under delegation point");
+        findTest(Name("a.child.example.org"), RRType::A(), Zone::DELEGATION,
+            true, rr_child_ns_);
+    }
+
+    {
+        SCOPED_TRACE("Looking under delegation point in GLUE_OK mode");
+        findTest(Name("a.child.example.org"), RRType::A(), Zone::DELEGATION,
+            true, rr_child_ns_, NULL, NULL, Zone::FIND_GLUE_OK);
+    }
+}
+
+// Tests combination of wildcard and ANY.
+TEST_F(MemoryZoneTest, anyWildcard) {
+    EXPECT_EQ(SUCCESS, zone_.add(rr_wild_));
+
+    // First try directly the name (normal match)
+    {
+        SCOPED_TRACE("Asking direcly for *");
+        RRsetList target;
+        findTest(Name("*.wild.example.org"), RRType::ANY(), Zone::SUCCESS,
+            true, ConstRRsetPtr(), &target);
+        ASSERT_EQ(1, target.size());
+        EXPECT_EQ(RRType::A(), (*target.begin())->getType());
+        EXPECT_EQ(Name("*.wild.example.org"), (*target.begin())->getName());
+    }
+
+    // Then a wildcard match
+    {
+        SCOPED_TRACE("Asking in the wild way");
+        RRsetList target;
+        findTest(Name("a.wild.example.org"), RRType::ANY(), Zone::SUCCESS,
+            true, ConstRRsetPtr(), &target);
+        ASSERT_EQ(1, target.size());
+        EXPECT_EQ(RRType::A(), (*target.begin())->getType());
+        EXPECT_EQ(Name("a.wild.example.org"), (*target.begin())->getName());
+    }
 }
 
-// same note as loadWildcard applies.
-TEST_F(MemoryZoneTest, loadEmptyWildcard) {
+// Test there's nothing in the wildcard in the middle if we load
+// wild.*.foo.example.org.
+TEST_F(MemoryZoneTest, emptyWildcard) {
     /*
      *            example.org.
      *                foo
@@ -681,17 +805,171 @@ TEST_F(MemoryZoneTest, loadEmptyWildcard) {
      *               wild
      */
     EXPECT_EQ(SUCCESS, zone_.add(rr_emptywild_));
-    findTest(Name("*.foo.example.org"), RRType::A(), Zone::NXRRSET);
-    findTest(Name("foo.example.org"), RRType::A(), Zone::NXRRSET);
+
+    {
+        SCOPED_TRACE("Asking for the original record under wildcard");
+        findTest(Name("wild.*.foo.example.org"), RRType::A(), Zone::SUCCESS,
+            true, rr_emptywild_);
+    }
+
+    {
+        SCOPED_TRACE("Asking for A record");
+        findTest(Name("a.foo.example.org"), RRType::A(), Zone::NXRRSET);
+        findTest(Name("*.foo.example.org"), RRType::A(), Zone::NXRRSET);
+        findTest(Name("foo.example.org"), RRType::A(), Zone::NXRRSET);
+    }
+
+    {
+        SCOPED_TRACE("Asking for ANY record");
+        RRsetList normalTarget;
+        findTest(Name("*.foo.example.org"), RRType::ANY(), Zone::NXRRSET, true,
+            ConstRRsetPtr(), &normalTarget);
+        EXPECT_EQ(0, normalTarget.size());
+
+        RRsetList wildTarget;
+        findTest(Name("a.foo.example.org"), RRType::ANY(), Zone::NXRRSET, true,
+            ConstRRsetPtr(), &wildTarget);
+        EXPECT_EQ(0, wildTarget.size());
+    }
+
+    {
+        SCOPED_TRACE("Asking on the non-terminal");
+        findTest(Name("wild.bar.foo.example.org"), RRType::A(),
+            Zone::NXRRSET);
+    }
 }
 
-// same note as loadWildcard applies.
-TEST_F(MemoryZoneTest, loadNestedEmptyWildcard) {
+// Same as emptyWildcard, but with multiple * in the path.
+TEST_F(MemoryZoneTest, nestedEmptyWildcard) {
     EXPECT_EQ(SUCCESS, zone_.add(rr_nested_emptywild_));
-    findTest(Name("*.foo.*.bar.example.org"), RRType::A(), Zone::NXRRSET);
-    findTest(Name("foo.*.bar.example.org"), RRType::A(), Zone::NXRRSET);
-    findTest(Name("*.bar.example.org"), RRType::A(), Zone::NXRRSET);
-    findTest(Name("bar.example.org"), RRType::A(), Zone::NXRRSET);
+
+    {
+        SCOPED_TRACE("Asking for the original record under wildcards");
+        findTest(Name("wild.*.foo.*.bar.example.org"), RRType::A(),
+            Zone::SUCCESS, true, rr_nested_emptywild_);
+    }
+
+    {
+        SCOPED_TRACE("Matching wildcard against empty nonterminal");
+
+        const char* names[] = {
+            "baz.foo.*.bar.example.org",
+            "baz.foo.baz.bar.example.org",
+            "*.foo.baz.bar.example.org",
+            NULL
+        };
+
+        for (const char** name(names); *name != NULL; ++ name) {
+            SCOPED_TRACE(string("Node ") + *name);
+            findTest(Name(*name), RRType::A(), Zone::NXRRSET);
+        }
+    }
+
+    // Domains to test
+    const char* names[] = {
+        "*.foo.*.bar.example.org",
+        "foo.*.bar.example.org",
+        "*.bar.example.org",
+        "bar.example.org",
+        NULL
+    };
+
+    {
+        SCOPED_TRACE("Asking directly for A on parent nodes");
+
+        for (const char** name(names); *name != NULL; ++ name) {
+            SCOPED_TRACE(string("Node ") + *name);
+            findTest(Name(*name), RRType::A(), Zone::NXRRSET);
+        }
+    }
+
+    {
+        SCOPED_TRACE("Asking for ANY on parent nodes");
+
+        for (const char** name(names); *name != NULL; ++ name) {
+            SCOPED_TRACE(string("Node ") + *name);
+
+            RRsetList target;
+            findTest(Name(*name), RRType::ANY(), Zone::NXRRSET, true,
+                ConstRRsetPtr(), &target);
+            EXPECT_EQ(0, target.size());
+        }
+    }
+}
+
+// We run this part twice from the below test, in two slightly different
+// situations
+void
+MemoryZoneTest::doCancelWildcardTest() {
+    // These should be canceled
+    {
+        SCOPED_TRACE("Canceled under foo.wild.example.org");
+        findTest(Name("aaa.foo.wild.example.org"), RRType::A(),
+            Zone::NXDOMAIN);
+        findTest(Name("zzz.foo.wild.example.org"), RRType::A(),
+            Zone::NXDOMAIN);
+    }
+
+    // This is existing, non-wildcard domain, shouldn't wildcard at all
+    {
+        SCOPED_TRACE("Existing domain under foo.wild.example.org");
+        findTest(Name("bar.foo.wild.example.org"), RRType::A(), Zone::SUCCESS,
+            true, rr_not_wild_);
+    }
+
+    // These should be caught by the wildcard
+    {
+        SCOPED_TRACE("Neighbor wildcards to foo.wild.example.org");
+
+        const char* names[] = {
+            "aaa.bbb.wild.example.org",
+            "aaa.zzz.wild.example.org",
+            "zzz.wild.example.org",
+            NULL
+        };
+
+        for (const char** name(names); *name != NULL; ++ name) {
+            SCOPED_TRACE(string("Node ") + *name);
+
+            findTest(Name(*name), RRType::A(), Zone::SUCCESS, false, rr_wild_,
+                NULL, NULL, Zone::FIND_DEFAULT, true);
+        }
+    }
+
+    // This shouldn't be wildcarded, it's an existing domain
+    {
+        SCOPED_TRACE("The foo.wild.example.org itself");
+        findTest(Name("foo.wild.example.org"), RRType::A(), Zone::NXRRSET);
+    }
+}
+
+/*
+ * This tests that if there's a name between the wildcard domain and the
+ * searched one, it will not trigger wildcard, for example, if we have
+ * *.wild.example.org and bar.foo.wild.example.org, then we know
+ * foo.wild.example.org exists and is not wildcard. Therefore, search for
+ * aaa.foo.wild.example.org should return NXDOMAIN.
+ *
+ * Tests few cases "around" the canceled wildcard match, to see something that
+ * shouldn't be canceled isn't.
+ */
+TEST_F(MemoryZoneTest, cancelWildcard) {
+    EXPECT_EQ(SUCCESS, zone_.add(rr_wild_));
+    EXPECT_EQ(SUCCESS, zone_.add(rr_not_wild_));
+
+    {
+        SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
+        doCancelWildcardTest();
+    }
+
+    // Try putting another one under foo.wild....
+    // The result should be the same but it will be done in another way in the
+    // code, because the foo.wild.example.org will exist in the tree.
+    EXPECT_EQ(SUCCESS, zone_.add(rr_not_wild_another_));
+    {
+        SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
+        doCancelWildcardTest();
+    }
 }
 
 TEST_F(MemoryZoneTest, loadBadWildcard) {
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index 4b1a64b..c5c5cd1 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -85,6 +85,8 @@ libdns___la_SOURCES += rrtype.cc
 libdns___la_SOURCES += question.h question.cc
 libdns___la_SOURCES += util/sha1.h util/sha1.cc
 libdns___la_SOURCES += tsigkey.h tsigkey.cc
+libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h
+libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.cc
 
 nodist_libdns___la_SOURCES = rdataclass.cc rrclass.h rrtype.h
 nodist_libdns___la_SOURCES += rrparamregistry.cc
@@ -117,5 +119,6 @@ libdns___include_HEADERS = \
 	tsigkey.h
 # Purposely not installing these headers:
 # util/*.h: used only internally, and not actually DNS specific
+# rdata/*/detail/*.h: these are internal use only
 # rrclass-placeholder.h
 # rrtype-placeholder.h
diff --git a/src/lib/dns/rdata/generic/detail/nsec_bitmap.cc b/src/lib/dns/rdata/generic/detail/nsec_bitmap.cc
new file mode 100644
index 0000000..a72058f
--- /dev/null
+++ b/src/lib/dns/rdata/generic/detail/nsec_bitmap.cc
@@ -0,0 +1,78 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+
+#include <vector>
+
+#include <dns/exceptions.h>
+
+using namespace std;
+
+namespace isc {
+namespace dns {
+namespace rdata {
+namespace generic {
+namespace detail {
+namespace nsec {
+void
+checkRRTypeBitmaps(const char* const rrtype_name,
+                   const vector<uint8_t>& typebits)
+{
+    bool first = true;
+    unsigned int lastblock = 0;
+    const size_t total_len = typebits.size();
+    size_t i = 0;
+
+    while (i < total_len) {
+        if (i + 2 > total_len) {
+            isc_throw(DNSMessageFORMERR, rrtype_name <<
+                      " RDATA from wire: incomplete bit map field");
+        }
+        const unsigned int block = typebits[i];
+        const size_t len = typebits[i + 1];
+        // Check that bitmap window blocks are in the correct order.
+        if (!first && block <= lastblock) {
+            isc_throw(DNSMessageFORMERR, rrtype_name <<
+                      " RDATA from wire: Disordered window blocks found: "
+                      << lastblock << " then " << block);
+        }
+        // Check for legal length
+        if (len < 1 || len > 32) {
+            isc_throw(DNSMessageFORMERR, rrtype_name <<
+                      " RDATA from wire: Invalid bitmap length: " << len);
+        }
+        // Check for overflow.
+        i += 2;
+        if (i + len > total_len) {
+            isc_throw(DNSMessageFORMERR, rrtype_name <<
+                      " RDATA from wire: bitmap length too large: " << len);
+        }
+        // The last octet of the bitmap must be non zero.
+        if (typebits[i + len - 1] == 0) {
+            isc_throw(DNSMessageFORMERR, rrtype_name <<
+                      " RDATA from wire: bitmap ending an all-zero byte");
+        }
+
+        i += len;
+        lastblock = block;
+        first = false;
+    }
+}
+}
+}
+}
+}
+}
+}
diff --git a/src/lib/dns/rdata/generic/detail/nsec_bitmap.h b/src/lib/dns/rdata/generic/detail/nsec_bitmap.h
new file mode 100644
index 0000000..6431e10
--- /dev/null
+++ b/src/lib/dns/rdata/generic/detail/nsec_bitmap.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+
+#include <vector>
+
+namespace isc {
+namespace dns {
+namespace rdata {
+namespace generic {
+namespace detail {
+namespace nsec {
+/// Check if a given "type bitmap" for NSEC/NSEC3 is valid.
+///
+/// This helper function checks given wire format data (stored in a
+/// \c std::vector) is a valid type bitmaps used for the NSEC and NSEC3 RRs
+/// according to RFC4034 and RFC5155.  The validation logic is the same
+/// for these two RRs, so a unified check function is provided.
+/// This function is essentially private and is only expected to be called
+/// from the \c NSEC and \c NSEC3 class implementations.
+///
+/// \exception DNSMessageFORMERR The bitmap is not valid.
+///
+/// \param rrtype_name Either "NSEC" or "NSEC3"; used as part of exception
+/// messages.
+/// \param typebits The type bitmaps in wire format.  The size of vector
+/// is the total length of the bitmaps.
+void checkRRTypeBitmaps(const char* const rrtype_name,
+                        const std::vector<uint8_t>& typebits);
+}
+}
+}
+}
+}
+}
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/rdata/generic/nsec3_50.cc b/src/lib/dns/rdata/generic/nsec3_50.cc
index c20fda2..01ffefa 100644
--- a/src/lib/dns/rdata/generic/nsec3_50.cc
+++ b/src/lib/dns/rdata/generic/nsec3_50.cc
@@ -30,11 +30,13 @@
 #include <dns/rrttl.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
+#include <dns/rdata/generic/detail/nsec_bitmap.h>
 
 #include <stdio.h>
 #include <time.h>
 
 using namespace std;
+using namespace isc::dns::rdata::generic::detail::nsec;
 
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
@@ -61,36 +63,57 @@ NSEC3::NSEC3(const string& nsec3_str) :
 {
     istringstream iss(nsec3_str);
     unsigned int hashalg, flags, iterations;
-    string salthex;
+    string iterations_str, salthex, nexthash;
 
-    iss >> hashalg >> flags >> iterations >> salthex;
+    iss >> hashalg >> flags >> iterations_str >> salthex >> nexthash;
     if (iss.bad() || iss.fail()) {
-        isc_throw(InvalidRdataText, "Invalid NSEC3 text");
+        isc_throw(InvalidRdataText, "Invalid NSEC3 text: " << nsec3_str);
     }
-    if (hashalg > 0xf) {
-        isc_throw(InvalidRdataText, "NSEC3 hash algorithm out of range");
+    if (hashalg > 0xff) {
+        isc_throw(InvalidRdataText,
+                  "NSEC3 hash algorithm out of range: " << hashalg);
     }
     if (flags > 0xff) {
-        isc_throw(InvalidRdataText, "NSEC3 flags out of range");
+        isc_throw(InvalidRdataText, "NSEC3 flags out of range: " << flags);
+    }
+    // Convert iteration.  To reject an invalid case where there's no space
+    // between iteration and salt, we extract this field as string and convert
+    // to integer.
+    try {
+        iterations = boost::lexical_cast<unsigned int>(iterations_str);
+    } catch (const boost::bad_lexical_cast&) {
+        isc_throw(InvalidRdataText, "Bad NSEC3 iteration: " << iterations_str);
     }
     if (iterations > 0xffff) {
-        isc_throw(InvalidRdataText, "NSEC3 iterations out of range");
+        isc_throw(InvalidRdataText, "NSEC3 iterations out of range: " <<
+            iterations);
     }
 
     vector<uint8_t> salt;
-    decodeHex(salthex, salt);
+    if (salthex != "-") {       // "-" means a 0-length salt
+        decodeHex(salthex, salt);
+    }
+    if (salt.size() > 255) {
+        isc_throw(InvalidRdataText, "NSEC3 salt is too long: "
+                  << salt.size() << " bytes");
+    }
 
-    string nextstr;
-    iss >> setw(32) >> nextstr;
     vector<uint8_t> next;
-    if (iss.bad() || iss.fail()) {
-        isc_throw(InvalidRdataText, "Invalid NSEC3 hash algorithm");
+    decodeBase32Hex(nexthash, next);
+    if (next.size() > 255) {
+        isc_throw(InvalidRdataText, "NSEC3 hash is too long: "
+                  << next.size() << " bytes");
     }
-    decodeBase32Hex(nextstr, next);
 
-    uint8_t bitmap[8 * 1024];       // 64k bits
-    vector<uint8_t> typebits;
+    // For NSEC3 empty bitmap is possible and allowed.
+    if (iss.eof()) {
+        impl_ = new NSEC3Impl(hashalg, flags, iterations, salt, next,
+                              vector<uint8_t>());
+        return;
+    }
 
+    vector<uint8_t> typebits;
+    uint8_t bitmap[8 * 1024];       // 64k bits
     memset(bitmap, 0, sizeof(bitmap));
     do { 
         string type;
@@ -104,7 +127,7 @@ NSEC3::NSEC3(const string& nsec3_str) :
                 isc_throw(InvalidRdataText, "Invalid RRtype in NSEC3");
             }
         }
-    } while(!iss.eof());
+    } while (!iss.eof());
 
     for (int window = 0; window < 256; window++) {
         int octet;
@@ -126,56 +149,46 @@ NSEC3::NSEC3(const string& nsec3_str) :
 }
 
 NSEC3::NSEC3(InputBuffer& buffer, size_t rdata_len) {
+    // NSEC3 RR must have at least 5 octets:
+    // hash algorithm(1), flags(1), iteration(2), saltlen(1)
     if (rdata_len < 5) {
-        isc_throw(InvalidRdataLength, "NSEC3 too short");
+        isc_throw(DNSMessageFORMERR, "NSEC3 too short, length: " << rdata_len);
     }
 
-    uint8_t hashalg = buffer.readUint8();
-    uint8_t flags = buffer.readUint8();
-    uint16_t iterations = buffer.readUint16();
-    rdata_len -= 4;
-
-    uint8_t saltlen = buffer.readUint8();
-    --rdata_len;
+    const uint8_t hashalg = buffer.readUint8();
+    const uint8_t flags = buffer.readUint8();
+    const uint16_t iterations = buffer.readUint16();
 
+    const uint8_t saltlen = buffer.readUint8();
+    rdata_len -= 5;
     if (rdata_len < saltlen) {
-        isc_throw(InvalidRdataLength, "NSEC3 salt too short");
+        isc_throw(DNSMessageFORMERR, "NSEC3 salt length is too large: " <<
+                  static_cast<unsigned int>(saltlen));
     }
 
     vector<uint8_t> salt(saltlen);
-    buffer.readData(&salt[0], saltlen);
-    rdata_len -= saltlen;
+    if (saltlen > 0) {
+        buffer.readData(&salt[0], saltlen);
+        rdata_len -= saltlen;
+    }
 
-    uint8_t nextlen = buffer.readUint8();
+    const uint8_t nextlen = buffer.readUint8();
     --rdata_len;
-
-    if (rdata_len < nextlen) {
-        isc_throw(InvalidRdataLength, "NSEC3 next hash too short");
+    if (nextlen == 0 || rdata_len < nextlen) {
+        isc_throw(DNSMessageFORMERR, "NSEC3 invalid hash length: " <<
+                  static_cast<unsigned int>(nextlen));
     }
 
     vector<uint8_t> next(nextlen);
     buffer.readData(&next[0], nextlen);
     rdata_len -= nextlen;
 
-    if (rdata_len == 0) {
-        isc_throw(InvalidRdataLength, "NSEC3 type bitmap too short");
-    }
-
     vector<uint8_t> typebits(rdata_len);
-    buffer.readData(&typebits[0], rdata_len);
-
-    int len = 0;
-    for (int i = 0; i < typebits.size(); i += len) {
-        if (i + 2 > typebits.size()) {
-            isc_throw(DNSMessageFORMERR, "Invalid rdata: "
-                                         "bad NSEC3 type bitmap");
-        }
-        len = typebits[i + 1];
-        if (len > 31) {
-            isc_throw(DNSMessageFORMERR, "Invalid rdata: "
-                                         "bad NSEC3 type bitmap");
-        }
-        i += 2;
+    if (rdata_len > 0) {
+        // Read and parse the bitmaps only when they exist; empty bitmap
+        // is possible for NSEC3.
+        buffer.readData(&typebits[0], rdata_len);
+        checkRRTypeBitmaps("NSEC3", typebits);
     }
 
     impl_ = new NSEC3Impl(hashalg, flags, iterations, salt, next, typebits);
@@ -327,10 +340,15 @@ NSEC3::getIterations() const {
     return (impl_->iterations_);
 }
 
-vector<uint8_t>&
+const vector<uint8_t>&
 NSEC3::getSalt() const {
     return (impl_->salt_);
 }
 
+const vector<uint8_t>&
+NSEC3::getNext() const {
+    return (impl_->next_);
+}
+
 // END_RDATA_NAMESPACE
 // END_ISC_NAMESPACE
diff --git a/src/lib/dns/rdata/generic/nsec3_50.h b/src/lib/dns/rdata/generic/nsec3_50.h
index 5532071..c766ade 100644
--- a/src/lib/dns/rdata/generic/nsec3_50.h
+++ b/src/lib/dns/rdata/generic/nsec3_50.h
@@ -43,7 +43,8 @@ public:
     uint8_t getHashalg() const;
     uint8_t getFlags() const;
     uint16_t getIterations() const;
-    std::vector<uint8_t>& getSalt() const;
+    const std::vector<uint8_t>& getSalt() const;
+    const std::vector<uint8_t>& getNext() const;
 
 private:
     NSEC3Impl* impl_;
diff --git a/src/lib/dns/rdata/generic/nsec_47.cc b/src/lib/dns/rdata/generic/nsec_47.cc
index 0859edd..5d92528 100644
--- a/src/lib/dns/rdata/generic/nsec_47.cc
+++ b/src/lib/dns/rdata/generic/nsec_47.cc
@@ -26,11 +26,13 @@
 #include <dns/rrttl.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
+#include <dns/rdata/generic/detail/nsec_bitmap.h>
 
 #include <stdio.h>
 #include <time.h>
 
 using namespace std;
+using namespace isc::dns::rdata::generic::detail::nsec;
 
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
@@ -103,43 +105,7 @@ NSEC::NSEC(InputBuffer& buffer, size_t rdata_len) {
 
     vector<uint8_t> typebits(rdata_len);
     buffer.readData(&typebits[0], rdata_len);
-
-    int len = 0;
-    bool first = true;
-    unsigned int block, lastblock = 0;
-    for (int i = 0; i < rdata_len; i += len) {
-        if (i + 2 > rdata_len) {
-            isc_throw(DNSMessageFORMERR, "NSEC RDATA from wire: "
-                      "incomplete bit map field");
-        }
-        block = typebits[i];
-        len = typebits[i + 1];
-        // Check that bitmap window blocks are in the correct order.
-        if (!first && block <= lastblock) {
-            isc_throw(DNSMessageFORMERR, "NSEC RDATA from wire: Disordered "
-                      "window blocks found: " << lastblock <<
-                      " then " << block);
-        }
-        // Check for legal length
-        if (len < 1 || len > 32) {
-            isc_throw(DNSMessageFORMERR, "NSEC RDATA from wire: Invalid bitmap "
-                      "length: " << len);
-        }
-        // Check for overflow.
-        i += 2;
-        if (i + len > rdata_len) {
-            isc_throw(DNSMessageFORMERR, "NSEC RDATA from wire: bitmap length "
-                      "too large: " << len);
-        }
-        // The last octet of the bitmap must be non zero.
-        if (typebits[i + len - 1] == 0) {
-            isc_throw(DNSMessageFORMERR, "NSEC RDATA from wire: bitmap ending "
-                      "an all-zero byte");
-        }
-
-        lastblock = block;
-        first = false;
-    }
+    checkRRTypeBitmaps("NSEC", typebits);
 
     impl_ = new NSECImpl(nextname, typebits);
 }
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index 48e4650..246adb7 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -36,6 +36,7 @@ run_unittests_SOURCES += rdata_dnskey_unittest.cc
 run_unittests_SOURCES += rdata_ds_unittest.cc
 run_unittests_SOURCES += rdata_nsec_unittest.cc
 run_unittests_SOURCES += rdata_nsec3_unittest.cc
+run_unittests_SOURCES += rdata_nsecbitmap_unittest.cc
 run_unittests_SOURCES += rdata_nsec3param_unittest.cc
 run_unittests_SOURCES += rdata_rrsig_unittest.cc
 run_unittests_SOURCES += rdata_tsig_unittest.cc
diff --git a/src/lib/dns/tests/rdata_mx_unittest.cc b/src/lib/dns/tests/rdata_mx_unittest.cc
index 4491f86..dd7677d 100644
--- a/src/lib/dns/tests/rdata_mx_unittest.cc
+++ b/src/lib/dns/tests/rdata_mx_unittest.cc
@@ -74,12 +74,9 @@ TEST_F(Rdata_MX_Test, toWireRenderer) {
 TEST_F(Rdata_MX_Test, toWireBuffer) {
     renderer.writeName(Name("example.com"));
     rdata_mx.toWire(obuffer);
-}
 
-TEST_F(Rdata_MX_Test, DISABLED_toWireBuffer) {
-// XXX: does not pass
     vector<unsigned char> data;
-    UnitTestUtil::readWireData("rdata_mx_toWire1", data);
+    UnitTestUtil::readWireData("rdata_mx_toWire2", data);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
                         obuffer.getLength(), &data[0], data.size());
 }
diff --git a/src/lib/dns/tests/rdata_nsec3_unittest.cc b/src/lib/dns/tests/rdata_nsec3_unittest.cc
index 6b3a0b5..749e262 100644
--- a/src/lib/dns/tests/rdata_nsec3_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec3_unittest.cc
@@ -46,59 +46,136 @@ public:
     string nsec3_txt;
 };
 
+TEST_F(Rdata_NSEC3_Test, fromText) {
+    // A normal case: the test constructor should successfully parse the
+    // text and construct nsec3_txt.  It will be tested against the wire format
+    // representation in the createFromWire test.
+
+    // Numeric parameters have possible maximum values.  Unusual, but must
+    // be accepted.
+    EXPECT_NO_THROW(generic::NSEC3("255 255 65535 D399EAAB "
+                                   "H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
+                                   "NS SOA RRSIG DNSKEY NSEC3PARAM"));
+
+    // 0-length salt
+    EXPECT_EQ(0, generic::NSEC3("1 1 1 - H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
+                                "A").getSalt().size());
+
+    // salt that has the possible max length
+    EXPECT_EQ(255, generic::NSEC3("1 1 1 " + string(255 * 2, '0') +
+                                  " H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
+                                  "NS").getSalt().size());
+
+    // hash that has the possible max length (see badText about the magic
+    // numbers)
+    EXPECT_EQ(255, generic::NSEC3("1 1 1 D399EAAB " +
+                                  string((255 * 8) / 5, '0') +
+                                  " NS").getNext().size());
+
+    // type bitmap is empty.  it's possible and allowed for NSEC3.
+    EXPECT_NO_THROW(generic::NSEC3(
+                        "1 1 1 D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6"));
+}
+
 TEST_F(Rdata_NSEC3_Test, toText) {
     const generic::NSEC3 rdata_nsec3(nsec3_txt);
     EXPECT_EQ(nsec3_txt, rdata_nsec3.toText());
 }
 
 TEST_F(Rdata_NSEC3_Test, badText) {
-    EXPECT_THROW(generic::NSEC3 rdata_nsec3("1 1 1 ADDAFEEE "
-                                            "0123456789ABCDEFGHIJKLMNOPQRSTUV "
-                                            "BIFF POW SPOON"),
+    EXPECT_THROW(generic::NSEC3("1 1 1 ADDAFEEE "
+                                "0123456789ABCDEFGHIJKLMNOPQRSTUV "
+                                "BIFF POW SPOON"),
                  InvalidRdataText);
-    EXPECT_THROW(generic::NSEC3 rdata_nsec3("1 1 1 ADDAFEE "
-                                            "WXYZWXYZWXYZ=WXYZWXYZ==WXYZWXYZW "
-                                            "A NS SOA"),
+    EXPECT_THROW(generic::NSEC3("1 1 1 ADDAFEE "
+                                "WXYZWXYZWXYZ=WXYZWXYZ==WXYZWXYZW A NS SOA"),
                  BadValue);     // bad hex
-    EXPECT_THROW(generic::NSEC3 rdata_nsec3("1 1 1 ADDAFEEE "
-                                            "WXYZWXYZWXYZ=WXYZWXYZ==WXYZWXYZW "
-                                            "A NS SOA"),
+    EXPECT_THROW(generic::NSEC3("1 1 1 -- H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
+                                "A"),
+                 BadValue); // this shouldn't be confused a valid empty salt
+    EXPECT_THROW(generic::NSEC3("1 1 1 ADDAFEEE "
+                                "WXYZWXYZWXYZ=WXYZWXYZ==WXYZWXYZW A NS SOA"),
                  BadValue);     // bad base32hex
-    EXPECT_THROW(generic::NSEC3 rdata_nsec3("1000000 1 1 ADDAFEEE "
-                                            "0123456789ABCDEFGHIJKLMNOPQRSTUV "
-                                            "A NS SOA"),
+    EXPECT_THROW(generic::NSEC3("1000000 1 1 ADDAFEEE "
+                                "0123456789ABCDEFGHIJKLMNOPQRSTUV A NS SOA"),
                  InvalidRdataText);
-    EXPECT_THROW(generic::NSEC3 rdata_nsec3("1 1000000 1 ADDAFEEE "
-                                            "0123456789ABCDEFGHIJKLMNOPQRSTUV "
-                                            "A NS SOA"),
+    EXPECT_THROW(generic::NSEC3("1 1000000 1 ADDAFEEE "
+                                "0123456789ABCDEFGHIJKLMNOPQRSTUV A NS SOA"),
                  InvalidRdataText);
-    EXPECT_THROW(generic::NSEC3 rdata_nsec3("1 1 1000000 ADDAFEEE "
-                                            "0123456789ABCDEFGHIJKLMNOPQRSTUV "
-                                            "A NS SOA"),
+    EXPECT_THROW(generic::NSEC3("1 1 1000000 ADDAFEEE "
+                                "0123456789ABCDEFGHIJKLMNOPQRSTUV A NS SOA"),
                  InvalidRdataText);
-}
 
-TEST_F(Rdata_NSEC3_Test, DISABLED_badText) { // this currently fails
+    // There should be a space between "1" and "D399EAAB" (salt)
     EXPECT_THROW(generic::NSEC3(
                      "1 1 1D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
                      "NS SOA RRSIG DNSKEY NSEC3PARAM"), InvalidRdataText);
+
+    // Salt is too long (possible max + 1 bytes)
+    EXPECT_THROW(generic::NSEC3("1 1 1 " + string(256 * 2, '0') +
+                                " H9RSFB7FPF2L8HG35CMPC765TDK23RP6 NS"),
+                 InvalidRdataText);
+
+    // Hash is too long.  Max = 255 bytes, base32-hex converts each 5 bytes
+    // of the original to 8 characters, so 260 * 8 / 5 is the smallest length
+    // of the encoded string that exceeds the max and doesn't require padding.
+    EXPECT_THROW(generic::NSEC3("1 1 1 D399EAAB " + string((260 * 8) / 5, '0') +
+                                " NS"),
+                 InvalidRdataText);
 }
 
 TEST_F(Rdata_NSEC3_Test, createFromWire) {
+    // Normal case
     const generic::NSEC3 rdata_nsec3(nsec3_txt);
     EXPECT_EQ(0, rdata_nsec3.compare(
                   *rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
                                         "rdata_nsec3_fromWire1")));
 
-    // Too short RDLENGTH
+    // A valid NSEC3 RR with empty type bitmap.
+    EXPECT_NO_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
+                                         "rdata_nsec3_fromWire15.wire"));
+
+    // Too short RDLENGTH: it doesn't even contain the first 5 octets.
     EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
-                                      "rdata_nsec3_fromWire2"),
-                 InvalidRdataLength);
+                                      "rdata_nsec3_fromWire2.wire"),
+                 DNSMessageFORMERR);
+
+    // Invalid bitmap cases are tested in Rdata_NSECBITMAP_Test.
 
-    // Invalid type bits
+    // salt length is too large
     EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
-                                      "rdata_nsec3_fromWire3"),
+                                      "rdata_nsec3_fromWire11.wire"),
                  DNSMessageFORMERR);
+
+    // empty salt.  unusual, but valid.
+    ConstRdataPtr rdata =
+        rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
+                             "rdata_nsec3_fromWire13.wire");
+    EXPECT_EQ(0, dynamic_cast<const generic::NSEC3&>(*rdata).getSalt().size());
+
+    // hash length is too large
+    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
+                                      "rdata_nsec3_fromWire12.wire"),
+                 DNSMessageFORMERR);
+
+    // empty hash.  invalid.
+    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
+                                      "rdata_nsec3_fromWire14.wire"),
+                 DNSMessageFORMERR);
+
+    //
+    // Short buffer cases.  The data is valid NSEC3 RDATA, but the buffer
+    // is trimmed at the end.  All cases should result in an exception from
+    // the buffer class.
+    vector<uint8_t> data;
+    UnitTestUtil::readWireData("rdata_nsec3_fromWire1", data);
+    const uint16_t rdlen = (data.at(0) << 8) + data.at(1);
+    for (int i = 0; i < rdlen; ++i) {
+        // intentionally construct a short buffer
+        InputBuffer b(&data[0] + 2, i);
+        EXPECT_THROW(createRdata(RRType::NSEC3(), RRClass::IN(), b, 39),
+                     InvalidBufferPosition);
+    }
 }
 
 TEST_F(Rdata_NSEC3_Test, toWireRenderer) {
diff --git a/src/lib/dns/tests/rdata_nsec_unittest.cc b/src/lib/dns/tests/rdata_nsec_unittest.cc
index f9ad027..8286dee 100644
--- a/src/lib/dns/tests/rdata_nsec_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec_unittest.cc
@@ -62,46 +62,7 @@ TEST_F(Rdata_NSEC_Test, createFromWire_NSEC) {
                                       "rdata_nsec_fromWire2"),
                  DNSMessageFORMERR);
 
-    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
-                                      "rdata_nsec_fromWire3"),
-                 DNSMessageFORMERR);
-
-    // A malformed NSEC bitmap length field that could cause overflow.
-    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
-                                      "rdata_nsec_fromWire4.wire"),
-                 DNSMessageFORMERR);
-
-    // The bitmap field is incomplete (only the first byte is included)
-    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
-                                      "rdata_nsec_fromWire5.wire"),
-                 DNSMessageFORMERR);
-
-    // Bitmap length is 0, which is invalid.
-    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
-                                      "rdata_nsec_fromWire6.wire"),
-                 DNSMessageFORMERR);
-
-    // A boundary case: longest possible bitmaps (32 maps).  This should be
-    // accepted.
-    EXPECT_NO_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
-                                         "rdata_nsec_fromWire7.wire"));
-
-    // Another boundary condition: 33 bitmaps, which should be rejected.
-    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
-                                      "rdata_nsec_fromWire8.wire"),
-                 DNSMessageFORMERR);
-
-    // Disordered bitmap window blocks.
-    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
-                                      "rdata_nsec_fromWire9.wire"),
-                 DNSMessageFORMERR);
-
-    // Bitmap ending with all-zero bytes.  Not necessarily harmful except
-    // the additional overhead of parsing, but invalid according to the
-    // spec anyway.
-    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
-                                      "rdata_nsec_fromWire10.wire"),
-                 DNSMessageFORMERR);
+    // Invalid bitmap cases are tested in Rdata_NSECBITMAP_Test.
 }
 
 TEST_F(Rdata_NSEC_Test, toWireRenderer_NSEC) {
diff --git a/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc b/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc
new file mode 100644
index 0000000..8a90878
--- /dev/null
+++ b/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc
@@ -0,0 +1,103 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/exceptions.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <gtest/gtest.h>
+
+#include <dns/tests/rdata_unittest.h>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace {
+class Rdata_NSECBITMAP_Test : public RdataTest {
+    // there's nothing to specialize
+};
+
+// Tests against various types of bogus NSEC/NSEC3 type bitmaps.
+// The syntax and semantics are common for both RR types, and our
+// implementation of that part is shared, so in theory it should be sufficient
+// to test for only one RR type.  But we check for both just in case.
+TEST_F(Rdata_NSECBITMAP_Test, createFromWire_NSEC) {
+    // A malformed NSEC bitmap length field that could cause overflow.
+    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
+                                      "rdata_nsec_fromWire4.wire"),
+                 DNSMessageFORMERR);
+    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
+                                      "rdata_nsec3_fromWire4.wire"),
+                 DNSMessageFORMERR);
+
+    // The bitmap field is incomplete (only the first byte is included)
+    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
+                                      "rdata_nsec_fromWire5.wire"),
+                 DNSMessageFORMERR);
+    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
+                                      "rdata_nsec3_fromWire5.wire"),
+                 DNSMessageFORMERR);
+
+    // Bitmap length is 0, which is invalid.
+    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
+                                      "rdata_nsec_fromWire6.wire"),
+                 DNSMessageFORMERR);
+    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
+                                      "rdata_nsec3_fromWire6.wire"),
+                 DNSMessageFORMERR);
+
+    // Too large bitmap length with a short buffer.
+    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
+                                      "rdata_nsec_fromWire3"),
+                 DNSMessageFORMERR);
+    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
+                                      "rdata_nsec3_fromWire3"),
+                 DNSMessageFORMERR);
+
+    // A boundary case: longest possible bitmaps (32 maps).  This should be
+    // accepted.
+    EXPECT_NO_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
+                                         "rdata_nsec_fromWire7.wire"));
+    EXPECT_NO_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
+                                         "rdata_nsec3_fromWire7.wire"));
+
+    // Another boundary condition: 33 bitmaps, which should be rejected.
+    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
+                                      "rdata_nsec_fromWire8.wire"),
+                 DNSMessageFORMERR);
+    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
+                                      "rdata_nsec3_fromWire8.wire"),
+                 DNSMessageFORMERR);
+
+    // Disordered bitmap window blocks.
+    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
+                                      "rdata_nsec_fromWire9.wire"),
+                 DNSMessageFORMERR);
+    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
+                                      "rdata_nsec3_fromWire9.wire"),
+                 DNSMessageFORMERR);
+
+    // Bitmap ending with all-zero bytes.  Not necessarily harmful except
+    // the additional overhead of parsing, but invalid according to the
+    // spec anyway.
+    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
+                                      "rdata_nsec_fromWire10.wire"),
+                 DNSMessageFORMERR);
+    EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
+                                      "rdata_nsec3_fromWire10.wire"),
+                 DNSMessageFORMERR);
+}
+}
diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am
index f6eb90b..1aaddb6 100644
--- a/src/lib/dns/tests/testdata/Makefile.am
+++ b/src/lib/dns/tests/testdata/Makefile.am
@@ -8,9 +8,16 @@ BUILT_SOURCES += rdata_nsec_fromWire4.wire rdata_nsec_fromWire5.wire
 BUILT_SOURCES += rdata_nsec_fromWire6.wire rdata_nsec_fromWire7.wire
 BUILT_SOURCES += rdata_nsec_fromWire8.wire rdata_nsec_fromWire9.wire
 BUILT_SOURCES += rdata_nsec_fromWire10.wire
+BUILT_SOURCES += rdata_nsec3_fromWire2.wire
+BUILT_SOURCES += rdata_nsec3_fromWire4.wire rdata_nsec3_fromWire5.wire
+BUILT_SOURCES += rdata_nsec3_fromWire6.wire rdata_nsec3_fromWire7.wire
+BUILT_SOURCES += rdata_nsec3_fromWire8.wire rdata_nsec3_fromWire9.wire
+BUILT_SOURCES += rdata_nsec3_fromWire10.wire rdata_nsec3_fromWire11.wire
+BUILT_SOURCES += rdata_nsec3_fromWire12.wire rdata_nsec3_fromWire13.wire
+BUILT_SOURCES += rdata_nsec3_fromWire14.wire rdata_nsec3_fromWire15.wire
 BUILT_SOURCES += rdata_rrsig_fromWire2.wire
 BUILT_SOURCES += rdata_soa_toWireUncompressed.wire
-BUILT_SOURCES +=  rdata_txt_fromWire2.wire rdata_txt_fromWire3.wire
+BUILT_SOURCES += rdata_txt_fromWire2.wire rdata_txt_fromWire3.wire
 BUILT_SOURCES += rdata_txt_fromWire4.wire rdata_txt_fromWire5.wire
 BUILT_SOURCES += rdata_tsig_fromWire1.wire rdata_tsig_fromWire2.wire
 BUILT_SOURCES += rdata_tsig_fromWire3.wire rdata_tsig_fromWire4.wire
@@ -43,14 +50,21 @@ EXTRA_DIST += name_toWire5.spec name_toWire6.spec
 EXTRA_DIST += question_fromWire question_toWire1 question_toWire2
 EXTRA_DIST += rdata_cname_fromWire rdata_dname_fromWire rdata_dnskey_fromWire
 EXTRA_DIST += rdata_ds_fromWire rdata_in_a_fromWire rdata_in_aaaa_fromWire
-EXTRA_DIST += rdata_mx_fromWire rdata_mx_toWire1 rdata_ns_fromWire
-EXTRA_DIST += rdata_nsec3_fromWire1 rdata_nsec3_fromWire2 rdata_nsec3_fromWire3
-EXTRA_DIST += rdata_nsec3param_fromWire1 rdata_nsec_fromWire1
-EXTRA_DIST += rdata_nsec_fromWire2 rdata_nsec_fromWire3
+EXTRA_DIST += rdata_mx_fromWire rdata_mx_toWire1 rdata_mx_toWire2
+EXTRA_DIST += rdata_ns_fromWire
+EXTRA_DIST += rdata_nsec_fromWire1 rdata_nsec_fromWire2 rdata_nsec_fromWire3
 EXTRA_DIST += rdata_nsec_fromWire4.spec rdata_nsec_fromWire5.spec
 EXTRA_DIST += rdata_nsec_fromWire6.spec rdata_nsec_fromWire7.spec
 EXTRA_DIST += rdata_nsec_fromWire8.spec rdata_nsec_fromWire9.spec
 EXTRA_DIST += rdata_nsec_fromWire10.spec
+EXTRA_DIST += rdata_nsec3param_fromWire1
+EXTRA_DIST += rdata_nsec3_fromWire1 rdata_nsec3_fromWire3
+EXTRA_DIST += rdata_nsec3_fromWire4.spec rdata_nsec3_fromWire5.spec
+EXTRA_DIST += rdata_nsec3_fromWire6.spec rdata_nsec3_fromWire7.spec
+EXTRA_DIST += rdata_nsec3_fromWire8.spec rdata_nsec3_fromWire9.spec
+EXTRA_DIST += rdata_nsec3_fromWire10.spec rdata_nsec3_fromWire11.spec
+EXTRA_DIST += rdata_nsec3_fromWire12.spec rdata_nsec3_fromWire13.spec
+EXTRA_DIST += rdata_nsec3_fromWire14.spec rdata_nsec3_fromWire15.spec
 EXTRA_DIST += rdata_opt_fromWire rdata_rrsig_fromWire1
 EXTRA_DIST += rdata_rrsig_fromWire2.spec
 EXTRA_DIST += rdata_soa_fromWire rdata_soa_toWireUncompressed.spec
@@ -68,6 +82,7 @@ EXTRA_DIST += rdata_tsig_fromWire9.spec
 EXTRA_DIST += rdata_tsig_toWire1.spec rdata_tsig_toWire2.spec
 EXTRA_DIST += rdata_tsig_toWire3.spec rdata_tsig_toWire4.spec
 EXTRA_DIST += rdata_tsig_toWire5.spec
+EXTRA_DIST += rdata_nsec3_fromWire2.spec
 
 .spec.wire:
 	./gen-wiredata.py -o $@ $<
diff --git a/src/lib/dns/tests/testdata/gen-wiredata.py.in b/src/lib/dns/tests/testdata/gen-wiredata.py.in
index e59063f..645430c 100755
--- a/src/lib/dns/tests/testdata/gen-wiredata.py.in
+++ b/src/lib/dns/tests/testdata/gen-wiredata.py.in
@@ -52,8 +52,11 @@ rdict_rrclass = dict([(dict_rrclass[k], k.upper()) for k in \
                           dict_rrclass.keys()])
 dict_algorithm = { 'rsamd5' : 1, 'dh' : 2, 'dsa' : 3, 'ecc' : 4,
                    'rsasha1' : 5 }
+dict_nsec3_algorithm = { 'reserved' : 0, 'sha1' : 1 }
 rdict_algorithm = dict([(dict_algorithm[k], k.upper()) for k in \
                             dict_algorithm.keys()])
+rdict_nsec3_algorithm = dict([(dict_nsec3_algorithm[k], k.upper()) for k in \
+                                  dict_nsec3_algorithm.keys()])
 
 header_xtables = { 'qr' : dict_qr, 'opcode' : dict_opcode,
                    'rcode' : dict_rcode }
@@ -274,14 +277,16 @@ class TXT:
                                     ' ' if len(wirestring_list[i]) > 0 else '',
                                     wirestring_list[i]))
 
-class NSEC:
-    rdlen = -1                  # auto-calculate
-    nextname = 'next.example.com'
+class NSECBASE:
+    '''Implements rendering NSEC/NSEC3 type bitmaps commonly used for
+    these RRs.  The NSEC and NSEC3 classes will be inherited from this
+    class.'''
     nbitmap = 1                 # number of bitmaps
     block = 0
-    maplen = -1                 # default bitmap length, auto-calculate
+    maplen = None              # default bitmap length, auto-calculate
     bitmap = '040000000003'     # an arbtrarily chosen bitmap sample
     def dump(self, f):
+        # first, construct the bitmpa data
         block_list = []
         maplen_list = []
         bitmap_list = []
@@ -296,30 +301,72 @@ class NSEC:
                 maplen_list.append(self.__dict__[key_maplen])
             else:
                 maplen_list.append(self.maplen)
-            if maplen_list[-1] < 0:
+            if maplen_list[-1] is None: # calculate it if not specified
                 maplen_list[-1] = int(len(bitmap_list[-1]) / 2)
             key_block = 'block' + str(i)
             if key_block in self.__dict__:
                block_list.append(self.__dict__[key_block])
             else:
                 block_list.append(self.block)
+
+        # dump RR-type specific part (NSEC or NSEC3)
+        self.dump_fixedpart(f, 2 * self.nbitmap + \
+                                int(len(''.join(bitmap_list)) / 2))
+
+        # dump the bitmap
+        for i in range(0, self.nbitmap):
+            f.write('# Bitmap: Block=%d, Length=%d\n' %
+                    (block_list[i], maplen_list[i]))
+            f.write('%02x %02x %s\n' %
+                    (block_list[i], maplen_list[i], bitmap_list[i]))
+
+class NSEC(NSECBASE):
+    rdlen = None                # auto-calculate
+    nextname = 'next.example.com'
+    def dump_fixedpart(self, f, bitmap_totallen):
         name_wire = encode_name(self.nextname)
-        rdlen = self.rdlen
-        if rdlen < 0:
+        if self.rdlen is None:
             # if rdlen needs to be calculated, it must be based on the bitmap
             # length, because the configured maplen can be fake.
-            rdlen = int(len(name_wire) / 2) + 2 * self.nbitmap
-            rdlen = rdlen + int(len(''.join(bitmap_list)) / 2)
-        f.write('\n# NSEC RDATA (RDLEN=%d)\n' % rdlen)
-        f.write('%04x\n' % rdlen);
+            self.rdlen = int(len(name_wire) / 2) + bitmap_totallen
+        f.write('\n# NSEC RDATA (RDLEN=%d)\n' % self.rdlen)
+        f.write('%04x\n' % self.rdlen);
         f.write('# Next Name=%s (%d bytes)\n' % (self.nextname,
                                                  int(len(name_wire) / 2)))
         f.write('%s\n' % name_wire)
-        for i in range(0, self.nbitmap):
-            f.write('# Bitmap: Block=%d, Length=%d\n' %
-                    (block_list[i], maplen_list[i]))
-            f.write('%02x %02x %s\n' %
-                    (block_list[i], maplen_list[i], bitmap_list[i]))
+
+class NSEC3(NSECBASE):
+    rdlen = None                # auto-calculate
+    hashalg = 1                 # SHA-1
+    optout = False              # opt-out flag
+    mbz = 0                     # other flag fields (none defined yet)
+    iterations = 1
+    saltlen = 5
+    salt = 's' * saltlen
+    hashlen = 20
+    hash = 'h' * hashlen
+    def dump_fixedpart(self, f, bitmap_totallen):
+        if self.rdlen is None:
+            # if rdlen needs to be calculated, it must be based on the bitmap
+            # length, because the configured maplen can be fake.
+            self.rdlen = 4 + 1 + len(self.salt) + 1 + len(self.hash) \
+                + bitmap_totallen
+        f.write('\n# NSEC3 RDATA (RDLEN=%d)\n' % self.rdlen)
+        f.write('%04x\n' % self.rdlen)
+        optout_val = 1 if self.optout else 0
+        f.write('# Hash Alg=%s, Opt-Out=%d, Other Flags=%0x, Iterations=%d\n' %
+                (code_totext(self.hashalg, rdict_nsec3_algorithm),
+                 optout_val, self.mbz, self.iterations))
+        f.write('%02x %02x %04x\n' %
+                (self.hashalg, (self.mbz << 1) | optout_val, self.iterations))
+        f.write("# Salt Len=%d, Salt='%s'\n" % (self.saltlen, self.salt))
+        f.write('%02x%s%s\n' % (self.saltlen,
+                                ' ' if len(self.salt) > 0 else '',
+                                encode_string(self.salt)))
+        f.write("# Hash Len=%d, Hash='%s'\n" % (self.hashlen, self.hash))
+        f.write('%02x%s%s\n' % (self.hashlen,
+                                ' ' if len(self.hash) > 0 else '',
+                                encode_string(self.hash)))
 
 class RRSIG:
     rdlen = -1                  # auto-calculate
@@ -415,7 +462,7 @@ def get_config_param(section):
                     'question' : (DNSQuestion, question_xtables),
                     'edns' : (EDNS, {}), 'soa' : (SOA, {}), 'txt' : (TXT, {}),
                     'rrsig' : (RRSIG, {}), 'nsec' : (NSEC, {}),
-                    'tsig' : (TSIG, {}) }
+                    'nsec3' : (NSEC3, {}), 'tsig' : (TSIG, {}) }
     s = section
     m = re.match('^([^:]+)/\d+$', section)
     if m:
diff --git a/src/lib/dns/tests/testdata/rdata_mx_toWire2 b/src/lib/dns/tests/testdata/rdata_mx_toWire2
new file mode 100644
index 0000000..ebd2f27
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_mx_toWire2
@@ -0,0 +1,12 @@
+#
+# compressed MX RDATA stored in an output buffer
+#
+# sentinel name: example.com.
+# 0  1  2  3  4  5  6  7  8  9 10  1  2 (bytes)
+#(7) e  x  a  m  p  l  e (3) c  o  m  .
+ 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# PREFERENCE: 10
+  00 0a
+# EXCHANGE: not compressed
+#(4) m  x ptr=0
+ 02 6d 78 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire1.spec
new file mode 100644
index 0000000..39a78d7
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire1.spec
@@ -0,0 +1,7 @@
+#
+# A malformed NSEC3 RDATA: bit map length is too large, causing overflow
+#
+
+[custom]
+sections: nsec3
+[nsec3]
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.spec
new file mode 100644
index 0000000..30417f5
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.spec
@@ -0,0 +1,8 @@
+#
+# An invalid NSEC3 RDATA: a bitmap block containing empty bytes
+#
+
+[custom]
+sections: nsec3
+[nsec3]
+bitmap: '01000000'
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.spec
new file mode 100644
index 0000000..80ec59f
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.spec
@@ -0,0 +1,8 @@
+#
+# An invalid NSEC3 RDATA: Saltlen is too large
+#
+
+[custom]
+sections: nsec3
+[nsec3]
+rdlen: 7
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.spec
new file mode 100644
index 0000000..1e01655
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.spec
@@ -0,0 +1,9 @@
+#
+# An invalid NSEC3 RDATA: Hash length is too large
+#
+
+[custom]
+sections: nsec3
+[nsec3]
+# only contains the first byte of hash
+rdlen: 12
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.spec
new file mode 100644
index 0000000..fcc9d53
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.spec
@@ -0,0 +1,9 @@
+#
+# A valid (but unusual) NSEC3 RDATA: salt is empty.
+#
+
+[custom]
+sections: nsec3
+[nsec3]
+saltlen: 0
+salt: ''
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.spec
new file mode 100644
index 0000000..a0550d5
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.spec
@@ -0,0 +1,9 @@
+#
+# An invalid NSEC3 RDATA: empty hash
+#
+
+[custom]
+sections: nsec3
+[nsec3]
+hashlen: 0
+hash: ''
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.spec
new file mode 100644
index 0000000..4993e03
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.spec
@@ -0,0 +1,10 @@
+#
+# NSEC3 RDATA with empty type bitmap.  It's okay.
+# The test data includes bytes for a bitmap field, but RDLEN indicates
+# it's not part of the RDATA and so it will be ignored.
+#
+
+[custom]
+sections: nsec3
+[nsec3]
+rdlen: 31
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2 b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2
deleted file mode 100644
index 0965a27..0000000
--- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2
+++ /dev/null
@@ -1,12 +0,0 @@
-#
-# NSEC3 RDATA with a bogus RDLEN (too short)
-#
-
-# RDLENGTH, 29 bytes (should be 39)
-00 1e
-
-# NSEC3 record:
-# 1 1 1 D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6 NS SOA RRSIG DNSKEY NSEC3PARAM
-01 01 00 01 04 d3 99 ea ab 14 8a 77 c7 ac ef cb
-c5 54 46 03 2b 2d 96 1c c5 eb 68 21 ef 26 00 07
-22 00 00 00 00 02 90
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.spec
new file mode 100644
index 0000000..0b6a5af
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.spec
@@ -0,0 +1,9 @@
+#
+# A malformed NSEC3 RDATA: RDLEN indicates it doesn't even contain the fixed
+# 5 octects
+#
+
+[custom]
+sections: nsec3
+[nsec3]
+rdlen: 4
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.spec
new file mode 100644
index 0000000..06d6eb4
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.spec
@@ -0,0 +1,9 @@
+#
+# A malformed NSEC3 RDATA: bit map length is too large, causing overflow
+#
+
+[custom]
+sections: nsec3
+[nsec3]
+maplen: 31
+bitmap: '01'
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire5.spec
new file mode 100644
index 0000000..2d5713c
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire5.spec
@@ -0,0 +1,13 @@
+#
+# A malformed NSEC3 RDATA: incomplete bit map field
+#
+
+[custom]
+sections: nsec3
+[nsec3]
+# only containing the block field of the bitmap
+rdlen: 32
+#dummy data
+maplen: 31
+#dummy data
+bitmap: '00'
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.spec
new file mode 100644
index 0000000..36e9e59
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.spec
@@ -0,0 +1,11 @@
+#
+# A malformed NSEC3 RDATA: bit map length being 0
+#
+
+[custom]
+sections: nsec3
+[nsec3]
+rdlen: 33
+maplen: 0
+# dummy data:
+bitmap: '01'
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire7.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire7.spec
new file mode 100644
index 0000000..338c0c9
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire7.spec
@@ -0,0 +1,9 @@
+#
+# NSEC3 RDATA with a longest bitmap field (32 bitmap bytes)
+#
+
+[custom]
+sections: nsec3
+[nsec3]
+maplen: 32
+bitmap: '0101010101010101010101010101010101010101010101010101010101010101'
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire8.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire8.spec
new file mode 100644
index 0000000..041714e
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire8.spec
@@ -0,0 +1,9 @@
+#
+# An invalid NSEC3 RDATA with an oversized bitmap field (33 bitmap bytes)
+#
+
+[custom]
+sections: nsec3
+[nsec3]
+maplen: 33
+bitmap: '010101010101010101010101010101010101010101010101010101010101010101'
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.spec
new file mode 100644
index 0000000..b04c84f
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.spec
@@ -0,0 +1,10 @@
+#
+# An invalid NSEC3 RDATA: disordered bitmap blocks
+#
+
+[custom]
+sections: nsec3
+[nsec3]
+nbitmap: 2
+block0: 2
+block1: 1
diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am
index 900f11b..416fd06 100644
--- a/src/lib/log/Makefile.am
+++ b/src/lib/log/Makefile.am
@@ -1,21 +1,19 @@
-if USE_LOG4CXX
 SUBDIRS = . compiler tests
-endif
 
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CPPFLAGS += $(LOG4CXX_INCLUDES)
+# AM_CPPFLAGS += $(LOG4CXX_INCLUDES)
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
 
 CLEANFILES = *.gcno *.gcda
 
 lib_LTLIBRARIES = liblog.la
 liblog_la_SOURCES  =
-liblog_la_SOURCES += dbglevels.h
+liblog_la_SOURCES += debug_levels.h logger_levels.h
 liblog_la_SOURCES += dummylog.h dummylog.cc
-if USE_LOG4CXX
 liblog_la_SOURCES += filename.h filename.cc
 liblog_la_SOURCES += logger.cc logger.h
+liblog_la_SOURCES += logger_impl.cc logger_impl.h
 liblog_la_SOURCES += logger_support.cc logger_support.h
 liblog_la_SOURCES += messagedef.cc messagedef.h
 liblog_la_SOURCES += message_dictionary.cc message_dictionary.h
@@ -25,10 +23,6 @@ liblog_la_SOURCES += message_reader.cc message_reader.h
 liblog_la_SOURCES += message_types.h
 liblog_la_SOURCES += root_logger_name.cc root_logger_name.h
 liblog_la_SOURCES += strutil.h strutil.cc
-liblog_la_SOURCES += xdebuglevel.cc xdebuglevel.h
-
-liblog_la_LDFLAGS = $(LOG4CXX_LDFLAGS)
-endif
 
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc
index 50e33a6..6f9c4e0 100644
--- a/src/lib/log/compiler/message.cc
+++ b/src/lib/log/compiler/message.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,9 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <cctype>
+#include <cstddef>
 #include <fstream>
 #include <iostream>
 #include <string>
@@ -47,7 +46,7 @@ static const char* VERSION = "1.0-0";
 ///
 /// \li A .h file containing message definition
 /// \li A .cc file containing code that adds the messages to the program's
-/// message disctionary at start-up time.
+/// message dictionary at start-up time.
 ///
 /// Alternatively, the program can produce a .py file that contains the
 /// message definitions.
@@ -56,20 +55,21 @@ static const char* VERSION = "1.0-0";
 /// \b Invocation<BR>
 /// The program is invoked with the command:
 ///
-/// <tt>message [-p] \<message-file\></tt>
+/// <tt>message [-v | -h | \<message-file\>]</tt>
 ///
 /// It reads the message file and writes out two files of the same name but with
 /// extensions of .h and .cc.
 ///
-/// If \c -p is specified, the C++ files are not written; instead a Python file
-/// of the same name (but with the file extension .py) is written.
+/// \-v causes it to print the version number and exit. \-h prints a help
+/// message (and exits).
 
 
 /// \brief Print Version
 ///
 /// Prints the program's version number.
 
-static void version() {
+void
+version() {
     cout << VERSION << "\n";
 }
 
@@ -77,16 +77,12 @@ static void version() {
 ///
 /// Prints program usage to stdout.
 
-static void usage() {
+void
+usage() {
     cout <<
-        "Usage: message [-h] [-p] [-v] <message-file>\n" <<
+        "Usage: message [-h] [-v] <message-file>\n" <<
         "\n" <<
         "-h       Print this message and exit\n" <<
-        "-p       Output a Python module holding the message definitions.\n" <<
-        "         By default a C++ header file and implementation file are\n" <<
-
-
-        "         written.\n" <<
         "-v       Print the program version and exit\n" <<
         "\n" <<
         "<message-file> is the name of the input message file.\n";
@@ -99,15 +95,13 @@ static void usage() {
 ///
 /// \return Current time
 
-static string currentTime() {
+string
+currentTime() {
 
-    // Get the current time.
+    // Get a text representation of the current time.
     time_t curtime;
     time(&curtime);
-
-    // Format it
-    char buffer[32];
-    ctime_r(&curtime, buffer);
+    char* buffer = ctime(&curtime);
 
     // Convert to string and strip out the trailing newline
     string current_time = buffer;
@@ -115,8 +109,6 @@ static string currentTime() {
 }
 
 
-
-
 /// \brief Create Header Sentinel
 ///
 /// Given the name of a file, create an #ifdef sentinel name.  The name is
@@ -127,7 +119,8 @@ static string currentTime() {
 ///
 /// \return Sentinel name
 
-static string sentinel(Filename& file) {
+string
+sentinel(Filename& file) {
 
     string name = file.name();
     string ext = file.extension();
@@ -143,11 +136,12 @@ static string sentinel(Filename& file) {
 /// characters.  This is used to handle the fact that the input file does not
 /// contain quotes, yet the string will be included in a C++ literal string.
 
-string quoteString(const string& instring) {
+string
+quoteString(const string& instring) {
 
     // Create the output string and reserve the space needed to hold the input
     // string. (Most input strings will not contain quotes, so this single
-    // reservation should be all that is needed.) 
+    // reservation should be all that is needed.)
     string outstring;
     outstring.reserve(instring.size());
 
@@ -172,11 +166,12 @@ string quoteString(const string& instring) {
 ///
 /// \return Sorted list of message IDs
 
-vector<MessageID> sortedIdentifiers(MessageDictionary* dictionary) {
-    vector<MessageID> ident;
+vector<string>
+sortedIdentifiers(MessageDictionary& dictionary) {
+    vector<string> ident;
 
-    for (MessageDictionary::const_iterator i = dictionary->begin();
-         i != dictionary->end(); ++i) {
+    for (MessageDictionary::const_iterator i = dictionary.begin();
+         i != dictionary.end(); ++i) {
         ident.push_back(i->first);
     }
     sort(ident.begin(), ident.end());
@@ -185,17 +180,83 @@ vector<MessageID> sortedIdentifiers(MessageDictionary* dictionary) {
 }
 
 
+/// \brief Split Namespace
+///
+/// The $NAMESPACE directive may well specify a namespace in the form a::b.
+/// Unfortunately, the C++ "namespace" statement can only accept a single
+/// string - to set up the namespace of "a::b" requires two statements, one
+/// for "namspace a" and the other for "namespace b".
+///
+/// This function returns the set of namespace components as a vector of
+/// strings.  A vector of one element, containing the empty string, is returned
+/// if the anonymous namespace is specified.
+///
+/// \param ns Argument to $NAMESPACE (passed by value, as we will be modifying
+/// it.)
+
+vector<string>
+splitNamespace(string ns) {
+
+    // Namespaces components are separated by double colon characters -
+    // convert to single colons.
+    size_t dcolon;
+    while ((dcolon = ns.find("::")) != string::npos) {
+        ns.replace(dcolon, 2, ":");
+    }
+
+    // ... and return the vector of namespace components split on the single
+    // colon.
+    return isc::strutil::tokens(ns, ":");
+}
+
+
+/// \brief Write Opening Namespace(s)
+///
+/// Writes the lines listing the namespaces in use.
+void
+writeOpeningNamespace(ostream& output, const vector<string>& ns) {
+    if (!ns.empty()) {
+
+        // Output namespaces in correct order
+        for (int i = 0; i < ns.size(); ++i) {
+            output << "namespace " << ns[i] << " {\n";
+        }
+        output << "\n";
+    }
+}
+
+
+/// \brief Write Closing Namespace(s)
+///
+/// Writes the lines listing the namespaces in use.
+void
+writeClosingNamespace(ostream& output, const vector<string>& ns) {
+    if (!ns.empty()) {
+        for (int i = ns.size() - 1; i >= 0; --i) {
+            output << "} // namespace " << ns[i] << "\n";
+        }
+        output << "\n";
+    }
+}
+
+
 /// \brief Write Header File
 ///
-/// Writes the C++ header file containing the symbol definitions.
+/// Writes the C++ header file containing the symbol definitions.  These are
+/// "extern" references to definitions in the .cc file.  As such, they should
+/// take up no space in the module in which they are included, and redundant
+/// references should be removed by the compiler.
 ///
 /// \param file Name of the message file.  The header file is written to a
 /// file of the same name but with a .h suffix.
 /// \param prefix Prefix string to use in symbols
+/// \param ns Namespace in which the definitions are to be placed.  An empty
+/// string indicates no namespace.
 /// \param dictionary Dictionary holding the message definitions.
 
-void writeHeaderFile(const string& file, const string& prefix,
-    MessageDictionary* dictionary)
+void
+writeHeaderFile(const string& file, const string& prefix,
+        const vector<string>& ns_components, MessageDictionary& dictionary)
 {
     Filename message_file(file);
     Filename header_file(message_file.useAsDefault(".h"));
@@ -208,7 +269,7 @@ void writeHeaderFile(const string& file, const string& prefix,
 
     try {
         if (hfile.fail()) {
-            throw MessageException(MSG_OPENOUT, header_file.fullName(),
+            throw MessageException(MSG_OPNMSGOUT, header_file.fullName(),
                 strerror(errno));
         }
 
@@ -223,27 +284,26 @@ void writeHeaderFile(const string& file, const string& prefix,
              "#define "  << sentinel_text << "\n" <<
              "\n" <<
              "#include <log/message_types.h>\n" <<
-             "\n" <<
-             "namespace {\n" <<
              "\n";
 
-        vector<MessageID> idents = sortedIdentifiers(dictionary);
-        for (vector<MessageID>::const_iterator j = idents.begin();
+        // Write the message identifiers, bounded by a namespace declaration
+        writeOpeningNamespace(hfile, ns_components);
+
+        vector<string> idents = sortedIdentifiers(dictionary);
+        for (vector<string>::const_iterator j = idents.begin();
             j != idents.end(); ++j) {
-            hfile << "isc::log::MessageID " << prefix << *j <<
-                " = \"" << *j << "\";\n";
+            hfile << "extern const isc::log::MessageID " << prefix << *j << ";\n";
         }
+        hfile << "\n";
+
+        writeClosingNamespace(hfile, ns_components);
 
         // ... and finally the postamble
-        hfile <<
-            "\n" <<
-            "} // Anonymous namespace\n" <<
-            "\n" <<
-            "#endif // " << sentinel_text << "\n";
+        hfile << "#endif // " << sentinel_text << "\n";
 
         // Report errors (if any) and exit
         if (hfile.fail()) {
-            throw MessageException(MSG_WRITERR, header_file.fullName(),
+            throw MessageException(MSG_MSGWRTERR, header_file.fullName(),
                 strerror(errno));
         }
 
@@ -260,18 +320,40 @@ void writeHeaderFile(const string& file, const string& prefix,
 ///
 /// Simple function for use in a call to transform
 
-char replaceNonAlphaNum(char c) {
+char
+replaceNonAlphaNum(char c) {
     return (isalnum(c) ? c : '_');
 }
 
 
 /// \brief Write Program File
 ///
-/// Writes the C++ source code file.  This defines an external objects whose
-/// constructor is run at initialization time.  The constructor adds the message
-/// definitions to the main global dictionary.
-
-void writeProgramFile(const string& file, MessageDictionary* dictionary)
+/// Writes the C++ source code file.  This defines the text of the message
+/// symbols, as well as the initializer object that sets the entries in the
+/// global dictionary.
+///
+/// The construction of the initializer object loads the dictionary with the
+/// message text.  However, nothing actually references it.  If the initializer
+/// were in a file by itself, the lack of things referencing it would cause the
+/// linker to ignore it when pulling modules out of the logging library in a
+/// static link.  By including it in the file with the symbol definitions, the
+/// module will get included in the link process to resolve the symbol
+/// definitions, and so the initializer object will be included in the final
+/// image. (Note that there are no such problems when the logging library is
+/// built as a dynamically-linked library: the whole library - including the
+/// initializer module - gets mapped into address space when the library is
+/// loaded, after which all the initializing code (including the constructors
+/// of objects declared outside functions) gets run.)
+///
+/// There _may_ be a problem when we come to port this to Windows.  Microsoft
+/// Visual Studio contains a "Whole Program Optimisation" option, where the
+/// optimisation is done at link-time, not compiler-time.  In this it _may_
+/// decide to remove the initializer object because of a lack of references
+/// to it.  But until BIND-10 is ported to Windows, we won't know.
+
+void
+writeProgramFile(const string& file, const string& prefix,
+    const vector<string>& ns_components, MessageDictionary& dictionary)
 {
     Filename message_file(file);
     Filename program_file(message_file.useAsDefault(".cc"));
@@ -280,7 +362,7 @@ void writeProgramFile(const string& file, MessageDictionary* dictionary)
     ofstream ccfile(program_file.fullName().c_str());
     try {
         if (ccfile.fail()) {
-            throw MessageException(MSG_OPENOUT, program_file.fullName(),
+            throw MessageException(MSG_OPNMSGOUT, program_file.fullName(),
                 strerror(errno));
         }
 
@@ -292,45 +374,53 @@ void writeProgramFile(const string& file, MessageDictionary* dictionary)
                 currentTime() << "\n" <<
              "\n" <<
              "#include <cstddef>\n" <<
+             "#include <log/message_types.h>\n" <<
              "#include <log/message_initializer.h>\n" <<
-             "\n" <<
-             "using namespace isc::log;\n" <<
-             "\n" <<
+             "\n";
+
+        // Declare the message symbols themselves.
+
+        writeOpeningNamespace(ccfile, ns_components);
+
+        vector<string> idents = sortedIdentifiers(dictionary);
+        for (vector<string>::const_iterator j = idents.begin();
+            j != idents.end(); ++j) {
+            ccfile << "extern const isc::log::MessageID " << prefix << *j <<
+                " = \"" << *j << "\";\n";
+        }
+        ccfile << "\n";
+
+        writeClosingNamespace(ccfile, ns_components);
+
+        // Now the code for the message initialization.
+
+        ccfile <<
              "namespace {\n" <<
              "\n" <<
              "const char* values[] = {\n";
 
         // Output the identifiers and the associated text.
-        vector<MessageID> idents = sortedIdentifiers(dictionary);
-        for (vector<MessageID>::const_iterator i = idents.begin();
+        idents = sortedIdentifiers(dictionary);
+        for (vector<string>::const_iterator i = idents.begin();
             i != idents.end(); ++i) {
                 ccfile << "    \"" << *i << "\", \"" <<
-                    quoteString(dictionary->getText(*i)) << "\",\n";
+                    quoteString(dictionary.getText(*i)) << "\",\n";
         }
 
+
         // ... and the postamble
         ccfile <<
             "    NULL\n" <<
             "};\n" <<
             "\n" <<
+            "const isc::log::MessageInitializer initializer(values);\n" <<
+            "\n" <<
             "} // Anonymous namespace\n" <<
             "\n";
 
-        // Now construct a unique name.  We don't put the message initializer as
-        // a static variable or in an anonymous namespace lest the C++
-        // compiler's optimizer decides it can optimise it away.
-        string unique_name = program_file.name() + program_file.extension() +
-            "_" + currentTime();
-        transform(unique_name.begin(), unique_name.end(), unique_name.begin(),
-            replaceNonAlphaNum);
-
-        // ... and write the initialization code
-        ccfile <<
-            "MessageInitializer " << unique_name << "(values);\n";
-
         // Report errors (if any) and exit
         if (ccfile.fail()) {
-            throw MessageException(MSG_WRITERR, program_file.fullName(),
+            throw MessageException(MSG_MSGWRTERR, program_file.fullName(),
                 strerror(errno));
         }
 
@@ -350,7 +440,8 @@ void writeProgramFile(const string& file, MessageDictionary* dictionary)
 ///
 /// \param reader Message Reader used to read the file
 
-static void warnDuplicates(MessageReader& reader) {
+void
+warnDuplicates(MessageReader& reader) {
 
     // Get the duplicates (the overflow) and, if present, sort them into some
     // order and remove those which occur more than once (which mean that they
@@ -375,19 +466,15 @@ static void warnDuplicates(MessageReader& reader) {
 /// Parses the options then dispatches to the appropriate function.  See the
 /// main file header for the invocation.
 
-int main(int argc, char** argv) {
-    
-    const struct option loptions[] = {          // Long options
-        {"help",    no_argument, NULL, 'h'},
-        {"version", no_argument, NULL, 'v'},
-        {NULL,      0,           NULL, 0  }
-    };
+int
+main(int argc, char* argv[]) {
+
     const char* soptions = "hv";               // Short options
 
     optind = 1;             // Ensure we start a new scan
     int  opt;               // Value of the option
 
-    while ((opt = getopt_long(argc, argv, soptions, loptions, NULL)) != -1) {
+    while ((opt = getopt(argc, argv, soptions)) != -1) {
         switch (opt) {
             case 'h':
                 usage();
@@ -424,19 +511,28 @@ int main(int argc, char** argv) {
         MessageReader reader(&dictionary);
         reader.readFile(message_file);
 
-        // Now write the header file.
-        writeHeaderFile(message_file, reader.getPrefix(), &dictionary);
+        // Get the namespace into which the message definitions will be put and
+        // split it into components.
+        vector<string> ns_components = splitNamespace(reader.getNamespace());
+
+        // Write the header file.
+        writeHeaderFile(message_file, reader.getPrefix(), ns_components,
+            dictionary);
+
+        // Write the file that defines the message symbols and text
+        writeProgramFile(message_file, reader.getPrefix(), ns_components,
+            dictionary);
 
-        // ... and the message text file.
-        writeProgramFile(message_file, &dictionary);
 
         // Finally, warn of any duplicates encountered.
         warnDuplicates(reader);
     }
     catch (MessageException& e) {
         // Create an error message from the ID and the text
-        MessageDictionary* global = MessageDictionary::globalDictionary();
-        string text = e.id() + ", " + global->getText(e.id());
+        MessageDictionary& global = MessageDictionary::globalDictionary();
+        string text = e.id();
+        text += ", ";
+        text += global.getText(e.id());
 
         // Format with arguments
         text = isc::strutil::format(text, e.arguments());
diff --git a/src/lib/log/dbglevels.h b/src/lib/log/dbglevels.h
deleted file mode 100644
index 35c6878..0000000
--- a/src/lib/log/dbglevels.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-// $Id$
-
-#ifndef __DBGLEVELS_H
-#define __DBGLEVELS_H
-
-/// \brief Defines Debug Levels
-///
-/// Defines the maximum and minimum debug levels and the number of levels.
-/// These are defined using #define as they are referenced in the construction
-/// of variables declared outside execution units.  (In this way we avoid the
-/// "static initialization fiasco" problem.)
-
-#define MIN_DEBUG_LEVEL (0)
-#define MAX_DEBUG_LEVEL (99)
-#define NUM_DEBUG_LEVEL (MAX_DEBUG_LEVEL - MIN_DEBUG_LEVEL + 1)
-
-#endif // __DBGLEVELS_H
diff --git a/src/lib/log/debug_levels.h b/src/lib/log/debug_levels.h
new file mode 100644
index 0000000..bb2b524
--- /dev/null
+++ b/src/lib/log/debug_levels.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __DEBUG_LEVELS_H
+#define __DEBUG_LEVELS_H
+
+/// \brief Defines Debug Levels
+///
+/// Defines the maximum and minimum debug levels and the number of levels.
+/// These are defined using #define as they are referenced in the construction
+/// of variables declared outside execution units.  (In this way we avoid the
+/// "static initialization fiasco" problem.)
+
+#define MIN_DEBUG_LEVEL (0)
+#define MAX_DEBUG_LEVEL (99)
+#define NUM_DEBUG_LEVEL (MAX_DEBUG_LEVEL - MIN_DEBUG_LEVEL + 1)
+
+#endif // __DEBUG_LEVELS_H
diff --git a/src/lib/log/documentation.txt b/src/lib/log/documentation.txt
index 1439ad5..0501587 100644
--- a/src/lib/log/documentation.txt
+++ b/src/lib/log/documentation.txt
@@ -13,19 +13,19 @@ Hierarchical Logging System
 When a program writes a message to the logging system, it does so using an
 instance of the Logger class.  As well as performing the write of the message,
 the logger identifies the source of the message: different sources can write
-to different destinations and can log different severities of messages.  For
-example, the "cache" logger could write messages of DEBUG severity or above
-to a file while all other components write messages of "INFO" severity or above
-to the Syslog file.
+to different destinations and can log different severities of messages.
+For example, the "cache" logger could write messages of DEBUG severity or
+above to a file while all other components write messages of "INFO" severity
+or above to the Syslog file.
 
-The loggers are hierarchical in that each logger is the child of another logger.
-The top of the hierarchy is the root logger, which does not have a parent.  The
-point of the hierarchy is that unless a logger is explicitly assigned an
-attribute (such as severity of message being logger), it picks it up from the
-parent.  (In BIND-10, there is the root logger (named after the program) and
-every other logger is a child of that.)  So in the example above, the
-INFO/Syslog attributes could be associated with the root logger while the
-DEBUG/file attributes are associated with the "cache" logger.
+The loggers are hierarchical in that each logger is the child of another
+logger.  The top of the hierarchy is the root logger, which does not have
+a parent.  The point of the hierarchy is that unless a logger is explicitly
+assigned an attribute (such as severity of message being logger), it picks
+it up from the parent.  (In BIND-10, there is the root logger (named after
+the program) and every other logger is a child of that.)  So in the example
+above, the INFO/Syslog attributes could be associated with the root logger
+while the DEBUG/file attributes are associated with the "cache" logger.
 
 
 Separation of Messages Definitions And Text
@@ -38,10 +38,11 @@ retrieve the message associated with it (e.g. "unable to open %s for input").
 substitutes any message parameters (in this example, the string that is an
 invalid filename) and logs it to the destination.
 
-In the BIND-10 system, a set of default messages are linked into the program.
-At run-time. each program reads a message file, updating the stored definitions;
-this updated text is logged.  However, to aid support, the message identifier
-so in the example above, the message finally logged would be something like:
+In the BIND-10 system, a set of default messages are linked into the
+program.  At run-time. each program reads a message file, updating the
+stored definitions; this updated text is logged.  However, to aid support,
+the message identifier so in the example above, the message finally logged
+would be something like:
 
     OPENIN, unable to open a.txt for input
 
@@ -57,11 +58,11 @@ The steps in using the system are:
    Ideally the file should have a file type of ".msg".
 
 2. Run it through the message compiler to produce the .h and .cc files.  It
-   is intended that this step be included in the build process.  However, for
-   not run the compiler (found in the "compiler" subdirectory) manually.  The
-   only argument is the name of the message file: it will produce as output
-   two files, having the same name as the input file but with file types of
-   ".h" and ".cc".
+   is intended that this step be included in the build process.  However,
+   for not run the compiler (found in the "compiler" subdirectory) manually.
+   The only argument is the name of the message file: it will produce as
+   output two files, having the same name as the input file but with file
+   types of ".h" and ".cc".
 
    The compiler is built in the "compiler" subdirectory of the "src/lib/log"
    directory.
@@ -70,13 +71,13 @@ The steps in using the system are:
    make sure that the .cc file is compiled and linked into your program -
    static initialization will add the symbols to the global dictionary.
 
-4. Declare loggers in your code and use them to log messages.  This is described
-   in more detail below.
+4. Declare loggers in your code and use them to log messages.  This is
+   described in more detail below.
 
 5. To set the debug level and run-time message file, call runTimeInit (declared
    in logger_support.h) in the main program unit.  This is a temporary solution
-   for Year 2, and will be replaced at a later date, the information coming from
-   the configuration database.
+   for Year 2, and will be replaced at a later date, the information coming
+   from the configuration database.
 
 
 Message Files
@@ -84,9 +85,9 @@ Message Files
 
 File Contents and Format
 ------------------------
-A message file is a file containing message definitions.  Typically there will
-be one message file for each component that declares message symbols.  An
-example file could be:
+A message file is a file containing message definitions.  Typically there
+will be one message file for each component that declares message symbols.
+An example file could be:
 
 -- BEGIN --
 
@@ -94,6 +95,7 @@ example file could be:
 # $ID:$
 
 $PREFIX TEST_
+$NAMESPACE isc::log
 TEST1       message %s is much too large
 + This message is a test for the general message code
 
@@ -104,8 +106,8 @@ UNKNOWN     unknown message
 
 Points to note:
 * Leading and trailing space are trimmed from the line.  Although the above
-  exampl,e has every line starting at column 1, the lines could be indented if
-  desired.
+  exampl,e has every line starting at column 1, the lines could be indented
+  if desired.
 
 * Blank lines are ignored.
 
@@ -113,16 +115,22 @@ Points to note:
   a line by themselves - inline comments will be interpreted as part of the
   text of the line.
 
-* Lines starting $ are directives.  At present, the only directive recognised
-  is $PREFIX, which has one argument: the string used to prefix symbols.  If
-  there is no facility directive, there is no prefix to the symbols. (Prefixes
-  are explained below.)
+* Lines starting $ are directives.  At present, two directives are recognised:
+
+  * $PREFIX, which has one argument: the string used to prefix symbols.  If
+    absent, there is no prefix to the symbols. (Prefixes are explained below.)
+  * $NAMESPACE, which has one argument: the namespace in which the symbols are
+    created.  (Specifying the argument as a double colon - i.e. "$NAMESPACE
+    ::" puts the symbol definitions in the unnamed namespace.  And not
+    including a $NAMESPACE directive will result in the symbols note being
+    put in any namespace.
 
 * Lines starting + indicate an explanation for the preceding message.  These
-  are intended to be processed by a separate program and used to generate an
-  error messages manual.  However they are treated like comments by the message
-  compiler.  As with comments, these must be on a line by themselves; if inline,
-  the text (including the leading "+") will be interpreted as part of the line.
+  are intended to be processed by a separate program and used to generate
+  an error messages manual.  However they are treated like comments by the
+  message compiler.  As with comments, these must be on a line by themselves;
+  if inline, the text (including the leading "+") will be interpreted as
+  part of the line.
 
 * Message lines.  These comprise a symbol name and a message, which may
   include zero or more printf-style tokens.  Symbol names will be upper-cased
@@ -132,12 +140,17 @@ Points to note:
 Message Compiler
 ----------------
 The message compiler is a program built in the src/log/compiler directory.
-It processes the message file to produce two files:
+It is invoked by the command:
+
+    message [-h] [-v] <message-file>
+
+("-v" prints the version number and exits; "-h" prints brief help text.)
+The message compiler processes the message file to produce two files:
 
 1) A C++ header file (called <message-file-name>.h) that holds lines of
 the form:
 
-   namespace {
+   namespace <namespace> {
    isc::log::MessageID PREFIX_IDENTIFIER = "IDENTIFIER";
       :
    }
@@ -146,12 +159,17 @@ The symbols define the keys in the global message dictionary.  At present
 they are defined as std::strings, but a future implementation could redefine
 them as numeric values.
 
+The namespace enclosing the symbols is set by the $NAMESPACE directive.
+
 The "PREFIX_" part of the symbol name is the string defined in the $PREFIX
 the argument to the directive.  So "$PREFIX MSG_" would prefix the identifer
 ABC with "MSG_" to give the symbol MSG_ABC.  Similarly "$PREFIX E" would
 prefix it with "E" to give the symbol EABC.  If no $PREFIX is given, no
 prefix appears (so the symbol in this example would be ABC).
 
+The header file also includes a couple of lines to ensure that the message
+text is included in the final program image.
+
 
 2) A C++ source file (called <message-file-name>.cc) that holds the code to
 insert the symbols and messages into the map.
@@ -201,8 +219,7 @@ To use the current version of the logging:
 
        isc::log::RootLoggerName("b10-auth");
 
-   It should be declared outside an execution unit to allow other statically-
-   declared loggers to pick it up.
+   This can be declared inside or outside an execution unit.
 
 2. In the code that needs to do logging, declare a logger with a given name,
    e.g.
@@ -216,18 +233,8 @@ To use the current version of the logging:
 
        isc::log::Logger logger("myname", true);
 
-   This is due to an apparent bug in the underlying log4cxx, where the deletion
-   of a statically-declared object at program termination can cause a segment
-   fault. (The destruction of internal memory structures can sometimes happen
-   out of order.)  By default the Logger class creates the structures in its
-   constructor but does not delete them in the destruction.  The default
-   behavious works because instead of reclaiming memory at program run-down,
-   the operating system reclaims it when the process is deleted.
-
-   Setting the second argument "true" causes the Logger's destructor to delete
-   the log4cxx structures.  This does not cause a problem if the program is
-   not terminating.  So use the second form when declaring an automatic
-   instance of isc::log::Logger on the stack.
+   The argument is ignored for underlying implementations other than log4cxx.
+   See below for the use of this argument.
 
 3. The main program unit should include a call to isc::log::runTimeInit()
    (defined in logger_support.h) to set the logging severity, debug log level,
@@ -235,12 +242,12 @@ To use the current version of the logging:
 
    a) The logging severity is one of the enum defined in logger.h, i.e.
 
-        isc::log::Logger::DEBUG
-        isc::log::Logger::INFO
-        isc::log::Logger::WARN
-        isc::log::Logger::ERROR
-        isc::log::Logger::FATAL
-        isc::log::Logger::NONE
+        isc::log::DEBUG
+        isc::log::INFO
+        isc::log::WARN
+        isc::log::ERROR
+        isc::log::FATAL
+        isc::log::NONE
 
    b) The debug log level is only interpreted when the severity is DEBUG and
       is an integer raning from 0 to 99.  0 should be used for the highest-level
@@ -291,7 +298,7 @@ the program started but attempts to open one or more network interfaces failed.
 WARN
 ----
 An unusual event  happened.  Although the program will continue working
-normally, the event was sufficiently out of the ordinary to warrant drawings
+normally, the event was sufficiently out of the ordinary to warrant drawing
 attention to it.  For example, at program start-up a zone was loaded that
 contained no resource records,
 
@@ -348,16 +355,8 @@ DEBUG is specifically chosen.
 b) Record system-related and packet-related messages via different loggers
 (e.g.  in the example given, sever events could be logged using the logger
 "auth" and packet-related events at that level logged using the logger
-"pkt-auth".)
-As the loggers are independent and the severity levels independent, fine-tuning
-of what and what is not recorded can be achieved.
-
-
-Outstanding Issues
-==================
-* Ability to configure system according to configuration database.
-* Update the build procedure to create .cc and .h files from the .msg file
-  during the build process. (Requires that the message compiler is built first.)
+"pkt-auth".)  As the loggers are independent and the severity levels
+independent, fine-tuning of what and what is not recorded can be achieved.
 
 
 Notes
@@ -369,3 +368,67 @@ the server starts up (or when triggered by a command) to read in a message
 file to overwrite the internal dictionary.  Writing it in C++ means there
 is only one piece of code that does this functionality.
 
+
+Outstanding Issues
+==================
+* Ability to configure system according to configuration database.
+* Update the build procedure to create .cc and .h files from the .msg file
+  during the build process. (Requires that the message compiler is built
+  first.)
+
+
+log4cxx Issues
+==============
+
+Second Argument in Logger Constructor
+-------------------------------------
+As noted above, when using log4cxx as the underlying implementation, the
+argument to the logger's constructor should be set true if declaring the
+logger within a method and set false (or omitted) if declaring the logger
+external to an execution unit.
+
+This is due to an apparent bug in the underlying log4cxx, where the deletion
+of a statically-declared object at program termination can cause a segment
+fault. (The destruction of internal memory structures can sometimes happen
+out of order.)  By default the Logger class creates the structures in
+its constructor but does not delete them in the destruction.  The default
+behavious works because instead of reclaiming memory at program run-down,
+the operating system reclaims it when the process is deleted.
+
+Setting the second argument "true" causes the Logger's destructor to delete
+the log4cxx structures.  This does not cause a problem if the program is
+not terminating.  So use the second form when declaring an automatic instance
+of isc::log::Logger on the stack.
+
+Building with log4cxx
+---------------------
+Owing to issues with versions of log4cxx on different systems, log4cxx was
+temporarily disabled.  To use log4cxx on your system:
+
+* Uncomment the log4cxx lines in configure.ac
+* In src/lib/log, replace the logger_impl.{cc,h} files with their log4cxx
+  equivalents, i.e.
+
+  cp logger_impl_log4cxx.h logger_impl.h
+  cp logger_impl_log4cxx.cc logger_impl.cc
+
+* In src/lib/log/Makefile.am, uncomment the lines:
+
+  # AM_CPPFLAGS += $(LOG4CXX_INCLUDES)
+
+  # liblog_la_SOURCES += xdebuglevel.cc xdebuglevel.h
+
+  # liblog_la_LDFLAGS = $(LOG4CXX_LDFLAGS)
+
+* In src/lib/log/test, re-enable testing of the log4cxx implementation
+  class, i.e.
+
+  cp logger_impl_log4cxx_unittest.cc logger_impl_unittest.cc
+
+  ... and uncomment the following lines in Makefile.am:
+
+  # run_unittests_SOURCES += logger_impl_unittest.cc
+
+  # run_unittests_SOURCES += xdebuglevel_unittest.cc
+
+Then rebuild the system from scratch.
diff --git a/src/lib/log/filename.cc b/src/lib/log/filename.cc
index 949ed9f..91835af 100644
--- a/src/lib/log/filename.cc
+++ b/src/lib/log/filename.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <iostream>
 #include <algorithm>
 #include <string>
@@ -106,7 +104,7 @@ Filename::expandWithDefault(const string& defname) const {
         (directory_.empty() ? def_directory : directory_) +
         (name_.empty() ? def_name : name_) +
         (extension_.empty() ? def_extension : extension_);
-    return retstring;
+    return (retstring);
 }
 
 // Use the stored name as default for a given name
@@ -132,7 +130,7 @@ Filename::useAsDefault(const string& name) const {
         (name_directory.empty() ? directory_ : name_directory) +
         (name_name.empty() ? name_ : name_name) +
         (name_extension.empty() ? extension_ : name_extension);
-    return retstring;
+    return (retstring);
 }
 
 
diff --git a/src/lib/log/filename.h b/src/lib/log/filename.h
index 29a9cc8..da9e560 100644
--- a/src/lib/log/filename.h
+++ b/src/lib/log/filename.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __FILENAME_H
 #define __FILENAME_H
 
@@ -80,22 +78,22 @@ public:
 
     /// \return Stored Filename
     std::string fullName() const {
-        return full_name_;
+        return (full_name_);
     }
 
     /// \return Directory of Given File Name
     std::string directory() const {
-        return directory_;
+        return (directory_);
     }
 
     /// \return Name of Given File Name
     std::string name() const {
-        return name_;
+        return (name_);
     }
 
     /// \return Extension of Given File Name
     std::string extension() const {
-        return extension_;
+        return (extension_);
     }
 
     /// \brief Expand Name with Default
diff --git a/src/lib/log/logger.cc b/src/lib/log/logger.cc
index 494e7bc..a2465de 100644
--- a/src/lib/log/logger.cc
+++ b/src/lib/log/logger.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,296 +12,164 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE
 
-// $Id$
-
-#include <iostream>
-
 #include <stdarg.h>
 #include <stdio.h>
 
-#include <log4cxx/appender.h>
-#include <log4cxx/basicconfigurator.h>
-#include <log4cxx/patternlayout.h>
-#include <log4cxx/consoleappender.h>
-
-#include <log/root_logger_name.h>
 #include <log/logger.h>
+#include <log/logger_impl.h>
 #include <log/message_dictionary.h>
 #include <log/message_types.h>
+#include <log/root_logger_name.h>
 #include <log/strutil.h>
-#include <log/xdebuglevel.h>
 
 using namespace std;
 
 namespace isc {
 namespace log {
 
-// Static initializations
-
-bool Logger::init_ = false;
+// Initialize Logger implementation.  Does not check whether the implementation
+// has already been initialized - that was done by the caller (getLoggerPtr()).
+void Logger::initLoggerImpl() {
+    loggerptr_ = new LoggerImpl(name_, infunc_);
+}
 
-// Destructor.  Delete log4cxx stuff if "don't delete" is clear.
+// Destructor.
 
 Logger::~Logger() {
-    if (exit_delete_) {
-        delete loggerptr_;
-    }
+    delete loggerptr_;
 }
 
-// Initialize logger - create a logger as a child of the root logger.  With
-// log4cxx this is assured by naming the logger <parent>.<child>.
-
-void
-Logger::initLogger() {
-
-    // Initialize basic logging if not already done.  This is a one-off for
-    // all loggers.
-    if (!init_) {
-
-        // TEMPORARY
-        // Add a suitable console logger to the log4cxx root logger.  (This
-        // is the logger at the root of the log4cxx tree, not the BIND-10 root
-        // logger, which is one level down.)  The chosen format is:
-        //
-        // YYYY-MM-DD hh:mm:ss.sss [logger] SEVERITY: text
-        //
-        // As noted, this is a temporary hack: it is done here to ensure that
-        // a suitable output and output pattern is set.  Future versions of the
-        // software will set this based on configuration data.
-
-        log4cxx::LayoutPtr layout(
-            new log4cxx::PatternLayout(
-                "%d{yyyy-MM-DD HH:mm:ss.SSS} %-5p [%c] %m\n"));
-        log4cxx::AppenderPtr console(
-            new log4cxx::ConsoleAppender(layout));
-        log4cxx::LoggerPtr sys_root_logger = log4cxx::Logger::getRootLogger();
-        sys_root_logger->addAppender(console);
-        
-        // Set the default logging to INFO
-        sys_root_logger->setLevel(log4cxx::Level::getInfo());
-
-        // All static stuff initialized
-        init_ = true;
-    }
+// Get Name of Logger
 
-    // Initialize this logger.  Name this as to whether the BIND-10 root logger
-    // name has been set.  (If not, this mucks up the hierarchy :-( ).
-    string root_name = RootLoggerName::getName();
-    if (root_name.empty() || (name_ == root_name)) {
-        loggerptr_ = new log4cxx::LoggerPtr(log4cxx::Logger::getLogger(name_));
-    }
-    else {
-        loggerptr_ = new log4cxx::LoggerPtr(
-            log4cxx::Logger::getLogger(root_name + "." + name_)
-        );
-    }
+std::string
+Logger::getName() {
+    return (getLoggerPtr()->getName());
 }
 
-
-// Set the severity for logging.  There is a 1:1 mapping between the logging
-// severity and the log4cxx logging levels, apart from DEBUG.
-//
-// In log4cxx, each of the logging levels (DEBUG, INFO, WARN etc.) has a numeric
-// value.  The level is set to one of these and any numeric level equal to or
-// above it that is reported.  For example INFO has a value of 20000 and ERROR
-// a value of 40000. So if a message of WARN severity (= 30000) is logged, it is
-// not logged when the logger's severity level is ERROR (as 30000 !>= 40000).
-// It is reported if the logger's severity level is set to WARN (as 30000 >=
-/// 30000) or INFO (30000 >= 20000).
-//
-// This gives a simple system for handling different debug levels.  The debug
-// level is a number between 0 and 99, with 0 being least verbose and 99 the
-// most.  To implement this seamlessly, when DEBUG is set, the numeric value
-// of the logging level is actually set to (DEBUG - debug-level).  Similarly
-// messages of level "n" are logged at a logging level of (DEBUG - n).  Thus if
-// the logging level is set to DEBUG and the debug level set to 25, the actual
-// level set is 10000 - 25 = 99975.
-//
-// Attempting to log a debug message of level 26 is an attempt to log a message
-// of level 10000 - 26 = 9974.  As 9974 !>= 9975, it is not logged.  A
-// message of level 25 is, because 9975 >= 9975.
-//
-// The extended set of logging levels is implemented by the XDebugLevel class.
+// Set the severity for logging.
 
 void
-Logger::setSeverity(Severity severity, int dbglevel) {
-    switch (severity) {
-        case NONE:
-            getLogger()->setLevel(log4cxx::Level::getOff());
-            break;
-
-        case FATAL:
-            getLogger()->setLevel(log4cxx::Level::getFatal());
-            break;
-
-        case ERROR:
-            getLogger()->setLevel(log4cxx::Level::getError());
-            break;
-
-        case WARN:
-            getLogger()->setLevel(log4cxx::Level::getWarn());
-            break;
-
-        case INFO:
-            getLogger()->setLevel(log4cxx::Level::getInfo());
-            break;
-
-        case DEBUG:
-            getLogger()->setLevel(
-                log4cxx::XDebugLevel::getExtendedDebug(dbglevel));
-            break;
-
-        // Will get here for DEFAULT or any other value.  This disables the
-        // logger's own severity and it defaults to the severity of the parent
-        // logger.
-        default:
-            getLogger()->setLevel(0);
-    }
+Logger::setSeverity(isc::log::Severity severity, int dbglevel) {
+    getLoggerPtr()->setSeverity(severity, dbglevel);
 }
 
-// Convert between numeric log4cxx logging level and BIND-10 logging severity.
-
-Logger::Severity
-Logger::convertLevel(int value) const {
-
-    // The order is optimised.  This is only likely to be called when testing
-    // for writing debug messages, so the check for DEBUG_INT is first.
-    if (value <= log4cxx::Level::DEBUG_INT) {
-        return (DEBUG);
-    } else if (value <= log4cxx::Level::INFO_INT) {
-        return (INFO);
-    } else if (value <= log4cxx::Level::WARN_INT) {
-        return (WARN);
-    } else if (value <= log4cxx::Level::ERROR_INT) {
-        return (ERROR);
-    } else if (value <= log4cxx::Level::FATAL_INT) {
-        return (FATAL);
-    } else {
-        return (NONE);
-    }
-}
+// Return the severity of the logger.
 
+isc::log::Severity
+Logger::getSeverity() {
+    return (getLoggerPtr()->getSeverity());
+}
 
-// Return the logging severity associated with this logger.
+// Get Effective Severity Level for Logger
 
-Logger::Severity
-Logger::getSeverityCommon(const log4cxx::LoggerPtr& ptrlogger,
-    bool check_parent) const {
+isc::log::Severity
+Logger::getEffectiveSeverity() {
+    return (getLoggerPtr()->getEffectiveSeverity());
+}
 
-    log4cxx::LevelPtr level = ptrlogger->getLevel();
-    if (level == log4cxx::LevelPtr()) {
+// Debug level (only relevant if messages of severity DEBUG are being logged).
 
-        // Null level returned, logging should be that of the parent.
+int
+Logger::getDebugLevel() {
+    return (getLoggerPtr()->getDebugLevel());
+}
 
-        if (check_parent) {
-            log4cxx::LoggerPtr parent = ptrlogger->getParent();
-            if (parent == log4cxx::LoggerPtr()) {
+// Check on the current severity settings
 
-                // No parent, so reached the end of the chain.  Return INFO
-                // severity.
-                return (INFO);
-            }
-            else {
-                return getSeverityCommon(parent, check_parent);
-            }
-        }
-        else {
-            return (DEFAULT);
-        }
-    } else {
-        return convertLevel(level->toInt());
-    }
+bool
+Logger::isDebugEnabled(int dbglevel) {
+    return (getLoggerPtr()->isDebugEnabled(dbglevel));
 }
 
+bool
+Logger::isInfoEnabled() {
+    return (getLoggerPtr()->isInfoEnabled());
+}
 
-// Get the debug level.  This returns 0 unless the severity is DEBUG.
+bool
+Logger::isWarnEnabled() {
+    return (getLoggerPtr()->isWarnEnabled());
+}
 
-int
-Logger::getDebugLevel() {
+bool
+Logger::isErrorEnabled() {
+    return (getLoggerPtr()->isErrorEnabled());
+}
 
-    log4cxx::LevelPtr level = getLogger()->getLevel();
-    if (level == log4cxx::LevelPtr()) {
-
-        // Null pointer returned, logging should be that of the parent.
-        return (0);
-        
-    } else {
-        int severity = level->toInt();
-        if (severity <= log4cxx::Level::DEBUG_INT) {
-            return (log4cxx::Level::DEBUG_INT - severity);
-        }
-        else {
-            return (0);
-        }
-    }
+bool
+Logger::isFatalEnabled() {
+    return (getLoggerPtr()->isFatalEnabled());
 }
 
-// Log an error message:
-// Common code.  Owing to the use of variable arguments, this must be inline
-// (hence the definition of the macro).  Also note that it expects that the
-// message buffer "message" is declared in the compilation unit.
-
-#define MESSAGE_SIZE (256)
-
-#define FORMAT_MESSAGE(message) \
-    { \
-    MessageDictionary* global = MessageDictionary::globalDictionary(); \
-    string format = global->getText(ident); \
-    va_list ap; \
-    va_start(ap, ident); \
-    vsnprintf(message, sizeof(message), format.c_str(), ap); \
-    message[sizeof(message) - 1] = '\0'; \
-    va_end(ap); \
-    }
-    
+// Format a message: looks up the message text in the dictionary and formats
+// it, replacing tokens with arguments.
+//
+// Owing to the use of variable arguments, this must be inline (hence the
+// definition of the macro).  Also note that it expects that the message buffer
+// "message" is declared in the compilation unit.
 
 // Output methods
 
 void
-Logger::debug(int dbglevel, isc::log::MessageID ident, ...) {
+Logger::debug(int dbglevel, const isc::log::MessageID& ident, ...) {
     if (isDebugEnabled(dbglevel)) {
-        char message[MESSAGE_SIZE];
-        FORMAT_MESSAGE(message);
-        LOG4CXX_DEBUG(getLogger(), ident << ", " << message);
+        va_list ap;
+        va_start(ap, ident);
+        getLoggerPtr()->debug(ident, ap);
+        va_end(ap);
     }
 }
 
 void
-Logger::info(isc::log::MessageID ident, ...) {
+Logger::info(const isc::log::MessageID& ident, ...) {
     if (isInfoEnabled()) {
-        char message[MESSAGE_SIZE];
-        FORMAT_MESSAGE(message);
-        LOG4CXX_INFO(getLogger(), ident << ", " << message);
+        va_list ap;
+        va_start(ap, ident);
+        getLoggerPtr()->info(ident, ap);
+        va_end(ap);
     }
 }
 
 void
-Logger::warn(isc::log::MessageID ident, ...) {
+Logger::warn(const isc::log::MessageID& ident, ...) {
     if (isWarnEnabled()) {
-        char message[MESSAGE_SIZE];
-        FORMAT_MESSAGE(message);
-        LOG4CXX_WARN(getLogger(), ident << ", " << message);
+        va_list ap;
+        va_start(ap, ident);
+        getLoggerPtr()->warn(ident, ap);
+        va_end(ap);
     }
 }
 
 void
-Logger::error(isc::log::MessageID ident, ...) {
+Logger::error(const isc::log::MessageID& ident, ...) {
     if (isErrorEnabled()) {
-        char message[MESSAGE_SIZE];
-        FORMAT_MESSAGE(message);
-        LOG4CXX_ERROR(getLogger(), ident << ", " << message);
+        va_list ap;
+        va_start(ap, ident);
+        getLoggerPtr()->error(ident, ap);
+        va_end(ap);
     }
 }
 
 void
-Logger::fatal(isc::log::MessageID ident, ...) {
+Logger::fatal(const isc::log::MessageID& ident, ...) {
     if (isFatalEnabled()) {
-        char message[MESSAGE_SIZE];
-        FORMAT_MESSAGE(message);
-        LOG4CXX_FATAL(getLogger(), ident << ", " << message);
+        va_list ap;
+        va_start(ap, ident);
+        getLoggerPtr()->fatal(ident, ap);
+        va_end(ap);
     }
 }
 
+bool Logger::operator==(Logger& other) {
+    return (*getLoggerPtr() == *other.getLoggerPtr());
+}
+
+// Protected methods (used for testing)
+
+void
+Logger::reset() {
+    LoggerImpl::reset();
+}
 
 } // namespace log
 } // namespace isc
diff --git a/src/lib/log/logger.h b/src/lib/log/logger.h
index f26f6d1..691eb73 100644
--- a/src/lib/log/logger.h
+++ b/src/lib/log/logger.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,36 +12,42 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __LOGGER_H
 #define __LOGGER_H
 
 #include <cstdlib>
 #include <string>
-#include <boost/lexical_cast.hpp>
-#include <log4cxx/logger.h>
 
-#include <log/dbglevels.h>
+#include <log/debug_levels.h>
+#include <log/logger_levels.h>
 #include <log/message_types.h>
 
 namespace isc {
 namespace log {
 
+/// \brief Logging API
+///
+/// This module forms the interface into the logging subsystem. Features of the
+/// system and its implementation are:
+///
+/// # Multiple logging objects can be created, each given a name; those with the
+///   same name share characteristics (like destination, level being logged
+///   etc.)
+/// # Messages can be logged at severity levels of FATAL, ERROR, WARN, INFO or
+///   DEBUG.  The DEBUG level has further sub-levels numbered 0 (least
+///   informative) to 99 (most informative).
+/// # Each logger has a severity level set associated with it.  When a message
+///   is logged, it is output only if it is logged at a level equal to the
+///   logger severity level or greater, e.g. if the logger's severity is WARN,
+///   only messages logged at WARN, ERROR or FATAL will be output.
+/// # Messages are identified by message identifiers, which are keys into a
+///   message dictionary.
+
+class LoggerImpl;   // Forward declaration of the implementation class
+
 class Logger {
 public:
 
-    /// \brief Severity Levels
-    typedef enum {
-        DEFAULT,    // Default to logging level of parent
-        DEBUG,
-        INFO,
-        WARN,
-        ERROR,
-        FATAL,
-        NONE        // Disable logging
-    } Severity;
-
     /// \brief Constructor
     ///
     /// Creates/attaches to a logger of a specific name.
@@ -50,35 +56,31 @@ public:
     /// this creates an instance of the root logger; otherwise it creates a
     /// child of the root logger.
     ///
-    /// \param exit_delete This argument is present to get round a bug in
-    /// log4cxx.  If a log4cxx logger is declared outside an execution unit, it
-    /// is not deleted until the program runs down.  At that point all such
-    /// objects - including internal log4cxx objects - are deleted.  However,
-    /// there seems to be a bug in log4cxx where the way that such objects are
-    /// destroyed causes a MutexException to be thrown (this is described in
+    /// \param infunc This argument is present to get round a bug in some
+    /// implementations of the logging system.  If the logger is declared in
+    /// a function (such that it will be deleted when the function exits,
+    /// before the program ends), set this true.  If declared outside a
+    /// function (such that it gets deleted during program rundown), set false
+    /// (the default).\n
+    /// \n
+    /// The problems encountered was that during program rundown, one logging
+    /// implementation (log4cxx) threw a MutexException (this is described in
     /// https://issues.apache.org/jira/browse/LOGCXX-322).  As this only occurs
     /// during program rundown, the issue is not serious - it just looks bad to
     /// have the program crash instead of shut down cleanly.\n
     /// \n
-    /// The original implementation of the isc::log::Logger had as a member a
-    /// log4cxx logger (actually a LoggerPtr).  If the isc:: Logger was declared
-    /// statically, when it was destroyed at the end of the program the internal
-    /// LoggerPtr was destroyed, which triggered the problem.  The problem did
-    /// not occur if the isc::log::Logger was created on the stack.  To get
-    /// round this, the internal LoggerPtr is now created dynamically.  The
-    /// exit_delete argument controls its destruction: if true, it is destroyed
-    /// in the ISC Logger destructor.  If false, it is not.\n
+    /// If log4cxx is chosen as the implementation, this flag controls the
+    /// deletion of the underlying log4cxx data structures when the logger is
+    /// deleted.  Setting it false for externally-declared loggers inhibits
+    /// their deletion; so at program exit the memory is not reclaimed during
+    /// program rundown, only when the process is delected.  Setting it true
+    /// for loggers that will be deleted in the normal running of the program
+    /// enables their deletion - which causes no issues as the problem only
+    /// manifests itself during program rundown.
     /// \n
-    /// When creating an isc::log::Logger on the stack, the argument should be
-    /// false (the default); when the Logger is destroyed, all the internal
-    /// log4cxx objects are destroyed.  As only the logger (and not the internal
-    /// log4cxx data structures are being destroyed), all is well.  However,
-    /// when creating the logger statically, the argument should be false.  This
-    /// means that the log4cxx objects are not destroyed at program rundown;
-    /// instead memory is reclaimed and files are closed when the process is
-    /// destroyed, something that does not trigger the bug.
-    Logger(const std::string& name, bool exit_delete = false) :
-        loggerptr_(), name_(name), exit_delete_(exit_delete)
+    /// The flag has no effect on non-log4cxx implementations.
+    Logger(const std::string& name, bool infunc = false) :
+        loggerptr_(NULL), name_(name), infunc_(infunc)
     {}
 
 
@@ -86,28 +88,10 @@ public:
     virtual ~Logger();
 
 
-    /// \brief Configure Options
-    ///
-    /// TEMPORARY: Pass in the command-line options to set the logging severity
-    /// for the root logger.  Future versions of the logger will get this
-    /// information from the configuration database.
-    ///
-    /// \param severity Severity level to log
-    /// \param dbglevel If the severity is DEBUG, this is the debug level.
-    /// This can be in the range 1 to 100 and controls the verbosity.  A value
-    /// outside these limits is silently coerced to the nearest boundary.
-    /// \param local_file If provided, the name of a message file to read in and
-    /// supersede one or more of the current messages.
-    static void runTimeInit(Severity severity = INFO, int dbglevel = 1,
-        const char* local_file = NULL);
-
-
     /// \brief Get Name of Logger
     ///
     /// \return The full name of the logger (including the root name)
-    virtual std::string getName() {
-        return getLogger()->getName();
-    }
+    virtual std::string getName();
 
 
     /// \brief Set Severity Level for Logger
@@ -119,31 +103,28 @@ public:
     /// \param dbglevel If the severity is DEBUG, this is the debug level.
     /// This can be in the range 1 to 100 and controls the verbosity.  A value
     /// outside these limits is silently coerced to the nearest boundary.
-    virtual void setSeverity(Severity severity, int dbglevel = 1);
+    virtual void setSeverity(isc::log::Severity severity, int dbglevel = 1);
 
 
     /// \brief Get Severity Level for Logger
     ///
     /// \return The current logging level of this logger.  In most cases though,
     /// the effective logging level is what is required.
-    virtual Severity getSeverity() {
-        return getSeverityCommon(getLogger(), false);
-    }
+    virtual isc::log::Severity getSeverity();
+
 
     /// \brief Get Effective Severity Level for Logger
     ///
     /// \return The effective severity level of the logger.  This is the same
     /// as getSeverity() if the logger has a severity level set, but otherwise
     /// is the severity of the parent.
-    virtual Severity getEffectiveSeverity() {
-        return getSeverityCommon(getLogger(), true);
-    }
+    virtual isc::log::Severity getEffectiveSeverity();
 
 
     /// \brief Return DEBUG Level
     ///
     /// \return Current setting of debug level.  This is returned regardless of
-    /// whether the 
+    /// whether the severity is set to debug.
     virtual int getDebugLevel();
 
 
@@ -152,35 +133,23 @@ public:
     /// \param dbglevel Level for which debugging is checked.  Debugging is
     /// enabled only if the logger has DEBUG enabled and if the dbglevel
     /// checked is less than or equal to the debug level set for the logger.
-    virtual bool
-    isDebugEnabled(int dbglevel = MIN_DEBUG_LEVEL) {
-        return (getLogger()->getEffectiveLevel()->toInt() <=
-            (log4cxx::Level::DEBUG_INT - dbglevel));
-    }
+    virtual bool isDebugEnabled(int dbglevel = MIN_DEBUG_LEVEL);
 
 
     /// \brief Is INFO Enabled?
-    virtual bool isInfoEnabled() {
-        return (getLogger()->isInfoEnabled());
-    }
+    virtual bool isInfoEnabled();
 
 
     /// \brief Is WARNING Enabled?
-    virtual bool isWarnEnabled() {
-        return (getLogger()->isWarnEnabled());
-    }
+    virtual bool isWarnEnabled();
 
 
     /// \brief Is ERROR Enabled?
-    virtual bool isErrorEnabled() {
-        return (getLogger()->isErrorEnabled());
-    }
+    virtual bool isErrorEnabled();
 
 
     /// \brief Is FATAL Enabled?
-    virtual bool isFatalEnabled() {
-        return (getLogger()->isFatalEnabled());
-    }
+    virtual bool isFatalEnabled();
 
 
     /// \brief Output Debug Message
@@ -189,38 +158,35 @@ public:
     /// are used for more verbose output.
     /// \param ident Message identification.
     /// \param ... Optional arguments for the message.
-    void debug(int dbglevel, MessageID ident, ...);
+    void debug(int dbglevel, const MessageID& ident, ...);
 
 
     /// \brief Output Informational Message
     ///
     /// \param ident Message identification.
     /// \param ... Optional arguments for the message.
-    void info(MessageID ident, ...);
+    void info(const MessageID& ident, ...);
 
 
     /// \brief Output Warning Message
     ///
     /// \param ident Message identification.
     /// \param ... Optional arguments for the message.
-    void warn(MessageID ident, ...);
+    void warn(const MessageID& ident, ...);
 
 
     /// \brief Output Error Message
     ///
     /// \param ident Message identification.
     /// \param ... Optional arguments for the message.
-    void error(MessageID ident, ...);
+    void error(const MessageID& ident, ...);
 
 
     /// \brief Output Fatal Message
     ///
     /// \param ident Message identification.
     /// \param ... Optional arguments for the message.
-    void fatal(MessageID ident, ...);
-
-
-protected:
+    void fatal(const MessageID& ident, ...);
 
     /// \brief Equality
     ///
@@ -228,96 +194,55 @@ protected:
     /// (This method is principally for testing.)
     ///
     /// \return true if the logger objects are instances of the same logger.
-    bool operator==(const Logger& other) const {
-        return (*loggerptr_ == *other.loggerptr_);
-    }
-
-
-    /// \brief Logger Initialized
-    ///
-    /// Check that the logger has been properly initialized.  (This method
-    /// is principally for testing.)
-    ///
-    /// \return true if this logger object has been initialized.
-    bool isInitialized() const {
-        return (loggerptr_ != NULL);
-    }
+    bool operator==(Logger& other);
 
+protected:
 
-    /// \brief Get Severity Level for Logger
-    ///
-    /// This is common code for getSeverity() and getEffectiveSeverity() -
-    /// it returns the severity of the logger; if not set (and the check_parent)
-    /// flag is set, it searches up the parent-child tree until a severity
-    /// level is found and uses that.
+    /// \brief Reset Global Data
     ///
-    /// \param ptrlogger Pointer to the log4cxx logger to check.
-    /// \param check_parent true to search up the tree, false to return the
-    /// current level.
-    ///
-    /// \return The effective severity level of the logger.  This is the same
-    /// as getSeverity() if the logger has a severity level set, but otherwise
-    /// is the severity of the parent.
-    Logger::Severity getSeverityCommon(const log4cxx::LoggerPtr& ptrlogger,
-        bool check_parent) const;
-
+    /// Used for testing, this calls upon the underlying logger implementation
+    /// to clear any global data.
+    static void reset();
 
-    /// \brief Convert Between BIND-10 and log4cxx Logging Levels
-    ///
-    /// Converts between the numeric value of the log4cxx logging level
-    /// and the BIND-10 severity level.
-    ///
-    /// \param value log4cxx numeric logging level
+private:
+    /// \brief Copy Constructor
     ///
-    /// \return BIND-10 logging severity
-    Severity convertLevel(int value) const;
-
+    /// Disabled (marked private) as it makes no sense to copy the logger -
+    /// just create another one of the same name.
+    Logger(const Logger&);
 
-    /// \brief Initialize log4cxx Logger
+    /// \brief Assignment Operator
     ///
-    /// Creates the log4cxx logger used internally.  A function is provided for
-    /// this so that the creation does not take place when this Logger object
-    /// is created but when it is used.  As the latter occurs in executable
-    /// code but the former can occur during initialization, this order
-    /// guarantees that anything that is statically initialized has completed
-    /// its initialization by the time the logger is used.
-    void initLogger();
+    /// Disabled (marked private) as it makes no sense to copy the logger -
+    /// just create another one of the same name.
+    Logger& operator=(const Logger&);
 
-
-    /// \brief Return log4cxx Logger
-    ///
-    /// Returns the log4cxx logger, initializing it if not already initialized.
+    /// \brief Initialize Implementation
     ///
-    /// \return Loggerptr object
-    log4cxx::LoggerPtr& getLogger() {
-        if (loggerptr_ == NULL) {
-            initLogger();
+    /// Returns the logger pointer.  If not yet set, the underlying
+    /// implementation class is initialized.\n
+    /// \n
+    /// The reason for this indirection is to avoid the "static initialization
+    /// fiacso", whereby we cannot rely on the order of static initializations.
+    /// The main problem is the root logger name - declared statically - which
+    /// is referenced by various loggers.  By deferring a reference to it until
+    /// after the program starts executing - by which time the root name object
+    /// will be initialized - we avoid this problem.
+    ///
+    /// \return Returns pointer to implementation
+    LoggerImpl* getLoggerPtr() {
+        if (!loggerptr_) {
+            initLoggerImpl();
         }
-        return *loggerptr_;
+        return (loggerptr_);
     }
 
+    /// \brief Initialize Underlying Implementation and Set loggerptr_
+    void initLoggerImpl();
 
-    /// \brief Read Local Message File
-    ///
-    /// Reads a local message file into the global dictionary, replacing any
-    /// definitions there.  Any messages found in the local file that do not
-    /// replace ones in the global dictionary are listed.
-    ///
-    /// \param file Local message file to be read.
-    static void readLocalMessageFile(const char* file);
-
-private:
-    // Note that loggerptr_ is a pointer to a LoggerPtr, which is itself a
-    // pointer to the underlying log4cxx logger.  This is due to the problems
-    // with memory deletion on program exit, explained in the comments for
-    // the "exit_delete" parameter in this class's constructor.
-
-    log4cxx::LoggerPtr*  loggerptr_;    ///< Pointer to the underlying logger
-    std::string          name_;         ///< Name of this logger]
-    bool                 exit_delete_;  ///< Delete loggerptr_ on exit?
-
-    // NOTE - THIS IS A PLACE HOLDER
-    static bool         init_;      ///< Set true when initialized
+    LoggerImpl*     loggerptr_;     ///< Pointer to the underlying logger
+    std::string     name_;          ///< Copy of the logger name
+    bool            infunc_;        ///< Copy of the infunc argument
 };
 
 } // namespace log
diff --git a/src/lib/log/logger_impl.cc b/src/lib/log/logger_impl.cc
new file mode 100644
index 0000000..4b19360
--- /dev/null
+++ b/src/lib/log/logger_impl.cc
@@ -0,0 +1,221 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE
+
+#include <iostream>
+#include <algorithm>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <boost/lexical_cast.hpp>
+
+#include <log/debug_levels.h>
+#include <log/root_logger_name.h>
+#include <log/logger.h>
+#include <log/logger_impl.h>
+#include <log/message_dictionary.h>
+#include <log/message_types.h>
+#include <log/root_logger_name.h>
+#include <log/strutil.h>
+
+using namespace std;
+
+namespace isc {
+namespace log {
+
+// Static initializations
+
+LoggerImpl::LoggerInfoMap LoggerImpl::logger_info_;
+LoggerImpl::LoggerInfo LoggerImpl::root_logger_info_(isc::log::INFO, 0);
+
+// Constructor
+LoggerImpl::LoggerImpl(const std::string& name, bool)
+{
+    // Are we the root logger?
+    if (name == getRootLoggerName()) {
+        is_root_ = true;
+        name_ = name;
+    } else {
+        is_root_ = false;
+        name_ = getRootLoggerName() + "." + name;
+    }
+}
+
+// Destructor. (Here because of virtual declaration.)
+
+LoggerImpl::~LoggerImpl() {
+}
+
+// Set the severity for logging.
+
+void
+LoggerImpl::setSeverity(isc::log::Severity severity, int dbglevel) {
+
+    // Silently coerce the debug level into the valid range of 0 to 99
+
+    int debug_level = max(MIN_DEBUG_LEVEL, min(MAX_DEBUG_LEVEL, dbglevel));
+    if (is_root_) {
+
+        // Can only set severity for the root logger, you can't disable it.
+        // Any attempt to do so is silently ignored.
+        if (severity != isc::log::DEFAULT) {
+            root_logger_info_ = LoggerInfo(severity, debug_level);
+        }
+
+    } else if (severity == isc::log::DEFAULT) {
+
+        // Want to set to default; this means removing the information
+        // about this logger from the logger_info_ if it is set.
+        LoggerInfoMap::iterator i = logger_info_.find(name_);
+        if (i != logger_info_.end()) {
+            logger_info_.erase(i);
+        }
+
+    } else {
+
+        // Want to set this information
+        logger_info_[name_] = LoggerInfo(severity, debug_level);
+    }
+}
+
+// Return severity level
+
+isc::log::Severity
+LoggerImpl::getSeverity() {
+
+    if (is_root_) {
+        return (root_logger_info_.severity);
+    }
+    else {
+        LoggerInfoMap::iterator i = logger_info_.find(name_);
+        if (i != logger_info_.end()) {
+           return ((i->second).severity);
+        }
+        else {
+            return (isc::log::DEFAULT);
+        }
+    }
+}
+
+// Get effective severity.  Either the current severity or, if not set, the
+// severity of the root level.
+
+isc::log::Severity
+LoggerImpl::getEffectiveSeverity() {
+
+    if (!is_root_ && !logger_info_.empty()) {
+
+        // Not root logger and there is at least one item in the info map for a
+        // logger.
+        LoggerInfoMap::iterator i = logger_info_.find(name_);
+        if (i != logger_info_.end()) {
+
+            // Found, so return the severity.
+            return ((i->second).severity);
+        }
+    }
+
+    // Must be the root logger, or this logger is defaulting to the root logger
+    // settings.
+    return (root_logger_info_.severity);
+}
+
+// Get the debug level.  This returns 0 unless the severity is DEBUG.
+
+int
+LoggerImpl::getDebugLevel() {
+
+    if (!is_root_ && !logger_info_.empty()) {
+
+        // Not root logger and there is something in the map, check if there
+        // is a setting for this one.
+        LoggerInfoMap::iterator i = logger_info_.find(name_);
+        if (i != logger_info_.end()) {
+
+            // Found, so return the debug level.
+            if ((i->second).severity == isc::log::DEBUG) {
+                return ((i->second).dbglevel);
+            } else {
+                return (0);
+            }
+        }
+    }
+
+    // Must be the root logger, or this logger is defaulting to the root logger
+    // settings.
+    if (root_logger_info_.severity == isc::log::DEBUG) {
+        return (root_logger_info_.dbglevel);
+    } else {
+        return (0);
+    }
+}
+
+// The code for isXxxEnabled is quite simple and is in the header.  The only
+// exception is isDebugEnabled() where we have the complication of the debug
+// levels.
+
+bool
+LoggerImpl::isDebugEnabled(int dbglevel) {
+
+    if (!is_root_ && !logger_info_.empty()) {
+
+        // Not root logger and there is something in the map, check if there
+        // is a setting for this one.
+        LoggerInfoMap::iterator i = logger_info_.find(name_);
+        if (i != logger_info_.end()) {
+
+            // Found, so return the debug level.
+            if ((i->second).severity <= isc::log::DEBUG) {
+                return ((i->second).dbglevel >= dbglevel);
+            } else {
+                return (false); // Nothing lower than debug
+            }
+        }
+    }
+
+    // Must be the root logger, or this logger is defaulting to the root logger
+    // settings.
+    if (root_logger_info_.severity <= isc::log::DEBUG) {
+        return (root_logger_info_.dbglevel >= dbglevel);
+    } else {
+       return (false);
+    }
+}
+
+// Output a general message
+
+void
+LoggerImpl::output(const char* sev_text, const MessageID& ident,
+    va_list ap)
+{
+    char message[512];      // Should be large enough for any message
+
+    // Obtain text of the message and substitute arguments.
+    const string format = MessageDictionary::globalDictionary().getText(ident);
+    vsnprintf(message, sizeof(message), format.c_str(), ap);
+
+    // Get the time in a struct tm format, and convert to text
+    time_t t_time;
+    time(&t_time);
+    struct tm* tm_time = localtime(&t_time);
+
+    char chr_time[32];
+    (void) strftime(chr_time, sizeof(chr_time), "%Y-%m-%d %H:%M:%S", tm_time);
+
+    // Now output.
+    std::cout << chr_time << " " << sev_text << " [" << getName() << "] " <<
+        ident << ", " << message << "\n";
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger_impl.h b/src/lib/log/logger_impl.h
new file mode 100644
index 0000000..9fc9cf9
--- /dev/null
+++ b/src/lib/log/logger_impl.h
@@ -0,0 +1,267 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOGGER_IMPL_H
+#define __LOGGER_IMPL_H
+
+#include <stdarg.h>
+#include <time.h>
+
+#include <cstdlib>
+#include <string>
+#include <map>
+#include <utility>
+
+#include <log/debug_levels.h>
+#include <log/logger.h>
+#include <log/message_types.h>
+#include <log/root_logger_name.h>
+
+namespace isc {
+namespace log {
+
+/// \brief Console Logger Implementation
+///
+/// The logger uses a "pimpl" idiom for implementation, where the base logger
+/// class contains little more than a pointer to the implementation class, and
+/// all actions are carried out by the latter.  This class is an implementation
+/// class that just outputs to stdout.
+
+class LoggerImpl {
+public:
+
+    /// \brief Information About Logger
+    ///
+    /// Holds a information about a logger, namely its severity and its debug
+    /// level.  This could be a std::pair, except that it gets confusing when
+    /// accessing the LoggerInfoMap: that returns a pair, so we to reference
+    /// elements we would use constructs like ((i->first).second);
+    struct LoggerInfo {
+        isc::log::Severity  severity;
+        int                 dbglevel;
+
+        LoggerInfo(isc::log::Severity sev = isc::log::INFO,
+            int dbg = MIN_DEBUG_LEVEL) : severity(sev), dbglevel(dbg)
+        {}
+    };
+
+
+    /// \brief Information About All Loggers
+    ///
+    /// Information about all loggers in the system - except the root logger -
+    /// is held in a map, linking name of the logger (excluding the root
+    /// name component) and its set severity and debug levels.  The root
+    /// logger information is held separately.
+    typedef std::map<std::string, LoggerInfo>   LoggerInfoMap;
+
+
+    /// \brief Constructor
+    ///
+    /// Creates a logger of the specific name.
+    ///
+    /// \param name Name of the logger.
+    ///
+    /// \param exit_delete This argument is present to get round a bug in
+    /// the log4cxx implementation.  It is unused here.
+    LoggerImpl(const std::string& name, bool);
+
+
+    /// \brief Destructor
+    virtual ~LoggerImpl();
+
+
+    /// \brief Get the full name of the logger (including the root name)
+    virtual std::string getName() {
+        return (name_);
+    }
+
+
+    /// \brief Set Severity Level for Logger
+    ///
+    /// Sets the level at which this logger will log messages.  If none is set,
+    /// the level is inherited from the parent.
+    ///
+    /// \param severity Severity level to log
+    /// \param dbglevel If the severity is DEBUG, this is the debug level.
+    /// This can be in the range 1 to 100 and controls the verbosity.  A value
+    /// outside these limits is silently coerced to the nearest boundary.
+    virtual void setSeverity(isc::log::Severity severity, int dbglevel = 1);
+
+
+    /// \brief Get Severity Level for Logger
+    ///
+    /// \return The current logging level of this logger.  In most cases though,
+    /// the effective logging level is what is required.
+    virtual isc::log::Severity getSeverity();
+
+
+    /// \brief Get Effective Severity Level for Logger
+    ///
+    /// \return The effective severity level of the logger.  This is the same
+    /// as getSeverity() if the logger has a severity level set, but otherwise
+    /// is the severity of the parent.
+    virtual isc::log::Severity getEffectiveSeverity();
+
+
+    /// \brief Return DEBUG Level
+    ///
+    /// \return Current setting of debug level.  This is returned regardless of
+    /// whether the
+    virtual int getDebugLevel();
+
+
+    /// \brief Returns if Debug Message Should Be Output
+    ///
+    /// \param dbglevel Level for which debugging is checked.  Debugging is
+    /// enabled only if the logger has DEBUG enabled and if the dbglevel
+    /// checked is less than or equal to the debug level set for the logger.
+    virtual bool
+    isDebugEnabled(int dbglevel = MIN_DEBUG_LEVEL);
+
+    /// \brief Is INFO Enabled?
+    virtual bool isInfoEnabled() {
+        return (isEnabled(isc::log::INFO));
+    }
+
+    /// \brief Is WARNING Enabled?
+    virtual bool isWarnEnabled() {
+        return (isEnabled(isc::log::WARN));
+    }
+
+    /// \brief Is ERROR Enabled?
+    virtual bool isErrorEnabled() {
+        return (isEnabled(isc::log::ERROR));
+    }
+
+    /// \brief Is FATAL Enabled?
+    virtual bool isFatalEnabled() {
+        return (isEnabled(isc::log::FATAL));
+    }
+
+
+    /// \brief Common Severity check
+    ///
+    /// Implements the common severity check.  As an optimisation, this checks
+    /// to see if any logger-specific levels have been set (a quick check as it
+    /// just involves seeing if the collection of logger information is empty).
+    /// if not, it returns the information for the root level; if so, it has
+    /// to take longer and look up the information in the map holding the
+    /// logging details.
+    virtual bool isEnabled(isc::log::Severity severity) {
+        if (logger_info_.empty()) {
+            return (root_logger_info_.severity <= severity);
+        }
+        else {
+            return (getSeverity() <= severity);
+        }
+    }
+
+
+    /// \brief Output General Message
+    ///
+    /// The message is formatted to include the date and time, the severity
+    /// and the logger generating the message.
+    ///
+    /// \param sev_text Severity level as a text string
+    /// \param ident Message identification
+    /// \param ap Variable argument list holding message arguments
+    void output(const char* sev_text, const MessageID& ident,
+        va_list ap);
+
+
+    /// \brief Output Debug Message
+    ///
+    /// \param ident Message identification.
+    /// \param text Text to log
+    /// \param ap Variable argument list holding message arguments
+    void debug(const MessageID& ident, va_list ap) {
+        output("DEBUG", ident, ap);
+    }
+
+
+    /// \brief Output Informational Message
+    ///
+    /// \param ident Message identification.
+    /// \param text Text to log
+    /// \param ap Variable argument list holding message arguments
+    void info(const MessageID& ident, va_list ap) {
+        output("INFO ", ident, ap);
+    }
+
+    /// \brief Output Warning Message
+    ///
+    /// \param ident Message identification.
+    /// \param text Text to log
+    /// \param ap Variable argument list holding message arguments
+    void warn(const MessageID& ident, va_list ap) {
+        output("WARN ", ident, ap);
+    }
+
+    /// \brief Output Error Message
+    ///
+    /// \param ident Message identification.
+    /// \param text Text to log
+    /// \param ap Variable argument list holding message arguments
+    void error(const MessageID& ident, va_list ap) {
+        output("ERROR", ident, ap);
+    }
+
+    /// \brief Output Fatal Message
+    ///
+    /// \param ident Message identification.
+    /// \param text Text to log
+    /// \param ap Variable argument list holding message arguments
+    void fatal(const MessageID& ident, va_list ap) {
+        output("FATAL", ident, ap);
+    }
+
+    /// \brief Equality
+    ///
+    /// Check if two instances of this logger refer to the same stream.
+    /// (This method is principally for testing.)
+    ///
+    /// \return true if the logger objects are instances of the same logger.
+    bool operator==(const LoggerImpl& other) {
+        return (name_ == other.name_);
+    }
+
+
+    /// \brief Reset Global Data
+    ///
+    /// Only used for testing, this clears all the logger information and
+    /// resets it back to default values.
+    static void reset() {
+        root_logger_info_ = LoggerInfo(isc::log::INFO, MIN_DEBUG_LEVEL);
+        logger_info_.clear();
+    }
+
+
+private:
+    bool                is_root_;           ///< true if a root logger
+    std::string         name_;              ///< Name of this logger
+
+    // Split the status of the root logger from this logger.  If - is will
+    // probably be the usual case - no per-logger setting is enabled, a
+    // quick check of logger_info_.empty() will return true and we can quickly
+    // return the root logger status without a length lookup in the map.
+
+    static LoggerInfo       root_logger_info_;  ///< Status of root logger
+    static LoggerInfoMap    logger_info_;       ///< Store of debug levels etc.
+};
+
+} // namespace log
+} // namespace isc
+
+
+#endif // __LOGGER_IMPL_H
diff --git a/src/lib/log/logger_impl_log4cxx.cc b/src/lib/log/logger_impl_log4cxx.cc
new file mode 100644
index 0000000..404fd03
--- /dev/null
+++ b/src/lib/log/logger_impl_log4cxx.cc
@@ -0,0 +1,241 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE
+
+#include <iostream>
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <log4cxx/appender.h>
+#include <log4cxx/basicconfigurator.h>
+#include <log4cxx/patternlayout.h>
+#include <log4cxx/consoleappender.h>
+
+#include <log/root_logger_name.h>
+#include <log/logger.h>
+#include <log/logger_impl.h>
+#include <log/message_dictionary.h>
+#include <log/message_types.h>
+#include <log/strutil.h>
+#include <log/xdebuglevel.h>
+
+using namespace std;
+
+namespace isc {
+namespace log {
+
+// Static initializations
+
+bool LoggerImpl::init_ = false;
+
+// Destructor.  Delete log4cxx stuff if "don't delete" is clear.
+
+LoggerImpl::~LoggerImpl() {
+    if (exit_delete_) {
+        delete loggerptr_;
+    }
+}
+
+// Initialize logger - create a logger as a child of the root logger.  With
+// log4cxx this is assured by naming the logger <parent>.<child>.
+
+void
+LoggerImpl::initLogger() {
+
+    // Initialize basic logging if not already done.  This is a one-off for
+    // all loggers.
+    if (!init_) {
+
+        // TEMPORARY
+        // Add a suitable console logger to the log4cxx root logger.  (This
+        // is the logger at the root of the log4cxx tree, not the BIND-10 root
+        // logger, which is one level down.)  The chosen format is:
+        //
+        // YYYY-MM-DD hh:mm:ss.sss [logger] SEVERITY: text
+        //
+        // As noted, this is a temporary hack: it is done here to ensure that
+        // a suitable output and output pattern is set.  Future versions of the
+        // software will set this based on configuration data.
+
+        log4cxx::LayoutPtr layout(
+            new log4cxx::PatternLayout(
+                "%d{yyyy-MM-DD HH:mm:ss.SSS} %-5p [%c] %m\n"));
+        log4cxx::AppenderPtr console(
+            new log4cxx::ConsoleAppender(layout));
+        log4cxx::LoggerPtr sys_root_logger = log4cxx::Logger::getRootLogger();
+        sys_root_logger->addAppender(console);
+
+        // Set the default logging to INFO
+        sys_root_logger->setLevel(log4cxx::Level::getInfo());
+
+        // All static stuff initialized
+        init_ = true;
+    }
+
+    // Initialize this logger.  Name this as to whether the BIND-10 root logger
+    // name has been set.  (If not, this mucks up the hierarchy :-( ).
+    string root_name = RootLoggerName::getName();
+    if (root_name.empty() || (name_ == root_name)) {
+        loggerptr_ = new log4cxx::LoggerPtr(log4cxx::Logger::getLogger(name_));
+    }
+    else {
+        loggerptr_ = new log4cxx::LoggerPtr(
+            log4cxx::Logger::getLogger(root_name + "." + name_)
+        );
+    }
+}
+
+
+// Set the severity for logging.  There is a 1:1 mapping between the logging
+// severity and the log4cxx logging levels, apart from DEBUG.
+//
+// In log4cxx, each of the logging levels (DEBUG, INFO, WARN etc.) has a numeric
+// value.  The level is set to one of these and any numeric level equal to or
+// above it that is reported.  For example INFO has a value of 20000 and ERROR
+// a value of 40000. So if a message of WARN severity (= 30000) is logged, it is
+// not logged when the logger's severity level is ERROR (as 30000 !>= 40000).
+// It is reported if the logger's severity level is set to WARN (as 30000 >=
+/// 30000) or INFO (30000 >= 20000).
+//
+// This gives a simple system for handling different debug levels.  The debug
+// level is a number between 0 and 99, with 0 being least verbose and 99 the
+// most.  To implement this seamlessly, when DEBUG is set, the numeric value
+// of the logging level is actually set to (DEBUG - debug-level).  Similarly
+// messages of level "n" are logged at a logging level of (DEBUG - n).  Thus if
+// the logging level is set to DEBUG and the debug level set to 25, the actual
+// level set is 10000 - 25 = 99975.
+//
+// Attempting to log a debug message of level 26 is an attempt to log a message
+// of level 10000 - 26 = 9974.  As 9974 !>= 9975, it is not logged.  A
+// message of level 25 is, because 9975 >= 9975.
+//
+// The extended set of logging levels is implemented by the XDebugLevel class.
+
+void
+LoggerImpl::setSeverity(isc::log::Severity severity, int dbglevel) {
+    switch (severity) {
+        case NONE:
+            getLogger()->setLevel(log4cxx::Level::getOff());
+            break;
+
+        case FATAL:
+            getLogger()->setLevel(log4cxx::Level::getFatal());
+            break;
+
+        case ERROR:
+            getLogger()->setLevel(log4cxx::Level::getError());
+            break;
+
+        case WARN:
+            getLogger()->setLevel(log4cxx::Level::getWarn());
+            break;
+
+        case INFO:
+            getLogger()->setLevel(log4cxx::Level::getInfo());
+            break;
+
+        case DEBUG:
+            getLogger()->setLevel(
+                log4cxx::XDebugLevel::getExtendedDebug(dbglevel));
+            break;
+
+        // Will get here for DEFAULT or any other value.  This disables the
+        // logger's own severity and it defaults to the severity of the parent
+        // logger.
+        default:
+            getLogger()->setLevel(0);
+    }
+}
+
+// Convert between numeric log4cxx logging level and BIND-10 logging severity.
+
+isc::log::Severity
+LoggerImpl::convertLevel(int value) {
+
+    // The order is optimised.  This is only likely to be called when testing
+    // for writing debug messages, so the check for DEBUG_INT is first.
+    if (value <= log4cxx::Level::DEBUG_INT) {
+        return (DEBUG);
+    } else if (value <= log4cxx::Level::INFO_INT) {
+        return (INFO);
+    } else if (value <= log4cxx::Level::WARN_INT) {
+        return (WARN);
+    } else if (value <= log4cxx::Level::ERROR_INT) {
+        return (ERROR);
+    } else if (value <= log4cxx::Level::FATAL_INT) {
+        return (FATAL);
+    } else {
+        return (NONE);
+    }
+}
+
+
+// Return the logging severity associated with this logger.
+
+isc::log::Severity
+LoggerImpl::getSeverityCommon(const log4cxx::LoggerPtr& ptrlogger,
+    bool check_parent) {
+
+    log4cxx::LevelPtr level = ptrlogger->getLevel();
+    if (level == log4cxx::LevelPtr()) {
+
+        // Null level returned, logging should be that of the parent.
+
+        if (check_parent) {
+            log4cxx::LoggerPtr parent = ptrlogger->getParent();
+            if (parent == log4cxx::LoggerPtr()) {
+
+                // No parent, so reached the end of the chain.  Return INFO
+                // severity.
+                return (INFO);
+            }
+            else {
+                return (getSeverityCommon(parent, check_parent));
+            }
+        }
+        else {
+            return (DEFAULT);
+        }
+    } else {
+        return (convertLevel(level->toInt()));
+    }
+}
+
+
+// Get the debug level.  This returns 0 unless the severity is DEBUG.
+
+int
+LoggerImpl::getDebugLevel() {
+
+    log4cxx::LevelPtr level = getLogger()->getLevel();
+    if (level == log4cxx::LevelPtr()) {
+
+        // Null pointer returned, logging should be that of the parent.
+        return (0);
+
+    } else {
+        int severity = level->toInt();
+        if (severity <= log4cxx::Level::DEBUG_INT) {
+            return (log4cxx::Level::DEBUG_INT - severity);
+        }
+        else {
+            return (0);
+        }
+    }
+}
+
+
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger_impl_log4cxx.h b/src/lib/log/logger_impl_log4cxx.h
new file mode 100644
index 0000000..3101347
--- /dev/null
+++ b/src/lib/log/logger_impl_log4cxx.h
@@ -0,0 +1,315 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOGGER_IMPL_LOG4CXX_H
+#define __LOGGER_IMPL_LOG4CXX_H
+
+#include <cstdlib>
+#include <string>
+#include <boost/lexical_cast.hpp>
+#include <log4cxx/logger.h>
+#include <log4cxx/logger.h>
+
+#include <log/debug_levels.h>
+#include <log/logger.h>
+#include <log/message_types.h>
+
+namespace isc {
+namespace log {
+
+/// \brief Log4cxx Logger Implementation
+///
+/// The logger uses a "pimpl" idiom for implementation, where the base logger
+/// class contains little more than a pointer to the implementation class, and
+/// all actions are carried out by the latter.  This class is an implementation
+/// class interfacing to the log4cxx logging system.
+
+class LoggerImpl {
+public:
+
+    /// \brief Constructor
+    ///
+    /// Creates/attaches to a logger of a specific name.
+    ///
+    /// \param name Name of the logger.  If the name is that of the root name,
+    /// this creates an instance of the root logger; otherwise it creates a
+    /// child of the root logger.
+    ///
+    /// \param exit_delete This argument is present to get round a bug in
+    /// log4cxx.  If a log4cxx logger is declared outside an execution unit, it
+    /// is not deleted until the program runs down.  At that point all such
+    /// objects - including internal log4cxx objects - are deleted.  However,
+    /// there seems to be a bug in log4cxx where the way that such objects are
+    /// destroyed causes a MutexException to be thrown (this is described in
+    /// https://issues.apache.org/jira/browse/LOGCXX-322).  As this only occurs
+    /// during program rundown, the issue is not serious - it just looks bad to
+    /// have the program crash instead of shut down cleanly.\n
+    /// \n
+    /// The original implementation of the isc::log::Logger had as a member a
+    /// log4cxx logger (actually a LoggerPtr).  If the isc:: Logger was declared
+    /// statically, when it was destroyed at the end of the program the internal
+    /// LoggerPtr was destroyed, which triggered the problem.  The problem did
+    /// not occur if the isc::log::Logger was created on the stack.  To get
+    /// round this, the internal LoggerPtr is now created dynamically.  The
+    /// exit_delete argument controls its destruction: if true, it is destroyed
+    /// in the ISC Logger destructor.  If false, it is not.\n
+    /// \n
+    /// When creating an isc::log::Logger on the stack, the argument should be
+    /// false (the default); when the Logger is destroyed, all the internal
+    /// log4cxx objects are destroyed.  As only the logger (and not the internal
+    /// log4cxx data structures are being destroyed), all is well.  However,
+    /// when creating the logger statically, the argument should be false.  This
+    /// means that the log4cxx objects are not destroyed at program rundown;
+    /// instead memory is reclaimed and files are closed when the process is
+    /// destroyed, something that does not trigger the bug.
+    LoggerImpl(const std::string& name, bool exit_delete = false) :
+        loggerptr_(NULL), name_(name), exit_delete_(exit_delete)
+    {}
+
+
+    /// \brief Destructor
+    virtual ~LoggerImpl();
+
+
+    /// \brief Get the full name of the logger (including the root name)
+    virtual std::string getName() {
+        return (getLogger()->getName());
+    }
+
+
+    /// \brief Set Severity Level for Logger
+    ///
+    /// Sets the level at which this logger will log messages.  If none is set,
+    /// the level is inherited from the parent.
+    ///
+    /// \param severity Severity level to log
+    /// \param dbglevel If the severity is DEBUG, this is the debug level.
+    /// This can be in the range 1 to 100 and controls the verbosity.  A value
+    /// outside these limits is silently coerced to the nearest boundary.
+    virtual void setSeverity(isc::log::Severity severity, int dbglevel = 1);
+
+
+    /// \brief Get Severity Level for Logger
+    ///
+    /// \return The current logging level of this logger.  In most cases though,
+    /// the effective logging level is what is required.
+    virtual isc::log::Severity getSeverity() {
+        return (getSeverityCommon(getLogger(), false));
+    }
+
+
+    /// \brief Get Effective Severity Level for Logger
+    ///
+    /// \return The effective severity level of the logger.  This is the same
+    /// as getSeverity() if the logger has a severity level set, but otherwise
+    /// is the severity of the parent.
+    virtual isc::log::Severity getEffectiveSeverity() {
+        return (getSeverityCommon(getLogger(), true));
+    }
+
+
+    /// \brief Return DEBUG Level
+    ///
+    /// \return Current setting of debug level.  This is returned regardless of
+    /// whether the
+    virtual int getDebugLevel();
+
+
+    /// \brief Returns if Debug Message Should Be Output
+    ///
+    /// \param dbglevel Level for which debugging is checked.  Debugging is
+    /// enabled only if the logger has DEBUG enabled and if the dbglevel
+    /// checked is less than or equal to the debug level set for the logger.
+    virtual bool
+    isDebugEnabled(int dbglevel = MIN_DEBUG_LEVEL) {
+        return (getLogger()->getEffectiveLevel()->toInt() <=
+            (log4cxx::Level::DEBUG_INT - dbglevel));
+    }
+
+
+    /// \brief Is INFO Enabled?
+    virtual bool isInfoEnabled() {
+        return (getLogger()->isInfoEnabled());
+    }
+
+
+    /// \brief Is WARNING Enabled?
+    virtual bool isWarnEnabled() {
+        return (getLogger()->isWarnEnabled());
+    }
+
+
+    /// \brief Is ERROR Enabled?
+    virtual bool isErrorEnabled() {
+        return (getLogger()->isErrorEnabled());
+    }
+
+
+    /// \brief Is FATAL Enabled?
+    virtual bool isFatalEnabled() {
+        return (getLogger()->isFatalEnabled());
+    }
+
+
+    /// \brief Output Debug Message
+    ///
+    /// \param ident Message identification.
+    /// \param text Text to log
+    void debug(const MessageID& ident, const char* text) {
+        LOG4CXX_DEBUG(getLogger(), ident << ", " << text);
+    }
+
+
+    /// \brief Output Informational Message
+    ///
+    /// \param ident Message identification.
+    /// \param text Text to log
+    void info(const MessageID& ident, const char* text) {
+        LOG4CXX_INFO(getLogger(), ident << ", " << text);
+    }
+
+
+    /// \brief Output Warning Message
+    ///
+    /// \param ident Message identification.
+    /// \param text Text to log
+    void warn(const MessageID& ident, const char* text) {
+        LOG4CXX_WARN(getLogger(), ident << ", " << text);
+    }
+
+
+    /// \brief Output Error Message
+    ///
+    /// \param ident Message identification.
+    /// \param text Text to log
+    void error(const MessageID& ident, const char* text) {
+        LOG4CXX_ERROR(getLogger(), ident << ", " << text);
+    }
+
+
+    /// \brief Output Fatal Message
+    ///
+    /// \param ident Message identification.
+    /// \param text Text to log
+    void fatal(const MessageID& ident, const char* text) {
+        LOG4CXX_FATAL(getLogger(), ident << ", " << text);
+    }
+
+    //@{
+    /// \brief Testing Methods
+    ///
+    /// The next set of methods are used in testing.  As they are accessed from
+    /// the main logger class, they must be public.
+
+    /// \brief Equality
+    ///
+    /// Check if two instances of this logger refer to the same stream.
+    /// (This method is principally for testing.)
+    ///
+    /// \return true if the logger objects are instances of the same logger.
+    bool operator==(LoggerImpl& other) {
+        return (*loggerptr_ == *other.loggerptr_);
+    }
+
+
+    /// \brief Logger Initialized
+    ///
+    /// Check that the logger has been properly initialized.  (This method
+    /// is principally for testing.)
+    ///
+    /// \return true if this logger object has been initialized.
+    bool isInitialized() {
+        return (loggerptr_ != NULL);
+    }
+
+    /// \brief Reset Global Data
+    ///
+    /// Only used for testing, this clears all the logger information and
+    /// resets it back to default values.  This is a no-op for log4cxx.
+    static void reset() {
+    }
+
+    //@}
+
+protected:
+
+    /// \brief Convert Between BIND-10 and log4cxx Logging Levels
+    ///
+    /// This method is marked protected to allow for unit testing.
+    ///
+    /// \param value log4cxx numeric logging level
+    ///
+    /// \return BIND-10 logging severity
+    isc::log::Severity convertLevel(int value);
+
+private:
+
+    /// \brief Get Severity Level for Logger
+    ///
+    /// This is common code for getSeverity() and getEffectiveSeverity() -
+    /// it returns the severity of the logger; if not set (and the check_parent)
+    /// flag is set, it searches up the parent-child tree until a severity
+    /// level is found and uses that.
+    ///
+    /// \param ptrlogger Pointer to the log4cxx logger to check.
+    /// \param check_parent true to search up the tree, false to return the
+    /// current level.
+    ///
+    /// \return The effective severity level of the logger.  This is the same
+    /// as getSeverity() if the logger has a severity level set, but otherwise
+    /// is the severity of the parent.
+    isc::log::Severity getSeverityCommon(const log4cxx::LoggerPtr& ptrlogger,
+        bool check_parent);
+
+
+
+    /// \brief Initialize log4cxx Logger
+    ///
+    /// Creates the log4cxx logger used internally.  A function is provided for
+    /// this so that the creation does not take place when this Logger object
+    /// is created but when it is used.  As the latter occurs in executable
+    /// code but the former can occur during initialization, this order
+    /// guarantees that anything that is statically initialized has completed
+    /// its initialization by the time the logger is used.
+    void initLogger();
+
+
+    /// \brief Return underlying log4cxx logger, initializing it if necessary
+    ///
+    /// \return Loggerptr object
+    log4cxx::LoggerPtr& getLogger() {
+        if (loggerptr_ == NULL) {
+            initLogger();
+        }
+        return (*loggerptr_);
+    }
+
+    // Members.  Note that loggerptr_ is a pointer to a LoggerPtr, which is
+    // itself a pointer to the underlying log4cxx logger.  This is due to the
+    // problems with memory deletion on program exit, explained in the comments
+    // for the "exit_delete" parameter in this class's constructor.
+
+    log4cxx::LoggerPtr*  loggerptr_;    ///< Pointer to the underlying logger
+    std::string          name_;         ///< Name of this logger]
+    bool                 exit_delete_;  ///< Delete loggerptr_ on exit?
+
+    // NOTE - THIS IS A PLACE HOLDER
+    static bool         init_;      ///< Set true when initialized
+};
+
+} // namespace log
+} // namespace isc
+
+
+#endif // __LOGGER_IMPL_LOG4CXX_H
diff --git a/src/lib/log/logger_levels.h b/src/lib/log/logger_levels.h
new file mode 100644
index 0000000..2f123e8
--- /dev/null
+++ b/src/lib/log/logger_levels.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOGGER_LEVELS_H
+#define __LOGGER_LEVELS_H
+
+namespace isc {
+namespace log {
+
+/// \brief Severity Levels
+///
+/// Defines the severity levels for logging.  This is shared between the logger
+/// and the implementations classes.
+///
+/// N.B. The order of the levels - DEBUG less than INFO less that WARN etc. is
+/// implicitly assumed in several implementations.  They must not be changed.
+
+typedef enum {
+    DEFAULT = 0,    // Default to logging level of the parent
+    DEBUG = 1,
+    INFO = 2,
+    WARN = 3,
+    ERROR = 4,
+    FATAL = 5,
+    NONE = 6    // Disable logging
+} Severity;
+
+}   // namespace log
+}   // namespace isc
+
+#endif // __LOGGER_LEVELS_H
diff --git a/src/lib/log/logger_support.cc b/src/lib/log/logger_support.cc
index c9ba858..1ac4481 100644
--- a/src/lib/log/logger_support.cc
+++ b/src/lib/log/logger_support.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,10 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE
 
-// $Id$
-
-
-
 /// \brief Temporary Logger Support
 ///
 /// Performs run-time initialization of the logger system.  In particular, it
@@ -28,13 +24,17 @@
 /// These functions will be replaced once the code has bneen written to obtain
 /// the logging parameters from the configuration database.
 
+#include <algorithm>
+#include <string>
 #include <vector>
+#include <boost/lexical_cast.hpp>
 
 #include <log/logger.h>
 #include <log/logger_support.h>
 #include <log/messagedef.h>
 #include <log/message_dictionary.h>
 #include <log/message_exception.h>
+#include <log/message_initializer.h>
 #include <log/message_reader.h>
 #include <log/message_types.h>
 #include <log/root_logger_name.h>
@@ -44,7 +44,8 @@ namespace log {
 
 using namespace std;
 
-// Declare a logger for the logging subsystem
+// Declare a logger for the logging subsystem.  This is a sub-logger of the
+// root logger and is used in all functions in this file.
 Logger logger("log");
 
 
@@ -57,22 +58,24 @@ Logger logger("log");
 /// \param file Name of the local message file
 static void
 readLocalMessageFile(const char* file) {
-    
-    MessageDictionary* dictionary = MessageDictionary::globalDictionary();
-    MessageReader reader(dictionary);
+
+    MessageDictionary& dictionary = MessageDictionary::globalDictionary();
+    MessageReader reader(&dictionary);
     try {
+        logger.info(MSG_RDLOCMES, file);
         reader.readFile(file, MessageReader::REPLACE);
 
         // File successfully read, list the duplicates
         MessageReader::MessageIDCollection unknown = reader.getNotAdded();
         for (MessageReader::MessageIDCollection::const_iterator
             i = unknown.begin(); i != unknown.end(); ++i) {
-                logger.warn(MSG_IDNOTFND, (*i).c_str());
+            string message_id = boost::lexical_cast<string>(*i);
+                logger.warn(MSG_IDNOTFND, message_id.c_str());
         }
     }
     catch (MessageException& e) {
         MessageID ident = e.id();
-        vector<MessageID> args = e.arguments();
+        vector<string> args = e.arguments();
         switch (args.size()) {
         case 0:
             logger.error(ident);
@@ -91,20 +94,33 @@ readLocalMessageFile(const char* file) {
 /// Logger Run-Time Initialization
 
 void
-runTimeInit(Logger::Severity severity, int dbglevel, const char* file) {
+initLogger(const string& root, isc::log::Severity severity, int dbglevel,
+    const char* file) {
 
-    // Create the application root logger.  This is the logger that has the
-    // name of the application (and is one level down from the log4cxx root
-    // logger).  All other loggers created in this application will be its
-    // child.
-    //
-    // The main purpose of the application root logger is to provide the root
-    // name in output message for all other loggers.
-    Logger logger(RootLoggerName::getName());
+    // Create the application root logger and set the default severity and
+    // debug level.  This is the logger that has the name of the application.
+    // All other loggers created in this application will be its children.
+    setRootLoggerName(root);
+    Logger root_logger(isc::log::getRootLoggerName(), true);
 
     // Set the severity associated with it.  If no other logger has a severity,
     // this will be the default.
-    logger.setSeverity(severity, dbglevel);
+    root_logger.setSeverity(severity, dbglevel);
+
+    // Check if there were any duplicate message IDs in the default dictionary
+    // and if so, log them.  Log using the logging facility root logger.
+    vector<string>& duplicates = MessageInitializer::getDuplicates();
+    if (!duplicates.empty()) {
+
+        // There are - sort and remove any duplicates.
+        sort(duplicates.begin(), duplicates.end());
+        vector<string>::iterator new_end =
+            unique(duplicates.begin(), duplicates.end());
+        for (vector<string>::iterator i = duplicates.begin(); i != new_end; ++i) {
+            logger.warn(MSG_DUPMSGID, i->c_str());
+        }
+
+    }
 
     // Replace any messages with local ones (if given)
     if (file) {
diff --git a/src/lib/log/logger_support.h b/src/lib/log/logger_support.h
index 85f838f..57d8383 100644
--- a/src/lib/log/logger_support.h
+++ b/src/lib/log/logger_support.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,11 +12,10 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __LOGGER_SUPPORT_H
 #define __LOGGER_SUPPORT_H
 
+#include <string>
 #include <log/logger.h>
 
 namespace isc {
@@ -24,17 +23,21 @@ namespace log {
 
 /// \brief Run-Time Initialization
 ///
-/// This code will be used until the logger is fully integrated into the BIND-10
-/// configuration database.  It performs run-time initialization of th logger,
-/// in particular supplying run-time choices to it:
+/// Performs run-time initialization of the logger in particular supplying:
+///
+/// - Name of the root logger
+/// - The severity (and if applicable, debug level) for the root logger.
+/// - Name of a local message file, containing localisation of message text.
 ///
-/// * The severity (and if applicable, debug level) at which to log
-/// * Name of a local message file, containing localisation of message text.
+/// This function is likely to change over time as more debugging options are
+/// held in the configuration database.
 ///
+/// \param root Name of the root logger
 /// \param severity Severity at which to log
 /// \param dbglevel Debug severiy (ignored if "severity" is not "DEBUG")
 /// \param file Name of the local message file.
-void runTimeInit(Logger::Severity severity, int dbglevel, const char* file);
+void initLogger(const std::string& root, isc::log::Severity severity,
+    int dbglevel, const char* file);
 
 } // namespace log
 } // namespace isc
diff --git a/src/lib/log/message_dictionary.cc b/src/lib/log/message_dictionary.cc
index 05c79f8..c091369 100644
--- a/src/lib/log/message_dictionary.cc
+++ b/src/lib/log/message_dictionary.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <cstddef>
 #include <log/message_dictionary.h>
 #include <log/message_types.h>
@@ -31,82 +29,81 @@ MessageDictionary::~MessageDictionary() {
 // Add message and note if ID already exists
 
 bool
-MessageDictionary::add(const MessageID& ident, const std::string& text) {
-    map<MessageID, string>::iterator i = dictionary_.find(ident);
+MessageDictionary::add(const string& ident, const string& text) {
+    Dictionary::iterator i = dictionary_.find(ident);
     bool not_found = (i == dictionary_.end());
     if (not_found) {
 
         // Message not already in the dictionary, so add it.
         dictionary_[ident] = text;
     }
-    
+
     return (not_found);
 }
 
 // Add message and note if ID does not already exist
 
 bool
-MessageDictionary::replace(const MessageID& ident, const std::string& text) {
-    map<MessageID, string>::iterator i = dictionary_.find(ident);
+MessageDictionary::replace(const string& ident, const string& text) {
+    Dictionary::iterator i = dictionary_.find(ident);
     bool found = (i != dictionary_.end());
     if (found) {
 
         // Exists, so replace it.
         dictionary_[ident] = text;
     }
-    
+
     return (found);
 }
 
 // Load a set of messages
 
-vector<MessageID>
+vector<std::string>
 MessageDictionary::load(const char* messages[]) {
-    vector<MessageID> duplicates;
+    vector<std::string> duplicates;
     int i = 0;
     while (messages[i]) {
 
         // ID present, so note it and point to text.
-        MessageID ident(messages[i++]);
+        const MessageID ident(messages[i++]);
         if (messages[i]) {
 
-            // Text not null, note it and point to next ident. 
+            // Text not null, note it and point to next ident.
             string text(messages[i++]);
 
             // Add ID and text to message dictionary, noting if the ID was
             // already present.
             bool added = add(ident, text);
             if (!added) {
-                duplicates.push_back(ident);
+                duplicates.push_back(boost::lexical_cast<string>(ident));
             }
         }
     }
-    return duplicates;
+    return (duplicates);
 }
 
-// Return message text or blank string
+// Return message text or blank string.  A reference is returned to a string
+// in the dictionary - this is fine, as the string is immediately used for
+// output.
 
-string
-MessageDictionary::getText(const MessageID& ident) const {
-    map<MessageID, string>::const_iterator i = dictionary_.find(ident);
+const string&
+MessageDictionary::getText(const string& ident) const {
+    static const string empty("");
+    Dictionary::const_iterator i = dictionary_.find(ident);
     if (i == dictionary_.end()) {
-        return string("");
+        return (empty);
     }
     else {
-        return i->second;
+        return (i->second);
     }
 }
 
 // Return global dictionary
 
-MessageDictionary*
+MessageDictionary&
 MessageDictionary::globalDictionary() {
-    static MessageDictionary* global = NULL;
-
-    if (global == NULL) {
-        global = new MessageDictionary();
-    }
-    return global;
+    static MessageDictionary global;
+    return (global);
 }
 
 
diff --git a/src/lib/log/message_dictionary.h b/src/lib/log/message_dictionary.h
index 0b2e704..0caa3ea 100644
--- a/src/lib/log/message_dictionary.h
+++ b/src/lib/log/message_dictionary.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __MESSAGE_DICTIONARY_H
 #define __MESSAGE_DICTIONARY_H
 
@@ -22,6 +20,8 @@
 #include <map>
 #include <vector>
 
+#include <boost/lexical_cast.hpp>
+
 #include <log/message_types.h>
 
 namespace isc {
@@ -48,6 +48,9 @@ namespace log {
 class MessageDictionary {
 public:
 
+    typedef std::map<std::string, std::string> Dictionary;
+    typedef Dictionary::const_iterator  const_iterator;
+
     // Default constructor and assignment operator are OK for this class
 
     /// \brief Virtual Destructor
@@ -63,7 +66,20 @@ public:
     ///
     /// \return true if the message was added to the dictionary, false if the
     /// message existed and it was not added.
-    virtual bool add(const MessageID& ident, const std::string& text);
+    virtual bool add(const MessageID& ident, const std::string& text) {
+        return (add(boost::lexical_cast<std::string>(ident), text));
+    }
+
+    /// \brief Add Message
+    ///
+    /// Alternate signature.
+    ///
+    /// \param ident Identification of the message to add
+    /// \param text Message text
+    ///
+    /// \return true if the message was added to the dictionary, false if the
+    /// message existed and it was not added.
+    virtual bool add (const std::string& ident, const std::string& test);
 
 
     /// \brief Replace Message
@@ -76,7 +92,21 @@ public:
     ///
     /// \return true if the message was added to the dictionary, false if the
     /// message did not exist and it was not added.
-    virtual bool replace(const MessageID& ident, const std::string& text);
+    virtual bool replace(const MessageID& ident, const std::string& text) {
+        return (replace(boost::lexical_cast<std::string>(ident), text));
+    }
+
+
+    /// \brief Replace Message
+    ///
+    /// Alternate signature.
+    ///
+    /// \param ident Identification of the message to replace
+    /// \param text Message text
+    ///
+    /// \return true if the message was added to the dictionary, false if the
+    /// message did not exist and it was not added.
+    virtual bool replace(const std::string& ident, const std::string& text);
 
 
     /// \brief Load Dictionary
@@ -94,7 +124,7 @@ public:
     /// \return Vector of message IDs that were not loaded because an ID of the
     /// same name already existing in the dictionary.  This vector may be
     /// empty.
-    virtual std::vector<MessageID> load(const char* elements[]);
+    virtual std::vector<std::string> load(const char* elements[]);
 
 
     /// \brief Get Message Text
@@ -106,30 +136,40 @@ public:
     /// \return Text associated with message or empty string if the ID is not
     /// recognised.  (Note: this precludes an ID being associated with an empty
     /// string.)
-    virtual std::string getText(const MessageID& ident) const;
+    virtual const std::string& getText(const MessageID& ident) const {
+        return(getText(boost::lexical_cast<std::string>(ident)));
+    }
+
+
+    /// \brief Get Message Text
+    ///
+    /// Alternate signature.
+    ///
+    /// \param ident Message identification
+    ///
+    /// \return Text associated with message or empty string if the ID is not
+    /// recognised.  (Note: this precludes an ID being associated with an empty
+    /// string.)
+    virtual const std::string& getText(const std::string& ident) const;
 
 
     /// \brief Number of Items in Dictionary
     ///
     /// \return Number of items in the dictionary
     virtual size_t size() const {
-        return dictionary_.size();
+        return (dictionary_.size());
     }
 
 
-    // Allow access to the internal map structure, but don't allow alteration.
-    typedef std::map<MessageID, std::string>::const_iterator const_iterator;
-
-
     /// \brief Return begin() iterator of internal map
     const_iterator begin() const {
-        return dictionary_.begin();
+        return (dictionary_.begin());
     }
 
 
     /// \brief Return end() iterator of internal map
     const_iterator end() const {
-        return dictionary_.end();
+        return (dictionary_.end());
     }
 
 
@@ -138,10 +178,10 @@ public:
     /// Returns a pointer to the singleton global dictionary.
     ///
     /// \return Pointer to global dictionary.
-    static MessageDictionary* globalDictionary();
+    static MessageDictionary& globalDictionary();
 
 private:
-    std::map<MessageID, std::string>  dictionary_;
+    Dictionary       dictionary_;   ///< Holds the ID to text lookups
 };
 
 } // namespace log
diff --git a/src/lib/log/message_exception.cc b/src/lib/log/message_exception.cc
index 562c381..1a69ca5 100644
--- a/src/lib/log/message_exception.cc
+++ b/src/lib/log/message_exception.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 /// \brief Body of Virtual Destructor
 
 #include <log/message_exception.h>
diff --git a/src/lib/log/message_exception.h b/src/lib/log/message_exception.h
index 537392d..30c6618 100644
--- a/src/lib/log/message_exception.h
+++ b/src/lib/log/message_exception.h
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __MESSAGE_EXCEPTION_H
 #define __MESSAGE_EXCEPTION_H
 
@@ -76,7 +74,7 @@ public:
     ///
     /// \return Exception Arguments
     std::vector<std::string> arguments() const {
-        return args_;
+        return (args_);
     }
 
 private:
diff --git a/src/lib/log/message_initializer.cc b/src/lib/log/message_initializer.cc
index 914ed17..0113497 100644
--- a/src/lib/log/message_initializer.cc
+++ b/src/lib/log/message_initializer.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <log/message_dictionary.h>
 #include <log/message_initializer.h>
 
@@ -24,8 +22,22 @@ namespace log {
 // associated text into it.
 
 MessageInitializer::MessageInitializer(const char* values[]) {
-    MessageDictionary* global = MessageDictionary::globalDictionary();
-    global->load(values);
+    MessageDictionary& global = MessageDictionary::globalDictionary();
+    std::vector<std::string> repeats = global.load(values);
+
+    // Append the IDs in the list just loaded (the "repeats") to the global list
+    // of duplicate IDs.
+    if (!repeats.empty()) {
+        std::vector<std::string>& duplicates = getDuplicates();
+        duplicates.insert(duplicates.end(), repeats.begin(), repeats.end());
+    }
+}
+
+// Return reference to duplicate array
+
+std::vector<std::string>& MessageInitializer::getDuplicates() {
+    static std::vector<std::string> duplicates;
+    return (duplicates);
 }
 
 } // namespace log
diff --git a/src/lib/log/message_initializer.h b/src/lib/log/message_initializer.h
index a776a02..7516823 100644
--- a/src/lib/log/message_initializer.h
+++ b/src/lib/log/message_initializer.h
@@ -12,11 +12,11 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __MESSAGEINITIALIZER_H
 #define __MESSAGEINITIALIZER_H
 
+#include <string>
+#include <vector>
 #include <log/message_dictionary.h>
 
 namespace isc {
@@ -52,9 +52,21 @@ public:
 
     /// \brief Constructor
     ///
-    /// The only method in the class, this adds the array of values to the
-    /// global dictionary.
+    /// Adds the array of values to the global dictionary, and notes any
+    /// duplicates.
+    ///
+    /// \param values NULL-terminated array of alternating identifier strings
+    /// and associated message text.
     MessageInitializer(const char* values[]);
+
+    /// \brief Return Duplicates
+    ///
+    /// When messages are added to the global dictionary, any duplicates are
+    /// recorded.  They can later be output through the logging system.
+    ///
+    /// \return List of duplicate message IDs when the global dictionary was
+    /// loaded.  Note that the duplicates list itself may contain duplicates.
+    static std::vector<std::string>& getDuplicates();
 };
 
 } // namespace log
diff --git a/src/lib/log/message_reader.cc b/src/lib/log/message_reader.cc
index 203b836..7ae7ae0 100644
--- a/src/lib/log/message_reader.cc
+++ b/src/lib/log/message_reader.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <errno.h>
 #include <string.h>
 
@@ -47,7 +45,7 @@ MessageReader::readFile(const string& file, MessageReader::Mode mode) {
     // Open the file
     ifstream infile(file.c_str());
     if (infile.fail()) {
-        throw MessageException(MSG_OPENIN, file, strerror(errno));
+        throw MessageException(MSG_OPNMSGIN, file, strerror(errno));
     }
 
     // Loop round reading it.
@@ -60,7 +58,7 @@ MessageReader::readFile(const string& file, MessageReader::Mode mode) {
 
     // Why did the loop terminate?
     if (!infile.eof()) {
-        throw MessageException(MSG_READERR, file, strerror(errno));
+        throw MessageException(MSG_MSGRDERR, file, strerror(errno));
     }
     infile.close();
 }
@@ -93,32 +91,45 @@ MessageReader::processLine(const string& line, MessageReader::Mode mode) {
 void
 MessageReader::parseDirective(const std::string& text) {
 
-    static string valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
 
-    // Regardless of what happens, all prefixes will be uppercase (as will
-    // all symbols).
-    string line = text;
-    isc::strutil::uppercase(line);
-    vector<string> tokens = isc::strutil::tokens(line);
+    // Break into tokens
+    vector<string> tokens = isc::strutil::tokens(text);
 
-    // Only $PREFIX is recognised so far, so we'll handle it here.
-    if (tokens[0] != string("$PREFIX")) {
+    // Uppercase directive and branch on valid ones
+    isc::strutil::uppercase(tokens[0]);
+    if (tokens[0] == string("$PREFIX")) {
+        parsePrefix(tokens);
+    } else if (tokens[0] == string("$NAMESPACE")) {
+        parseNamespace(tokens);
+    } else {
         throw MessageException(MSG_UNRECDIR, tokens[0]);
+    }
+}
 
-    } else if (tokens.size() < 2) {
-        throw MessageException(MSG_PRFNOARG);
+// Process $PREFIX
+
+void
+MessageReader::parsePrefix(const vector<string>& tokens) {
+
+    // Check argument count
 
+    static string valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
+    if (tokens.size() < 2) {
+        throw MessageException(MSG_PRFNOARG);
     } else if (tokens.size() > 2) {
         throw MessageException(MSG_PRFEXTRARG);
 
     }
 
+    // As a style, we are going to have the symbols in uppercase
+    string prefix = tokens[1];
+    isc::strutil::uppercase(prefix);
+
     // Token is potentially valid providing it only contains alphabetic
     // and numeric characters (and underscores) and does not start with a
     // digit.
-    
-    if ((tokens[1].find_first_not_of(valid) != string::npos) ||
-        (std::isdigit(tokens[1][0]))) {
+    if ((prefix.find_first_not_of(valid) != string::npos) ||
+        (std::isdigit(prefix[0]))) {
 
         // Invalid character in string or it starts with a digit.
         throw MessageException(MSG_PRFINVARG, tokens[1]);
@@ -132,7 +143,45 @@ MessageReader::parseDirective(const std::string& text) {
 
     // Prefix has not been set, so set it and return success.
 
-    prefix_ = tokens[1];
+    prefix_ = prefix;
+}
+
+// Process $NAMESPACE.  A lot of the processing is similar to that of $PREFIX,
+// except that only limited checks will be done on the namespace (to avoid a
+// lot of parsing and separating out of the namespace components.)
+
+void
+MessageReader::parseNamespace(const vector<string>& tokens) {
+
+    // Check argument count
+
+    static string valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_:"
+        "abcdefghijklmnopqrstuvwxyz";
+
+    if (tokens.size() < 2) {
+        throw MessageException(MSG_NSNOARG);
+
+    } else if (tokens.size() > 2) {
+        throw MessageException(MSG_NSEXTRARG);
+
+    }
+
+    // Token is potentially valid providing it only contains alphabetic
+    // and numeric characters (and underscores and colons).
+    if (tokens[1].find_first_not_of(valid) != string::npos) {
+
+        // Invalid character in string or it starts with a digit.
+        throw MessageException(MSG_NSINVARG, tokens[1]);
+    }
+
+    // All OK - unless the namespace has already been set.
+    if (ns_.size() != 0) {
+        throw MessageException(MSG_DUPLNS);
+    }
+
+    // Prefix has not been set, so set it and return success.
+
+    ns_ = tokens[1];
 }
 
 // Process message.  By the time this method is called, the line has been
@@ -150,11 +199,11 @@ MessageReader::parseMessage(const std::string& text, MessageReader::Mode mode) {
     if (first_delim == string::npos) {
 
         // Just a single token in the line - this is not valid
-        throw MessageException(MSG_ONETOKEN, text);
+        throw MessageException(MSG_NOMSGTXT, text);
     }
 
     // Extract the first token into the message ID
-    MessageID ident = text.substr(0, first_delim);
+    string ident = text.substr(0, first_delim);
 
     // Locate the start of the message text
     size_t first_text = text.find_first_not_of(delimiters, first_delim);
@@ -163,7 +212,7 @@ MessageReader::parseMessage(const std::string& text, MessageReader::Mode mode) {
         // ?? This happens if there are trailing delimiters, which should not
         // occur as we have stripped trailing spaces off the line.  Just treat
         // this as a single-token error for simplicity's sake.
-        throw MessageException(MSG_ONETOKEN, text);
+        throw MessageException(MSG_NOMSGTXT, text);
     }
 
     // Add the result to the dictionary and to the non-added list if the add to
diff --git a/src/lib/log/message_reader.h b/src/lib/log/message_reader.h
index 84ffce9..d07c7f2 100644
--- a/src/lib/log/message_reader.h
+++ b/src/lib/log/message_reader.h
@@ -12,12 +12,9 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __MESSAGE_READER_H
 #define __MESSAGE_READER_H
 
-#include <iostream>
 #include <map>
 #include <string>
 #include <vector>
@@ -50,7 +47,7 @@ public:
     } Mode;
 
     /// \brief Visible collection types
-    typedef std::vector<MessageID>   MessageIDCollection;
+    typedef std::vector<std::string>   MessageIDCollection;
 
     /// \brief Constructor
     ///
@@ -79,7 +76,7 @@ public:
     ///
     /// \return Pointer to current dictionary object
     MessageDictionary* getDictionary() const {
-        return dictionary_;
+        return (dictionary_);
     }
 
 
@@ -116,11 +113,27 @@ public:
     virtual void processLine(const std::string& line, Mode mode = ADD);
 
 
+    /// \brief Get Namespace
+    ///
+    /// \return Argument to the $NAMESPACE directive (if present)
+    virtual std::string getNamespace() const {
+        return (ns_);
+    }
+
+
+    /// \brief Clear Namespace
+    ///
+    /// Clears the current namespace.
+    virtual void clearNamespace() {
+        ns_ = "";
+    }
+
+
     /// \brief Get Prefix
     ///
     /// \return Argument to the $PREFIX directive (if present)
     virtual std::string getPrefix() const {
-        return prefix_;
+        return (prefix_);
     }
 
 
@@ -139,7 +152,7 @@ public:
     ///
     /// \return Collection of messages not added
     MessageIDCollection getNotAdded() const {
-        return not_added_;
+        return (not_added_);
     }
 
 private:
@@ -163,10 +176,24 @@ private:
     /// \param line Line of text that starts with "$",
     void parseDirective(const std::string& line);
 
+
+    /// \brief Parse $PREFIX line
+    ///
+    /// \param tokens $PREFIX line split into tokens
+    void parsePrefix(const std::vector<std::string>& tokens);
+
+
+    /// \brief Parse $NAMESPACE line
+    ///
+    /// \param tokens $NAMESPACE line split into tokens
+    void parseNamespace(const std::vector<std::string>& tokens);
+
+
     /// Attributes
     MessageDictionary*  dictionary_;    ///< Dictionary to add messages to
     MessageIDCollection not_added_;     ///< List of IDs not added
-    std::string         prefix_;        ///< Input of $PREFIX statement
+    std::string         prefix_;        ///< Argument of $PREFIX statement
+    std::string         ns_;            ///< Argument of $NAMESPACE statement
 };
 
 } // namespace log
diff --git a/src/lib/log/message_types.h b/src/lib/log/message_types.h
index b101401..9f625a9 100644
--- a/src/lib/log/message_types.h
+++ b/src/lib/log/message_types.h
@@ -12,17 +12,22 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __MESSAGE_TYPES_H
 #define __MESSAGE_TYPES_H
 
-#include <string>
+#include <string.h>
 
 namespace isc {
 namespace log {
 
-typedef std::string MessageID;
+typedef const char* MessageID;
+
+/// \brief Compare MessageID for Equality
+///
+/// \param m1 First message ID
+/// \param m2 Second message ID
+/// \return true if they are equal, false if not
+bool equalMessageID(const MessageID& m1, const MessageID& m2);
 
 } // namespace log
 } // namespace isc
diff --git a/src/lib/log/messagedef.cc b/src/lib/log/messagedef.cc
index 7dfa4f6..f680a74 100644
--- a/src/lib/log/messagedef.cc
+++ b/src/lib/log/messagedef.cc
@@ -1,27 +1,57 @@
-// File created from messagedef.mes on Mon Jan 17 15:25:32 2011
+// File created from messagedef.mes on Mon Feb 14 11:07:45 2011
 
 #include <cstddef>
+#include <log/message_types.h>
 #include <log/message_initializer.h>
 
-using namespace isc::log;
+namespace isc {
+namespace log {
+
+extern const isc::log::MessageID MSG_DUPLNS = "DUPLNS";
+extern const isc::log::MessageID MSG_DUPLPRFX = "DUPLPRFX";
+extern const isc::log::MessageID MSG_DUPMSGID = "DUPMSGID";
+extern const isc::log::MessageID MSG_IDNOTFND = "IDNOTFND";
+extern const isc::log::MessageID MSG_MSGRDERR = "MSGRDERR";
+extern const isc::log::MessageID MSG_MSGWRTERR = "MSGWRTERR";
+extern const isc::log::MessageID MSG_NOMSGTXT = "NOMSGTXT";
+extern const isc::log::MessageID MSG_NSEXTRARG = "NSEXTRARG";
+extern const isc::log::MessageID MSG_NSINVARG = "NSINVARG";
+extern const isc::log::MessageID MSG_NSNOARG = "NSNOARG";
+extern const isc::log::MessageID MSG_OPNMSGIN = "OPNMSGIN";
+extern const isc::log::MessageID MSG_OPNMSGOUT = "OPNMSGOUT";
+extern const isc::log::MessageID MSG_PRFEXTRARG = "PRFEXTRARG";
+extern const isc::log::MessageID MSG_PRFINVARG = "PRFINVARG";
+extern const isc::log::MessageID MSG_PRFNOARG = "PRFNOARG";
+extern const isc::log::MessageID MSG_RDLOCMES = "RDLOCMES";
+extern const isc::log::MessageID MSG_UNRECDIR = "UNRECDIR";
+
+} // namespace log
+} // namespace isc
 
 namespace {
 
 const char* values[] = {
+    "DUPLNS", "duplicate $NAMESPACE directive found",
     "DUPLPRFX", "duplicate $PREFIX directive found",
+    "DUPMSGID", "duplicate message ID (%s) in compiled code",
     "IDNOTFND", "could not replace message for '%s': no such message identification",
-    "ONETOKEN", "a line containing a message ID ('%s') and nothing else was found",
-    "OPENIN", "unable to open message file %s for input: %s",
-    "OPENOUT", "unable to open %s for output: %s",
+    "MSGRDERR", "error reading from message file %s: %s",
+    "MSGWRTERR", "error writing to %s: %s",
+    "NOMSGTXT", "a line containing a message ID ('%s') and nothing else was found",
+    "NSEXTRARG", "$NAMESPACE directive has too many arguments",
+    "NSINVARG", "$NAMESPACE directive has an invalid argument ('%s')",
+    "NSNOARG", "no arguments were given to the $NAMESPACE directive",
+    "OPNMSGIN", "unable to open message file %s for input: %s",
+    "OPNMSGOUT", "unable to open %s for output: %s",
     "PRFEXTRARG", "$PREFIX directive has too many arguments",
     "PRFINVARG", "$PREFIX directive has an invalid argument ('%s')",
     "PRFNOARG", "no arguments were given to the $PREFIX directive",
-    "READERR", "error reading from %s: %s",
+    "RDLOCMES", "reading local message file %s",
     "UNRECDIR", "unrecognised directive '%s'",
-    "WRITERR", "error writing to %s: %s",
     NULL
 };
 
+const isc::log::MessageInitializer initializer(values);
+
 } // Anonymous namespace
 
-MessageInitializer messagedef_cc_Mon_Jan_17_15_25_32_2011(values);
diff --git a/src/lib/log/messagedef.h b/src/lib/log/messagedef.h
index ae0a99d..eb8f4ea 100644
--- a/src/lib/log/messagedef.h
+++ b/src/lib/log/messagedef.h
@@ -1,24 +1,32 @@
-// File created from messagedef.mes on Mon Jan 17 15:25:32 2011
+// File created from messagedef.mes on Mon Feb 14 11:07:45 2011
 
 #ifndef __MESSAGEDEF_H
 #define __MESSAGEDEF_H
 
 #include <log/message_types.h>
 
-namespace {
+namespace isc {
+namespace log {
 
-isc::log::MessageID MSG_DUPLPRFX = "DUPLPRFX";
-isc::log::MessageID MSG_IDNOTFND = "IDNOTFND";
-isc::log::MessageID MSG_ONETOKEN = "ONETOKEN";
-isc::log::MessageID MSG_OPENIN = "OPENIN";
-isc::log::MessageID MSG_OPENOUT = "OPENOUT";
-isc::log::MessageID MSG_PRFEXTRARG = "PRFEXTRARG";
-isc::log::MessageID MSG_PRFINVARG = "PRFINVARG";
-isc::log::MessageID MSG_PRFNOARG = "PRFNOARG";
-isc::log::MessageID MSG_READERR = "READERR";
-isc::log::MessageID MSG_UNRECDIR = "UNRECDIR";
-isc::log::MessageID MSG_WRITERR = "WRITERR";
+extern const isc::log::MessageID MSG_DUPLNS;
+extern const isc::log::MessageID MSG_DUPLPRFX;
+extern const isc::log::MessageID MSG_DUPMSGID;
+extern const isc::log::MessageID MSG_IDNOTFND;
+extern const isc::log::MessageID MSG_MSGRDERR;
+extern const isc::log::MessageID MSG_MSGWRTERR;
+extern const isc::log::MessageID MSG_NOMSGTXT;
+extern const isc::log::MessageID MSG_NSEXTRARG;
+extern const isc::log::MessageID MSG_NSINVARG;
+extern const isc::log::MessageID MSG_NSNOARG;
+extern const isc::log::MessageID MSG_OPNMSGIN;
+extern const isc::log::MessageID MSG_OPNMSGOUT;
+extern const isc::log::MessageID MSG_PRFEXTRARG;
+extern const isc::log::MessageID MSG_PRFINVARG;
+extern const isc::log::MessageID MSG_PRFNOARG;
+extern const isc::log::MessageID MSG_RDLOCMES;
+extern const isc::log::MessageID MSG_UNRECDIR;
 
-} // Anonymous namespace
+} // namespace log
+} // namespace isc
 
 #endif // __MESSAGEDEF_H
diff --git a/src/lib/log/messagedef.mes b/src/lib/log/messagedef.mes
index 1535fc6..55b3e7c 100644
--- a/src/lib/log/messagedef.mes
+++ b/src/lib/log/messagedef.mes
@@ -12,9 +12,8 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id$
-
 $PREFIX MSG_
+$NAMESPACE isc::log
 
 # \brief Message Utility Message File
 #
@@ -24,6 +23,22 @@ $PREFIX MSG_
 # chicken-and-egg situation where we need the files to build the message
 # compiler, yet we need the compiler to build the files.
 
+DUPMSGID  duplicate message ID (%s) in compiled code
++ Indicative of a programming error, when it started up, BIND10 detected that
++ the given message ID had been registered by one or more modules.  (All message
++ IDs should be unique throughout BIND10.)  This has no impact on the operation
++ of the server other that erroneous messages may be logged.  (When BIND10 loads
++ the message IDs (and their associated text), if a duplicate ID is found it is
++ discarded.  However, when the module that supplied the duplicate ID logs that
++ particular message, the text supplied by the module that added the original
++ ID will be output - something that may bear no relation to the condition being
++ logged.
+
+DUPLNS    duplicate $NAMESPACE directive found
++ When reading a message file, more than one $NAMESPACE directive was found.  In
++ this version of the code, such a condition is regarded as an error and the
++ read will be abandonded.
+
 DUPLPRFX    duplicate $PREFIX directive found
 + When reading a message file, more than one $PREFIX directive was found.  In
 + this version of the code, such a condition is regarded as an error and the
@@ -40,17 +55,40 @@ IDNOTFND    could not replace message for '%s': no such message identification
 + This message may appear a number of times in the file, once for every such
 + unknown mnessage identification.
 
-ONETOKEN    a line containing a message ID ('%s') and nothing else was found
+MSGRDERR    error reading from message file %s: %s
++ The specified error was encountered reading from the named message file.
+
+MSGWRTERR   error writing to %s: %s
++ The specified error was encountered by the message compiler when writing to
++ the named output file.
+
+NSEXTRARG  $NAMESPACE directive has too many arguments
++ The $NAMESPACE directive takes a single argument, a namespace in which all the
++ generated symbol names are placed.  This error is generated when the
++ compiler finds a $NAMESPACE directive with more than one argument.
+
+NSINVARG    $NAMESPACE directive has an invalid argument ('%s')
++ The $NAMESPACE argument should be a valid C++ namespace.  The reader does a
++ cursory check on its validity, checking that the characters in the namspace
++ are correct.  The error is generated when the reader finds an invalid
++ character. (Valid are alphanumeric characters, underscroes and colons.)
+
+NOMSGTXT    a line containing a message ID ('%s') and nothing else was found
 + Message definitions comprise lines starting with a message identification (a
 + symbolic name for the message) and followed by the text of the message.  This
 + error is generated when a line is found in the message file that contains just
-+ the message identification.
++ the message identification and no text.
 
-OPENIN      unable to open message file %s for input: %s
+NSNOARG     no arguments were given to the $NAMESPACE directive
++ The $NAMESPACE directive takes a single argument, a namespace in which all the
++ generated symbol names are placed.  This error is generated when the
++ compiler finds a $NAMESPACE directive with no arguments.
+
+OPNMSGIN     unable to open message file %s for input: %s
 + The program was not able to open the specified input message file for the
 + reason given.
 
-OPENOUT     unable to open %s for output: %s
+OPNMSGOUT   unable to open %s for output: %s
 + The program was not able to open the specified output file for the reason
 + given.
 
@@ -69,14 +107,13 @@ PRFINVARG   $PREFIX directive has an invalid argument ('%s')
 PRFNOARG    no arguments were given to the $PREFIX directive
 + The $PREFIX directive takes a single argument, a prefix to be added to the
 + symbol names when a C++ .h file is created.  This error is generated when the
-+ compiler finds a $PREFIX directive with noa rguments.
++ compiler finds a $PREFIX directive with no arguments.
 
-READERR     error reading from %s: %s
-+ The specified error was encountered reading from the named input file.
+RDLOCMES    reading local message file %s
++ This is an informational message output by BIND10 when it starts to read a
++ local message file.  (A local message file may replace the text of one of more
++ messages; the ID of the message will not be changed though.)
 
 UNRECDIR    unrecognised directive '%s'
 + A line starting with a dollar symbol was found, but the first word on the line
 + (shown in the message) was not a recognised message compiler directive.
-
-WRITERR     error writing to %s: %s
-+ The specified error was encountered writing to the named output file.
diff --git a/src/lib/log/root_logger_name.cc b/src/lib/log/root_logger_name.cc
index 9378857..58d9407 100644
--- a/src/lib/log/root_logger_name.cc
+++ b/src/lib/log/root_logger_name.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,15 +12,33 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <string>
 #include <root_logger_name.h>
 
 namespace isc {
 namespace log {
 
-std::string RootLoggerName::name_("");
+namespace {
+
+// Obtain the root logger name in a way that is safe for statically-initialized
+// objects.
 
+std::string&
+getRootLoggerNameInternal() {
+    static std::string root_name;
+    return (root_name);
 }
+
+} // Anonymous namespace
+
+void
+setRootLoggerName(const std::string& name) {
+    getRootLoggerNameInternal() = name;
 }
+
+const std::string& getRootLoggerName() {
+    return (getRootLoggerNameInternal());
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/root_logger_name.h b/src/lib/log/root_logger_name.h
index 80691d1..9d50332 100644
--- a/src/lib/log/root_logger_name.h
+++ b/src/lib/log/root_logger_name.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __ROOT_LOGGER_NAME_H
 #define __ROOT_LOGGER_NAME_H
 
@@ -21,44 +19,26 @@
 
 /// \brief Define Name of Root Logger
 ///
-/// In the log4cxx system, the root logger is ".".  The definition for the
-/// BIND-10 system is that the root logger of a program has the name of the
-/// program.  This (trivial) class stores the name of the program in a
-/// location accessible to the logger classes.
+/// In BIND-10, the name root logger of a program is the name of the program
+/// itself (in contrast to packages such as log4cxx where the root logger name
+//  is something like ".").  These trivial functions allow the setting and
+// getting of that name by the logger classes.
 
 namespace isc {
 namespace log {
 
-class RootLoggerName {
-public:
-
-    /// \brief Constructor
-    ///
-    /// Sets the root logger name.  Although the name is static, setting the
-    /// name in the constructor allows static initialization of the name by
-    /// declaring an external instance of the class in the main execution unit.
-    RootLoggerName(const std::string& name) {
-        setName(name);
-    } 
-
-    /// \brief Set Root Logger Name
-    ///
-    /// \param name Name of the root logger.  This should be the program
-    /// name.
-    static void setName(const std::string& name) {
-        name_ = name;
-    }
+/// \brief Set Root Logger Name
+///
+/// This function should be called by the program's initialization code before
+/// any logging functions are called.
+///
+/// \param name Name of the root logger.  This should be the program name.
+void setRootLoggerName(const std::string& name);
 
-    /// \brief Get Root Logger Name
-    ///
-    /// \return Name of the root logger.
-    static std::string getName() {
-        return name_;
-    }
-    
-private:
-    static std::string name_;      ///< Name of the root logger
-};
+/// \brief Get Root Logger Name
+///
+/// \return Name of the root logger.
+const std::string& getRootLoggerName();
 
 }
 }
diff --git a/src/lib/log/strutil.cc b/src/lib/log/strutil.cc
index 4b96601..8ed6c27 100644
--- a/src/lib/log/strutil.cc
+++ b/src/lib/log/strutil.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,10 +12,7 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #include <numeric>
-#include <iostream>
 
 #include <string.h>
 #include <strutil.h>
@@ -58,7 +55,7 @@ trim(const string& instring) {
         }
     }
 
-    return retstring;
+    return (retstring);
 }
 
 // Tokenise string.  As noted in the header, this is locally written to avoid
@@ -89,7 +86,7 @@ tokens(const std::string text, const std::string& delim) {
         }
     }
 
-    return result;
+    return (result);
 }
 
 // Local function to pass to accumulate() for summing up string lengths.
@@ -131,7 +128,7 @@ format(const std::string& format, const std::vector<std::string>& args) {
         }
     }
 
-    return result;
+    return (result);
 }
 
 } // namespace log
diff --git a/src/lib/log/strutil.h b/src/lib/log/strutil.h
index cb0b793..87c0153 100644
--- a/src/lib/log/strutil.h
+++ b/src/lib/log/strutil.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __STRUTIL_H
 #define __STRUTIL_H
 
@@ -88,7 +86,7 @@ std::vector<std::string> tokens(const std::string text,
 ///
 /// \return Uppercase version of the argument
 inline char toUpper(char chr) {
-    return static_cast<char>(std::toupper(static_cast<int>(chr)));
+    return (static_cast<char>(std::toupper(static_cast<int>(chr))));
 }
 
 
@@ -113,7 +111,7 @@ inline void uppercase(std::string& text) {
 ///
 /// \return Lowercase version of the argument
 inline char toLower(char chr) {
-    return static_cast<char>(std::tolower(static_cast<int>(chr)));
+    return (static_cast<char>(std::tolower(static_cast<int>(chr))));
 }
 
 /// \brief Lowercase String
diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am
index e3a023a..1845706 100644
--- a/src/lib/log/tests/Makefile.am
+++ b/src/lib/log/tests/Makefile.am
@@ -11,8 +11,6 @@ endif
 
 CLEANFILES = *.gcno *.gcda
 
-EXTRA_DIST = localdef.mes
-
 TESTS =
 if HAVE_GTEST
 TESTS += run_unittests
@@ -24,13 +22,13 @@ run_unittests_SOURCES += message_reader_unittest.cc
 run_unittests_SOURCES += message_initializer_unittest.cc
 run_unittests_SOURCES += message_initializer_unittest_2.cc
 run_unittests_SOURCES += strutil_unittest.cc
-run_unittests_SOURCES += xdebuglevel_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
+
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
 run_unittests_LDADD  = $(GTEST_LDADD)
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
-run_unittests_LDADD += -llog4cxx
 endif
 
 TESTS += logger_support_test
diff --git a/src/lib/log/tests/filename_unittest.cc b/src/lib/log/tests/filename_unittest.cc
index c33be9f..f3032eb 100644
--- a/src/lib/log/tests/filename_unittest.cc
+++ b/src/lib/log/tests/filename_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
-
 #include <string>
 
 #include <gtest/gtest.h>
diff --git a/src/lib/log/tests/localdef.mes b/src/lib/log/tests/localdef.mes
deleted file mode 100644
index 98e197d..0000000
--- a/src/lib/log/tests/localdef.mes
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-# PERFORMANCE OF THIS SOFTWARE.
-
-# \brief Local Definitions
-#
-# Holds local definitions of some of the messages produced by the program
-# logger_support_test, and is used as input to check that run-time message
-# replacement works.
-
-NOTHERE     this message is not in the global dictionary
-READERR     replacement read error, parameters: '%s' and '%s'
-UNRECDIR    replacement unrecognised directive message, parameter is '%s'
diff --git a/src/lib/log/tests/logger_impl_log4cxx_unittest.cc b/src/lib/log/tests/logger_impl_log4cxx_unittest.cc
new file mode 100644
index 0000000..cab2678
--- /dev/null
+++ b/src/lib/log/tests/logger_impl_log4cxx_unittest.cc
@@ -0,0 +1,91 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/root_logger_name.h>
+#include <log/logger.h>
+#include <log/logger_impl.h>
+#include <log/messagedef.h>
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+/// \brief Log4cxx Implementation Tests
+///
+/// Some tests of methods that are not directly tested by the logger unit tests
+/// (when the logger is configured to use log4cxx)
+
+namespace isc {
+namespace log {
+
+/// \brief Test Logger
+///
+/// This logger is a subclass of the logger implementation class under test, but
+/// makes protected methods public (for testing)
+
+class TestLoggerImpl : public LoggerImpl {
+public:
+    /// \brief constructor
+    TestLoggerImpl(const string& name) : LoggerImpl(name, true)
+    {}
+
+
+    /// \brief Conversion Between log4cxx Number and BIND-10 Severity
+    Severity convertLevel(int value) {
+        return (LoggerImpl::convertLevel(value));
+    }
+};
+
+} // namespace log
+} // namespace isc
+
+
+class LoggerImplTest : public ::testing::Test {
+protected:
+    LoggerImplTest()
+    {
+    }
+};
+
+// Test the number to severity conversion function
+
+TEST_F(LoggerImplTest, ConvertLevel) {
+
+    // Create a logger
+    RootLoggerName::setName("test3");
+    TestLoggerImpl logger("alpha");
+
+    // Basic 1:1
+    EXPECT_EQ(isc::log::DEBUG, logger.convertLevel(log4cxx::Level::DEBUG_INT));
+    EXPECT_EQ(isc::log::INFO, logger.convertLevel(log4cxx::Level::INFO_INT));
+    EXPECT_EQ(isc::log::WARN, logger.convertLevel(log4cxx::Level::WARN_INT));
+    EXPECT_EQ(isc::log::WARN, logger.convertLevel(log4cxx::Level::WARN_INT));
+    EXPECT_EQ(isc::log::ERROR, logger.convertLevel(log4cxx::Level::ERROR_INT));
+    EXPECT_EQ(isc::log::FATAL, logger.convertLevel(log4cxx::Level::FATAL_INT));
+    EXPECT_EQ(isc::log::FATAL, logger.convertLevel(log4cxx::Level::FATAL_INT));
+    EXPECT_EQ(isc::log::NONE, logger.convertLevel(log4cxx::Level::OFF_INT));
+
+    // Now some debug levels
+    EXPECT_EQ(isc::log::DEBUG,
+        logger.convertLevel(log4cxx::Level::DEBUG_INT - 1));
+    EXPECT_EQ(isc::log::DEBUG,
+        logger.convertLevel(log4cxx::Level::DEBUG_INT - MAX_DEBUG_LEVEL));
+    EXPECT_EQ(isc::log::DEBUG,
+        logger.convertLevel(log4cxx::Level::DEBUG_INT - 2 * MAX_DEBUG_LEVEL));
+}
diff --git a/src/lib/log/tests/logger_support_test.cc b/src/lib/log/tests/logger_support_test.cc
index acca4f6..4d8863e 100644
--- a/src/lib/log/tests/logger_support_test.cc
+++ b/src/lib/log/tests/logger_support_test.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,12 +12,11 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: $
-
 /// \brief Example Program
 ///
 /// Simple example program showing how to use the logger.
 
+#include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 
@@ -32,12 +31,8 @@
 
 using namespace isc::log;
 
-// Declare root logger and a logger to use an example.
-//RootLoggerName root_name("testing");
-
-RootLoggerName root("alpha");
+// Declare logger to use an example.
 Logger logger_ex("example");
-Logger logger_dlm("dlm");
 
 // The program is invoked:
 //
@@ -47,31 +42,31 @@ Logger logger_dlm("dlm");
 // "level" is the debug level, a number between 0 and 99
 // "local_file" is the name of a local file.
 //
-// The program sets the attributes on the root logger.  Looking
-// at the output determines whether the program worked.e root logger.  Looking
-// at the output determines whether the 
+// The program sets the attributes on the root logger and logs a set of
+// messages.  Looking at the output determines whether the program worked.
 
 int main(int argc, char** argv) {
 
-    Logger::Severity    severity = Logger::INFO;
-    int                 dbglevel = -1;
-    const char*         localfile = NULL;
-    int                 option;
+    isc::log::Severity  severity = isc::log::INFO;  // Default logger severity
+    int                 dbglevel = -1;              // Logger debug level
+    const char*         localfile = NULL;           // Local message file
+    int                 option;                     // For getopt() processing
+    Logger              logger_dlm("dlm", true);    // Another example logger
 
     // Parse options
     while ((option = getopt(argc, argv, "s:d:")) != -1) {
         switch (option) {
             case 's':
                 if (strcmp(optarg, "debug") == 0) {
-                    severity = Logger::DEBUG;
+                    severity = isc::log::DEBUG;
                 } else if (strcmp(optarg, "info") == 0) {
-                    severity = Logger::INFO;
+                    severity = isc::log::INFO;
                 } else if (strcmp(optarg, "warn") == 0) {
-                    severity = Logger::WARN;
+                    severity = isc::log::WARN;
                 } else if (strcmp(optarg, "error") == 0) {
-                    severity = Logger::ERROR;
+                    severity = isc::log::ERROR;
                 } else if (strcmp(optarg, "fatal") == 0) {
-                    severity = Logger::FATAL;
+                    severity = isc::log::FATAL;
                 } else {
                     std::cout << "Unrecognised severity option: " <<
                         optarg << "\n";
@@ -94,16 +89,16 @@ int main(int argc, char** argv) {
     }
 
     // Update the logging parameters
-    runTimeInit(severity, dbglevel, localfile);
+    initLogger("alpha", severity, dbglevel, localfile);
 
     // Log a few messages
-    logger_ex.fatal(MSG_WRITERR, "test1", "42");
+    logger_ex.fatal(MSG_MSGWRTERR, "test1", "42");
     logger_ex.error(MSG_UNRECDIR, "false");
-    logger_dlm.warn(MSG_READERR, "a.txt", "dummy test");
-    logger_dlm.info(MSG_OPENIN, "example.msg", "dummy test");
+    logger_dlm.warn(MSG_MSGRDERR, "a.txt", "dummy test");
+    logger_dlm.info(MSG_OPNMSGIN, "example.msg", "dummy test");
     logger_ex.debug(0, MSG_UNRECDIR, "[abc]");
     logger_ex.debug(24, MSG_UNRECDIR, "[24]");
     logger_ex.debug(25, MSG_UNRECDIR, "[25]");
     logger_ex.debug(26, MSG_UNRECDIR, "[26]");
-    return 0;
+    return (0);
 }
diff --git a/src/lib/log/tests/logger_unittest.cc b/src/lib/log/tests/logger_unittest.cc
index e15ec42..4eff622 100644
--- a/src/lib/log/tests/logger_unittest.cc
+++ b/src/lib/log/tests/logger_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: $
-
 #include <iostream>
 #include <string>
 
@@ -41,19 +39,8 @@ public:
     TestLogger(const string& name) : Logger(name, true)
     {}
 
-    /// \brief Logger Equality
-    bool operator==(const TestLogger& other) {
-        return Logger::operator==(other);
-    }
-
-    /// \brief Logger is Null
-    bool isInitialized() const {
-        return Logger::isInitialized();
-    }
-
-    /// \brief Conversion Between log4cxx Number and BIND-10 Severity
-    Severity convertLevel(int value) {
-        return Logger::convertLevel(value);
+    static void reset() {
+        Logger::reset();
     }
 };
 
@@ -66,6 +53,10 @@ protected:
     LoggerTest()
     {
     }
+
+    ~LoggerTest() {
+        TestLogger::reset();
+    }
 };
 
 
@@ -74,7 +65,7 @@ protected:
 TEST_F(LoggerTest, Name) {
 
     // Create a logger
-    RootLoggerName::setName("test1");
+    setRootLoggerName("test1");
     Logger logger("alpha");
 
     // ... and check the name
@@ -88,7 +79,7 @@ TEST_F(LoggerTest, GetLogger) {
 
     // Set the root logger name (not strictly needed, but this will be the
     // case in the program(.
-    RootLoggerName::setName("test2");
+    setRootLoggerName("test2");
 
     const string name1 = "alpha";
     const string name2 = "beta";
@@ -96,85 +87,44 @@ TEST_F(LoggerTest, GetLogger) {
     // Instantiate two loggers that should be the same
     TestLogger logger1(name1);
     TestLogger logger2(name1);
-
-    // And check they are null at this point.
-    EXPECT_FALSE(logger1.isInitialized());
-    EXPECT_FALSE(logger2.isInitialized());
-
-    // Do some random operation
-    EXPECT_TRUE(logger1.isFatalEnabled());
-    EXPECT_TRUE(logger2.isFatalEnabled());
-
-    // And check they initialized and equal
-    EXPECT_TRUE(logger1.isInitialized());
-    EXPECT_TRUE(logger2.isInitialized());
+    // And check they equal
     EXPECT_TRUE(logger1 == logger2);
 
     // Instantiate another logger with another name and check that it
     // is different to the previously instantiated ones.
     TestLogger logger3(name2);
-    EXPECT_FALSE(logger3.isInitialized());
-    EXPECT_TRUE(logger3.isFatalEnabled());
-    EXPECT_TRUE(logger3.isInitialized());
     EXPECT_FALSE(logger1 == logger3);
 }
 
-// Test the number to severity conversion function
-
-TEST_F(LoggerTest, ConvertLevel) {
-
-    // Create a logger
-    RootLoggerName::setName("test3");
-    TestLogger logger("alpha");
-
-    // Basic 1:1
-    EXPECT_EQ(Logger::DEBUG, logger.convertLevel(log4cxx::Level::DEBUG_INT));
-    EXPECT_EQ(Logger::INFO, logger.convertLevel(log4cxx::Level::INFO_INT));
-    EXPECT_EQ(Logger::WARN, logger.convertLevel(log4cxx::Level::WARN_INT));
-    EXPECT_EQ(Logger::WARN, logger.convertLevel(log4cxx::Level::WARN_INT));
-    EXPECT_EQ(Logger::ERROR, logger.convertLevel(log4cxx::Level::ERROR_INT));
-    EXPECT_EQ(Logger::FATAL, logger.convertLevel(log4cxx::Level::FATAL_INT));
-    EXPECT_EQ(Logger::FATAL, logger.convertLevel(log4cxx::Level::FATAL_INT));
-    EXPECT_EQ(Logger::NONE, logger.convertLevel(log4cxx::Level::OFF_INT));
-
-    // Now some debug levels
-    EXPECT_EQ(Logger::DEBUG,
-        logger.convertLevel(log4cxx::Level::DEBUG_INT - 1));
-    EXPECT_EQ(Logger::DEBUG,
-        logger.convertLevel(log4cxx::Level::DEBUG_INT - MAX_DEBUG_LEVEL));
-    EXPECT_EQ(Logger::DEBUG,
-        logger.convertLevel(log4cxx::Level::DEBUG_INT - 2 * MAX_DEBUG_LEVEL));
-}
-
 // Check that the logger levels are get set properly.
 
 TEST_F(LoggerTest, Severity) {
 
     // Create a logger
-    RootLoggerName::setName("test3");
+    setRootLoggerName("test3");
     TestLogger logger("alpha");
 
     // Now check the levels
-    logger.setSeverity(Logger::NONE);
-    EXPECT_EQ(Logger::NONE, logger.getSeverity());
+    logger.setSeverity(isc::log::NONE);
+    EXPECT_EQ(isc::log::NONE, logger.getSeverity());
 
-    logger.setSeverity(Logger::FATAL);
-    EXPECT_EQ(Logger::FATAL, logger.getSeverity());
+    logger.setSeverity(isc::log::FATAL);
+    EXPECT_EQ(isc::log::FATAL, logger.getSeverity());
 
-    logger.setSeverity(Logger::ERROR);
-    EXPECT_EQ(Logger::ERROR, logger.getSeverity());
+    logger.setSeverity(isc::log::ERROR);
+    EXPECT_EQ(isc::log::ERROR, logger.getSeverity());
 
-    logger.setSeverity(Logger::WARN);
-    EXPECT_EQ(Logger::WARN, logger.getSeverity());
+    logger.setSeverity(isc::log::WARN);
+    EXPECT_EQ(isc::log::WARN, logger.getSeverity());
 
-    logger.setSeverity(Logger::INFO);
-    EXPECT_EQ(Logger::INFO, logger.getSeverity());
+    logger.setSeverity(isc::log::INFO);
+    EXPECT_EQ(isc::log::INFO, logger.getSeverity());
 
-    logger.setSeverity(Logger::DEBUG);
-    EXPECT_EQ(Logger::DEBUG, logger.getSeverity());
+    logger.setSeverity(isc::log::DEBUG);
+    EXPECT_EQ(isc::log::DEBUG, logger.getSeverity());
 
-    logger.setSeverity(Logger::DEFAULT);
-    EXPECT_EQ(Logger::DEFAULT, logger.getSeverity());
+    logger.setSeverity(isc::log::DEFAULT);
+    EXPECT_EQ(isc::log::DEFAULT, logger.getSeverity());
 }
 
 // Check that the debug level is set correctly.
@@ -182,40 +132,40 @@ TEST_F(LoggerTest, Severity) {
 TEST_F(LoggerTest, DebugLevels) {
 
     // Create a logger
-    RootLoggerName::setName("test4");
+    setRootLoggerName("test4");
     TestLogger logger("alpha");
 
     // Debug level should be 0 if not at debug severity
-    logger.setSeverity(Logger::NONE, 20);
+    logger.setSeverity(isc::log::NONE, 20);
     EXPECT_EQ(0, logger.getDebugLevel());
 
-    logger.setSeverity(Logger::INFO, 42);
+    logger.setSeverity(isc::log::INFO, 42);
     EXPECT_EQ(0, logger.getDebugLevel());
 
     // Should be the value set if the severity is set to DEBUG though.
-    logger.setSeverity(Logger::DEBUG, 32);
+    logger.setSeverity(isc::log::DEBUG, 32);
     EXPECT_EQ(32, logger.getDebugLevel());
 
-    logger.setSeverity(Logger::DEBUG, 97);
+    logger.setSeverity(isc::log::DEBUG, 97);
     EXPECT_EQ(97, logger.getDebugLevel());
 
     // Try the limits
-    logger.setSeverity(Logger::DEBUG, -1);
+    logger.setSeverity(isc::log::DEBUG, -1);
     EXPECT_EQ(0, logger.getDebugLevel());
 
-    logger.setSeverity(Logger::DEBUG, 0);
+    logger.setSeverity(isc::log::DEBUG, 0);
     EXPECT_EQ(0, logger.getDebugLevel());
 
-    logger.setSeverity(Logger::DEBUG, 1);
+    logger.setSeverity(isc::log::DEBUG, 1);
     EXPECT_EQ(1, logger.getDebugLevel());
 
-    logger.setSeverity(Logger::DEBUG, 98);
+    logger.setSeverity(isc::log::DEBUG, 98);
     EXPECT_EQ(98, logger.getDebugLevel());
 
-    logger.setSeverity(Logger::DEBUG, 99);
+    logger.setSeverity(isc::log::DEBUG, 99);
     EXPECT_EQ(99, logger.getDebugLevel());
 
-    logger.setSeverity(Logger::DEBUG, 100);
+    logger.setSeverity(isc::log::DEBUG, 100);
     EXPECT_EQ(99, logger.getDebugLevel());
 }
 
@@ -228,28 +178,28 @@ TEST_F(LoggerTest, SeverityInheritance) {
     // implementation (in this case log4cxx) will set a parent-child
     // relationship if the loggers are named <parent> and <parent>.<child>.
 
-    RootLoggerName::setName("test5");
+    setRootLoggerName("test5");
     TestLogger parent("alpha");
     TestLogger child("alpha.beta");
 
     // By default, newly created loggers should have a level of DEFAULT
     // (i.e. default to parent)
-    EXPECT_EQ(Logger::DEFAULT, parent.getSeverity());
-    EXPECT_EQ(Logger::DEFAULT, child.getSeverity());
+    EXPECT_EQ(isc::log::DEFAULT, parent.getSeverity());
+    EXPECT_EQ(isc::log::DEFAULT, child.getSeverity());
 
     // Set the severity of the child to something other than the default -
     // check it changes and that of the parent does not.
-    child.setSeverity(Logger::INFO);
-    EXPECT_EQ(Logger::DEFAULT, parent.getSeverity());
-    EXPECT_EQ(Logger::INFO, child.getSeverity());
+    child.setSeverity(isc::log::INFO);
+    EXPECT_EQ(isc::log::DEFAULT, parent.getSeverity());
+    EXPECT_EQ(isc::log::INFO, child.getSeverity());
 
     // Reset the child severity and set that of the parent
-    child.setSeverity(Logger::DEFAULT);
-    EXPECT_EQ(Logger::DEFAULT, parent.getSeverity());
-    EXPECT_EQ(Logger::DEFAULT, child.getSeverity());
-    parent.setSeverity(Logger::WARN);
-    EXPECT_EQ(Logger::WARN, parent.getSeverity());
-    EXPECT_EQ(Logger::DEFAULT, child.getSeverity());
+    child.setSeverity(isc::log::DEFAULT);
+    EXPECT_EQ(isc::log::DEFAULT, parent.getSeverity());
+    EXPECT_EQ(isc::log::DEFAULT, child.getSeverity());
+    parent.setSeverity(isc::log::WARN);
+    EXPECT_EQ(isc::log::WARN, parent.getSeverity());
+    EXPECT_EQ(isc::log::DEFAULT, child.getSeverity());
 }
 
 // Check that severity is inherited.
@@ -260,7 +210,7 @@ TEST_F(LoggerTest, EffectiveSeverityInheritance) {
     // implementation (in this case log4cxx) will set a parent-child
     // relationship if the loggers are named <parent> and <parent>.<child>.
 
-    RootLoggerName::setName("test6");
+    setRootLoggerName("test6");
     Logger parent("test6");
     Logger child("test6.beta");
 
@@ -268,58 +218,58 @@ TEST_F(LoggerTest, EffectiveSeverityInheritance) {
     // (i.e. default to parent) and the root should have a default severity
     // of INFO.  However, the latter is only enforced when created by the
     // RootLogger class, so explicitly set it for the parent for now.
-    parent.setSeverity(Logger::INFO);
-    EXPECT_EQ(Logger::INFO, parent.getEffectiveSeverity());
+    parent.setSeverity(isc::log::INFO);
+    EXPECT_EQ(isc::log::INFO, parent.getEffectiveSeverity());
 
-    EXPECT_EQ(Logger::DEFAULT, child.getSeverity());
-    EXPECT_EQ(Logger::INFO, child.getEffectiveSeverity());
+    EXPECT_EQ(isc::log::DEFAULT, child.getSeverity());
+    EXPECT_EQ(isc::log::INFO, child.getEffectiveSeverity());
 
     // Set the severity of the child to something other than the default -
     // check it changes and that of the parent does not.
-    child.setSeverity(Logger::FATAL);
-    EXPECT_EQ(Logger::INFO, parent.getEffectiveSeverity());
-    EXPECT_EQ(Logger::FATAL, child.getEffectiveSeverity());
+    child.setSeverity(isc::log::FATAL);
+    EXPECT_EQ(isc::log::INFO, parent.getEffectiveSeverity());
+    EXPECT_EQ(isc::log::FATAL, child.getEffectiveSeverity());
 
     // Reset the child severity and check again.
-    child.setSeverity(Logger::DEFAULT);
-    EXPECT_EQ(Logger::INFO, parent.getEffectiveSeverity());
-    EXPECT_EQ(Logger::INFO, child.getEffectiveSeverity());
+    child.setSeverity(isc::log::DEFAULT);
+    EXPECT_EQ(isc::log::INFO, parent.getEffectiveSeverity());
+    EXPECT_EQ(isc::log::INFO, child.getEffectiveSeverity());
 
     // Change the parwnt's severity and check it is reflects in the child.
-    parent.setSeverity(Logger::WARN);
-    EXPECT_EQ(Logger::WARN, parent.getEffectiveSeverity());
-    EXPECT_EQ(Logger::WARN, child.getEffectiveSeverity());
+    parent.setSeverity(isc::log::WARN);
+    EXPECT_EQ(isc::log::WARN, parent.getEffectiveSeverity());
+    EXPECT_EQ(isc::log::WARN, child.getEffectiveSeverity());
 }
 
 // Test the isXxxxEnabled methods.
 
 TEST_F(LoggerTest, IsXxxEnabled) {
 
-    RootLoggerName::setName("test7");
+    setRootLoggerName("test7");
     Logger logger("test7");
 
-    logger.setSeverity(Logger::INFO);
+    logger.setSeverity(isc::log::INFO);
     EXPECT_FALSE(logger.isDebugEnabled());
     EXPECT_TRUE(logger.isInfoEnabled());
     EXPECT_TRUE(logger.isWarnEnabled());
     EXPECT_TRUE(logger.isErrorEnabled());
     EXPECT_TRUE(logger.isFatalEnabled());
 
-    logger.setSeverity(Logger::WARN);
+    logger.setSeverity(isc::log::WARN);
     EXPECT_FALSE(logger.isDebugEnabled());
     EXPECT_FALSE(logger.isInfoEnabled());
     EXPECT_TRUE(logger.isWarnEnabled());
     EXPECT_TRUE(logger.isErrorEnabled());
     EXPECT_TRUE(logger.isFatalEnabled());
 
-    logger.setSeverity(Logger::ERROR);
+    logger.setSeverity(isc::log::ERROR);
     EXPECT_FALSE(logger.isDebugEnabled());
     EXPECT_FALSE(logger.isInfoEnabled());
     EXPECT_FALSE(logger.isWarnEnabled());
     EXPECT_TRUE(logger.isErrorEnabled());
     EXPECT_TRUE(logger.isFatalEnabled());
 
-    logger.setSeverity(Logger::FATAL);
+    logger.setSeverity(isc::log::FATAL);
     EXPECT_FALSE(logger.isDebugEnabled());
     EXPECT_FALSE(logger.isInfoEnabled());
     EXPECT_FALSE(logger.isWarnEnabled());
@@ -328,14 +278,14 @@ TEST_F(LoggerTest, IsXxxEnabled) {
 
     // Check various debug levels
 
-    logger.setSeverity(Logger::DEBUG);
+    logger.setSeverity(isc::log::DEBUG);
     EXPECT_TRUE(logger.isDebugEnabled());
     EXPECT_TRUE(logger.isInfoEnabled());
     EXPECT_TRUE(logger.isWarnEnabled());
     EXPECT_TRUE(logger.isErrorEnabled());
     EXPECT_TRUE(logger.isFatalEnabled());
 
-    logger.setSeverity(Logger::DEBUG, 45);
+    logger.setSeverity(isc::log::DEBUG, 45);
     EXPECT_TRUE(logger.isDebugEnabled());
     EXPECT_TRUE(logger.isInfoEnabled());
     EXPECT_TRUE(logger.isWarnEnabled());
@@ -346,14 +296,14 @@ TEST_F(LoggerTest, IsXxxEnabled) {
     // the severity of the parent logger.
 
     Logger child("test7.child");
-    logger.setSeverity(Logger::FATAL);
+    logger.setSeverity(isc::log::FATAL);
     EXPECT_FALSE(child.isDebugEnabled());
     EXPECT_FALSE(child.isInfoEnabled());
     EXPECT_FALSE(child.isWarnEnabled());
     EXPECT_FALSE(child.isErrorEnabled());
     EXPECT_TRUE(child.isFatalEnabled());
 
-    logger.setSeverity(Logger::INFO);
+    logger.setSeverity(isc::log::INFO);
     EXPECT_FALSE(child.isDebugEnabled());
     EXPECT_TRUE(child.isInfoEnabled());
     EXPECT_TRUE(child.isWarnEnabled());
@@ -366,29 +316,29 @@ TEST_F(LoggerTest, IsXxxEnabled) {
 
 TEST_F(LoggerTest, IsDebugEnabledLevel) {
 
-    RootLoggerName::setName("test8");
+    setRootLoggerName("test8");
     Logger logger("test8");
 
     int MID_LEVEL = (MIN_DEBUG_LEVEL + MAX_DEBUG_LEVEL) / 2;
 
-    logger.setSeverity(Logger::DEBUG);
+    logger.setSeverity(isc::log::DEBUG);
     EXPECT_TRUE(logger.isDebugEnabled(MIN_DEBUG_LEVEL));
     EXPECT_FALSE(logger.isDebugEnabled(MID_LEVEL));
     EXPECT_FALSE(logger.isDebugEnabled(MAX_DEBUG_LEVEL));
 
-    logger.setSeverity(Logger::DEBUG, MIN_DEBUG_LEVEL);
+    logger.setSeverity(isc::log::DEBUG, MIN_DEBUG_LEVEL);
     EXPECT_TRUE(logger.isDebugEnabled(MIN_DEBUG_LEVEL));
     EXPECT_FALSE(logger.isDebugEnabled(MID_LEVEL));
     EXPECT_FALSE(logger.isDebugEnabled(MAX_DEBUG_LEVEL));
 
-    logger.setSeverity(Logger::DEBUG, MID_LEVEL);
+    logger.setSeverity(isc::log::DEBUG, MID_LEVEL);
     EXPECT_TRUE(logger.isDebugEnabled(MIN_DEBUG_LEVEL));
     EXPECT_TRUE(logger.isDebugEnabled(MID_LEVEL - 1));
     EXPECT_TRUE(logger.isDebugEnabled(MID_LEVEL));
     EXPECT_FALSE(logger.isDebugEnabled(MID_LEVEL + 1));
     EXPECT_FALSE(logger.isDebugEnabled(MAX_DEBUG_LEVEL));
 
-    logger.setSeverity(Logger::DEBUG, MAX_DEBUG_LEVEL);
+    logger.setSeverity(isc::log::DEBUG, MAX_DEBUG_LEVEL);
     EXPECT_TRUE(logger.isDebugEnabled(MIN_DEBUG_LEVEL));
     EXPECT_TRUE(logger.isDebugEnabled(MID_LEVEL));
     EXPECT_TRUE(logger.isDebugEnabled(MAX_DEBUG_LEVEL));
diff --git a/src/lib/log/tests/message_dictionary_unittest.cc b/src/lib/log/tests/message_dictionary_unittest.cc
index 78aa851..a92585c 100644
--- a/src/lib/log/tests/message_dictionary_unittest.cc
+++ b/src/lib/log/tests/message_dictionary_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,21 +12,36 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
-
 #include <cstddef>
 #include <string>
 #include <gtest/gtest.h>
 #include <log/message_dictionary.h>
+#include <log/message_initializer.h>
 #include <log/message_types.h>
 
 using namespace isc;
 using namespace isc::log;
 using namespace std;
 
+// set up another message initializer.  This will add a symbol found in the
+// logging library and a symbol not found in the logging library.  When the
+// global dictionary is loaded, the former should be marked as a duplicate
+// and the latter should be present.
+
+static const char* values[] = {
+    "DUPLNS", "duplicate $NAMESPACE directive found",
+    "NEWSYM", "new symbol added",
+    NULL
+};
+
+MessageInitializer init(values);
+
+
+
+
 class MessageDictionaryTest : public ::testing::Test {
 protected:
-    MessageDictionaryTest() : 
+    MessageDictionaryTest() :
         alpha_id("ALPHA"), alpha_text("This is alpha"),
         beta_id("BETA"), beta_text("This is beta"),
         gamma_id("GAMMA"), gamma_text("This is gamma")
@@ -42,17 +57,6 @@ protected:
 
 };
 
-
-// Check that the global dictionary is a singleton.
-
-TEST_F(MessageDictionaryTest, GlobalTest) {
-    MessageDictionary* global = MessageDictionary::globalDictionary();
-    EXPECT_FALSE(NULL == global);
-
-    MessageDictionary* global2 = MessageDictionary::globalDictionary();
-    EXPECT_EQ(global2, global);
-}
-
 // Check that adding messages works
 
 TEST_F(MessageDictionaryTest, Add) {
@@ -122,7 +126,7 @@ TEST_F(MessageDictionaryTest, LoadTest) {
     EXPECT_EQ(0, dictionary1.size());
 
     // Load a dictionary1.
-    vector<MessageID> duplicates = dictionary1.load(data1);
+    vector<string> duplicates = dictionary1.load(data1);
     EXPECT_EQ(3, dictionary1.size());
     EXPECT_EQ(string(data1[1]), dictionary1.getText(data1[0]));
     EXPECT_EQ(string(data1[3]), dictionary1.getText(data1[2]));
@@ -157,7 +161,7 @@ TEST_F(MessageDictionaryTest, Lookups) {
     };
 
     MessageDictionary dictionary;
-    vector<MessageID> duplicates = dictionary.load(data);
+    vector<string> duplicates = dictionary.load(data);
     EXPECT_EQ(3, dictionary.size());
     EXPECT_EQ(0, duplicates.size());
 
@@ -171,3 +175,23 @@ TEST_F(MessageDictionaryTest, Lookups) {
     EXPECT_EQ(string(""), dictionary.getText(""));
     EXPECT_EQ(string(""), dictionary.getText("\n\n\n"));
 }
+
+// Check that the global dictionary is a singleton.
+
+TEST_F(MessageDictionaryTest, GlobalTest) {
+    MessageDictionary& global = MessageDictionary::globalDictionary();
+    MessageDictionary& global2 = MessageDictionary::globalDictionary();
+    EXPECT_TRUE(&global2 == &global);
+}
+
+// Check that the global dictionary has detected the duplicate and the
+// new symbol.
+
+TEST_F(MessageDictionaryTest, GlobalLoadTest) {
+    vector<string>& duplicates = MessageInitializer::getDuplicates();
+    ASSERT_EQ(1, duplicates.size());
+    EXPECT_EQ(string("DUPLNS"), duplicates[0]);
+
+    string text = MessageDictionary::globalDictionary().getText("NEWSYM");
+    EXPECT_EQ(string("new symbol added"), text);
+}
diff --git a/src/lib/log/tests/message_initializer_unittest.cc b/src/lib/log/tests/message_initializer_unittest.cc
index 6a1019f..0cd1879 100644
--- a/src/lib/log/tests/message_initializer_unittest.cc
+++ b/src/lib/log/tests/message_initializer_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
-
 #include <cstddef>
 #include <string>
 #include <gtest/gtest.h>
@@ -61,12 +59,12 @@ protected:
 // messages.
 
 TEST_F(MessageInitializerTest, MessageTest) {
-    MessageDictionary* global = MessageDictionary::globalDictionary();
+    MessageDictionary& global = MessageDictionary::globalDictionary();
 
-    EXPECT_EQ(string("global message one"), global->getText("GLOBAL1"));
-    EXPECT_EQ(string("global message two"), global->getText("GLOBAL2"));
-    EXPECT_EQ(string("global message three"), global->getText("GLOBAL3"));
-    EXPECT_EQ(string("global message four"), global->getText("GLOBAL4"));
-    EXPECT_EQ(string("global message five"), global->getText("GLOBAL5"));
-    EXPECT_EQ(string("global message six"), global->getText("GLOBAL6"));
+    EXPECT_EQ(string("global message one"), global.getText("GLOBAL1"));
+    EXPECT_EQ(string("global message two"), global.getText("GLOBAL2"));
+    EXPECT_EQ(string("global message three"), global.getText("GLOBAL3"));
+    EXPECT_EQ(string("global message four"), global.getText("GLOBAL4"));
+    EXPECT_EQ(string("global message five"), global.getText("GLOBAL5"));
+    EXPECT_EQ(string("global message six"), global.getText("GLOBAL6"));
 }
diff --git a/src/lib/log/tests/message_initializer_unittest_2.cc b/src/lib/log/tests/message_initializer_unittest_2.cc
index c005033..94abb08 100644
--- a/src/lib/log/tests/message_initializer_unittest_2.cc
+++ b/src/lib/log/tests/message_initializer_unittest_2.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
-
 // The sole purpose of this file is to provide a set of message definitions
 // in a separate compilation unit from the one in which their presence is
 // checked.  This tests that merely declaring the MessageInitializer object
diff --git a/src/lib/log/tests/message_reader_unittest.cc b/src/lib/log/tests/message_reader_unittest.cc
index 2891805..36288f2 100644
--- a/src/lib/log/tests/message_reader_unittest.cc
+++ b/src/lib/log/tests/message_reader_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
-
 #include <algorithm>
 #include <string>
 #include <gtest/gtest.h>
@@ -76,7 +74,7 @@ TEST_F(MessageReaderTest, BlanksAndComments) {
 
     // ... and (b) nothing gets added to either the map or the not-added section.
     EXPECT_EQ(0, dictionary_->size());
-    vector<MessageID> not_added = reader_.getNotAdded();
+    vector<string> not_added = reader_.getNotAdded();
     EXPECT_EQ(0, not_added.size());
 }
 
@@ -85,14 +83,15 @@ TEST_F(MessageReaderTest, BlanksAndComments) {
 
 void
 processLineException(MessageReader& reader, const char* what,
-    MessageID& expected) {
+    const MessageID& expected) {
 
     try {
         reader.processLine(what);
         FAIL() << "MessageReader::processLine() should throw an exception " <<
             " with message ID " << expected << " for '" << what << "'\n";
     } catch (MessageException& e) {
-        EXPECT_EQ(expected, e.id());
+        EXPECT_EQ(boost::lexical_cast<string>(expected),
+            boost::lexical_cast<string>(e.id()));
     } catch (...) {
         FAIL() << "Unknown exception thrown by MessageReader::processLine()\n";
     }
@@ -102,13 +101,13 @@ processLineException(MessageReader& reader, const char* what,
 
 TEST_F(MessageReaderTest, Prefix) {
 
-    // Check that no prefix is present
+    // Check that no $PREFIX is present
     EXPECT_EQ(string(""), reader_.getPrefix());
 
-    // Check that a prefix directive with no argument generates an error.
+    // Check that a $PREFIX directive with no argument generates an error.
     processLineException(reader_, "$PREFIX", MSG_PRFNOARG);
 
-    // Check a prefix with multiple arguments is invalid
+    // Check a $PREFIX with multiple arguments is invalid
     processLineException(reader_, "$prefix A B", MSG_PRFEXTRARG);
 
     // Prefixes should be alphanumeric (with underscores) and not start
@@ -132,6 +131,43 @@ TEST_F(MessageReaderTest, Prefix) {
     EXPECT_EQ(string(""), reader_.getPrefix());
 }
 
+// Check that it can parse a namespace
+
+TEST_F(MessageReaderTest, Namespace) {
+
+    // Check that no $NAMESPACE is present
+    EXPECT_EQ(string(""), reader_.getNamespace());
+
+    // Check that a $NAMESPACE directive with no argument generates an error.
+    processLineException(reader_, "$NAMESPACE", MSG_NSNOARG);
+
+    // Check a $NAMESPACE with multiple arguments is invalid
+    processLineException(reader_, "$namespace A B", MSG_NSEXTRARG);
+
+    // Namespaces should be alphanumeric (with underscores and colons)
+    processLineException(reader_, "$namespace ab[cd", MSG_NSINVARG);
+
+    // A valid $NAMESPACE should be accepted
+    EXPECT_NO_THROW(reader_.processLine("$NAMESPACE isc"));
+    EXPECT_EQ(string("isc"), reader_.getNamespace());
+
+    // (Check that we can clear the namespace)
+    reader_.clearNamespace();
+    EXPECT_EQ(string(""), reader_.getNamespace());
+
+    // Check that a valid namespace can include colons
+    EXPECT_NO_THROW(reader_.processLine("$NAMESPACE isc::log"));
+    EXPECT_EQ(string("isc::log"), reader_.getNamespace());
+
+    // Check that the indication of the anonymous namespace will be recognised.
+    reader_.clearNamespace();
+    EXPECT_NO_THROW(reader_.processLine("$NAMESPACE ::"));
+    EXPECT_EQ(string("::"), reader_.getNamespace());
+
+    // ... and that another $NAMESPACE is rejected
+    processLineException(reader_, "$NAMESPACE ABC", MSG_DUPLNS);
+}
+
 // Check that it can parse a line
 
 TEST_F(MessageReaderTest, ValidMessageAddDefault) {
@@ -148,7 +184,7 @@ TEST_F(MessageReaderTest, ValidMessageAddDefault) {
     EXPECT_EQ(2, dictionary_->size());
 
     // ... and ensure no messages were not added
-    vector<MessageID> not_added = reader_.getNotAdded();
+    vector<string> not_added = reader_.getNotAdded();
     EXPECT_EQ(0, not_added.size());
 }
 
@@ -168,7 +204,7 @@ TEST_F(MessageReaderTest, ValidMessageAdd) {
     EXPECT_EQ(2, dictionary_->size());
 
     // ... and ensure no messages were not added
-    vector<MessageID> not_added = reader_.getNotAdded();
+    vector<string> not_added = reader_.getNotAdded();
     EXPECT_EQ(0, not_added.size());
 }
 
@@ -191,7 +227,7 @@ TEST_F(MessageReaderTest, ValidMessageReplace) {
     EXPECT_EQ(2, dictionary_->size());
 
     // ... and ensure no messages were not added
-    vector<MessageID> not_added = reader_.getNotAdded();
+    vector<string> not_added = reader_.getNotAdded();
     EXPECT_EQ(0, not_added.size());
 }
 
@@ -219,7 +255,7 @@ TEST_F(MessageReaderTest, Overflows) {
     EXPECT_EQ(2, dictionary_->size());
 
     // ... and ensure no overflows
-    vector<MessageID> not_added = reader_.getNotAdded();
+    vector<string> not_added = reader_.getNotAdded();
     ASSERT_EQ(2, not_added.size());
 
     sort(not_added.begin(), not_added.end());
diff --git a/src/lib/log/tests/root_logger_name_unittest.cc b/src/lib/log/tests/root_logger_name_unittest.cc
index 6994dc6..8665794 100644
--- a/src/lib/log/tests/root_logger_name_unittest.cc
+++ b/src/lib/log/tests/root_logger_name_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
-
 #include <string>
 
 #include <gtest/gtest.h>
@@ -37,8 +35,8 @@ TEST_F(RootLoggerNameTest, SetGet) {
     const std::string name2 = "test2";
 
     // Check that Set/Get works
-    RootLoggerName::setName(name1);
-    EXPECT_EQ(name1, RootLoggerName::getName());
+    setRootLoggerName(name1);
+    EXPECT_EQ(name1, getRootLoggerName());
 
     // We could not test that the root logger name is initialised
     // correctly (as there is one instance of it and we don't know
@@ -47,6 +45,6 @@ TEST_F(RootLoggerNameTest, SetGet) {
     //
     // (There was always the outside chance that the root logger name
     // was initialised with name1 and that setName() has no effect.)
-    RootLoggerName::setName(name2);
-    EXPECT_EQ(name2, RootLoggerName::getName());
+    setRootLoggerName(name2);
+    EXPECT_EQ(name2, getRootLoggerName());
 }
diff --git a/src/lib/log/tests/run_time_init_test.sh.in b/src/lib/log/tests/run_time_init_test.sh.in
index be52ded..e2bdf6f 100755
--- a/src/lib/log/tests/run_time_init_test.sh.in
+++ b/src/lib/log/tests/run_time_init_test.sh.in
@@ -14,7 +14,7 @@
 # PERFORMANCE OF THIS SOFTWARE.
 
 failcount=0
-localmes=@abs_srcdir@/localdef.mes
+localmes=@abs_builddir@/localdef_mes_$$
 tempfile=@abs_builddir@/run_time_init_test_tempfile_$$
 
 passfail() {
@@ -25,20 +25,28 @@ passfail() {
     fi
     failcount=`expr $failcount + $1`
 }
-    
+
+# Create the local message file for testing
+
+cat > $localmes << .
+NOTHERE     this message is not in the global dictionary
+MSGRDERR    replacement read error, parameters: '%s' and '%s'
+UNRECDIR    replacement unrecognised directive message, parameter is '%s'
+.
+
 echo -n "1. runInitTest default parameters: "
 cat > $tempfile << .
-FATAL [alpha.example] WRITERR, error writing to test1: 42
+FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
 ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
-WARN  [alpha.dlm] READERR, error reading from a.txt: dummy test
-INFO  [alpha.dlm] OPENIN, unable to open message file example.msg for input: dummy test
+WARN  [alpha.dlm] MSGRDERR, error reading from message file a.txt: dummy test
+INFO  [alpha.dlm] OPNMSGIN, unable to open message file example.msg for input: dummy test
 .
 ./logger_support_test | cut -d' ' -f3- | diff $tempfile -
 passfail $?
 
 echo -n "2. Severity filter: "
 cat > $tempfile << .
-FATAL [alpha.example] WRITERR, error writing to test1: 42
+FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
 ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
 .
 ./logger_support_test -s error | cut -d' ' -f3- | diff $tempfile -
@@ -46,10 +54,10 @@ passfail $?
 
 echo -n "3. Debug level: "
 cat > $tempfile << .
-FATAL [alpha.example] WRITERR, error writing to test1: 42
+FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
 ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
-WARN  [alpha.dlm] READERR, error reading from a.txt: dummy test
-INFO  [alpha.dlm] OPENIN, unable to open message file example.msg for input: dummy test
+WARN  [alpha.dlm] MSGRDERR, error reading from message file a.txt: dummy test
+INFO  [alpha.dlm] OPNMSGIN, unable to open message file example.msg for input: dummy test
 DEBUG [alpha.example] UNRECDIR, unrecognised directive '[abc]'
 DEBUG [alpha.example] UNRECDIR, unrecognised directive '[24]'
 DEBUG [alpha.example] UNRECDIR, unrecognised directive '[25]'
@@ -60,14 +68,14 @@ passfail $?
 echo -n "4. Local message replacement: "
 cat > $tempfile << .
 WARN  [alpha.log] IDNOTFND, could not replace message for 'NOTHERE': no such message identification
-FATAL [alpha.example] WRITERR, error writing to test1: 42
+FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
 ERROR [alpha.example] UNRECDIR, replacement unrecognised directive message, parameter is 'false'
-WARN  [alpha.dlm] READERR, replacement read error, parameters: 'a.txt' and 'dummy test'
-INFO  [alpha.dlm] OPENIN, unable to open message file example.msg for input: dummy test
+WARN  [alpha.dlm] MSGRDERR, replacement read error, parameters: 'a.txt' and 'dummy test'
 .
-./logger_support_test $localmes | cut -d' ' -f3- | diff $tempfile -
+./logger_support_test -s warn $localmes | cut -d' ' -f3- | diff $tempfile -
 passfail $?
 
+rm -f $localmes
 rm -f $tempfile
 
 if [ $failcount -eq 0 ]; then
diff --git a/src/lib/log/tests/run_unittests.cc b/src/lib/log/tests/run_unittests.cc
index b91ce24..bd3c4c9 100644
--- a/src/lib/log/tests/run_unittests.cc
+++ b/src/lib/log/tests/run_unittests.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: run_unittests.cc 3020 2010-09-26 03:47:26Z jinmei $
-
 #include <gtest/gtest.h>
 
 int
diff --git a/src/lib/log/tests/strutil_unittest.cc b/src/lib/log/tests/strutil_unittest.cc
index 6e657f3..b3fceef 100644
--- a/src/lib/log/tests/strutil_unittest.cc
+++ b/src/lib/log/tests/strutil_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
-
 #include <string>
 
 #include <gtest/gtest.h>
diff --git a/src/lib/log/tests/xdebuglevel_unittest.cc b/src/lib/log/tests/xdebuglevel_unittest.cc
index 2cb0952..ca80e5a 100644
--- a/src/lib/log/tests/xdebuglevel_unittest.cc
+++ b/src/lib/log/tests/xdebuglevel_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: $
-
 #include <iostream>
 #include <string>
 
@@ -21,7 +19,7 @@
 
 #include <log4cxx/level.h>
 #include <log/xdebuglevel.h>
-#include <log/dbglevels.h>
+#include <log/debug_levels.h>
 
 /// \brief XDebugLevel (Debug Extension to Level Class)
 ///
diff --git a/src/lib/log/xdebuglevel.cc b/src/lib/log/xdebuglevel.cc
index 7dddcff..c17a515 100644
--- a/src/lib/log/xdebuglevel.cc
+++ b/src/lib/log/xdebuglevel.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
- 
 #include <cassert>
 #include <algorithm>
 #include <syslog.h>
@@ -21,7 +19,7 @@
 #include <boost/lexical_cast.hpp>
 
 #include <xdebuglevel.h>
-#include <dbglevels.h>
+#include <debug_levels.h>
 #include <log4cxx/helpers/stringhelper.h>
 
 using namespace log4cxx;
@@ -71,7 +69,7 @@ XDebugLevel::getExtendedDebug(int level) {
     int actual = std::max(MIN_DEBUG_LEVEL, std::min(MAX_DEBUG_LEVEL, level));
 
     // ... and return a pointer to the appropriate logging level object
-    return dbglevels_[actual - MIN_DEBUG_LEVEL];
+    return (dbglevels_[actual - MIN_DEBUG_LEVEL]);
 }
 
 // Convert an integer (an absolute logging level number, not a debug level) to a
@@ -80,7 +78,7 @@ XDebugLevel::getExtendedDebug(int level) {
 
 LevelPtr
 XDebugLevel::toLevel(int val) {
-    return toLevel(val, getExtendedDebug(MIN_DEBUG_LEVEL));
+    return (toLevel(val, getExtendedDebug(MIN_DEBUG_LEVEL)));
 }
 
 LevelPtr
@@ -89,10 +87,10 @@ XDebugLevel::toLevel(int val, const LevelPtr& defaultLevel) {
     // Note the reversal of the notion of MIN and MAX - see the header file for
     // details.
     if ((val >= XDEBUG_MAX_LEVEL_INT) && (val <= XDEBUG_MIN_LEVEL_INT)) {
-        return getExtendedDebug(XDEBUG_MIN_LEVEL_INT - val);
+        return (getExtendedDebug(XDEBUG_MIN_LEVEL_INT - val));
     }
     else {
-        return defaultLevel;
+        return (defaultLevel);
     }
 }
 
@@ -100,7 +98,7 @@ XDebugLevel::toLevel(int val, const LevelPtr& defaultLevel) {
 
 LevelPtr
 XDebugLevel::toLevelLS(const LogString& sArg) {
-    return toLevelLS(sArg, getExtendedDebug(0));
+    return (toLevelLS(sArg, getExtendedDebug(0)));
 }
 
 LevelPtr
@@ -111,7 +109,7 @@ XDebugLevel::toLevelLS(const LogString& sArg, const LevelPtr& defaultLevel) {
     if (length < 5) {
 
         // String can't possibly start DEBUG so we don't know what it is.
-        return defaultLevel;
+        return (defaultLevel);
     }
     else {
         if (strncasecmp(name.c_str(), "DEBUG", 5) == 0) {
@@ -121,7 +119,7 @@ XDebugLevel::toLevelLS(const LogString& sArg, const LevelPtr& defaultLevel) {
             if (length == 5) {
 
                 // It is plain "DEBUG".  Take this as level 0.
-                return getExtendedDebug(0);
+                return (getExtendedDebug(0));
             }
             else {
 
@@ -132,17 +130,17 @@ XDebugLevel::toLevelLS(const LogString& sArg, const LevelPtr& defaultLevel) {
                 // if DEBUG99 has been specified.
                 try {
                     int level = boost::lexical_cast<int>(name.substr(5));
-                    return getExtendedDebug(level);
+                    return (getExtendedDebug(level));
                 }
-                catch (boost::bad_lexical_cast&) {
-                    return defaultLevel;
+                catch ((boost::bad_lexical_cast&) ){
+                    return (defaultLevel);
                 }
             }
         }
         else {
 
             // Unknown string - return default.
-            return defaultLevel;
+            return (defaultLevel);
         }
     }
 }
diff --git a/src/lib/log/xdebuglevel.h b/src/lib/log/xdebuglevel.h
index 4d28ead..fce3de4 100644
--- a/src/lib/log/xdebuglevel.h
+++ b/src/lib/log/xdebuglevel.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,15 +12,13 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
-
 #ifndef __XDEBUGLEVEL_H
 #define __XDEBUGLEVEL_H
 
 #include <syslog.h>
 #include <log4cxx/level.h>
 
-#include <dbglevels.h>
+#include <debug_levels.h>
 
 namespace log4cxx {
 
diff --git a/src/lib/python/isc/cc/data.py b/src/lib/python/isc/cc/data.py
index c75fbfe..ce1bba0 100644
--- a/src/lib/python/isc/cc/data.py
+++ b/src/lib/python/isc/cc/data.py
@@ -100,8 +100,8 @@ def split_identifier_list_indices(identifier):
 
     i = id_str.find('[')
     if i < 0:
-        if identifier.find(']') >= 0:
-            raise DataTypeError("Bad format in identifier: " + str(identifier))
+        if id_str.find(']') >= 0:
+            raise DataTypeError("Bad format in identifier (] but no [): " + str(identifier))
         return identifier, None
 
     # keep the non-index part of that to replace later
@@ -110,7 +110,7 @@ def split_identifier_list_indices(identifier):
     while i >= 0:
         e = id_str.find(']')
         if e < i + 1:
-            raise DataTypeError("Bad format in identifier: " + str(identifier))
+            raise DataTypeError("Bad format in identifier (] before [): " + str(identifier))
         try:
             indices.append(int(id_str[i+1:e]))
         except ValueError:
@@ -118,9 +118,9 @@ def split_identifier_list_indices(identifier):
         id_str = id_str[e + 1:]
         i = id_str.find('[')
         if i > 0:
-            raise DataTypeError("Bad format in identifier: " + str(identifier))
+            raise DataTypeError("Bad format in identifier ([ within []): " + str(identifier))
     if id.find(']') >= 0 or len(id_str) > 0:
-        raise DataTypeError("Bad format in identifier: " + str(identifier))
+        raise DataTypeError("Bad format in identifier (extra ]): " + str(identifier))
 
     # we replace the final part of the original identifier with
     # the stripped string
diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py
index e7fdb86..0e602b7 100644
--- a/src/lib/python/isc/config/ccsession.py
+++ b/src/lib/python/isc/config/ccsession.py
@@ -374,20 +374,34 @@ class UIModuleCCSession(MultiConfigData):
         self._set_current_config(config)
 
 
-    def add_value(self, identifier, value_str):
+    def add_value(self, identifier, value_str = None):
         """Add a value to a configuration list. Raises a DataTypeError
            if the value does not conform to the list_item_spec field
-           of the module config data specification"""
+           of the module config data specification. If value_str is
+           not given, we add the default as specified by the .spec
+           file."""
         module_spec = self.find_spec_part(identifier)
         if (type(module_spec) != dict or "list_item_spec" not in module_spec):
             raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list")
-        value = isc.cc.data.parse_value_str(value_str)
+
         cur_list, status = self.get_value(identifier)
         if not cur_list:
             cur_list = []
+
+        # Hmm. Do we need to check for duplicates?
+        value = None
+        if value_str is not None:
+            value = isc.cc.data.parse_value_str(value_str)
+        else:
+            if "item_default" in module_spec["list_item_spec"]:
+                value = module_spec["list_item_spec"]["item_default"]
+
+        if value is None:
+            raise isc.cc.data.DataNotFoundError("No value given and no default for " + str(identifier))
+            
         if value not in cur_list:
             cur_list.append(value)
-        self.set_value(identifier, cur_list)
+            self.set_value(identifier, cur_list)
 
     def remove_value(self, identifier, value_str):
         """Remove a value from a configuration list. The value string
diff --git a/src/lib/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py
index 1e3afd1..582c11c 100644
--- a/src/lib/python/isc/config/config_data.py
+++ b/src/lib/python/isc/config/config_data.py
@@ -121,6 +121,7 @@ def find_spec_part(element, identifier):
         # strip list selector part
         # don't need it for the spec part, so just drop it
         id, list_indices = isc.cc.data.split_identifier_list_indices(id_part)
+        # is this part still needed? (see below)
         if type(cur_el) == dict and 'map_item_spec' in cur_el.keys():
             found = False
             for cur_el_item in cur_el['map_item_spec']:
@@ -128,15 +129,24 @@ def find_spec_part(element, identifier):
                     cur_el = cur_el_item
                     found = True
             if not found:
-                raise isc.cc.data.DataNotFoundError(id + " in " + str(cur_el))
+                raise isc.cc.data.DataNotFoundError(id + " not found")
+        elif type(cur_el) == dict and 'list_item_spec' in cur_el.keys():
+            cur_el = cur_el['list_item_spec']
         elif type(cur_el) == list:
             found = False
             for cur_el_item in cur_el:
                 if cur_el_item['item_name'] == id:
                     cur_el = cur_el_item
+                    # if we need to go further, we may need to 'skip' a step here
+                    # but not if we're done
+                    if id_parts[-1] != id_part and type(cur_el) == dict:
+                        if "map_item_spec" in cur_el:
+                            cur_el = cur_el["map_item_spec"]
+                        elif "list_item_spec" in cur_el:
+                            cur_el = cur_el["list_item_spec"]
                     found = True
             if not found:
-                raise isc.cc.data.DataNotFoundError(id + " in " + str(cur_el))
+                raise isc.cc.data.DataNotFoundError(id + " not found")
         else:
             raise isc.cc.data.DataNotFoundError("Not a correct config specification")
     return cur_el
@@ -354,26 +364,63 @@ class MultiConfigData:
            See get_value() for a general way to find a configuration
            value
         """
-        if identifier[0] == '/':
-            identifier = identifier[1:]
-        module, sep, id = identifier.partition("/")
         try:
+            if identifier[0] == '/':
+                identifier = identifier[1:]
+            module, sep, id = identifier.partition("/")
+            # if there is a 'higher-level' list index specified, we need
+            # to check if that list specification has a default that
+            # overrides the more specific default in the final spec item
+            # (ie. list_default = [1, 2, 3], list_item_spec=int, default=0)
+            # def default list[1] should return 2, not 0
+            id_parts = isc.cc.data.split_identifier(id)
+            id_prefix = ""
+            while len(id_parts) > 0:
+                id_part = id_parts.pop(0)
+                item_id, list_indices = isc.cc.data.split_identifier_list_indices(id_part)
+                id_list = module + "/" + id_prefix + "/" + item_id
+                id_prefix += "/" + id_part
+                if list_indices is not None:
+                    # there's actually two kinds of default here for
+                    # lists; they can have a default value (like an
+                    # empty list), but their elements can  also have
+                    # default values.
+                    # So if the list item *itself* is a default,
+                    # we need to get the value out of that. If not, we
+                    # need to find the default for the specific element.
+                    list_value, type = self.get_value(id_list) 
+                    list_spec = find_spec_part(self._specifications[module].get_config_spec(), id_prefix)
+                    if type == self.DEFAULT:
+                        if 'item_default' in list_spec:
+                            list_value = list_spec['item_default']
+                            for i in list_indices:
+                                if i < len(list_value):
+                                    list_value = list_value[i]
+                                else:
+                                    # out of range, return None
+                                    return None
+                                
+                            if len(id_parts) > 0:
+                                rest_of_id = "/".join(id_parts)
+                                return isc.cc.data.find(list_value, rest_of_id)
+                            else:
+                                return list_value
+                    else:
+                        # we do have a non-default list, see if our indices
+                        # exist
+                        for i in list_indices:
+                            if i < len(list_value):
+                                list_value = list_value[i]
+                            else:
+                                # out of range, return None
+                                return None
+                    
             spec = find_spec_part(self._specifications[module].get_config_spec(), id)
             if 'item_default' in spec:
-                id, list_indices = isc.cc.data.split_identifier_list_indices(id)
-                if list_indices is not None and \
-                   type(spec['item_default']) == list:
-                    if len(list_indices) == 1:
-                        default_list = spec['item_default']
-                        index = list_indices[0]
-                        if index < len(default_list):
-                            return default_list[index]
-                        else:
-                            return None
-                else:
-                    return spec['item_default']
+                return spec['item_default']
             else:
                 return None
+
         except isc.cc.data.DataNotFoundError as dnfe:
             return None
 
@@ -398,13 +445,60 @@ class MultiConfigData:
                 return value, self.DEFAULT
         return None, self.NONE
 
-    def get_value_maps(self, identifier = None):
+    def _append_value_item(self, result, spec_part, identifier, all, first = False):
+        # Look at the spec; it is a list of items, or a map containing 'item_name' etc
+        if type(spec_part) == list:
+            for spec_part_element in spec_part:
+                spec_part_element_name = spec_part_element['item_name']
+                self._append_value_item(result, spec_part_element, identifier + "/" + spec_part_element_name, all)
+        elif type(spec_part) == dict:
+            # depending on item type, and the value of argument 'all'
+            # we need to either add an item, or recursively go on
+            # In the case of a list that is empty, we do need to show that
+            item_name = spec_part['item_name']
+            item_type = spec_part['item_type']
+            if item_type == "list" and (all or first):
+                spec_part_list = spec_part['list_item_spec']
+                list_value, status = self.get_value(identifier)
+                if list_value is None:
+                    print("Error: identifier '%s' not found" % identifier)
+                    return
+                if type(list_value) != list:
+                    # the identifier specified a single element
+                    self._append_value_item(result, spec_part_list, identifier, all)
+                else:
+                    list_len = len(list_value)
+                    if len(list_value) == 0 and (all or first):
+                        entry = _create_value_map_entry(identifier,
+                                                        item_type,
+                                                        [], status)
+                        result.append(entry)
+                    else:
+                        for i in range(len(list_value)):
+                            self._append_value_item(result, spec_part_list, "%s[%d]" % (identifier, i), all)
+            elif item_type == "map":
+                # just show the specific contents of a map, we are
+                # almost never interested in just its name
+                spec_part_map = spec_part['map_item_spec']
+                self._append_value_item(result, spec_part_map, identifier, all)
+            else:
+                value, status = self.get_value(identifier)
+                entry = _create_value_map_entry(identifier,
+                                                item_type,
+                                                value, status)
+                result.append(entry)
+        return
+
+
+    def get_value_maps(self, identifier = None, all = False):
         """Returns a list of dicts, containing the following values:
            name: name of the entry (string)
            type: string containing the type of the value (or 'module')
            value: value of the entry if it is a string, int, double or bool
-           modified: true if the value is a local change
-           default: true if the value has been changed
+           modified: true if the value is a local change that has not
+                     been committed
+           default: true if the value has not been changed (i.e. the
+                    value is the default from the specification)
            TODO: use the consts for those last ones
            Throws DataNotFoundError if the identifier is bad
         """
@@ -412,8 +506,14 @@ class MultiConfigData:
         if not identifier:
             # No identifier, so we need the list of current modules
             for module in self._specifications.keys():
-                entry = _create_value_map_entry(module, 'module', None)
-                result.append(entry)
+                if all:
+                    spec = self.get_module_spec(module)
+                    if spec:
+                        spec_part = spec.get_config_spec()
+                        self._append_value_item(result, spec_part, module, all, True)
+                else:
+                    entry = _create_value_map_entry(module, 'module', None)
+                    result.append(entry)
         else:
             if identifier[0] == '/':
                 identifier = identifier[1:]
@@ -421,42 +521,7 @@ class MultiConfigData:
             spec = self.get_module_spec(module)
             if spec:
                 spec_part = find_spec_part(spec.get_config_spec(), id)
-                if type(spec_part) == list:
-                    # list of items to show
-                    for item in spec_part:
-                        value, status = self.get_value("/" + identifier\
-                                              + "/" + item['item_name'])
-                        entry = _create_value_map_entry(item['item_name'],
-                                                        item['item_type'],
-                                                        value, status)
-                        result.append(entry)
-                elif type(spec_part) == dict:
-                    # Sub-specification
-                    item = spec_part
-                    if item['item_type'] == 'list':
-                        li_spec = item['list_item_spec']
-                        value, status =  self.get_value("/" + identifier)
-                        if type(value) == list:
-                            for list_value in value:
-                                result_part2 = _create_value_map_entry(
-                                                   li_spec['item_name'],
-                                                   li_spec['item_type'],
-                                                   list_value)
-                                result.append(result_part2)
-                        elif value is not None:
-                            entry = _create_value_map_entry(
-                                        li_spec['item_name'],
-                                        li_spec['item_type'],
-                                        value, status)
-                            result.append(entry)
-                    else:
-                        value, status = self.get_value("/" + identifier)
-                        if value is not None:
-                            entry = _create_value_map_entry(
-                                        item['item_name'],
-                                        item['item_type'],
-                                        value, status)
-                            result.append(entry)
+                self._append_value_item(result, spec_part, identifier, all, True)
         return result
 
     def set_value(self, identifier, value):
diff --git a/src/lib/python/isc/config/tests/config_data_test.py b/src/lib/python/isc/config/tests/config_data_test.py
index 4710b00..1aded94 100644
--- a/src/lib/python/isc/config/tests/config_data_test.py
+++ b/src/lib/python/isc/config/tests/config_data_test.py
@@ -358,6 +358,8 @@ class TestMultiConfigData(unittest.TestCase):
         self.assertEqual(1, value)
         value = self.mcd.get_default_value("Spec2/item5[0]")
         self.assertEqual('a', value)
+        value = self.mcd.get_default_value("Spec2/item5[1]")
+        self.assertEqual('b', value)
         value = self.mcd.get_default_value("Spec2/item5[5]")
         self.assertEqual(None, value)
         value = self.mcd.get_default_value("Spec2/item5[0][1]")
@@ -392,6 +394,10 @@ class TestMultiConfigData(unittest.TestCase):
         self.assertEqual(None, value)
         self.assertEqual(MultiConfigData.NONE, status)
 
+        value, status = self.mcd.get_value("Spec2/item5")
+        self.assertEqual(['a', 'b'], value)
+        self.assertEqual(MultiConfigData.DEFAULT, status)
+
         value, status = self.mcd.get_value("Spec2/item5[0]")
         self.assertEqual("a", value)
         self.assertEqual(MultiConfigData.DEFAULT, status)
@@ -400,6 +406,11 @@ class TestMultiConfigData(unittest.TestCase):
         self.assertEqual(None, value)
         self.assertEqual(MultiConfigData.NONE, status)
 
+        value, status = self.mcd.get_value("Spec2/item5[1]")
+        self.assertEqual("b", value)
+        self.assertEqual(MultiConfigData.DEFAULT, status)
+
+
 
     def test_get_value_maps(self):
         maps = self.mcd.get_value_maps()
@@ -423,32 +434,34 @@ class TestMultiConfigData(unittest.TestCase):
         self.mcd._set_current_config({ "Spec2": { "item1": 2 } })
         self.mcd.set_value("Spec2/item3", False)
         maps = self.mcd.get_value_maps("/Spec2")
-        self.assertEqual([{'default': False, 'type': 'integer', 'name': 'item1', 'value': 2, 'modified': False},
-                          {'default': True, 'type': 'real', 'name': 'item2', 'value': 1.1, 'modified': False},
-                          {'default': False, 'type': 'boolean', 'name': 'item3', 'value': False, 'modified': True},
-                          {'default': True, 'type': 'string', 'name': 'item4', 'value': 'test', 'modified': False},
-                          {'default': True, 'type': 'list', 'name': 'item5', 'value': ['a', 'b'], 'modified': False},
-                          {'default': True, 'type': 'map', 'name': 'item6', 'value': {}, 'modified': False}], maps)
+        self.assertEqual([{'default': False, 'type': 'integer', 'name': 'Spec2/item1', 'value': 2, 'modified': False},
+                          {'default': True, 'type': 'real', 'name': 'Spec2/item2', 'value': 1.1, 'modified': False},
+                          {'default': False, 'type': 'boolean', 'name': 'Spec2/item3', 'value': False, 'modified': True},
+                          {'default': True, 'type': 'string', 'name': 'Spec2/item4', 'value': 'test', 'modified': False},
+                          {'default': True, 'type': 'list', 'name': 'Spec2/item5', 'value': ['a', 'b'], 'modified': False},
+                          {'default': True, 'type': 'string', 'name': 'Spec2/item6/value1', 'value': 'default', 'modified': False},
+                          {'default': False, 'type': 'integer', 'name': 'Spec2/item6/value2', 'value': None, 'modified': False}], maps)
         maps = self.mcd.get_value_maps("Spec2")
-        self.assertEqual([{'default': False, 'type': 'integer', 'name': 'item1', 'value': 2, 'modified': False},
-                          {'default': True, 'type': 'real', 'name': 'item2', 'value': 1.1, 'modified': False},
-                          {'default': False, 'type': 'boolean', 'name': 'item3', 'value': False, 'modified': True},
-                          {'default': True, 'type': 'string', 'name': 'item4', 'value': 'test', 'modified': False},
-                          {'default': True, 'type': 'list', 'name': 'item5', 'value': ['a', 'b'], 'modified': False},
-                          {'default': True, 'type': 'map', 'name': 'item6', 'value': {}, 'modified': False}], maps)
+        self.assertEqual([{'default': False, 'type': 'integer', 'name': 'Spec2/item1', 'value': 2, 'modified': False},
+                          {'default': True, 'type': 'real', 'name': 'Spec2/item2', 'value': 1.1, 'modified': False},
+                          {'default': False, 'type': 'boolean', 'name': 'Spec2/item3', 'value': False, 'modified': True},
+                          {'default': True, 'type': 'string', 'name': 'Spec2/item4', 'value': 'test', 'modified': False},
+                          {'default': True, 'type': 'list', 'name': 'Spec2/item5', 'value': ['a', 'b'], 'modified': False},
+                          {'default': True, 'type': 'string', 'name': 'Spec2/item6/value1', 'value': 'default', 'modified': False},
+                          {'default': False, 'type': 'integer', 'name': 'Spec2/item6/value2', 'value': None, 'modified': False}], maps)
         maps = self.mcd.get_value_maps("/Spec2/item5")
-        self.assertEqual([{'default': False, 'type': 'string', 'name': 'list_element', 'value': 'a', 'modified': False},
-                          {'default': False, 'type': 'string', 'name': 'list_element', 'value': 'b', 'modified': False}], maps)
+        self.assertEqual([{'default': True, 'type': 'string', 'name': 'Spec2/item5[0]', 'value': 'a', 'modified': False},
+                          {'default': True, 'type': 'string', 'name': 'Spec2/item5[1]', 'value': 'b', 'modified': False}], maps)
         maps = self.mcd.get_value_maps("/Spec2/item5[0]")
-        self.assertEqual([{'default': True, 'modified': False, 'name': 'list_element', 'type': 'string', 'value': 'a'}], maps)
+        self.assertEqual([{'default': True, 'modified': False, 'name': 'Spec2/item5[0]', 'type': 'string', 'value': 'a'}], maps)
         maps = self.mcd.get_value_maps("/Spec2/item1")
-        self.assertEqual([{'default': False, 'type': 'integer', 'name': 'item1', 'value': 2, 'modified': False}], maps)
+        self.assertEqual([{'default': False, 'type': 'integer', 'name': 'Spec2/item1', 'value': 2, 'modified': False}], maps)
         maps = self.mcd.get_value_maps("/Spec2/item2")
-        self.assertEqual([{'default': True, 'type': 'real', 'name': 'item2', 'value': 1.1, 'modified': False}], maps)
+        self.assertEqual([{'default': True, 'type': 'real', 'name': 'Spec2/item2', 'value': 1.1, 'modified': False}], maps)
         maps = self.mcd.get_value_maps("/Spec2/item3")
-        self.assertEqual([{'default': False, 'type': 'boolean', 'name': 'item3', 'value': False, 'modified': True}], maps)
+        self.assertEqual([{'default': False, 'type': 'boolean', 'name': 'Spec2/item3', 'value': False, 'modified': True}], maps)
         maps = self.mcd.get_value_maps("/Spec2/item4")
-        self.assertEqual([{'default': True, 'type': 'string', 'name': 'item4', 'value': 'test', 'modified': False}], maps)
+        self.assertEqual([{'default': True, 'type': 'string', 'name': 'Spec2/item4', 'value': 'test', 'modified': False}], maps)
 
         module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec24.spec")
         self.mcd.set_specification(module_spec)
@@ -456,9 +469,30 @@ class TestMultiConfigData(unittest.TestCase):
         self.assertEqual([], maps)
         self.mcd._set_current_config({ "Spec24": { "item": [] } })
         maps = self.mcd.get_value_maps("/Spec24/item")
-        self.assertEqual([], maps)
-
+        self.assertEqual([{'default': False, 'modified': False, 'name': 'Spec24/item', 'type': 'list', 'value': []}], maps)
 
+        module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec22.spec")
+        self.mcd.set_specification(module_spec)
+        expected = [{'default': True,
+                     'modified': False,
+                     'name': 'Spec22/value9/v91',
+                     'type': 'string',
+                     'value': 'def'},
+                    {'default': True,
+                     'modified': False,
+                     'name': 'Spec22/value9/v92/v92a',
+                     'type': 'string',
+                     'value': 'Hello'
+                    },
+                    {'default': True,
+                     'modified': False,
+                     'name': 'Spec22/value9/v92/v92b',
+                     'type': 'integer',
+                     'value': 47806
+                    }
+                   ]
+        maps = self.mcd.get_value_maps("/Spec22/value9")
+        self.assertEqual(expected, maps)
 
     def test_set_value(self):
         module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec2.spec")




More information about the bind10-changes mailing list