Merge the last green changeset of mozilla-inbound to mozilla-central
authorEhsan Akhgari <ehsan@mozilla.com>
Mon, 26 Sep 2011 10:35:13 -0400
changeset 78880 44ef245b870628514f0fd9ba667788b56b0ecd65
parent 78879 7d983fc8a13c41fef193013d146474284bc4e877 (current diff)
parent 78826 c722928d8b69b18daf5c6a29e1c66ea35cf7e230 (diff)
child 78881 42f80b4629c212f4bade981660d879396e9a6a0c
child 78888 a0bac7d8cc0cd502b00690a9db22b9c1aee90771
child 83221 ff51ddfdf5d1c04f37c272b745454af2d58be08d
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge the last green changeset of mozilla-inbound to mozilla-central
toolkit/toolkit-makefiles.sh
--- a/browser/components/build/Makefile.in
+++ b/browser/components/build/Makefile.in
@@ -49,19 +49,23 @@ SHARED_LIBRARY_LIBS = \
 	$(NULL)
 
 ifneq (,$(filter windows cocoa gtk2, $(MOZ_WIDGET_TOOLKIT)))
 SHARED_LIBRARY_LIBS += ../shell/src/$(LIB_PREFIX)shellservice_s.$(LIB_SUFFIX)
 endif
 
 EXTRA_DSO_LDOPTS += $(call EXPAND_LIBNAME_PATH,unicharutil_external_s,$(LIBXUL_DIST)/lib)
 
+# migration requires mozreg
 LOCAL_INCLUDES += -I$(srcdir)/../migration/src
 SHARED_LIBRARY_LIBS += ../migration/src/$(LIB_PREFIX)migration_s.$(LIB_SUFFIX)
+EXTRA_DSO_LDOPTS += $(LIBXUL_DIST)/lib/$(LIB_PREFIX)mozreg_s.$(LIB_SUFFIX)
 
+# This has to come after the above chunk, because mozreg_s has dependencies on
+# stuff in MOZ_COMPONENT_LIBS.
 EXTRA_DSO_LDOPTS += \
 	$(LIBXUL_DIST)/lib/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \
 	$(MOZ_COMPONENT_LIBS) \
 	$(NULL)
 
 # Mac: Need to link with CoreFoundation for Mac Migrators (PList reading code)
 # GTK2: Need to link with glib for GNOME shell service
 ifneq (,$(filter cocoa gtk2,$(MOZ_WIDGET_TOOLKIT)))
--- a/browser/components/migration/src/nsProfileMigrator.cpp
+++ b/browser/components/migration/src/nsProfileMigrator.cpp
@@ -51,28 +51,41 @@
 #include "nsIWindowWatcher.h"
 
 #include "nsCOMPtr.h"
 #include "nsBrowserCompsCID.h"
 #include "nsComponentManagerUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsServiceManagerUtils.h"
 
+#include "NSReg.h"
 #include "nsStringAPI.h"
 #include "nsUnicharUtils.h"
 #ifdef XP_WIN
 #include <windows.h>
 #include "nsIWindowsRegKey.h"
 #include "nsILocalFileWin.h"
 #else
 #include <limits.h>
 #endif
 
 #include "nsAutoPtr.h"
 
+#ifndef MAXPATHLEN
+#ifdef PATH_MAX
+#define MAXPATHLEN PATH_MAX
+#elif defined(_MAX_PATH)
+#define MAXPATHLEN _MAX_PATH
+#elif defined(CCHMAXPATH)
+#define MAXPATHLEN CCHMAXPATH
+#else
+#define MAXPATHLEN 1024
+#endif
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 // nsIProfileMigrator
 
 #define MIGRATION_WIZARD_FE_URL "chrome://browser/content/migration/migration.xul"
 #define MIGRATION_WIZARD_FE_FEATURES "chrome,dialog,modal,centerscreen,titlebar"
 
 NS_IMETHODIMP
 nsProfileMigrator::Migrate(nsIProfileStartup* aStartup)
@@ -126,16 +139,25 @@ nsProfileMigrator::Migrate(nsIProfileSta
   return ww->OpenWindow(nsnull, 
                         MIGRATION_WIZARD_FE_URL,
                         "_blank",
                         MIGRATION_WIZARD_FE_FEATURES,
                         params,
                         getter_AddRefs(migrateWizard));
 }
 
+NS_IMETHODIMP
+nsProfileMigrator::Import()
+{
+  if (ImportRegistryProfiles(NS_LITERAL_CSTRING("Firefox")))
+    return NS_OK;
+
+  return NS_ERROR_FAILURE;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // nsProfileMigrator
 
 NS_IMPL_ISUPPORTS1(nsProfileMigrator, nsIProfileMigrator)
 
 #ifdef XP_WIN
 
 #define INTERNAL_NAME_IEXPLORE        "iexplore"
@@ -240,8 +262,115 @@ nsProfileMigrator::GetDefaultBrowserMigr
   CHECK_MIGRATOR("safari");
 #endif
   CHECK_MIGRATOR("opera");
 
 #undef CHECK_MIGRATOR
 #endif
   return NS_ERROR_FAILURE;
 }
+
+PRBool
+nsProfileMigrator::ImportRegistryProfiles(const nsACString& aAppName)
+{
+  nsresult rv;
+
+  nsCOMPtr<nsIToolkitProfileService> profileSvc
+    (do_GetService(NS_PROFILESERVICE_CONTRACTID));
+  NS_ENSURE_TRUE(profileSvc, PR_FALSE);
+
+  nsCOMPtr<nsIProperties> dirService
+    (do_GetService("@mozilla.org/file/directory_service;1"));
+  NS_ENSURE_TRUE(dirService, PR_FALSE);
+
+  nsCOMPtr<nsILocalFile> regFile;
+#ifdef XP_WIN
+  rv = dirService->Get(NS_WIN_APPDATA_DIR, NS_GET_IID(nsILocalFile),
+                       getter_AddRefs(regFile));
+  NS_ENSURE_SUCCESS(rv, PR_FALSE);
+  regFile->AppendNative(aAppName);
+  regFile->AppendNative(NS_LITERAL_CSTRING("registry.dat"));
+#elif defined(XP_MACOSX)
+  rv = dirService->Get(NS_MAC_USER_LIB_DIR, NS_GET_IID(nsILocalFile),
+                       getter_AddRefs(regFile));
+  NS_ENSURE_SUCCESS(rv, PR_FALSE);
+  regFile->AppendNative(aAppName);
+  regFile->AppendNative(NS_LITERAL_CSTRING("Application Registry"));
+#elif defined(XP_OS2)
+  rv = dirService->Get(NS_OS2_HOME_DIR, NS_GET_IID(nsILocalFile),
+                       getter_AddRefs(regFile));
+  NS_ENSURE_SUCCESS(rv, PR_FALSE);
+  regFile->AppendNative(aAppName);
+  regFile->AppendNative(NS_LITERAL_CSTRING("registry.dat"));
+#else
+  rv = dirService->Get(NS_UNIX_HOME_DIR, NS_GET_IID(nsILocalFile),
+                       getter_AddRefs(regFile));
+  NS_ENSURE_SUCCESS(rv, PR_FALSE);
+  nsCAutoString dotAppName;
+  ToLowerCase(aAppName, dotAppName);
+  dotAppName.Insert('.', 0);
+  
+  regFile->AppendNative(dotAppName);
+  regFile->AppendNative(NS_LITERAL_CSTRING("appreg"));
+#endif
+
+  nsCAutoString path;
+  rv = regFile->GetNativePath(path);
+  NS_ENSURE_SUCCESS(rv, PR_FALSE);
+
+  if (NR_StartupRegistry())
+    return PR_FALSE;
+
+  PRBool migrated = PR_FALSE;
+  HREG reg = nsnull;
+  RKEY profiles = 0;
+  REGENUM enumstate = 0;
+  char profileName[MAXREGNAMELEN];
+
+  if (NR_RegOpen(path.get(), &reg))
+    goto cleanup;
+
+  if (NR_RegGetKey(reg, ROOTKEY_COMMON, "Profiles", &profiles))
+    goto cleanup;
+
+  while (!NR_RegEnumSubkeys(reg, profiles, &enumstate,
+                            profileName, MAXREGNAMELEN, REGENUM_CHILDREN)) {
+#ifdef DEBUG_bsmedberg
+    printf("Found profile %s.\n", profileName);
+#endif
+
+    RKEY profile = 0;
+    if (NR_RegGetKey(reg, profiles, profileName, &profile)) {
+      NS_ERROR("Could not get the key that was enumerated.");
+      continue;
+    }
+
+    char profilePath[MAXPATHLEN];
+    if (NR_RegGetEntryString(reg, profile, "directory",
+                             profilePath, MAXPATHLEN))
+      continue;
+
+    nsCOMPtr<nsILocalFile> profileFile
+      (do_CreateInstance("@mozilla.org/file/local;1"));
+    if (!profileFile)
+      continue;
+
+#if defined (XP_MACOSX)
+    rv = profileFile->SetPersistentDescriptor(nsDependentCString(profilePath));
+#else
+    NS_ConvertUTF8toUTF16 widePath(profilePath);
+    rv = profileFile->InitWithPath(widePath);
+#endif
+    if (NS_FAILED(rv)) continue;
+
+    nsCOMPtr<nsIToolkitProfile> tprofile;
+    profileSvc->CreateProfile(profileFile, nsnull,
+                              nsDependentCString(profileName),
+                              getter_AddRefs(tprofile));
+    migrated = PR_TRUE;
+  }
+
+cleanup:
+  if (reg)
+    NR_RegClose(reg);
+  NR_ShutdownRegistry();
+  return migrated;
+}
--- a/browser/components/migration/src/nsProfileMigrator.h
+++ b/browser/components/migration/src/nsProfileMigrator.h
@@ -53,12 +53,18 @@ public:
 
   nsProfileMigrator() { }
 
 protected:
   ~nsProfileMigrator() { }
 
   nsresult GetDefaultBrowserMigratorKey(nsACString& key,
                                         nsCOMPtr<nsIBrowserProfileMigrator>& bpm);
+
+  /**
+   * Import profiles from ~/.firefox/
+   * @return PR_TRUE if any profiles imported.
+   */
+  PRBool ImportRegistryProfiles(const nsACString& aAppName);
 };
 
 #endif
 
new file mode 100644
--- /dev/null
+++ b/modules/libreg/Makefile.in
@@ -0,0 +1,50 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1998
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE		= libreg
+
+DIRS		= include src
+
+include $(topsrcdir)/config/rules.mk
+
new file mode 100644
--- /dev/null
+++ b/modules/libreg/include/Makefile.in
@@ -0,0 +1,50 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1998
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE		= libreg
+
+EXPORTS		= VerReg.h NSReg.h
+
+include $(topsrcdir)/config/rules.mk
+
new file mode 100644
--- /dev/null
+++ b/modules/libreg/include/NSReg.h
@@ -0,0 +1,574 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Daniel Veditz <dveditz@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/* NSReg.h
+ */
+#ifndef _NSREG_H_
+#define _NSREG_H_
+
+typedef void (*nr_RegPackCallbackFunc) (void *userData, int32 bytes, int32 totalBytes);
+
+typedef int32   REGERR;
+typedef int32   RKEY;
+typedef uint32  REGENUM;
+typedef void *  HREG;
+
+typedef struct _reginfo
+{
+   uint16  size;        /* must be initialized to sizeof(REGINFO) */
+   uint16  entryType;
+   uint32  entryLength;
+} REGINFO;
+
+#define REGERR_OK           (0)
+#define REGERR_FAIL         (1)
+#define REGERR_NOMORE       (2)
+#define REGERR_NOFIND       (3)
+#define REGERR_BADREAD      (4)
+#define REGERR_BADLOCN      (5)
+#define REGERR_PARAM        (6)
+#define REGERR_BADMAGIC     (7)
+#define REGERR_BADCHECK     (8)
+#define REGERR_NOFILE       (9)
+#define REGERR_MEMORY       (10)
+#define REGERR_BUFTOOSMALL  (11)
+#define REGERR_NAMETOOLONG  (12)
+#define REGERR_REGVERSION   (13)
+#define REGERR_DELETED      (14)
+#define REGERR_BADTYPE      (15)
+#define REGERR_NOPATH       (16)
+#define REGERR_BADNAME      (17)
+#define REGERR_READONLY     (18)
+#define REGERR_BADUTF8      (19)
+
+
+/* Total path length */
+#define MAXREGPATHLEN   (2048)
+/* Name on the path (including null terminator) */
+#define MAXREGNAMELEN   (512)
+/* Value of an entry */
+#define MAXREGVALUELEN  (0x7FFF)
+
+/* Standard keys */
+#define ROOTKEY_USERS                   (0x01)
+#define ROOTKEY_COMMON                  (0x02)
+#define ROOTKEY_CURRENT_USER            (0x03)
+#define ROOTKEY_PRIVATE                 (0x04)
+
+/* enumeration styles */
+#define REGENUM_NORMAL                  (0x00)
+#define REGENUM_CHILDREN                REGENUM_NORMAL
+#define REGENUM_DESCEND                 (0x01)
+#define REGENUM_DEPTH_FIRST             (0x02)
+
+/* entry data types */
+#define REGTYPE_ENTRY                 (0x0010)
+#define REGTYPE_ENTRY_STRING_UTF      (REGTYPE_ENTRY + 1)
+#define REGTYPE_ENTRY_INT32_ARRAY     (REGTYPE_ENTRY + 2)
+#define REGTYPE_ENTRY_BYTES           (REGTYPE_ENTRY + 3)
+#define REGTYPE_ENTRY_FILE            (REGTYPE_ENTRY + 4)
+
+#define REG_DELETE_LIST_KEY  "Mozilla/XPInstall/Delete List"
+#define REG_REPLACE_LIST_KEY "Mozilla/XPInstall/Replace List"
+#define REG_UNINSTALL_DIR    "Mozilla/XPInstall/Uninstall/"
+#define REG_REPLACE_SRCFILE  "ReplacementFile"
+#define REG_REPLACE_DESTFILE "DestinationFile"
+
+#define UNINSTALL_NAV_STR "_"
+
+
+#define UNIX_GLOBAL_FLAG     "MOZILLA_SHARED_REGISTRY"
+
+/* libreg functions are not DLLexported and may have hidden visibility */
+#define VR_INTERFACE(type)     type
+
+PR_BEGIN_EXTERN_C
+
+
+
+/* ---------------------------------------------------------------------
+ * Registry API -- General
+ * ---------------------------------------------------------------------
+ */
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegSetBufferSize - Sets the file buffer size
+ *
+ * Parameters:
+ *    hReg       - handle to opened registry
+ *    bufsize    - size of the new buffer
+ *
+ * Output:
+ * ---------------------------------------------------------------------
+ *    returns the size of the buffer or -1 for err
+ */
+VR_INTERFACE(int) NR_RegSetBufferSize(
+         HREG hReg,        /* handle to opened registry */
+         int  bufsize
+       );
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegOpen - Open a netscape XP registry
+ *
+ * Parameters:
+ *    filename   - registry file to open. NULL or ""  opens the standard
+ *                 local registry.
+ *    hReg       - OUT: handle to opened registry
+ *
+ * Output:
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegOpen(
+         const char *filename, /* reg. file to open (NULL == standard registry) */
+         HREG *hReg            /* OUT: handle to opened registry */
+       );
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegClose - Close a netscape XP registry
+ *
+ * Parameters:
+ *    hReg     - handle of open registry to be closed.
+ *
+ * After calling this routine the handle is no longer valid
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegClose(
+         HREG hReg         /* handle of open registry to close */
+       );
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegFlush - Manually flush data in a netscape XP registry
+ *
+ * Parameters:
+ *    hReg     - handle of open registry to be flushed.
+ *
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegFlush(
+         HREG hReg         /* handle of open registry to flush */
+       );
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegIsWritable - Check read/write status of open registry
+ *
+ * Parameters:
+ *    hReg     - handle of open registry to query
+ * Returns:
+ *    REGERR_OK if writable, REGERR_READONLY if not, possibly
+ *    other errors for an invalid hReg
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegIsWritable(
+         HREG hReg         /* handle of open registry to query */
+       );
+
+VR_INTERFACE(REGERR) NR_RegPack(
+         HREG hReg,         /* handle of open registry to pack */
+         void *userData,
+         nr_RegPackCallbackFunc fn
+       );
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegSetUsername - Set the current username
+ * 
+ * If the current user profile name is not set then trying to use
+ * HKEY_CURRENT_USER will result in an error.
+ *
+ * Parameters:
+ *     name     - name of the current user
+ *
+ * Output:
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegSetUsername(
+         const char *name  /* name of current user */
+       );
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegGetUniqueName
+ * 
+ * Returns a unique name that can be used for anonymous key/value names
+ *
+ * Parameters:
+ *     hReg     - handle of open registry
+ *     outbuf   - where to put the string
+ *     buflen   - how big the buffer is
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegGetUniqueName(
+         HREG hReg,     /* handle of open registry */
+         char* outbuf,  /* buffer to hold key name */
+         uint32 buflen  /* size of buffer */
+       );
+
+
+/* ---------------------------------------------------------------------
+ * DO NOT USE -- Will be removed 
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegGetUsername(
+         char **name        /* on return, an alloc'ed copy of the current user name */
+       );
+
+
+
+
+
+
+/* ---------------------------------------------------------------------
+ * Registry API -- Key Management functions
+ * ---------------------------------------------------------------------
+ */
+
+/* ---------------------------------------------------------------------
+ * NR_RegAddKey - Add a key node to the registry
+ *
+ * Can also be used to find an existing node for convenience.
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - registry key obtained from NR_RegGetKey(),
+ *               or one of the standard top-level keys
+ *    path     - relative path of key to be added.  Intermediate
+ *               nodes will be added if necessary.
+ *    newkey   - If not null returns RKEY of new or found node
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegAddKey(
+         HREG hReg,        /* handle of open registry */
+         RKEY key,         /* root key */
+         char *path,       /* relative path of subkey to add */
+         RKEY *newKey      /* if not null returns newly created key */
+       );
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegAddKeyRaw - Add a key node to the registry
+ *
+ *      This routine is different from NR_RegAddKey() in that it takes 
+ *      a keyname rather than a path.
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - registry key obtained from NR_RegGetKey(),
+ *               or one of the standard top-level keys
+ *    keyname  - name of key to be added. No parsing of this
+ *               name happens.
+ *    newkey   - if not null the RKEY of the new key is returned
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegAddKeyRaw(
+         HREG hReg,        /* handle of open registry */
+         RKEY key,         /* root key */
+         char *keyname,    /* name of key to add */
+         RKEY *newKey      /* if not null returns newly created key */
+       );
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegDeleteKey - Delete the specified key
+ *
+ * Note that delete simply orphans blocks and makes no attempt
+ * to reclaim space in the file. Use NR_RegPack()
+ *
+ * Cannot be used to delete keys with child keys
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - starting node RKEY, typically one of the standard ones.
+ *    path     - relative path of key to delete
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegDeleteKey(
+         HREG hReg,        /* handle of open registry */
+         RKEY key,         /* root key */
+         char *path        /* relative path of subkey to delete */
+       );
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegDeleteKeyRaw - Delete the specified raw key
+ *
+ * Note that delete simply orphans blocks and makes no attempt
+ * to reclaim space in the file. Use NR_RegPack()
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY or parent to the raw key you wish to delete
+ *    keyname  - name of child key to delete
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegDeleteKeyRaw(
+         HREG hReg,        /* handle of open registry */
+         RKEY key,         /* root key */
+         char *keyname     /* name subkey to delete */
+       );
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegGetKey - Get the RKEY value of a node from its path
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - starting node RKEY, typically one of the standard ones.
+ *    path     - relative path of key to find.  (a blank path just gives you
+ *               the starting key--useful for verification, VersionRegistry)
+ *    result   - if successful the RKEY of the specified sub-key
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegGetKey(
+         HREG hReg,        /* handle of open registry */
+         RKEY key,         /* root key */
+         const char *path, /* relative path of subkey to find */
+         RKEY *result      /* returns RKEY of specified sub-key */
+       );
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegGetKeyRaw - Get the RKEY value of a node from its keyname
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - starting node RKEY, typically one of the standard ones.
+ *    keyname  - keyname of key to find.  (a blank keyname just gives you
+ *               the starting key--useful for verification, VersionRegistry)
+ *    result   - if successful the RKEY of the specified sub-key
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegGetKeyRaw(
+         HREG hReg,        /* handle of open registry */
+         RKEY key,         /* root key */
+         char *keyname,       /* name of key to get */
+         RKEY *result      /* returns RKEY of specified sub-key */
+       );
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegEnumSubkeys - Enumerate the subkey names for the specified key
+ *
+ * Returns REGERR_NOMORE at end of enumeration.
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY of key to enumerate--obtain with NR_RegGetKey()
+ *    eState   - enumerations state, must contain NULL to start
+ *    buffer   - location to store subkey names.  Once an enumeration
+ *               is started user must not modify contents since values
+ *               are built using the previous contents.
+ *    bufsize  - size of buffer for names
+ *    style    - 0 returns direct child keys only, REGENUM_DESCEND
+ *               returns entire sub-tree
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegEnumSubkeys(
+         HREG    hReg,        /* handle of open registry */
+         RKEY    key,         /* containing key */
+         REGENUM *state,      /* enum state, must be NULL to start */
+         char    *buffer,     /* buffer for entry names */
+         uint32  bufsize,     /* size of buffer */
+         uint32  style        /* 0: children only; REGENUM_DESCEND: sub-tree */
+       );
+
+
+
+/* ---------------------------------------------------------------------
+ * Registry API -- Entry Management functions
+ * ---------------------------------------------------------------------
+ */
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegGetEntryInfo - Get some basic info about the entry data
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
+ *    name     - name of entry
+ *    info     - return: Entry info object
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegGetEntryInfo(
+         HREG    hReg,     /* handle of open registry */
+         RKEY    key,      /* containing key */
+         char    *name,    /* entry name */
+         REGINFO *info     /* returned entry info */
+       );
+
+       
+/* ---------------------------------------------------------------------
+ * NR_RegGetEntryString - Get the UTF string value associated with the
+ *                       named entry of the specified key.
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
+ *    name     - name of entry
+ *    buffer   - destination for string
+ *    bufsize  - size of buffer
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegGetEntryString(
+         HREG   hReg,      /* handle of open registry */
+         RKEY   key,       /* containing key */
+         const char *name, /* entry name */
+         char   *buffer,   /* buffer to hold value (UTF String) */
+         uint32 bufsize    /* length of buffer */
+       );
+
+/* ---------------------------------------------------------------------
+ * NR_RegGetEntry - Get the value data associated with the
+ *                  named entry of the specified key.
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
+ *    name     - name of entry
+ *    buffer   - destination for data
+ *    size     - in:  size of buffer
+ *               out: size of actual data (incl. \0 term. for strings)
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegGetEntry(
+         HREG   hReg,      /* handle of open registry */
+         RKEY   key,       /* containing key */
+         char   *name,     /* entry name */
+         void   *buffer,   /* buffer to hold value */
+         uint32 *size      /* in:length of buffer */
+       );                  /* out: data length, >>includes<< null terminator*/
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegSetEntryString - Store a UTF-8 string value associated with the
+ *                       named entry of the specified key.  Used for
+ *                       both creation and update.
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
+ *    name     - name of entry
+ *    buffer   - UTF-8 String to store
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegSetEntryString(
+         HREG hReg,        /* handle of open registry */
+         RKEY key,         /* containing key */
+         char *name,       /* entry name */
+         char *buffer      /* UTF String value */
+       );
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegSetEntry - Store value data associated with the named entry
+ *                  of the specified key.  Used for both creation and update.
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
+ *    name     - name of entry
+ *    type     - type of data to be stored
+ *    buffer   - data to store
+ *    size     - length of data to store in bytes
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegSetEntry(
+         HREG   hReg,        /* handle of open registry */
+         RKEY   key,         /* containing key */
+         char   *name,       /* entry name */
+         uint16 type,        /* type of value data */
+         void   *buffer,     /* data buffer */
+         uint32 size         /* data length in bytes; incl. null term for strings */
+       );
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegDeleteEntry - Delete the named entry
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
+ *    name     - name of entry
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegDeleteEntry(
+         HREG hReg,        /* handle of open registry */
+         RKEY key,         /* containing key */
+         char *name        /* value name */
+       );
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegEnumEntries - Enumerate the entry names for the specified key
+ *
+ * Returns REGERR_NOMORE at end of enumeration.
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
+ *    eState   - enumerations state, must contain NULL to start
+ *    buffer   - location to store entry names
+ *    bufsize  - size of buffer for names
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegEnumEntries(
+         HREG    hReg,        /* handle of open registry */
+         RKEY    key,         /* containing key */
+         REGENUM *state,      /* enum state, must be NULL to start */
+         char    *buffer,     /* buffer for entry names */
+         uint32  bufsize,     /* size of buffer */
+         REGINFO *info        /* optional; returns info about entry */
+       );
+
+
+VR_INTERFACE(void)      NR_ShutdownRegistry(void);
+VR_INTERFACE(REGERR)    NR_StartupRegistry(void);
+
+
+PR_END_EXTERN_C
+
+#endif   /* _NSREG_H_ */
+
+/* EOF: NSReg.h */
+
new file mode 100644
--- /dev/null
+++ b/modules/libreg/include/VerReg.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Daniel Veditz <dveditz@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/* VerReg.h
+ * XP Version Registry functions
+ */
+#ifndef _VERREG_H_
+#define _VERREG_H_
+
+#include "NSReg.h"
+
+typedef struct _version
+{
+   int32   major;
+   int32   minor;
+   int32   release;
+   int32   build;
+   int32   check;
+} VERSION;
+
+
+/* CreateRegistry flags */
+#define CR_NEWREGISTRY 1
+
+PR_BEGIN_EXTERN_C
+/* ---------------------------------------------------------------------
+ * Version Registry Operations
+ * ---------------------------------------------------------------------
+ */
+/* global registry operations */
+/* VR_CreateRegistry is available only in the STANDALONE_REGISTRY builds */
+VR_INTERFACE(REGERR) VR_CreateRegistry(char *installation, char *programPath, char *versionStr);
+VR_INTERFACE(REGERR) VR_SetRegDirectory(const char *path);
+VR_INTERFACE(REGERR) VR_PackRegistry(void *userData,  nr_RegPackCallbackFunc pdCallbackFunction);
+VR_INTERFACE(REGERR) VR_Close(void);
+
+/* component-level functions */
+VR_INTERFACE(REGERR) VR_Install(char *component_path, char *filepath, char *version, int bDirectory);
+VR_INTERFACE(REGERR) VR_Remove(char *component_path);
+VR_INTERFACE(REGERR) VR_InRegistry(char *path);
+VR_INTERFACE(REGERR) VR_ValidateComponent(char *path);
+VR_INTERFACE(REGERR) VR_Enum(char *component_path, REGENUM *state, char *buffer, uint32 buflen);
+
+/* dealing with parts of individual components */
+VR_INTERFACE(REGERR) VR_GetVersion(char *component_path, VERSION *result);
+VR_INTERFACE(REGERR) VR_GetPath(char *component_path, uint32 sizebuf, char *buf);
+VR_INTERFACE(REGERR) VR_SetRefCount(char *component_path, int refcount);
+VR_INTERFACE(REGERR) VR_GetRefCount(char *component_path, int *result);
+VR_INTERFACE(REGERR) VR_GetDefaultDirectory(char *component_path, uint32 sizebuf, char *buf);
+VR_INTERFACE(REGERR) VR_SetDefaultDirectory(char *component_path, char *directory);
+
+/* uninstall functions */
+VR_INTERFACE(REGERR) VR_UninstallCreateNode(char *regPackageName, char *userPackageName);
+VR_INTERFACE(REGERR) VR_UninstallAddFileToList(char *regPackageName, char *vrName);
+VR_INTERFACE(REGERR) VR_UninstallFileExistsInList(char *regPackageName, char *vrName);
+VR_INTERFACE(REGERR) VR_UninstallEnumSharedFiles(char *component_path, REGENUM *state, char *buffer, uint32 buflen);
+VR_INTERFACE(REGERR) VR_UninstallDeleteFileFromList(char *component_path, char *vrName);
+VR_INTERFACE(REGERR) VR_UninstallDeleteSharedFilesKey(char *regPackageName);
+VR_INTERFACE(REGERR) VR_UninstallDestroy(char *regPackageName);
+VR_INTERFACE(REGERR) VR_EnumUninstall(REGENUM *state, char* userPackageName,
+                                    int32 len1, char*regPackageName, int32 len2, XP_Bool bSharedList);
+VR_INTERFACE(REGERR) VR_GetUninstallUserName(char *regPackageName, char *outbuf, uint32 buflen);
+
+PR_END_EXTERN_C
+
+#endif   /* _VERREG_H_ */
+
+/* EOF: VerReg.h */
+
new file mode 100644
--- /dev/null
+++ b/modules/libreg/src/Makefile.in
@@ -0,0 +1,82 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1998
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+include $(srcdir)/objs.mk
+
+MODULE		= libreg
+LIBRARY_NAME	= mozreg_s
+DIST_INSTALL = 1
+
+CSRCS		= $(MODULES_LIBREG_SRC_LCSRCS) nr_bufio.c
+
+BIN_SRCS	= VerReg.c reg.c vr_stubs.c
+PROGOBJS        = $(addprefix R_,$(BIN_SRCS:.c=.o))
+
+# We don't want a shared lib.  Static lib only.
+FORCE_STATIC_LIB = 1
+
+# Force use of PIC
+FORCE_USE_PIC	= 1
+
+# We do want this in the static libraries list
+EXPORT_LIBRARY = 1
+
+USE_STATIC_LIBS = 1
+
+SDK_LIBRARY = $(LIBRARY)
+
+include $(topsrcdir)/config/config.mk
+
+DEFINES         += -DUSE_BUFFERED_REGISTRY_IO	
+
+include $(topsrcdir)/config/rules.mk
+
+R_%.o: %.c
+	$(CC) $(OUTOPTION)$@ -c $(COMPILE_CFLAGS) -DSTANDALONE_REGISTRY $<
+
+ifdef _MSC_VER
+# Don't include directives about which CRT to use
+OS_COMPILE_CXXFLAGS += -Zl
+OS_COMPILE_CFLAGS += -Zl
+DEFINES += -D_USE_ANSI_CPP
+endif
new file mode 100644
--- /dev/null
+++ b/modules/libreg/src/VerReg.c
@@ -0,0 +1,1766 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Daniel Veditz <dveditz@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/* ====================================================================
+ * VerReg.c
+ * XP Version Registry functions (prototype)
+ * ====================================================================
+ */
+
+/* --------------------------------------------------------------------
+ * Install 'Navigator' produces a tree of:
+ *
+ *      /Components/Netscape/Web/Navigator/
+ *              ...Path="c:\netscape\program\netscape.exe"
+ *              ...Version=4.0.0.0
+ *
+ * --------------------------------------------------------------------
+ */
+#include <fcntl.h>
+#include <errno.h>
+
+#if defined(XP_WIN) 
+#include <io.h>
+#endif
+
+#if defined(XP_OS2)
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef STANDALONE_REGISTRY
+#include <stdlib.h>
+#include <assert.h>
+#endif /*STANDALONE_REGISTRY*/
+
+#include "reg.h"
+#include "NSReg.h"
+#include "VerReg.h"
+
+/* -------- local defines --------------- 
+*/
+#define MAXREGVERLEN 32     /* Version=12345.12345.12345.12345 */
+
+#define VERSTR          "Version"
+#define CHKSTR          "Check"
+#define PATHSTR         "Path"
+#define DIRSTR          "Directory"
+#define NAVHOME         "InstallDir"
+#define REFCSTR         "RefCount"
+#define SHAREDSTR       "Shared"
+#define PACKAGENAMESTR  "PackageName"
+#define SHAREDFILESSTR  "/Shared Files"
+
+#define VERSION_NAME    "Mozilla"
+#define NAVIGATOR_NODE  "/mozilla.org"
+#define CURRENT_VER     "CurrentVersion"
+
+#define PATH_ROOT(p)   ( ((p) && *(p)==PATHDEL) ? ROOTKEY_VERSIONS : curver )
+#define UNIX_ROOT(p)   ( ((p) && *(p)==PATHDEL) ? ROOTKEY_VERSIONS : unixver )
+
+ 
+/* ---------------------------------------------------------------------
+ * Global variables
+ * ---------------------------------------------------------------------
+ */
+static int isInited = 0;
+static RKEY curver = 0;
+static char gCurstr[MAXREGNAMELEN];
+
+static HREG vreg = 0;
+
+static char *app_dir = NULL;
+
+char *verRegName = NULL;
+
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+/* Extra Unix variables to deal with two registries 
+ *   "vreg" is always the writable registry.
+ *   If "vreg" is the local registry then "unixreg" will
+ *   be the global registry read-only (unless we couldn't
+ *   open it).
+ */
+#if !defined(STANDALONE_REGISTRY)
+static HREG unixreg = 0;
+static RKEY unixver = 0;
+#endif
+XP_Bool bGlobalRegistry = FALSE;
+#endif
+
+#ifndef STANDALONE_REGISTRY
+PRLock *vr_lock = NULL;
+#endif
+
+
+/* ---------------------------------------------------------------------
+ * local functions
+ * ---------------------------------------------------------------------
+ */
+static REGERR vr_Init(void);
+static XP_Bool vr_CompareDirs( char *dir1, char *dir2 );
+static REGERR vr_SetCurrentNav( char *product, char *programPath, char *versionStr);
+static REGERR vr_ParseVersion(char *verstr, VERSION *result);
+
+#ifdef USE_CHECKSUM
+static REGERR vr_GetCheck(char *path, int32 *check);
+#endif
+
+static REGERR vr_SetPathname(HREG reg, RKEY key, char *entry, char *dir);
+static REGERR vr_GetPathname(HREG reg, RKEY key, char *entry, char *buf, uint32 sizebuf);
+
+static REGERR vr_FindKey(char *name, HREG *hreg, RKEY *key);
+
+static REGERR vr_GetUninstallItemPath(char *regPackageName, char *regbuf, uint32 regbuflen);
+static REGERR vr_convertPackageName(char *regPackageName, char *convertedPackageName, uint32 convertedDataLength);
+static REGERR vr_unmanglePackageName(char *mangledPackageName, char *regPackageName, uint32 regPackageLength);
+
+/* --------------------------------------------------------------------- */
+
+static REGERR vr_Init(void)
+{
+
+    REGERR  err = REGERR_OK;
+    char    *regname = vr_findVerRegName();
+#if defined(XP_UNIX) && !defined(XP_MACOSX) || defined(STANDALONE_REGISTRY)
+    char    curstr[MAXREGNAMELEN];
+    RKEY    navKey;
+#endif
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+    char    *regbuf = NULL;
+#endif
+
+#ifndef STANDALONE_REGISTRY
+    if (vr_lock == NULL)
+        return REGERR_FAIL;
+#endif
+    PR_Lock(vr_lock);
+
+    if (!isInited)
+    {
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+        /* need browser directory to find the correct registry */
+        if (app_dir != NULL) {
+            regbuf = (char*)XP_ALLOC( 10 + XP_STRLEN(app_dir) );
+            if (regbuf != NULL ) {
+                XP_STRCPY( regbuf, app_dir );
+                XP_STRCAT( regbuf, "/registry" );
+            } 
+            else {
+                err = REGERR_MEMORY;
+            }
+        } 
+        if ( err != REGERR_OK )
+            goto done;
+
+        if (bGlobalRegistry) 
+            regname = regbuf;
+#endif
+
+        /* Open version registry */
+        err = NR_RegOpen( regname, &vreg );
+
+#ifndef STANDALONE_REGISTRY
+        if (err == REGERR_OK) 
+        {
+            /* find/set the current nav node */
+            err = vr_SetCurrentNav( VERSION_NAME, app_dir, NULL );
+            if ( REGERR_OK != err ) {
+                /* couldn't find or set current nav -- big problems! */
+                NR_RegClose( vreg );
+                goto done;
+            }
+        }
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+        /* try to open shared Unix registry, but not an error if you can't */
+        unixreg = NULL;
+        if (!bGlobalRegistry && err == REGERR_OK ) {
+            unixver = 0;
+            if (NR_RegOpen( regbuf, &unixreg ) == REGERR_OK) {
+                if (NR_RegGetKey( unixreg, ROOTKEY_VERSIONS, NAVIGATOR_NODE, 
+                    &navKey) == REGERR_OK) 
+                {
+                    if (NR_RegGetEntryString( unixreg, navKey, CURRENT_VER,
+                        curstr, sizeof(curstr)) == REGERR_OK ) 
+                    {
+                        NR_RegGetKey( unixreg, navKey, curstr, &unixver );
+                    }
+                }
+            }
+        }
+#endif
+
+        if (err == REGERR_OK) {
+            /* successfully opened! */
+            isInited = 1;
+        }
+        goto done;
+#else
+        if (err != REGERR_OK)
+            goto done;
+
+        /* Determine 'curver' key and ensure correct structure by adding */
+
+        /* ...find top-level "Navigator" node (add if missing) */
+        err = NR_RegAddKey( vreg, ROOTKEY_VERSIONS, NAVIGATOR_NODE, &navKey );
+        if (err != REGERR_OK)
+            goto done;
+
+        /* ...look for "Current Version" entry */
+        err = NR_RegGetEntryString( vreg, navKey, CURRENT_VER, curstr,
+                                    sizeof(curstr) );
+        if ( err == REGERR_NOFIND ) {
+            /* If not found create one with the built-in version */
+            err = NR_RegSetEntryString( vreg, navKey, CURRENT_VER, VERSION_NAME );
+            XP_STRCPY( curstr, VERSION_NAME );
+        }
+        if ( err != REGERR_OK )
+            goto done;
+
+        /* ...look for "curstr" child key of the navigator node */
+        err = NR_RegAddKey( vreg, navKey, curstr, &curver );
+
+        if (err == REGERR_OK) {
+            /* successfully opened! */
+            isInited = 1;
+        }
+#endif
+    }
+
+done:
+    PR_Unlock(vr_lock);
+#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(STANDALONE_REGISTRY)
+    XP_FREEIF(regbuf);
+#endif
+    return err;
+
+}   /* Init */
+
+
+
+#if defined(XP_WIN) || defined(XP_OS2)
+#define VR_FILE_SEP '\\'
+#elif defined(XP_UNIX) || defined(XP_BEOS)
+#define VR_FILE_SEP '/'
+#endif
+
+static XP_Bool vr_CompareDirs( char *dir1, char *dir2 )
+{
+    int len1,len2;
+   
+    XP_ASSERT( dir1 && dir2 );
+    if (!dir1 || !dir2) return FALSE;
+
+    len1 = XP_STRLEN( dir1 );
+    len2 = XP_STRLEN( dir2 );
+
+    if ( dir1[len1-1] == VR_FILE_SEP )
+        len1--;
+    if ( dir2[len2-1] == VR_FILE_SEP )
+        len2--;
+
+    if ( len1 != len2 )
+        return FALSE;
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+    return ( XP_STRNCMP(dir1, dir2, len1) == 0 );
+#else
+    return ( XP_STRNCASECMP(dir1, dir2, len1) == 0 );
+#endif
+}
+
+
+REGERR vr_ParseVersion(char *verstr, VERSION *result)
+{
+
+    result->major = result->minor = result->release = result->build = 0;
+    result->major = atoi(verstr);
+    while (*verstr && *verstr != '.')
+        verstr++;
+    if (*verstr)
+    {
+        verstr++;
+        result->minor = atoi(verstr);
+        while (*verstr && *verstr != '.')
+            verstr++;
+        if (*verstr)
+        {
+            verstr++;
+            result->release = atoi(verstr);
+            while (*verstr && *verstr != '.')
+                verstr++;
+            if (*verstr)
+            {
+                verstr++;
+                result->build = atoi(verstr);
+                while (*verstr && *verstr != '.')
+                    verstr++;
+            }
+        }
+    }
+
+    return REGERR_OK;
+
+}   /* ParseVersion */
+
+
+
+#ifdef USE_CHECKSUM
+#define BLKSIZ 16384
+
+static REGERR vr_GetCheck(char *path, int32 *check)
+{
+
+    int fh;
+    char *buf;
+    int actual;
+    char *p;
+    int i;
+    int chk;
+
+    XP_ASSERT(path);
+    XP_ASSERT(check);
+    
+    *check = chk = 0;
+
+#ifdef NEED_XP_FIXES
+    /* open file for read */
+    fh = open(path, O_RDONLY| O_BINARY);
+    if (fh < 0)
+    {
+        switch (errno)
+        { 
+        case ENOENT:    /* file not found */
+            return REGERR_NOFILE;
+
+        case EACCES:    /* file in use */
+        
+#ifdef EMFILE        
+        case EMFILE:    /* too many files open */
+#endif
+        default:
+            return REGERR_FAIL;
+        }
+    }
+
+    buf = malloc(BLKSIZ);
+    if (!buf)
+    {
+        close(fh);
+        return REGERR_MEMORY;
+    }
+
+    do
+    {
+        /* read a block */
+        actual = read(fh, buf, BLKSIZ);
+        /* add to checksum */
+        for (p=buf, i=0; i<actual; i++, p++)
+            chk += *p;
+
+        /* if the block was partial, we're done, else loop */
+    } while (actual == BLKSIZ);
+
+    /* close file */
+    close(fh);
+    free(buf);
+#endif
+
+    /* return calculated checksum */
+    *check = chk;
+
+    return REGERR_OK;
+
+}   /* GetCheck */
+
+#endif /* USE_CHECKSUM */
+
+
+
+static REGERR vr_SetPathname(HREG reg, RKEY key, char *entry, char *dir)
+{
+    REGERR err;
+    int32  datalen = XP_STRLEN(dir)+1; /* include '\0' */
+
+    err = NR_RegSetEntry( reg, key, entry, REGTYPE_ENTRY_FILE, dir, datalen);
+
+    return err;
+}
+
+
+
+static REGERR vr_GetPathname(HREG reg, RKEY key, char *entry, char *buf, uint32 sizebuf)
+{
+    return NR_RegGetEntry( reg, key, entry, (void*)buf, &sizebuf );
+}
+
+
+
+/* create default tree with 'installation' under Navigator */
+/* set Current to the installation string */
+static REGERR vr_SetCurrentNav( char *installation, char *programPath, char *versionStr)
+{
+    REGERR      err;
+    REGENUM     state;
+    RKEY        navKey;
+    int         bFound;
+    int         nCopy;
+    char        regname[MAXREGNAMELEN];
+    char        dirbuf[MAXREGNAMELEN];
+
+    XP_ASSERT( installation ); /* required */
+    XP_ASSERT( programPath );  /* required */
+    if ( !installation || !programPath )
+        return REGERR_PARAM;
+
+    err = NR_RegAddKey( vreg, ROOTKEY_VERSIONS, NAVIGATOR_NODE, &navKey );
+    if (err != REGERR_OK)
+        goto done;
+
+    /* ...look for "Current Version" entry */
+    err = NR_RegGetEntryString( vreg, navKey, CURRENT_VER, gCurstr, sizeof(gCurstr));
+    if ( err == REGERR_NOFIND )
+    {
+        /* No current installation, we can simply add a new one  */
+        err = NR_RegAddKey( vreg, navKey, installation, &curver );
+
+        /* ... add Path and Version properties */
+        if ( err == REGERR_OK ) 
+        {
+            err = vr_SetPathname( vreg, curver, NAVHOME, programPath );
+            if ( REGERR_OK == err && versionStr != NULL && *versionStr != '\0')
+            {
+                err = NR_RegSetEntryString( vreg, curver, VERSTR, versionStr );
+            }
+        }
+
+        if ( REGERR_OK == err ) {
+            /* successfully added, make it the current version */
+            err = NR_RegSetEntryString(vreg, navKey, CURRENT_VER, installation);
+        }
+
+        if (err != REGERR_OK)
+            goto done;
+    }
+    else if ( REGERR_OK == err )
+    {
+        /* found one: if we're lucky we got the right one */
+        bFound = FALSE;
+        err = NR_RegGetKey( vreg, navKey, gCurstr, &curver );
+        if ( REGERR_OK == err ) {
+            err = vr_GetPathname( vreg, curver, NAVHOME, dirbuf, sizeof(dirbuf) );
+            if ( REGERR_OK == err ) {
+                bFound = vr_CompareDirs(dirbuf, programPath);
+            }
+            else if ( REGERR_NOFIND == err ) {
+                /* assume this is the right one since it's 'Current' */
+                err = vr_SetPathname( vreg, curver, NAVHOME, programPath );
+                bFound = TRUE;
+            }
+        }
+        
+        /* Look for an existing installation if not found */
+        state = 0;
+        while (!bFound && ((err == REGERR_OK) || (err == REGERR_NOFILE)) ) {
+            err = NR_RegEnumSubkeys( vreg, navKey, &state, gCurstr,
+                    sizeof(gCurstr), REGENUM_NORMAL );
+
+            if (REGERR_OK == err ) {
+                err = vr_GetPathname( vreg, state, NAVHOME, dirbuf, sizeof(dirbuf) );
+                if (REGERR_OK == err ) {
+                    if (vr_CompareDirs( dirbuf, programPath )) {
+                        bFound = TRUE;
+                        curver = (RKEY)state;
+                    }
+                }
+                else if ( err == REGERR_NOFIND ) {
+                    /* wasn't a navigator node */
+                    err = REGERR_OK;
+                }
+            }
+        }
+
+        /* found the right one, make it current */
+        if (bFound) {
+            err = NR_RegSetEntryString( vreg, navKey, CURRENT_VER, gCurstr );
+            /* update version (curver already set) */
+            if ( REGERR_OK == err && versionStr != NULL && *versionStr != '\0' ) {
+                err = NR_RegSetEntryString( vreg, curver, VERSTR, versionStr );
+            }
+        }
+        /* otherwise if no current installation matches */
+        else if ( err == REGERR_NOMORE )
+        {
+            /* look for an empty slot to put new installation */
+            nCopy = 1;
+            XP_STRCPY( regname, installation );
+            do {
+                err = NR_RegGetKey( vreg, navKey, regname, &curver );
+                if (err == REGERR_OK) {
+                    nCopy++;
+                    sprintf( regname, "%s #%d", installation, nCopy );
+                }
+            } while (err==REGERR_OK);
+
+            if (err != REGERR_NOFIND)
+                goto done;  /* real error, bail */
+
+            /* found an unused name -- add it */
+            err = NR_RegAddKey( vreg, navKey, regname, &curver );
+            if ( err != REGERR_OK )
+                goto done;
+
+            /* add path and version properties */
+            err = vr_SetPathname( vreg, curver, NAVHOME, programPath );
+            if ( REGERR_OK == err && versionStr != NULL && *versionStr != '\0' ) {
+                err = NR_RegSetEntryString( vreg, curver, VERSTR, versionStr );
+            }
+
+            if ( REGERR_OK == err ) {
+                /* everything's OK, make it current */
+                err = NR_RegSetEntryString(vreg,navKey,CURRENT_VER,regname);
+            }
+        }
+    }
+done:
+    return err;
+}
+
+
+
+
+/* assumes registries are open (only use after vr_Init() returns OK).
+ * For UNIX look first in the global, then in the local if not found
+ * -- returns both hreg and key of the named node (if found)
+ */
+static REGERR vr_FindKey(char *component_path, HREG *hreg, RKEY *key)
+{
+    REGERR err = REGERR_NOFIND;
+    RKEY rootkey;
+
+#if !defined(STANDALONE_REGISTRY) && defined(XP_UNIX) && !defined(XP_MACOSX)
+    if (unixreg != NULL) {
+        *hreg = unixreg;
+        rootkey = UNIX_ROOT(component_path);
+        if (rootkey)
+            err = NR_RegGetKey( *hreg, rootkey, component_path, key );
+        else
+            err = REGERR_NOFIND;
+    }
+    if (unixreg == NULL || err == REGERR_NOFIND ) 
+#endif
+    {
+        *hreg = vreg;
+        rootkey = PATH_ROOT(component_path);
+        if (rootkey)
+            err = NR_RegGetKey( *hreg, rootkey, component_path, key );
+        else
+            err = REGERR_NOFIND;
+    }
+
+    return err;
+}
+
+
+
+/* ---------------------------------------------------------------------
+ * Interface
+ * ---------------------------------------------------------------------
+ */
+
+#ifndef STANDALONE_REGISTRY
+VR_INTERFACE(REGERR) VR_PackRegistry(void *userData, nr_RegPackCallbackFunc fn)
+{
+    REGERR err;
+
+    /* make sure vreg (src) is open */
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    err = NR_RegPack( vreg, userData, fn );
+
+    return err;
+
+}   /* PackRegistry */
+#endif /* STANDALONE_REGISTRY */
+
+
+VR_INTERFACE(REGERR) VR_CreateRegistry( char *installation, char *programPath, char *versionStr )
+{
+    REGERR      err;
+    char *      regname = vr_findVerRegName();
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+    char *      regbuf = NULL;
+#endif
+
+    if ( installation == NULL || *installation == '\0' )
+        return REGERR_PARAM;
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+#ifndef STANDALONE_REGISTRY
+    if (bGlobalRegistry)
+#endif 
+    {
+        regbuf = (char*)XP_ALLOC( 10 + XP_STRLEN(programPath) );
+        if (regbuf == NULL) 
+            return REGERR_MEMORY;
+
+        XP_STRCPY( regbuf, programPath );
+        XP_STRCAT( regbuf, "registry" );
+        regname = regbuf;
+    }
+#endif /* XP_UNIX */
+
+    PR_Lock(vr_lock);
+
+    /* automatically creates it if not found */
+    err = NR_RegOpen( regname, &vreg );
+    if (err == REGERR_OK) 
+    {
+        /* create default tree with 'installation' under Navigator */
+        /* set Current to the installation string */
+
+        err = vr_SetCurrentNav( installation, programPath, versionStr );
+
+        if ( REGERR_OK == err )
+            isInited = 1;
+        else
+            NR_RegClose( vreg );
+    }
+
+    PR_Unlock(vr_lock);
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+    XP_FREEIF( regbuf );
+#endif
+    return err;
+
+}   /* CreateRegistry */
+
+
+VR_INTERFACE(REGERR) VR_Close(void)
+{
+    REGERR err = REGERR_OK;
+
+#ifndef STANDALONE_REGISTRY
+    if (vr_lock == NULL)
+        return REGERR_FAIL;
+#endif
+
+    PR_Lock(vr_lock);
+
+    if (isInited) {
+#if !defined(STANDALONE_REGISTRY) && defined(XP_UNIX) && !defined(XP_MACOSX)
+        if ( unixreg != NULL )
+            NR_RegClose( unixreg );
+#endif
+        err = NR_RegClose( vreg );
+        isInited = 0;
+    }
+
+    PR_Unlock(vr_lock);
+
+    return err;
+}   /* Close */
+
+
+
+VR_INTERFACE(REGERR) VR_GetVersion(char *component_path, VERSION *result)
+{
+    REGERR  err;
+    RKEY    key;
+    HREG    hreg;
+    VERSION ver;
+    char    buf[MAXREGNAMELEN];
+
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    hreg = vreg;
+
+    err = vr_FindKey( component_path, &hreg, &key );
+    if (err != REGERR_OK)
+        return err;
+
+    err = NR_RegGetEntryString( hreg, key, VERSTR, buf, sizeof(buf) );
+    if (err != REGERR_OK)
+        return err;
+
+    vr_ParseVersion(buf, &ver);
+
+    memcpy(result, &ver, sizeof(VERSION));
+
+    return REGERR_OK;
+
+}   /* GetVersion */
+
+
+
+VR_INTERFACE(REGERR) VR_GetPath(char *component_path, uint32 sizebuf, char *buf)
+{
+    REGERR err;
+    RKEY key;
+    HREG hreg;
+
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    hreg = vreg;
+
+    err = vr_FindKey( component_path, &hreg, &key );
+    if (err != REGERR_OK)
+        return err;
+    
+    err = vr_GetPathname( hreg, key, PATHSTR, buf, sizebuf );
+
+    return err;
+
+}   /* GetPath */
+
+
+
+VR_INTERFACE(REGERR) VR_SetDefaultDirectory(char *component_path, char *directory)
+{
+    REGERR err;
+    RKEY rootkey;
+    RKEY key;
+
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    rootkey = PATH_ROOT(component_path);
+
+    err = NR_RegGetKey( vreg, rootkey, component_path, &key );
+    if (err != REGERR_OK)
+        return err;
+    
+    err = vr_SetPathname( vreg, key, DIRSTR, directory );
+
+    return err;
+}
+
+
+
+VR_INTERFACE(REGERR) VR_GetDefaultDirectory(char *component_path, uint32 sizebuf, char *buf)
+{
+    REGERR err;
+    RKEY key;
+    HREG hreg;
+
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    hreg = vreg;
+
+    err = vr_FindKey( component_path, &hreg, &key );
+    if (err != REGERR_OK)
+        return err;
+
+    err = vr_GetPathname( hreg, key, DIRSTR, buf, sizebuf );
+
+    return err;
+}
+
+
+
+VR_INTERFACE(REGERR) VR_Install(char *component_path, char *filepath, char *version, int bDirectory)
+{
+    REGERR err;
+    RKEY rootKey;
+    RKEY key;
+
+    /* Initialize the registry in case this is first call */
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    /* Use curver if path is relative */
+    rootKey = PATH_ROOT(component_path);
+
+    /* Make sure path components (keys) exist by calling Add */
+    /* (special "" component must always exist, and Add fails) */
+    if ( component_path != NULL && *component_path == '\0' ) {
+        err = NR_RegGetKey( vreg, rootKey, component_path, &key );
+    }
+    else {
+        err = NR_RegAddKey( vreg, rootKey, component_path, &key );
+    }
+    if (err != REGERR_OK)
+        return err;
+
+    if ( version != NULL && *version != '\0' ) {
+        /* Add "Version" entry with values like "4.0.0.0" */
+        err = NR_RegSetEntryString( vreg, key, VERSTR, version );
+        if (err != REGERR_OK)
+            goto abort;
+    }
+
+    if ( filepath != NULL && *filepath != '\0' ) {
+        /* add "Path" entry */
+        err = vr_SetPathname( vreg, key, (bDirectory)?DIRSTR:PATHSTR, filepath );
+
+        if (err != REGERR_OK)
+            goto abort;
+    }
+
+    return REGERR_OK;
+
+abort:
+    NR_RegDeleteKey( vreg, rootKey, component_path );
+    return err;
+
+}   /* Install */
+
+
+
+VR_INTERFACE(REGERR) VR_Remove(char *component_path)
+{
+    REGERR err;
+    RKEY rootkey;
+
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    rootkey = PATH_ROOT(component_path);
+
+    return NR_RegDeleteKey( vreg, rootkey, component_path );
+
+}   /* Remove */
+
+VR_INTERFACE(REGERR) VR_Enum(char *component_path, REGENUM *state, 
+                                         char *buffer, uint32 buflen)
+{
+    REGERR  err;
+    RKEY    rootkey;
+    RKEY    key;
+
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    if ( component_path == NULL )
+        rootkey = ROOTKEY_VERSIONS;
+    else
+        rootkey = PATH_ROOT(component_path);
+
+    err = NR_RegGetKey( vreg, rootkey, component_path, &key );
+    if (err != REGERR_OK)
+        return err;
+
+    err = NR_RegEnumSubkeys( vreg, key, state, buffer, buflen, REGENUM_DEPTH_FIRST);
+
+    return err;
+
+}   /* Enum */
+
+VR_INTERFACE(REGERR) VR_InRegistry(char *component_path)
+{
+    REGERR err;
+    RKEY key;
+    HREG hreg;
+
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    return vr_FindKey( component_path, &hreg, &key );
+}   /* InRegistry */
+
+
+
+VR_INTERFACE(REGERR) VR_ValidateComponent(char *component_path)
+{
+    REGERR err;
+    RKEY key;
+    char path[MAXREGPATHLEN];
+    HREG hreg;
+
+
+#ifdef USE_CHECKSUM
+    char buf[MAXREGNAMELEN];
+    long calculatedCheck;
+    int storedCheck;
+#endif
+
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    err = vr_FindKey( component_path, &hreg, &key );
+    if ( err != REGERR_OK )
+        return err;
+
+    err = VR_GetPath( component_path, sizeof(path), path );
+    if ( err != REGERR_OK ) {
+        if ( err == REGERR_NOFIND ) {
+            err = REGERR_NOPATH;
+        }
+        return err;
+    }
+
+    {
+        uint32 len;
+        struct stat  statStruct;
+
+        /* directories are stored with a trailing separator -- if we */
+        /* have one of these we have to remove it for stat to work */
+        len = strlen(path);
+        if ( path[len-1] == VR_FILE_SEP )
+            path[len-1] = 0;
+
+        if ( stat ( path, &statStruct ) != 0 ) {
+            err = REGERR_NOFILE;
+        }
+    }
+    if (err != REGERR_OK)
+        return err;
+
+
+#if defined(USE_CHECKSUM) && !defined(XP_UNIX)
+    err = NR_RegGetEntryString( vreg, key, CHKSTR, buf, sizeof(buf) );
+    if (err != REGERR_OK)
+        return err;
+
+    storedCheck = atoi(buf);
+
+    err = vr_GetCheck(filepath, &calculatedCheck);
+    if (err != REGERR_OK)
+        return err;
+
+    if (storedCheck != calculatedCheck)
+    {
+        return REGERR_BADCHECK;
+    }
+#endif /* USE_CHECKSUM */
+
+    return REGERR_OK;
+
+}   /* CheckEntry */
+
+
+
+VR_INTERFACE(REGERR) VR_SetRegDirectory(const char *path)
+{
+    char *tmp;
+
+    tmp = XP_STRDUP(path);
+    if (NULL == tmp) {
+        return REGERR_MEMORY;
+    }
+
+    PR_Lock(vr_lock);
+
+    XP_FREEIF(app_dir);
+    app_dir = tmp;
+    
+    PR_Unlock(vr_lock);
+
+    return REGERR_OK;
+}
+
+
+
+VR_INTERFACE(REGERR) VR_SetRefCount(char *component_path, int refcount)
+{
+    REGERR err;
+    RKEY rootKey;
+    RKEY key = 0;
+    char rcstr[MAXREGNAMELEN];
+
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    /* Use curver if path is relative */
+    rootKey = PATH_ROOT(component_path);
+
+    /* Make sure path components (keys) exist by calling Add */
+    /* (special "" component must always exist, and Add fails) */
+    if ( component_path != NULL && *component_path == '\0' ) {
+        err = REGERR_PARAM;
+    }
+    else {
+        err = NR_RegAddKey( vreg, rootKey, component_path, &key );
+    }
+    
+    if (err != REGERR_OK)
+        return err;
+
+    *rcstr = '\0';
+    /* itoa(refcount, rcstr, 10); */
+    XP_SPRINTF(rcstr, "%d", refcount);
+    
+    if ( rcstr != NULL && *rcstr != '\0' ) {
+        /* Add "RefCount" */
+        err = NR_RegSetEntryString( vreg, key, REFCSTR, rcstr );
+    }
+    
+    return err;
+}   /* SetRefCount */
+
+
+
+VR_INTERFACE(REGERR) VR_GetRefCount(char *component_path, int *result)
+{
+    REGERR  err;
+    RKEY    rootkey;
+    RKEY    key;
+    char    buf[MAXREGNAMELEN];
+
+    *result = -1; 
+
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    /* "Uninstall" only happens in the writable registry, so no
+     * need to search the shared one on Unix using vr_FindKey()
+     */
+    rootkey = PATH_ROOT(component_path);
+    err = NR_RegGetKey( vreg, rootkey, component_path, &key );
+    if (err != REGERR_OK)
+        return err;
+
+    err = NR_RegGetEntryString( vreg, key, REFCSTR, buf, sizeof(buf) );
+    if (err != REGERR_OK)
+        return err;
+
+    *result = atoi( buf );
+
+    return REGERR_OK;
+
+}   /* GetRefCount */
+
+static REGERR vr_GetUninstallItemPath(char *regPackageName, char *regbuf, uint32 regbuflen)
+{
+    XP_Bool bSharedUninstall = FALSE;
+    XP_Bool bNavPackage = FALSE;
+    uint32 len = 0;
+    uint32 sharedstrlen = 0;
+    uint32 curstrlen = 0;
+    uint32 curregbuflen = 0;
+
+    /* determine install type */
+    if (*regPackageName == '\0') {
+        bNavPackage = TRUE;
+    }
+    else if ( *regPackageName == PATHDEL) {
+        bSharedUninstall = TRUE;
+    }
+
+    /* create uninstall path prefix */
+    len = XP_STRLEN(REG_UNINSTALL_DIR);
+    if (len < regbuflen)
+    {
+        XP_STRCPY( regbuf, REG_UNINSTALL_DIR );
+    }
+    else
+    {
+        return REGERR_BUFTOOSMALL;
+    }
+    if (bSharedUninstall)
+    {
+        sharedstrlen = XP_STRLEN(SHAREDSTR);
+        if (sharedstrlen < (regbuflen - len))
+            XP_STRCAT( regbuf, SHAREDSTR );
+        else 
+            return REGERR_BUFTOOSMALL;
+    }
+    else
+    {
+        curstrlen = XP_STRLEN(gCurstr);
+        if (curstrlen < (regbuflen - len))
+            XP_STRCAT( regbuf, gCurstr );
+        else 
+            return REGERR_BUFTOOSMALL;
+        if (1 < (regbuflen - len - curstrlen))
+            XP_STRCAT( regbuf, "/" );
+        else 
+            return REGERR_BUFTOOSMALL;
+    }  
+
+    /* add final uninstall node name */
+    len = 0;
+    curregbuflen = XP_STRLEN(regbuf);
+    if ( bNavPackage ) {
+        len = XP_STRLEN(UNINSTALL_NAV_STR);
+        if (len < (regbuflen - curregbuflen))
+            XP_STRCAT( regbuf, UNINSTALL_NAV_STR );
+        else 
+            return REGERR_BUFTOOSMALL;
+    }
+    else {
+        len = XP_STRLEN(regPackageName);
+        if (len < (regbuflen - curregbuflen))
+            XP_STRCAT( regbuf, regPackageName );
+        else 
+            return REGERR_BUFTOOSMALL;
+    }
+    return REGERR_OK;
+}
+
+
+/**
+ * Replaces all '/' with '_',in the given string.If an '_' already exists in the
+ * given string, it is escaped by adding another '_' to it.
+ */
+static REGERR vr_convertPackageName(char *regPackageName, char *convertedPackageName, uint32 convertedDataLength)
+{
+    uint32 length = 0;
+    uint32 i;
+    uint32 j = 0;
+
+    length = XP_STRLEN(regPackageName);
+    
+    if (convertedDataLength <= length)
+        return REGERR_BUFTOOSMALL;
+
+    for (i=0, j=0; i<length; i++, j++)
+    {
+        if (j < (convertedDataLength-1))
+        {
+            convertedPackageName[j] = regPackageName[i];
+        }
+        else
+        {
+            return REGERR_BUFTOOSMALL;
+        }
+        if (regPackageName[i] == '_')
+        {
+            if ((j+1) < (convertedDataLength-1))
+            {
+                convertedPackageName[j+1] = '_';
+            }
+            else
+            {
+                return REGERR_BUFTOOSMALL;
+            }
+            j = j + 1;
+        }
+    }
+
+    if (convertedPackageName[j-1] == '/')
+        convertedPackageName[j-1] = '\0';
+    else
+    {
+        if (j < convertedDataLength)
+        {
+            convertedPackageName[j] = '\0';
+        }
+        else
+        {
+            return REGERR_BUFTOOSMALL;
+        }
+    }
+
+    length = 0;
+    length = XP_STRLEN(convertedPackageName);
+    for (i=1; i<length; i++)
+    {
+        if (convertedPackageName[i] == '/')
+            convertedPackageName[i] = '_';
+    }
+
+    return REGERR_OK;
+}
+
+static REGERR vr_unmanglePackageName(char *mangledPackageName, char *regPackageName, uint32 regPackageLength)
+{
+    uint32 length = 0;
+    uint32 i = 0;
+    uint32 j = 0;
+
+    length = XP_STRLEN(mangledPackageName);
+    
+    if (regPackageLength <= length)
+        return REGERR_BUFTOOSMALL;
+
+    while (i < length)
+    {
+        if ((mangledPackageName[i] != '_') || (i == length-1)){
+            if (j < (regPackageLength - 1))
+                regPackageName[j] = mangledPackageName[i];
+            else
+                return REGERR_BUFTOOSMALL;
+            j = j + 1;  
+            i = i + 1;
+        } else if (mangledPackageName[i + 1] != '_') { 
+            if (j < (regPackageLength - 1))
+                regPackageName[j] = '/';
+            else
+                return REGERR_BUFTOOSMALL;
+            j = j + 1;
+            i = i + 1;
+        } else {
+            if (j < (regPackageLength - 1))
+                regPackageName[j] = '_';
+            else
+                return REGERR_BUFTOOSMALL;
+            j = j + 1;
+            i = i + 2;
+        }
+    }
+    if (j < regPackageLength)
+        regPackageName[j] = '\0';
+    else
+        return REGERR_BUFTOOSMALL;
+    return REGERR_OK;
+}
+
+VR_INTERFACE(REGERR) VR_UninstallCreateNode(char *regPackageName, char *userPackageName)
+{
+    REGERR err;
+    RKEY key = 0;
+    char *regbuf;
+    uint32 regbuflen = 0;
+
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    if ( regPackageName == NULL )
+        err = REGERR_PARAM;
+   
+    if ( userPackageName == NULL)
+        err = REGERR_PARAM;
+
+    regbuflen = 256 + XP_STRLEN(regPackageName);
+    regbuf = (char*)XP_ALLOC( regbuflen );
+    if (regbuf != NULL )
+    {
+        err = vr_GetUninstallItemPath(regPackageName, regbuf, regbuflen);  
+        if (err != REGERR_OK)
+        {
+            XP_FREE(regbuf);
+            return err;
+        }
+        err = NR_RegAddKey( vreg, ROOTKEY_PRIVATE, regbuf, &key );
+        XP_FREE(regbuf);
+    }
+    else
+    {
+        err = REGERR_MEMORY;
+    }
+
+    if (err == REGERR_OK)
+        err = NR_RegSetEntryString(vreg, key, PACKAGENAMESTR, userPackageName);
+  
+    return err;
+
+}   /* UninstallCreateNode */
+
+VR_INTERFACE(REGERR) VR_GetUninstallUserName(char *regPackageName, char *outbuf, uint32 buflen)
+{
+    REGERR err;
+    RKEY key = 0;
+    char *regbuf = NULL;
+    char *convertedName = NULL;
+    uint32 convertedDataLength = 0;
+    uint32 regbuflen = 0;
+
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    if ( regPackageName == NULL || *regPackageName == '\0' || outbuf == NULL )
+        return REGERR_PARAM;
+   
+    convertedDataLength = 2 * XP_STRLEN(regPackageName) + 1;
+    convertedName = (char*)XP_ALLOC(convertedDataLength);
+    if (convertedName == NULL ) {
+        err = REGERR_MEMORY;
+        return err;
+    }
+  
+    err = vr_convertPackageName(regPackageName, convertedName, convertedDataLength);
+    if (err != REGERR_OK) {
+        XP_FREE(convertedName);
+        return err;
+    }
+    regbuflen = 256 + XP_STRLEN(convertedName);
+    regbuf = (char*)XP_ALLOC( regbuflen );
+    if (regbuf == NULL ) {
+        err = REGERR_MEMORY;
+    } else {
+        err = vr_GetUninstallItemPath(convertedName, regbuf, regbuflen);
+        if (err == REGERR_OK) {
+            err = NR_RegGetKey( vreg, ROOTKEY_PRIVATE, regbuf, &key );
+        }
+        XP_FREE(regbuf);
+    }
+
+    if (err == REGERR_OK)
+        err = NR_RegGetEntryString( vreg, key, PACKAGENAMESTR, outbuf, buflen );
+  
+    XP_FREE(convertedName);
+    return err;
+
+}   /* GetUninstallName */
+
+VR_INTERFACE(REGERR) VR_UninstallAddFileToList(char *regPackageName, char *vrName)
+{
+    REGERR err;
+    RKEY key = 0;
+    char *regbuf;
+    uint32 regbuflen = 0;
+    uint32 curregbuflen = 0;
+    uint32 len = 0;
+    
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    if ( regPackageName == NULL )
+        err = REGERR_PARAM;
+
+    if ( vrName == NULL )
+        err = REGERR_PARAM;
+
+    regbuflen = 256 + XP_STRLEN(regPackageName);
+    regbuf = (char*)XP_ALLOC( regbuflen );
+    if (regbuf != NULL )
+    {
+        err = vr_GetUninstallItemPath(regPackageName, regbuf, regbuflen);
+        if (err == REGERR_OK)
+        {
+            curregbuflen = XP_STRLEN(regbuf);
+            len = XP_STRLEN(SHAREDFILESSTR);
+            if (len < (regbuflen - curregbuflen))
+            {
+                XP_STRCAT(regbuf, SHAREDFILESSTR);
+                err = NR_RegAddKey( vreg, ROOTKEY_PRIVATE, regbuf, &key );
+            }
+            else
+                err = REGERR_BUFTOOSMALL;
+        }
+        XP_FREEIF(regbuf);
+    }
+    else
+    {
+        err = REGERR_MEMORY;
+    }
+
+    if (err == REGERR_OK)
+        err = NR_RegSetEntryString( vreg, key, vrName, "");
+  
+    return err;
+
+}   /* UninstallAddFileToList */
+
+VR_INTERFACE(REGERR) VR_UninstallFileExistsInList(char *regPackageName, char *vrName)
+{
+    REGERR err;
+    RKEY key = 0;
+    char *regbuf;
+    char  sharedfilesstr[MAXREGNAMELEN];
+    uint32 regbuflen = 0;
+    uint32 curregbuflen = 0;
+    uint32 len = 0;
+    
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    if ( regPackageName == NULL )
+        err = REGERR_PARAM;
+
+    if ( vrName == NULL )
+        err = REGERR_PARAM;
+
+    regbuflen = 256 + XP_STRLEN(regPackageName);
+    regbuf = (char*)XP_ALLOC( regbuflen );
+    if (regbuf != NULL )
+    {
+        err = vr_GetUninstallItemPath(regPackageName, regbuf, regbuflen);
+        if (err == REGERR_OK)
+        {
+            curregbuflen = XP_STRLEN(regbuf);
+            len = XP_STRLEN(SHAREDFILESSTR);
+            if (len < (regbuflen - curregbuflen))
+            {
+                XP_STRCAT(regbuf, SHAREDFILESSTR);
+                err = NR_RegGetKey( vreg, ROOTKEY_PRIVATE, regbuf, &key );
+            }
+            else
+                err = REGERR_BUFTOOSMALL;
+        }
+        XP_FREEIF(regbuf);
+    }
+    else
+    {
+        err = REGERR_MEMORY;
+    }
+    
+    if (err == REGERR_OK)
+        err = NR_RegGetEntryString( vreg, key, vrName, sharedfilesstr,
+                                    sizeof(sharedfilesstr) );
+    return err;
+
+}   /* UninstallFileExistsInList */
+
+VR_INTERFACE(REGERR) VR_UninstallEnumSharedFiles(char *component_path, REGENUM *state, 
+                                         char *buffer, uint32 buflen)
+{
+    REGERR err;
+    RKEY key = 0;
+    char *regbuf;
+    char *converted_component_path;
+    uint32 convertedDataLength = 0;
+    uint32 regbuflen = 0;
+    uint32 curregbuflen = 0;
+    uint32 len = 0;
+ 
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    if ( component_path == NULL )
+        return REGERR_PARAM;
+
+    convertedDataLength = 2 * XP_STRLEN(component_path) + 1;
+    converted_component_path = (char*)XP_ALLOC(convertedDataLength);
+    if (converted_component_path == NULL ) {
+        err = REGERR_MEMORY;
+        return err;
+    }
+    err = vr_convertPackageName(component_path, converted_component_path, convertedDataLength);
+    if (err != REGERR_OK)
+    {
+        XP_FREEIF(converted_component_path);
+        return err;
+    }
+
+    regbuflen = 256 + XP_STRLEN(converted_component_path);
+    regbuf = (char*)XP_ALLOC( regbuflen );
+    if (regbuf != NULL )
+    {
+        err = vr_GetUninstallItemPath(converted_component_path, regbuf, regbuflen); 
+        if (err == REGERR_OK)
+        {
+            curregbuflen = XP_STRLEN(regbuf);
+            len = XP_STRLEN(SHAREDFILESSTR);
+            if (len < (regbuflen - curregbuflen))
+            {
+                 XP_STRCAT(regbuf, SHAREDFILESSTR);
+                 err = NR_RegGetKey( vreg, ROOTKEY_PRIVATE, regbuf, &key );
+            }
+            else
+                err = REGERR_BUFTOOSMALL;
+        }
+        XP_FREE(regbuf);
+    }
+    else
+    {
+        err = REGERR_MEMORY;
+    }
+    
+    XP_FREE(converted_component_path);
+
+    if (err == REGERR_OK)
+        err = NR_RegEnumEntries( vreg, key, state, buffer, buflen, NULL);
+
+    return err;
+
+}   /* UninstallEnumSharedFiles */
+
+VR_INTERFACE(REGERR) VR_UninstallDeleteFileFromList(char *component_path, char *vrName)
+{
+    REGERR err;
+    RKEY key = 0;
+    char *regbuf;
+    char *converted_component_path;
+    uint32 convertedDataLength = 0;
+    uint32 regbuflen = 0;
+    uint32 curregbuflen = 0;
+    uint32 len = 0;
+        
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    if ( component_path == NULL )
+        err = REGERR_PARAM;
+
+    if ( vrName == NULL )
+        err = REGERR_PARAM;
+
+    convertedDataLength = 2 * XP_STRLEN(component_path) + 1;
+    converted_component_path = (char*)XP_ALLOC(convertedDataLength);
+    if (converted_component_path == NULL ) {
+        err = REGERR_MEMORY;
+        return err;
+    }
+    err = vr_convertPackageName(component_path, converted_component_path, convertedDataLength);
+    if (err != REGERR_OK)
+    {
+        XP_FREEIF(converted_component_path);
+        return err;
+    }
+
+    regbuflen = 256 + XP_STRLEN(converted_component_path);
+    regbuf = (char*)XP_ALLOC( regbuflen );
+    if (regbuf != NULL )
+    {
+        err = vr_GetUninstallItemPath(converted_component_path, regbuf, regbuflen);  
+         if (err == REGERR_OK)
+        {
+            curregbuflen = XP_STRLEN(regbuf);
+            len = XP_STRLEN(SHAREDFILESSTR);
+            if (len < (regbuflen - curregbuflen))
+            {
+                XP_STRCAT(regbuf, SHAREDFILESSTR);
+                err = NR_RegGetKey( vreg, ROOTKEY_PRIVATE, regbuf, &key );
+            }
+            else
+                err = REGERR_BUFTOOSMALL;
+        }
+        XP_FREE(regbuf);
+    }
+    else
+    {
+        err = REGERR_MEMORY;
+    }
+    
+    XP_FREE(converted_component_path);
+
+    if (err == REGERR_OK)
+        err = NR_RegDeleteEntry( vreg, key, vrName);
+
+    return err;
+
+}   /* UninstallDeleteFileFromList */
+
+VR_INTERFACE(REGERR) VR_UninstallDeleteSharedFilesKey(char *component_path)
+{
+    REGERR err;
+    char *regbuf;
+    char *converted_component_path;
+    uint32 convertedDataLength = 0;
+    uint32 regbuflen = 0;
+    uint32 curregbuflen = 0;
+    uint32 len = 0;
+        
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    if ( component_path == NULL )
+        err = REGERR_PARAM;
+
+    convertedDataLength = 2 * XP_STRLEN(component_path) + 1;
+    converted_component_path = (char*)XP_ALLOC(convertedDataLength);
+    if (converted_component_path == NULL ) {
+        err = REGERR_MEMORY;
+        return err;
+    }
+    err = vr_convertPackageName(component_path, converted_component_path, convertedDataLength);
+    if (err != REGERR_OK)
+    {
+        XP_FREEIF(converted_component_path);
+        return err;
+    }
+
+    regbuflen = 256 + XP_STRLEN(converted_component_path);
+    regbuf = (char*)XP_ALLOC( regbuflen );
+    if (regbuf != NULL )
+    {
+        err = vr_GetUninstallItemPath(converted_component_path, regbuf, regbuflen); 
+        if (err == REGERR_OK)
+        {
+            curregbuflen = XP_STRLEN(regbuf);
+            len = XP_STRLEN(SHAREDFILESSTR);
+            if (len < (regbuflen - curregbuflen))
+            {
+                XP_STRCAT(regbuf, SHAREDFILESSTR);
+                err = NR_RegDeleteKey( vreg, ROOTKEY_PRIVATE, regbuf );
+            }
+            else
+                err = REGERR_BUFTOOSMALL;
+        }
+        XP_FREE(regbuf);
+    }
+    else
+    {
+        err = REGERR_MEMORY;
+    }
+
+    XP_FREE(converted_component_path);
+    return err;
+
+}   /* UninstallDeleteSharedFilesKey */
+
+VR_INTERFACE(REGERR) VR_UninstallDestroy(char *component_path)
+{
+    REGERR err;
+    char *regbuf;
+    char *converted_component_path;
+    uint32 convertedDataLength = 0;
+    uint32 regbuflen = 0;
+        
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    if ( component_path == NULL )
+        err = REGERR_PARAM;
+    
+    convertedDataLength = 2 * XP_STRLEN(component_path) + 1;
+    converted_component_path = (char*)XP_ALLOC(convertedDataLength);
+    if (converted_component_path == NULL ) {
+        err = REGERR_MEMORY;
+        return err;
+    }
+    err = vr_convertPackageName(component_path, converted_component_path, convertedDataLength);
+    if (err != REGERR_OK)
+    {
+        XP_FREEIF(converted_component_path);
+        return err;
+    }
+
+    regbuflen = 256 + XP_STRLEN(converted_component_path);
+    regbuf = (char*)XP_ALLOC( regbuflen );
+    if (regbuf != NULL )
+    {
+        err = vr_GetUninstallItemPath(converted_component_path, regbuf, regbuflen);  
+        if (err == REGERR_OK)
+        {
+            err = NR_RegDeleteKey( vreg, ROOTKEY_PRIVATE, regbuf );
+        }
+        else
+        {
+            err = REGERR_BUFTOOSMALL;
+        }
+        XP_FREE(regbuf);
+    }
+    else
+    {
+        err = REGERR_MEMORY;
+    }
+    
+    XP_FREE(converted_component_path);
+    return err;
+
+}   /* UninstallDestroy */
+
+VR_INTERFACE(REGERR) VR_EnumUninstall(REGENUM *state, char* userPackageName,
+                                    int32 len1, char*regPackageName, int32 len2, XP_Bool bSharedList)
+{
+    REGERR err;
+    RKEY key;
+    RKEY key1;
+    char regbuf[MAXREGPATHLEN+1] = {0};
+    char temp[MAXREGPATHLEN+1] = {0};
+   
+    err = vr_Init();
+    if (err != REGERR_OK)
+        return err;
+
+    XP_STRCPY( regbuf, REG_UNINSTALL_DIR );
+    if (bSharedList)
+    {
+        XP_STRCAT( regbuf, SHAREDSTR );
+    }
+    else
+    {
+        XP_STRCAT( regbuf, gCurstr );
+    }  
+                   
+    err = NR_RegGetKey( vreg, ROOTKEY_PRIVATE, regbuf, &key );
+    if (err != REGERR_OK)
+        return err;
+
+    *regbuf = '\0';
+    *userPackageName = '\0';
+    err = NR_RegEnumSubkeys( vreg, key, state, regbuf, sizeof(regbuf), REGENUM_CHILDREN);
+
+    if (err == REGERR_OK && !bSharedList )
+    {
+        if (XP_STRCMP(regbuf, UNINSTALL_NAV_STR) == 0)
+        {
+            /* skip Communicator package, get the next one instead */
+            err = NR_RegEnumSubkeys( vreg, key, state, regbuf, sizeof(regbuf), REGENUM_CHILDREN);
+        }
+    }
+    if (err != REGERR_OK)
+        return err;
+
+    err = NR_RegGetKey( vreg, key, regbuf, &key1 );
+    if (err != REGERR_OK)
+        return err;
+
+    err = NR_RegGetEntryString( vreg, key1, PACKAGENAMESTR, userPackageName, len1);
+
+    if (err != REGERR_OK)
+    {
+        *userPackageName = '\0';
+        return err;
+    }
+
+    if (len2 <= (int32)XP_STRLEN(regbuf))
+    {
+        err =  REGERR_BUFTOOSMALL;
+        *userPackageName = '\0';
+        return err;
+    }
+    
+    *regPackageName = '\0';
+    if (bSharedList)
+    {
+        XP_STRCPY(temp, "/");
+        XP_STRCAT(temp, regbuf);
+        *regbuf = '\0';
+        XP_STRCPY(regbuf, temp);
+    }
+
+    err = vr_unmanglePackageName(regbuf, regPackageName, len2);
+    return err;
+
+}   /* EnumUninstall */
+
+/* EOF: VerReg.c */
new file mode 100644
--- /dev/null
+++ b/modules/libreg/src/nr_bufio.c
@@ -0,0 +1,758 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Daniel Veditz <dveditz@netscape.com>
+ *   Edward Kandrot <kandrot@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*------------------------------------------------------------------------
+ * nr_bufio
+ * 
+ * Buffered I/O routines to improve registry performance
+ * the routines mirror fopen(), fclose() et al
+ *
+ * Inspired by the performance gains James L. Nance <jim_nance@yahoo.com>
+ * got using NSPR memory-mapped I/O for the registry. Unfortunately NSPR 
+ * doesn't support mmapio on the Mac.
+ *-----------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#if defined(XP_MACOSX)
+#include <Carbon/Carbon.h>
+#endif
+
+#if defined(SUNOS4)
+#include <unistd.h>  /* for SEEK_SET */
+#endif /* SUNOS4 */
+
+#include "prerror.h"
+#include "prlog.h"
+
+#include "vr_stubs.h"
+#include "nr_bufio.h"
+
+
+#define BUFIO_BUFSIZE_DEFAULT   0x2000
+
+#define STARTS_IN_BUF(f) ((f->fpos >= f->datastart) && \
+                         (f->fpos < (f->datastart+f->datasize)))
+
+#define ENDS_IN_BUF(f,c) (((f->fpos + c) > (PRUint32)f->datastart) && \
+                         ((f->fpos + c) <= (PRUint32)(f->datastart+f->datasize)))
+
+#if DEBUG_dougt
+static num_reads = 0;
+#endif
+
+
+struct BufioFileStruct 
+{
+    FILE    *fd;        /* real file descriptor */
+    PRInt32 fsize;      /* total size of file */
+    PRInt32 fpos;       /* our logical position in the file */
+    PRInt32 datastart;  /* the file position at which the buffer starts */
+    PRInt32 datasize;   /* the amount of data actually in the buffer*/
+    PRInt32 bufsize;	/* size of the in memory buffer */
+    PRBool  bufdirty;   /* whether the buffer been written to */
+    PRInt32 dirtystart;
+    PRInt32 dirtyend;
+    PRBool  readOnly;   /* whether the file allows writing or not */
+#ifdef DEBUG_dveditzbuf
+    PRUint32 reads;
+    PRUint32 writes;
+#endif
+    char    *data;      /* the data buffer */
+};
+
+
+static PRBool _bufio_loadBuf( BufioFile* file, PRUint32 count );
+static int    _bufio_flushBuf( BufioFile* file );
+
+#ifdef XP_OS2
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <share.h>
+#include <io.h>
+
+FILE* os2_fileopen(const char* name, const char* mode)
+{
+    int access = O_RDWR;
+    int descriptor;
+    int pmode = 0;
+
+    /* Fail if only one character is passed in - this shouldn't happen */
+    if (mode[1] == '\0') {
+        return NULL;
+    }
+    /* Only possible options are wb+, rb+, wb and rb */
+    if (mode[0] == 'w' && mode[1] == 'b') {
+        access |= (O_TRUNC | O_CREAT);
+        if (mode[2] == '+') {
+            access |= O_RDWR;
+            pmode = S_IREAD | S_IWRITE;
+        } else {
+            access |= O_WRONLY;
+            pmode = S_IWRITE;
+        }
+    }
+    if (mode[0] == 'r' && mode[1] == 'b') {
+        if (mode[2] == '+') {
+            access |= O_RDWR;
+            pmode = S_IREAD | S_IWRITE;
+        } else {
+            access = O_RDONLY;
+            pmode = S_IREAD;
+        }
+    }
+
+    descriptor = sopen(name, access, SH_DENYNO, pmode);
+    if (descriptor != -1) {
+        return fdopen(descriptor, mode);
+    }
+    return NULL;
+}
+#endif
+
+/** 
+ * like fopen() this routine takes *native* filenames, not NSPR names.
+ */
+BufioFile*  bufio_Open(const char* name, const char* mode)
+{
+    FILE        *fd;
+    BufioFile   *file = NULL;
+
+#ifdef XP_OS2
+    fd = os2_fileopen( name, mode );
+#else
+    fd = fopen( name, mode );
+#endif
+    
+    if ( fd )
+    {
+        /* file opened successfully, initialize the bufio structure */
+
+        file = PR_NEWZAP( BufioFile );
+        if ( file )
+        {
+            file->fd = fd;
+            file->bufsize = BUFIO_BUFSIZE_DEFAULT;  /* set the default buffer size */
+
+            file->data = (char*)PR_Malloc( file->bufsize );
+            if ( file->data )
+            {
+                /* get file size to finish initialization of bufio */
+                if ( !fseek( fd, 0, SEEK_END ) )
+                {
+                    file->fsize = ftell( fd );
+
+                    file->readOnly = strcmp(mode,XP_FILE_READ) == 0 || 
+                                     strcmp(mode,XP_FILE_READ_BIN) == 0;
+                }
+                else
+                {
+                    PR_Free( file->data );
+                    PR_DELETE( file );
+                }
+            }
+            else
+                PR_DELETE( file );
+        }
+
+        /* close file if we couldn't create BufioFile */
+        if (!file)
+        {
+            fclose( fd );
+            PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
+        }
+    }
+    else
+    {
+        /* couldn't open file. Figure out why and set NSPR errors */
+        
+        switch (errno)
+        {
+            /* file not found */
+#if defined(XP_MACOSX)
+            case fnfErr:
+#else
+            case ENOENT:
+#endif
+                PR_SetError(PR_FILE_NOT_FOUND_ERROR,0);
+                break;
+
+            /* file in use */
+#if defined(XP_MACOSX)
+            case opWrErr:
+#else
+            case EACCES:
+#endif
+                PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR,0);
+                break;
+
+            default:
+                PR_SetError(PR_UNKNOWN_ERROR,0);
+                break;
+        }
+    }
+
+    return file;
+}
+
+
+
+/**
+ * close the buffered file and destroy BufioFile struct
+ */
+int bufio_Close(BufioFile* file)
+{
+    int retval = -1;
+
+    if ( file )
+    {
+        if ( file->bufdirty )
+            _bufio_flushBuf( file );
+
+        retval = fclose( file->fd );
+
+        if ( file->data )
+            PR_Free( file->data );
+
+        PR_DELETE( file );
+#if DEBUG_dougt
+        printf(" --- > Buffered registry read fs hits (%d)\n", num_reads);
+#endif
+    }
+
+    return retval;
+}
+
+
+
+/**
+ * change the logical position in the file. Equivalent to fseek()
+ */
+int bufio_Seek(BufioFile* file, PRInt32 offset, int whence)
+{
+    if (!file)
+        return -1;
+
+    switch(whence) 
+    {
+      case SEEK_SET:
+	    file->fpos = offset;
+	    break;
+	  case SEEK_END:
+	    file->fpos = file->fsize + offset;
+	    break;
+	  case SEEK_CUR:
+	    file->fpos = file->fpos + offset;
+	    break;
+	  default:
+	    return -1;
+    }
+
+    if ( file->fpos < 0 ) 
+        file->fpos = 0;
+
+    return 0;
+}
+
+
+
+/**
+ * like ftell() returns the current position in the file, or -1 for error
+ */
+PRInt32 bufio_Tell(BufioFile* file)
+{
+    if (file)
+        return file->fpos;
+    else
+        return -1;
+}
+
+
+
+PRUint32 bufio_Read(BufioFile* file, char* dest, PRUint32 count)
+{
+    PRInt32     startOffset;
+    PRInt32     endOffset;
+    PRInt32     leftover;
+    PRUint32    bytesCopied;
+    PRUint32    bytesRead;
+    PRUint32    retcount = 0;
+
+    /* sanity check arguments */
+    if ( !file || !dest || count == 0 || file->fpos >= file->fsize )
+        return 0;
+
+    /* Adjust amount to read if we're near EOF */
+    if ( (file->fpos + count) > (PRUint32)file->fsize )
+        count = file->fsize - file->fpos;
+
+
+    /* figure out how much of the data we want is already buffered */
+
+    startOffset = file->fpos - file->datastart;
+    endOffset = startOffset + count;
+
+    if ( startOffset >= 0 && startOffset < file->datasize )
+    {
+        /* The beginning of what we want is in the buffer  */
+        /* so copy as much as is available of what we want */
+
+        if ( endOffset <= file->datasize )
+            bytesCopied = count;
+        else
+            bytesCopied = file->datasize - startOffset;
+
+        memcpy( dest, file->data + startOffset, bytesCopied );
+        retcount = bytesCopied;
+        file->fpos += bytesCopied;
+#ifdef DEBUG_dveditzbuf
+        file->reads++;
+#endif
+
+        /* Was that all we wanted, or do we need to get more? */
+
+        leftover = count - bytesCopied;
+        PR_ASSERT( leftover >= 0 );     /* negative left? something's wrong! */
+
+        if ( leftover )
+        {
+            /* need data that's not in the buffer */
+
+            /* if what's left fits in a buffer then load the buffer with the */
+            /* new area before giving the data, otherwise just read right    */
+            /* into the user's dest buffer */
+
+            if ( _bufio_loadBuf( file, leftover ) )
+            {
+                startOffset = file->fpos - file->datastart;
+
+                /* we may not have been able to load as much as we wanted */
+                if ( startOffset > file->datasize )
+                    bytesRead = 0;
+                else if ( startOffset+leftover <= file->datasize )
+                    bytesRead = leftover;
+                else
+                    bytesRead = file->datasize - startOffset;
+
+                if ( bytesRead )
+                {
+                    memcpy( dest+bytesCopied, file->data+startOffset, bytesRead );
+                    file->fpos += bytesRead;
+                    retcount += bytesRead;
+#ifdef DEBUG_dveditzbuf
+                    file->reads++;
+#endif
+                }
+            }
+            else 
+            {
+                /* we need more than we could load into a buffer, so */
+                /* skip buffering and just read the data directly    */
+
+                if ( fseek( file->fd, file->fpos, SEEK_SET ) == 0 )
+                {
+#if DEBUG_dougt
+                    ++num_reads;
+#endif
+                    bytesRead = fread(dest+bytesCopied, 1, leftover, file->fd);
+                    file->fpos += bytesRead;
+                    retcount += bytesRead;
+                }
+                else 
+                {
+                    /* XXX seek failed, couldn't load more data -- help! */
+                    /* should we call PR_SetError() ? */
+                }
+            }
+        }
+    }
+    else
+    {
+        /* range doesn't start in the loaded buffer but it might end there */
+        if ( endOffset > 0 && endOffset <= file->datasize )
+            bytesCopied = endOffset;
+        else
+            bytesCopied = 0;
+
+        leftover = count - bytesCopied;
+
+        if ( bytesCopied )
+        {
+            /* the tail end of the range we want is already buffered */
+            /* first copy the buffered data to the dest area         */
+            memcpy( dest+leftover, file->data, bytesCopied );
+#ifdef DEBUG_dveditzbuf
+            file->reads++;
+#endif
+        }
+            
+        /* now pick up the part that's not already in the buffer */
+
+        if ( _bufio_loadBuf( file, leftover ) )
+        {
+            /* we were able to load some data */
+            startOffset = file->fpos - file->datastart;
+
+            /* we may not have been able to read as much as we wanted */
+            if ( startOffset > file->datasize )
+                bytesRead = 0;
+            else if ( startOffset+leftover <= file->datasize )
+                bytesRead = leftover;
+            else
+                bytesRead = file->datasize - startOffset;
+
+            if ( bytesRead )
+            {
+                memcpy( dest, file->data+startOffset, bytesRead );
+#ifdef DEBUG_dveditzbuf
+                file->reads++;
+#endif
+            }
+        }
+        else
+        {
+            /* leftover data doesn't fit so skip buffering */
+            if ( fseek( file->fd, file->fpos, SEEK_SET ) == 0 )
+            {
+                bytesRead = fread(dest, 1, leftover, file->fd);
+#if DEBUG_dougt
+                ++num_reads;
+#endif        
+            }
+            else
+                bytesRead = 0;
+        }
+
+        /* if we couldn't read all the leftover, don't tell caller */
+        /* about the tail end we copied from the first buffer      */
+        if ( bytesRead == (PRUint32)leftover )
+            retcount = bytesCopied + bytesRead;
+        else
+            retcount = bytesRead;
+
+        file->fpos += retcount;
+    }
+
+    return retcount;
+}
+
+
+
+/**
+ * Buffered writes
+ */
+PRUint32 bufio_Write(BufioFile* file, const char* src, PRUint32 count)
+{
+    const char* newsrc;
+    PRInt32  startOffset;
+    PRInt32  endOffset;
+    PRUint32 leftover;
+    PRUint32 retcount = 0;
+    PRUint32 bytesWritten = 0;
+    PRUint32 bytesCopied = 0;
+
+    /* sanity check arguments */
+    if ( !file || !src || count == 0 || file->readOnly )
+        return 0;
+
+    /* Write to the current buffer if we can, otherwise load a new buffer */
+
+    startOffset = file->fpos - file->datastart;
+    endOffset = startOffset + count;
+
+    if ( startOffset >= 0 && startOffset <  file->bufsize )
+    {
+        /* the area we want to write starts in the buffer */
+
+        if ( endOffset <= file->bufsize )
+            bytesCopied = count;
+        else
+            bytesCopied = file->bufsize - startOffset;
+
+        memcpy( file->data + startOffset, src, bytesCopied );
+        file->bufdirty = PR_TRUE;
+        endOffset = startOffset + bytesCopied;
+        file->dirtystart = PR_MIN( startOffset, file->dirtystart );
+        file->dirtyend   = PR_MAX( endOffset,   file->dirtyend );
+#ifdef DEBUG_dveditzbuf
+        file->writes++;
+#endif
+
+        if ( endOffset > file->datasize )
+            file->datasize = endOffset;
+
+        retcount = bytesCopied;
+        file->fpos += bytesCopied;
+
+        /* was that all we had to write, or is there more? */
+        leftover = count - bytesCopied;
+        newsrc = src+bytesCopied;
+    }
+    else
+    {
+        /* range doesn't start in the loaded buffer but it might end there */
+        if ( endOffset > 0 && endOffset <= file->bufsize )
+            bytesCopied = endOffset;
+        else
+            bytesCopied = 0;
+
+        leftover = count - bytesCopied;
+        newsrc = src;
+
+        if ( bytesCopied )
+        {
+            /* the tail end of the write range is already in the buffer */
+            memcpy( file->data, src+leftover, bytesCopied );
+            file->bufdirty      = PR_TRUE;
+            file->dirtystart    = 0;
+            file->dirtyend      = PR_MAX( endOffset, file->dirtyend );
+#ifdef DEBUG_dveditzbuf
+            file->writes++;
+#endif
+
+            if ( endOffset > file->datasize )
+                file->datasize = endOffset;
+        }
+    }
+
+    /* if we only wrote part of the request pick up the leftovers */
+    if ( leftover )
+    {
+        /* load the buffer with the new range, if possible */
+        if ( _bufio_loadBuf( file, leftover ) )
+        {
+            startOffset = file->fpos - file->datastart;
+            endOffset   = startOffset + leftover;
+
+            memcpy( file->data+startOffset, newsrc, leftover );
+            file->bufdirty      = PR_TRUE;
+            file->dirtystart    = startOffset;
+            file->dirtyend      = endOffset;
+#ifdef DEBUG_dveditzbuf
+            file->writes++;
+#endif
+            if ( endOffset > file->datasize )
+                file->datasize = endOffset;
+
+            bytesWritten = leftover;
+        }
+        else
+        {
+            /* request didn't fit in a buffer, write directly */
+            if ( fseek( file->fd, file->fpos, SEEK_SET ) == 0 )
+                bytesWritten = fwrite( newsrc, 1, leftover, file->fd );
+            else
+                bytesWritten = 0; /* seek failed! */
+        }
+
+        if ( retcount )
+        {
+            /* we already counted the first part we wrote */
+            retcount    += bytesWritten;
+            file->fpos  += bytesWritten;
+        }
+        else
+        {
+            retcount    = bytesCopied + bytesWritten;
+            file->fpos  += retcount;
+        }
+    }
+
+    if ( file->fpos > file->fsize )
+        file->fsize = file->fpos;
+    
+    return retcount;
+}
+
+
+
+int bufio_Flush(BufioFile* file)
+{
+    if ( file->bufdirty )
+        _bufio_flushBuf( file );
+    
+    return fflush(file->fd);
+}
+
+
+
+/*---------------------------------------------------------------------------*
+ * internal helper functions
+ *---------------------------------------------------------------------------*/
+/**
+ * Attempts to load the buffer with the requested amount of data.
+ * Returns PR_TRUE if it was able to load *some* of the requested
+ * data, but not necessarily all. Returns PR_FALSE if the read fails
+ * or if the requested amount wouldn't fit in the buffer.
+ */
+static PRBool _bufio_loadBuf( BufioFile* file, PRUint32 count )
+{
+    PRInt32     startBuf;
+    PRInt32     endPos;
+    PRInt32     endBuf;
+    PRUint32    bytesRead;
+
+    /* no point in buffering more than the physical buffer will hold */
+    if ( count > (PRUint32)file->bufsize )
+        return PR_FALSE;
+
+    /* Is caller asking for data we already have? */
+    if ( STARTS_IN_BUF(file) && ENDS_IN_BUF(file,count) )
+    {   
+        PR_ASSERT(0);
+        return PR_TRUE;
+    }
+
+    /* if the buffer's dirty make sure we successfully flush it */
+    if ( file->bufdirty && _bufio_flushBuf(file) != 0 )
+        return PR_FALSE;
+
+    /* For now we're not trying anything smarter than simple paging. */
+    /* Slide over if necessary to fit the entire request             */
+    startBuf = ( file->fpos / file->bufsize ) * file->bufsize;
+    endPos = file->fpos + count;
+    endBuf = startBuf + file->bufsize;
+    if ( endPos > endBuf )
+        startBuf += (endPos - endBuf);
+
+    if ( fseek( file->fd, startBuf, SEEK_SET ) != 0 )
+        return PR_FALSE;
+    else
+    {
+#if DEBUG_dougt
+        ++num_reads;
+#endif
+        bytesRead = fread( file->data, 1, file->bufsize, file->fd );
+        file->datastart  = startBuf;
+        file->datasize   = bytesRead;
+        file->bufdirty   = PR_FALSE;
+        file->dirtystart = file->bufsize;
+        file->dirtyend   = 0;
+#ifdef DEBUG_dveditzbuf
+        printf("REG: buffer read %d (%d) after %d reads\n",startBuf,file->fpos,file->reads);
+        file->reads = 0;
+        file->writes = 0;
+#endif
+        return PR_TRUE;
+    }
+}
+
+
+
+static int _bufio_flushBuf( BufioFile* file )
+{
+    PRUint32 written;
+    PRUint32 dirtyamt;
+    PRInt32  startpos;
+
+    PR_ASSERT(file);
+    if ( !file || !file->bufdirty )
+        return 0;
+
+    startpos = file->datastart + file->dirtystart;
+    if ( !fseek( file->fd, startpos, SEEK_SET ) )
+    {
+        dirtyamt = file->dirtyend - file->dirtystart;
+        written = fwrite( file->data+file->dirtystart, 1, dirtyamt, file->fd );
+        if ( written == dirtyamt )
+        {
+#ifdef DEBUG_dveditzbuf
+            printf("REG: buffer flush %d - %d after %d writes\n",startpos,startpos+written,file->writes);
+            file->writes = 0;
+#endif
+            file->bufdirty   = PR_FALSE;
+            file->dirtystart = file->bufsize;
+            file->dirtyend   = 0;
+            return 0;
+        }
+    }
+    return -1;
+}
+
+
+
+/*
+*  sets the file buffer size to bufsize, clearing the buffer in the process.
+*
+*  accepts bufsize of -1 to mean default buffer size, defined by BUFIO_BUFSIZE_DEFAULT
+*  returns new buffers size, or -1 if error occurred
+*/
+
+int bufio_SetBufferSize(BufioFile* file, int bufsize)
+{
+    char    *newBuffer;
+    int     retVal = -1;
+
+    PR_ASSERT(file);
+    if (!file)
+        return retVal;
+
+    if (bufsize == -1)
+        bufsize = BUFIO_BUFSIZE_DEFAULT;
+    if (bufsize == file->bufsize)
+        return bufsize;
+
+    newBuffer = (char*)PR_Malloc( bufsize );
+    if (newBuffer)
+    {
+        /* if the buffer's dirty make sure we successfully flush it */
+        if ( file->bufdirty && _bufio_flushBuf(file) != 0 )
+        {
+            PR_Free( newBuffer );
+            return -1;
+        }
+
+
+        file->bufsize = bufsize;
+        if ( file->data )
+            PR_Free( file->data );
+        file->data = newBuffer;
+        file->datasize = 0;
+        file->datastart = 0;
+        retVal = bufsize;
+    }
+ 
+    return retVal;
+}
+
+
+/* EOF nr_bufio.c */
new file mode 100644
--- /dev/null
+++ b/modules/libreg/src/nr_bufio.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Daniel Veditz <dveditz@netscape.com>
+ *   Edward Kandrot <kandrot@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* nr_bufio.h
+ * Buffered I/O routines to improve registry performance
+ * 
+ * the routines mirror fopen(), fclose() et al
+ *
+ * __NOTE__: the filenames are *native* filenames, not NSPR names.
+ */
+
+#ifndef _NR_BUFIO_H_
+#define _NR_BUFIO_H_
+
+typedef struct BufioFileStruct BufioFile;
+
+BufioFile*  bufio_Open(const char* name, const char* mode);
+int         bufio_Close(BufioFile* file);
+int         bufio_Seek(BufioFile* file, PRInt32 offset, int whence);
+PRUint32    bufio_Read(BufioFile* file, char* dest, PRUint32 count);
+PRUint32    bufio_Write(BufioFile* file, const char* src, PRUint32 count);
+PRInt32     bufio_Tell(BufioFile* file);
+int         bufio_Flush(BufioFile* file);
+int         bufio_SetBufferSize(BufioFile* file, int bufsize);
+
+#endif  /* _NR_BUFIO_H_ */
+
new file mode 100644
--- /dev/null
+++ b/modules/libreg/src/objs.mk
@@ -0,0 +1,47 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 2000
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+MODULES_LIBREG_SRC_LCSRCS = \
+		reg.c \
+		VerReg.c \
+		vr_stubs.c \
+		$(NULL)
+
+
+MODULES_LIBREG_SRC_CSRCS := $(addprefix $(topsrcdir)/modules/libreg/src/, $(MODULES_LIBREG_SRC_LCSRCS))
+
+
new file mode 100644
--- /dev/null
+++ b/modules/libreg/src/reg.c
@@ -0,0 +1,3920 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Daniel Veditz <dveditz@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/* ====================================================================
+ * reg.c
+ * XP Registry functions
+ * ====================================================================
+ */
+
+/* TODO:
+ *  - Replace 'malloc' in NR_RegPack with the Netscape XP equivalent
+ *  - Solve DOS 'errno' problem mentioned below
+ *  - Solve rename across volume problem described in VR_PackRegistry
+ */
+
+/* Preprocessor Defines
+ *  STANDALONE_REGISTRY - define if not linking with Navigator
+ *  NOCACHE_HDR         - define if multi-process access to registry
+ *  SELF_REPAIR         - undefine to skip header update on open
+ *  VERIFY_READ         - define TRUE to double-check short reads
+ *
+#define NOCACHE_HDR     1
+ */
+#define SELF_REPAIR     1
+#ifdef DEBUG
+#define VERIFY_READ     1
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef XP_UNIX
+#include <limits.h>
+#endif
+
+#ifdef STANDALONE_REGISTRY
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#else
+#include "prtypes.h"
+#include "prlog.h"
+#include "prerror.h"
+#include "prprf.h"
+#endif /*STANDALONE_REGISTRY*/
+
+#if defined(SUNOS4)
+#include <unistd.h>  /* for SEEK_SET */
+#endif /* SUNOS4 */
+
+#include "reg.h"
+#include "NSReg.h"
+
+#if defined(XP_MACOSX)
+#define MAX_PATH PATH_MAX
+#elif defined(XP_UNIX)
+#ifndef MAX_PATH
+#ifdef PATH_MAX
+#define MAX_PATH PATH_MAX
+#else
+#define MAX_PATH 1024
+#endif
+#endif
+#elif defined(XP_OS2)
+#ifndef MAX_PATH
+#define MAX_PATH 260
+#endif
+#elif defined(WIN32)
+#define MAX_PATH _MAX_PATH
+#elif defined(XP_BEOS)
+#include <limits.h>
+#define MAX_PATH PATH_MAX
+#endif
+
+ 
+ /* NOTE! It is EXREMELY important that node names be in UTF-8; otherwise
+ * backwards path search for delim char will fail for multi-byte/Unicode names
+ */
+
+/* ====================================================================
+ * Overview
+ * --------------------------------------------------------------------
+ *
+ *  Layers:
+ *      Interface
+ *          Path Parsing
+ *              Key/Entry Management
+ *                  Block I/O
+ *                      Virtual I/O
+ *
+ * The functions in this file search and add to a binary Registry file
+ * quite efficiently.  So efficiently, squeezing out space left by
+ * deleted and updated objects requires a separate "pack" operation.
+ *
+ * Terms:
+ * As used here, a 'key' is a node in the tree. The root of the tree
+ * exists in an otherwise empty Registry as is itself a key.  Every key
+ * has 0 or more sub-keys. Every key also has 0 or more 'entry's. Both
+ * entries and keys have names. Entries also have values associated.
+ * Names and values are simply strings of characters. These strings
+ * may be quoted so that they can include path delimiter and equals
+ * sign characters which are otherwise reserved.
+ * ====================================================================
+ */
+
+/* --------------------------------------------------------------------
+ * Module Global Data
+ *
+ * use of this data must be protected by the reglist lock
+ * --------------------------------------------------------------------
+ */
+
+#if !defined(STANDALONE_REGISTRY)
+static PRLock   *reglist_lock = NULL;
+#endif
+
+static REGFILE  *RegList = NULL;
+static int32    regStartCount = 0;
+char            *globalRegName = NULL;
+static char     *user_name = NULL;
+
+/* --------------------------------------------------------------------
+ * Registry List management
+ * --------------------------------------------------------------------
+ */
+static void nr_AddNode(REGFILE* pReg);
+static void nr_DeleteNode(REGFILE *pReg);
+static REGFILE* vr_findRegFile(const char *filename);
+
+/* -------------------------------------------------------------------- */
+
+static void nr_AddNode(REGFILE* pReg)
+{
+    /* add node to head of list */
+    pReg->next = RegList;
+    pReg->prev = NULL;
+
+    RegList = pReg;
+
+    if ( pReg->next != NULL ) {
+        pReg->next->prev = pReg;
+    }
+}
+
+static void nr_DeleteNode(REGFILE* pReg)
+{
+    /* if at head of list... */
+    if ( pReg->prev == NULL ) {
+        RegList = pReg->next;
+    }
+    else {
+        pReg->prev->next = pReg->next;
+    }
+
+    if ( pReg->next != NULL ) {
+        pReg->next->prev = pReg->prev;
+    }
+
+    /* free memory */
+#ifndef STANDALONE_REGISTRY
+    if ( pReg->lock != NULL )
+        PR_DestroyLock( pReg->lock );
+#endif
+    XP_FREEIF( pReg->filename );
+    XP_FREE( pReg );
+}
+
+static REGFILE* vr_findRegFile(const char *filename)
+{
+    REGFILE *pReg;
+
+    pReg = RegList;
+    while( pReg != NULL ) {
+#if defined(XP_UNIX) && !defined(XP_MACOSX) || defined XP_BEOS
+        if ( 0 == XP_STRCMP( filename, pReg->filename ) ) {
+#else
+        if ( 0 == XP_STRCASECMP( filename, pReg->filename ) ) {
+#endif
+            break;
+        }
+        pReg = pReg->next;
+    }
+
+    return pReg;
+}
+
+
+/* --------------------------------------------------------------------
+ * Virtual I/O
+ *  Platform-specifics go in this section
+ * --------------------------------------------------------------------
+ */
+static REGERR nr_OpenFile(const char *path, FILEHANDLE *fh);
+static REGERR nr_CloseFile(FILEHANDLE *fh); /* Note: fh is a pointer */
+static REGERR nr_ReadFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer);
+static REGERR nr_WriteFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer);
+static REGERR nr_LockRange(FILEHANDLE fh, REGOFF offset, int32 len);
+static REGERR nr_UnlockRange(FILEHANDLE fh, REGOFF offset, int32 len);
+static int32  nr_GetFileLength(FILEHANDLE fh);
+/* -------------------------------------------------------------------- */
+
+#ifdef STANDALONE_REGISTRY
+static REGERR nr_OpenFile(const char *path, FILEHANDLE *fh)
+{
+    XP_ASSERT( path != NULL );
+    XP_ASSERT( fh != NULL );
+
+    /* Open the file for exclusive random read/write */
+    (*fh) = vr_fileOpen(path, XP_FILE_UPDATE_BIN);
+    if ( !VALID_FILEHANDLE(*fh) )
+    {
+        switch (errno)
+        {
+        case ENOENT:    /* file not found */
+            return REGERR_NOFILE;
+
+        case EROFS:     /* read-only file system */
+        case EACCES:    /* file in use or read-only file*/
+            /* try read only */
+            (*fh) = vr_fileOpen(path, XP_FILE_READ_BIN);
+            if ( VALID_FILEHANDLE(*fh) )
+                return REGERR_READONLY;
+            else
+                return REGERR_FAIL;
+
+        default:
+            return REGERR_FAIL;
+        }
+    }
+
+    return REGERR_OK;
+
+}   /* OpenFile */
+#else
+static REGERR nr_OpenFile(const char *path, FILEHANDLE *fh)
+{
+    PR_ASSERT( path != NULL );
+    PR_ASSERT( fh != NULL );
+
+    /* Open the file for exclusive random read/write */
+    *fh = XP_FileOpen(path, XP_FILE_UPDATE_BIN);
+    if ( !VALID_FILEHANDLE(*fh) )
+    {
+        XP_StatStruct st;
+        if ( XP_Stat( path, &st ) != 0 )
+        {
+            /* file doesn't exist, so create */
+            *fh = XP_FileOpen(path, XP_FILE_TRUNCATE_BIN);
+        }
+    }
+
+    if ( !VALID_FILEHANDLE(*fh) )
+    {
+      /* For whatever reason we failed every attempt of getting */
+      /* a read/write registry. Let's try a read-only registry. */
+      (*fh) = XP_FileOpen(path, XP_FILE_READ_BIN);
+      if ( VALID_FILEHANDLE(*fh) )
+        return REGERR_READONLY;
+      else
+        /* we are in big trouble now */
+        return REGERR_FAIL;
+    }
+
+    /* succeded in getting a read/write registry */
+    return REGERR_OK;
+
+}   /* OpenFile */
+#endif
+
+
+static REGERR nr_CloseFile(FILEHANDLE *fh)
+{
+    /* NOTE: 'fh' is a pointer, unlike other Close functions
+     *       This is necessary so that nr_CloseFile can set it to NULL
+     */
+
+    XP_ASSERT( fh != NULL );
+    if ( VALID_FILEHANDLE(*fh) )
+        XP_FileClose(*fh);
+    (*fh) = NULL;
+    return REGERR_OK;
+
+}   /* CloseFile */
+
+
+
+static REGERR nr_ReadFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer)
+{
+#if VERIFY_READ
+    #define        FILLCHAR  0xCC
+    unsigned char* p;
+    unsigned char* dbgend = (unsigned char*)buffer+len;
+#endif
+
+    int32 readlen;
+    REGERR err = REGERR_OK;
+
+    XP_ASSERT(len > 0);
+    XP_ASSERT(buffer != NULL);
+    XP_ASSERT(fh != NULL);
+
+#if VERIFY_READ
+    XP_MEMSET(buffer, FILLCHAR, len);
+#endif
+
+    if (XP_FileSeek(fh, offset, SEEK_SET) != 0 ) {
+        err = REGERR_FAIL;
+    }
+    else {
+        readlen = XP_FileRead(buffer, len, fh );
+        /* PR_READ() returns an unreliable length, check EOF separately */
+        if (readlen < 0) {
+#if !defined(STANDALONE_REGISTRY) || (!defined(XP_MACOSX))
+    #if defined(STANDALONE_REGISTRY)
+            if (errno == EBADF) /* bad file handle, not open for read, etc. */
+    #else
+            if (PR_GetError() == PR_BAD_DESCRIPTOR_ERROR)
+    #endif
+                err = REGERR_FAIL;
+            else
+#endif
+                err = REGERR_BADREAD;
+        }
+        else if (readlen < len) {
+#if VERIFY_READ
+            /* PR_READ() says we hit EOF but return length is unreliable. */
+            /* If buffer has new data beyond what PR_READ() says it got */
+            /* we'll assume the read was OK--this is a gamble but */
+            /* missing errors will cause fewer problems than too many. */
+            p = (unsigned char*)buffer + readlen;
+            while ( (p < dbgend) && (*p == (unsigned char)FILLCHAR) ) {
+                p++;
+            }
+
+            /* really was EOF if it's all FILLCHAR's */
+            if ( p == dbgend ) {
+                err = REGERR_BADREAD;
+            }
+#else
+            err = REGERR_BADREAD;
+#endif
+        }
+    }
+
+    return err;
+
+}   /* ReadFile */
+
+
+
+static REGERR nr_WriteFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer)
+{
+
+    /* Note: 'offset' will commonly be the end of the file, in which
+     * case this function extends the file to 'offset'+'len'. This may
+     * be a two-step operation on some platforms.
+     */
+    XP_ASSERT(len > 0);
+    XP_ASSERT(buffer);
+    XP_ASSERT(fh != NULL);
+
+    if (XP_FileSeek(fh, offset, SEEK_SET) != 0)
+        return REGERR_FAIL;
+
+    if ((int32)XP_FileWrite(buffer, len, fh) != len)
+    {
+        /* disk full or some other catastrophic error */
+        return REGERR_FAIL;
+    }
+
+    return REGERR_OK;
+
+}   /* WriteFile */
+
+
+
+static REGERR nr_LockRange(FILEHANDLE fh, REGOFF offset, int32 len)
+{
+    /* TODO: Implement XP lock function with built-in retry. */
+
+    return REGERR_OK;
+
+}   /* LockRange */
+
+
+
+static REGERR nr_UnlockRange(FILEHANDLE fh, REGOFF offset, int32 len)
+{
+    /* TODO: Implement XP unlock function with built-in retry. */
+
+    return REGERR_OK;
+
+}   /* UnlockRange */
+
+
+
+#if SELF_REPAIR
+static int32 nr_GetFileLength(FILEHANDLE fh)
+{
+    int32 length;
+    int32 curpos;
+
+    curpos = XP_FileTell(fh);
+    XP_FileSeek(fh, 0, SEEK_END);
+    length = XP_FileTell(fh);
+    XP_FileSeek(fh, curpos, SEEK_SET);
+    return length;
+
+}   /* GetFileLength */
+#endif
+
+
+
+/* --------------------------------------------------------------------
+ * Numeric converters
+ * --------------------------------------------------------------------
+ * The converters read and write integers in a common format so we
+ * can transport registries without worrying about endian problems.
+ *
+ * The buffers *MUST* be the appropriate size!
+ * --------------------------------------------------------------------
+ */
+static uint32 nr_ReadLong(char *buffer);
+static uint16 nr_ReadShort(char *buffer);
+static void   nr_WriteLong(uint32 num, char *buffer);
+static void   nr_WriteShort(uint16 num, char *buffer);
+/* -------------------------------------------------------------------- */
+
+
+
+static uint16 nr_ReadShort(char *buffer)
+{
+    uint16 val;
+    uint8 *p = (uint8*)buffer;
+ 
+    val = (uint16)(*p + (uint16)( *(p+1) * 0x100 ));
+
+    return val;
+}
+
+
+
+static uint32 nr_ReadLong(char *buffer)
+{
+    uint32 val;
+    uint8 *p = (uint8*)buffer;
+
+    val = *p
+        + (uint32)(*(p+1) * 0x100L)
+        + (uint32)(*(p+2) * 0x10000L )
+        + (uint32)(*(p+3) * 0x1000000L );
+
+    return val;
+}
+
+
+
+static void  nr_WriteLong(uint32 num, char *buffer)
+{
+    uint8 *p = (uint8*)buffer;
+    *p++ = (uint8)(num & 0x000000FF);
+    num /= 0x100;
+    *p++ = (uint8)(num & 0x000000FF);
+    num /= 0x100;
+    *p++ = (uint8)(num & 0x000000FF);
+    num /= 0x100;
+    *p   = (uint8)(num & 0x000000FF);
+}
+
+
+
+static void  nr_WriteShort(uint16 num, char *buffer)
+{
+    uint8 *p = (uint8*)buffer;
+
+    *p = (uint8)(num & 0x00FF);
+    *(p+1) = (uint8)(num / 0x100);
+}
+
+
+
+/* --------------------------------------------------------------------
+ * Block I/O
+ * --------------------------------------------------------------------
+ */
+static REGERR nr_ReadHdr(REGFILE *reg); /* Reads the file header, creates file if empty */
+static REGERR nr_WriteHdr(REGFILE *reg);    /* Writes the file header */
+static REGERR nr_CreateRoot(REGFILE *reg);
+
+static REGERR nr_Lock(REGFILE *reg);
+static REGERR nr_Unlock(REGFILE *reg);
+
+static REGERR nr_ReadDesc(REGFILE *reg, REGOFF offset, REGDESC *desc);      /* reads a desc */
+static REGERR nr_ReadName(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf);
+static REGERR nr_ReadData(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf);
+
+static REGERR nr_WriteDesc(REGFILE *reg, REGDESC *desc);                    /* writes a desc */
+static REGERR nr_WriteString(REGFILE *reg, char *string, REGDESC *desc);    /* writes a string */
+static REGERR nr_WriteData(REGFILE *reg, char *string, uint32 len, REGDESC *desc);  /* writes a string */
+
+static REGERR nr_AppendDesc(REGFILE *reg, REGDESC *desc, REGOFF *result);   /* adds a desc */
+static REGERR nr_AppendName(REGFILE *reg, char *name, REGDESC *desc);       /* adds a name */
+static REGERR nr_AppendString(REGFILE *reg, char *string, REGDESC *desc);   /* adds a string */
+static REGERR nr_AppendData(REGFILE *reg, char *string, uint32 len, REGDESC *desc); /* adds a string */
+
+static XP_Bool nr_IsValidUTF8(char *string);    /* checks if a string is UTF-8 encoded */
+/* -------------------------------------------------------------------- */
+
+
+
+static REGERR nr_ReadHdr(REGFILE *reg)
+{
+
+    int err;
+    long filelength;
+    char hdrBuf[sizeof(REGHDR)];
+
+    XP_ASSERT(reg);
+    reg->hdrDirty = 0;
+
+    err = nr_ReadFile(reg->fh, 0, sizeof(REGHDR), &hdrBuf);
+
+    switch (err)
+    {
+    case REGERR_BADREAD:
+        /* header doesn't exist, so create one */
+        err = nr_CreateRoot(reg);
+        break;
+
+    case REGERR_OK:
+        /* header read successfully -- convert */
+        reg->hdr.magic    = nr_ReadLong ( hdrBuf + HDR_MAGIC );
+        reg->hdr.verMajor = nr_ReadShort( hdrBuf + HDR_VERMAJOR );
+        reg->hdr.verMinor = nr_ReadShort( hdrBuf + HDR_VERMINOR );
+        reg->hdr.avail    = nr_ReadLong ( hdrBuf + HDR_AVAIL );
+        reg->hdr.root     = nr_ReadLong ( hdrBuf + HDR_ROOT );
+
+        /* check to see if it's the right file type */
+        if (reg->hdr.magic != MAGIC_NUMBER) {
+            err = REGERR_BADMAGIC;
+            break;
+        }
+
+        /* Check registry version
+         * If the major version is bumped we're incompatible
+         * (minor version just means some new features were added)
+         *
+         * Upgrade code will go here in the future...
+         */
+        if ( reg->hdr.verMajor > MAJOR_VERSION ) {
+            err = REGERR_REGVERSION;
+            break;
+        }
+
+#if SELF_REPAIR
+        if ( reg->inInit && !(reg->readOnly) ) {
+            filelength = nr_GetFileLength(reg->fh);
+            if (reg->hdr.avail != filelength)
+            {
+                reg->hdr.avail = filelength;
+                reg->hdrDirty = 1;
+#if NOCACHE_HDR
+                err = nr_WriteHdr(reg);
+#endif
+            }
+        }
+#endif  /* SELF_REPAIR */
+        break;
+
+    default:
+        /* unexpected error from nr_ReadFile()*/
+        XP_ASSERT(FALSE);
+        err = REGERR_FAIL;
+        break;
+    }   /* switch */
+
+    return err;
+
+}   /* ReadHdr */
+
+
+
+static REGERR nr_WriteHdr(REGFILE *reg)
+{
+    REGERR err;
+    char hdrBuf[sizeof(REGHDR)];
+
+    XP_ASSERT(reg);
+
+    if (reg->readOnly)
+        return REGERR_READONLY;
+
+    /* convert to XP int format */
+    nr_WriteLong ( reg->hdr.magic,    hdrBuf + HDR_MAGIC );
+    nr_WriteShort( reg->hdr.verMajor, hdrBuf + HDR_VERMAJOR );
+    nr_WriteShort( reg->hdr.verMinor, hdrBuf + HDR_VERMINOR );
+    nr_WriteLong ( reg->hdr.avail,    hdrBuf + HDR_AVAIL );
+    nr_WriteLong ( reg->hdr.root,     hdrBuf + HDR_ROOT );
+
+    /* err = nr_WriteFile(reg->fh, 0, sizeof(REGHDR), &reg->hdr); */
+    err = nr_WriteFile(reg->fh, 0, sizeof(hdrBuf), &hdrBuf);
+
+    if (err == REGERR_OK)
+        reg->hdrDirty = 0;
+
+    return err;
+
+}   /* WriteHdr */
+
+
+
+static REGERR nr_CreateRoot(REGFILE *reg)
+{
+    /* Called when an empty file is detected by ReadHdr */
+    REGERR err;
+    REGDESC root;
+
+    XP_ASSERT(reg);
+
+    /* Create 'hdr' */
+    reg->hdr.magic      = MAGIC_NUMBER;
+    reg->hdr.verMajor   = MAJOR_VERSION;
+    reg->hdr.verMinor   = MINOR_VERSION;
+    reg->hdr.root       = 0;
+    reg->hdr.avail      = HDRRESERVE;
+
+    /* Create root descriptor */
+    root.location   = 0;
+    root.left       = 0;
+    root.value      = 0;
+    root.down       = 0;
+    root.type       = REGTYPE_KEY;
+    root.valuelen   = 0;
+    root.valuebuf   = 0;
+    root.parent     = 0;
+
+    err = nr_AppendName(reg, ROOTKEY_STR, &root);
+    if (err != REGERR_OK)
+        return err;
+
+    err = nr_AppendDesc(reg, &root, &reg->hdr.root);
+    if (err != REGERR_OK)
+        return err;
+
+    return nr_WriteHdr(reg);    /* actually commit to disk */
+
+    /* Create standard top-level nodes */
+
+}   /* CreateRoot */
+
+
+
+static REGERR nr_Lock(REGFILE *reg)
+{
+    REGERR status;
+
+    /* lock file */
+    status = nr_LockRange(reg->fh, 0, sizeof(REGHDR));
+
+    if (status == REGERR_OK)
+    {
+        /* lock the object */
+        PR_Lock( reg->lock );
+
+#if NOCACHE_HDR
+        /* try to refresh header info */
+        status = nr_ReadHdr(reg);
+        if ( status != REGERR_OK ) {
+            PR_Unlock( reg->lock );
+        }
+#endif
+    }
+
+    return status;
+}   /* Lock */
+
+
+
+static REGERR nr_Unlock(REGFILE *reg)
+{
+    PR_Unlock( reg->lock );
+
+    return nr_UnlockRange(reg->fh, 0, sizeof(REGHDR));
+}   /* Unlock */
+
+
+
+static REGERR nr_ReadDesc(REGFILE *reg, REGOFF offset, REGDESC *desc)
+{
+
+    REGERR err;
+    char descBuf[ DESC_SIZE ];
+
+    XP_ASSERT(reg);
+    XP_ASSERT(offset >= HDRRESERVE);
+    XP_ASSERT(offset < reg->hdr.avail);
+    XP_ASSERT(desc);
+
+    err = nr_ReadFile(reg->fh, offset, DESC_SIZE, &descBuf);
+    if (err == REGERR_OK)
+    {
+        desc->location  = nr_ReadLong ( descBuf + DESC_LOCATION );
+        desc->name      = nr_ReadLong ( descBuf + DESC_NAME );
+        desc->namelen   = nr_ReadShort( descBuf + DESC_NAMELEN );
+        desc->type      = nr_ReadShort( descBuf + DESC_TYPE );
+        desc->left      = nr_ReadLong ( descBuf + DESC_LEFT );
+        desc->value     = nr_ReadLong ( descBuf + DESC_VALUE );
+        desc->valuelen  = nr_ReadLong ( descBuf + DESC_VALUELEN );
+        desc->parent    = nr_ReadLong ( descBuf + DESC_PARENT );
+
+        if ( TYPE_IS_ENTRY(desc->type) ) {
+            desc->down = 0;
+            desc->valuebuf  = nr_ReadLong( descBuf + DESC_VALUEBUF );
+        }
+        else {  /* TYPE is KEY */
+            desc->down      = nr_ReadLong( descBuf + DESC_DOWN );
+            desc->valuebuf  = 0;
+        }
+
+        if (desc->location != offset)
+            err = REGERR_BADLOCN;
+        else if ( desc->type & REGTYPE_DELETED )
+            err = REGERR_DELETED;
+    }
+
+    return err;
+
+}   /* ReadDesc */
+
+
+
+static REGERR nr_ReadName(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf)
+{
+
+    REGERR err;
+
+    XP_ASSERT(reg);
+    XP_ASSERT(desc->name > 0);
+    XP_ASSERT(desc->name < reg->hdr.avail);
+    XP_ASSERT(buflen > 0);
+    XP_ASSERT(buf);
+
+    if ( desc->namelen > buflen )
+        return REGERR_BUFTOOSMALL;
+
+    err = nr_ReadFile(reg->fh, desc->name, desc->namelen, buf);
+
+    buf[buflen-1] = '\0';   /* avoid runaways */
+
+    return err;
+
+}   /* ReadName */
+
+
+
+static REGERR nr_ReadData(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf)
+{
+
+    REGERR err;
+
+    XP_ASSERT(reg);
+    XP_ASSERT(desc->value > 0);
+    XP_ASSERT(desc->value < reg->hdr.avail);
+    XP_ASSERT(buflen > 0);
+    XP_ASSERT(buf);
+
+    if ( desc->valuelen > buflen )
+        return REGERR_BUFTOOSMALL;
+
+    err = nr_ReadFile(reg->fh, desc->value, desc->valuelen, buf);
+
+    return err;
+
+}   /* nr_ReadData */
+
+
+
+static REGERR nr_WriteDesc(REGFILE *reg, REGDESC *desc)
+{
+    char descBuf[ DESC_SIZE ];
+
+    XP_ASSERT(reg);
+    XP_ASSERT(desc);
+    XP_ASSERT( desc->location >= HDRRESERVE );
+    XP_ASSERT( desc->location < reg->hdr.avail );
+
+    if (reg->readOnly)
+        return REGERR_READONLY;
+
+    /* convert to XP int format */
+    nr_WriteLong ( desc->location,  descBuf + DESC_LOCATION );
+    nr_WriteLong ( desc->name,      descBuf + DESC_NAME );
+    nr_WriteShort( desc->namelen,   descBuf + DESC_NAMELEN );
+    nr_WriteShort( desc->type,      descBuf + DESC_TYPE );
+    nr_WriteLong ( desc->left,      descBuf + DESC_LEFT );
+    nr_WriteLong ( desc->value,     descBuf + DESC_VALUE );
+    nr_WriteLong ( desc->valuelen,  descBuf + DESC_VALUELEN );
+    nr_WriteLong ( desc->parent,    descBuf + DESC_PARENT );
+
+    if ( TYPE_IS_ENTRY(desc->type) ) {
+        XP_ASSERT( 0 == desc->down );
+        nr_WriteLong( desc->valuebuf,  descBuf + DESC_VALUEBUF );
+    }
+    else {  /* TYPE is KEY */
+        XP_ASSERT( 0 == desc->valuebuf );
+        nr_WriteLong( desc->down,      descBuf + DESC_DOWN );
+    }
+
+    return nr_WriteFile(reg->fh, desc->location, DESC_SIZE, descBuf);
+}   /* nr_WriteDesc */
+
+
+
+static REGERR nr_AppendDesc(REGFILE *reg, REGDESC *desc, REGOFF *result)
+{
+
+    REGERR err;
+    char descBuf[ DESC_SIZE ];
+
+    XP_ASSERT(reg);
+    XP_ASSERT(desc);
+    XP_ASSERT(result);
+
+    *result = 0;
+
+    if (reg->readOnly)
+        return REGERR_READONLY;
+
+    desc->location = reg->hdr.avail;
+
+    /* convert to XP int format */
+    nr_WriteLong ( desc->location,  descBuf + DESC_LOCATION );
+    nr_WriteLong ( desc->name,      descBuf + DESC_NAME );
+    nr_WriteShort( desc->namelen,   descBuf + DESC_NAMELEN );
+    nr_WriteShort( desc->type,      descBuf + DESC_TYPE );
+    nr_WriteLong ( desc->left,      descBuf + DESC_LEFT );
+    nr_WriteLong ( desc->value,     descBuf + DESC_VALUE );
+    nr_WriteLong ( desc->valuelen,  descBuf + DESC_VALUELEN );
+    nr_WriteLong ( desc->parent,    descBuf + DESC_PARENT );
+
+    if ( TYPE_IS_ENTRY(desc->type) ) {
+        XP_ASSERT( 0 == desc->down );
+        nr_WriteLong( desc->valuebuf,  descBuf + DESC_VALUEBUF );
+    }
+    else {  /* TYPE is KEY */
+        XP_ASSERT( 0 == desc->valuebuf );
+        nr_WriteLong( desc->down,      descBuf + DESC_DOWN );
+    }
+
+    err = nr_WriteFile(reg->fh, reg->hdr.avail, DESC_SIZE, descBuf);
+
+    if (err == REGERR_OK)
+    {
+        *result = reg->hdr.avail;
+        reg->hdr.avail += DESC_SIZE;
+        reg->hdrDirty = 1;
+#if NOCACHE_HDR
+        err = nr_WriteHdr(reg);
+#endif
+    }
+
+    return err;
+
+}   /* AppendDesc */
+
+
+
+static REGERR nr_AppendName(REGFILE *reg, char *name, REGDESC *desc)
+{
+    REGERR err;
+    int len;
+    char *p;
+
+    XP_ASSERT(reg);
+    XP_ASSERT(name);
+    XP_ASSERT(desc);
+
+    if (!nr_IsValidUTF8(name))
+        return REGERR_BADUTF8;
+    if (reg->readOnly)
+        return REGERR_READONLY;
+
+    len = XP_STRLEN(name) + 1;
+
+    /* check for valid name parameter */
+    if ( len == 1 )
+        return REGERR_PARAM;
+
+    if ( len > MAXREGNAMELEN )
+        return REGERR_NAMETOOLONG;
+
+    for ( p = name; (*p != 0); p++ ) {
+        if ( INVALID_NAME_CHAR(*p) )
+            return REGERR_BADNAME;
+    }
+
+    /* save the name */
+    err = nr_WriteFile(reg->fh, reg->hdr.avail, len, name);
+
+    /* if write successful update the desc and hdr */
+    if (err == REGERR_OK)
+    {
+        desc->namelen = (uint16)len;
+        desc->name = reg->hdr.avail;
+        reg->hdr.avail += len;
+        reg->hdrDirty = 1;
+#if NOCACHE_HDR
+        err = nr_WriteHdr(reg);
+#endif
+    }
+
+    return err;
+
+}   /* nr_AppendName */
+
+
+
+static REGERR nr_WriteString(REGFILE *reg, char *string, REGDESC *desc)
+{
+    uint32 len;
+
+    XP_ASSERT(string);
+    if (!nr_IsValidUTF8(string))
+        return REGERR_BADUTF8;
+    if (reg->readOnly)
+        return REGERR_READONLY;
+    len = XP_STRLEN(string) + 1;
+
+    return nr_WriteData( reg, string, len, desc );
+
+}   /* nr_WriteString */
+
+
+
+static REGERR nr_WriteData(REGFILE *reg, char *string, uint32 len, REGDESC *desc)
+{
+    REGERR err;
+
+    XP_ASSERT(reg);
+    XP_ASSERT(string);
+    XP_ASSERT(desc);
+
+    if (reg->readOnly)
+        return REGERR_READONLY;
+
+    if ( len == 0 )
+        return REGERR_PARAM;
+
+    if ( len > MAXREGVALUELEN )
+        return REGERR_NAMETOOLONG;
+
+    /* save the data in the same place if it fits */
+    if ( len <= desc->valuebuf ) {
+        err = nr_WriteFile( reg->fh, desc->value, len, string );
+        if ( err == REGERR_OK ) {
+            desc->valuelen = len;
+        }
+    }
+    else {
+        /* otherwise append new data */
+        err = nr_AppendData( reg, string, len, desc );
+    }
+
+    return err;
+
+}   /* nr_WriteData */
+
+
+
+static REGERR nr_AppendString(REGFILE *reg, char *string, REGDESC *desc)
+{
+    uint32 len;
+
+    XP_ASSERT(string);
+    if (!nr_IsValidUTF8(string))
+        return REGERR_BADUTF8;
+    if (reg->readOnly)
+        return REGERR_READONLY;
+    len = XP_STRLEN(string) + 1;
+
+    return nr_AppendData( reg, string, len, desc );
+
+}   /* nr_AppendString */
+
+
+
+static REGERR nr_AppendData(REGFILE *reg, char *string, uint32 len, REGDESC *desc)
+{
+    REGERR err;
+
+    XP_ASSERT(reg);
+    XP_ASSERT(string);
+    XP_ASSERT(desc);
+
+    if (reg->readOnly)
+        return REGERR_READONLY;
+
+    if ( len == 0 )
+        return REGERR_PARAM;
+
+    if ( len > MAXREGVALUELEN )
+        return REGERR_NAMETOOLONG;
+
+    /* save the string */
+    err = nr_WriteFile(reg->fh, reg->hdr.avail, len, string);
+    if (err == REGERR_OK)
+    {
+        desc->value     = reg->hdr.avail;
+        desc->valuelen  = len;
+        desc->valuebuf  = len;
+
+        reg->hdr.avail += len;
+        reg->hdrDirty   = 1;
+#if NOCACHE_HDR
+        err = nr_WriteHdr(reg);
+#endif
+    }
+
+    return err;
+
+}   /* nr_AppendData */
+
+static XP_Bool nr_IsValidUTF8(char *string)
+{
+    int follow = 0;
+    char *c;
+    unsigned char ch;
+
+    XP_ASSERT(string);
+    if ( !string )
+        return FALSE;
+
+    for ( c = string; *c != '\0'; c++ )
+    {
+        ch = (unsigned char)*c;
+        if( follow == 0 )
+        {
+            /* expecting an initial byte */
+            if ( ch <= 0x7F )
+            {
+                /* standard byte -- do nothing */
+            }
+            else if ((0xC0 & ch) == 0x80)
+            {
+                /* follow byte illegal here */
+                return FALSE;
+            }
+            else if ((0xE0 & ch) == 0xC0)
+            {
+                follow = 1;
+            }
+            else if ((0xF0 & ch) == 0xE0)
+            {
+                follow = 2;
+            }
+            else
+            { 
+                /* unexpected (unsupported) initial byte */
+                return FALSE;
+            }
+        }
+        else 
+        {
+            XP_ASSERT( follow > 0 );
+            if ((0xC0 & ch) == 0x80)
+            {
+                /* expecting follow byte and found one */
+                follow--;
+            }
+            else 
+            {
+                /* invalid state */
+                return FALSE;
+            }
+        }
+    } /* for */
+
+    if ( follow != 0 )
+    {
+        /* invalid state -- interrupted character */
+        return FALSE;
+    }
+    
+    return TRUE;
+}   /* checks if a string is UTF-8 encoded */
+
+/* --------------------------------------------------------------------
+ * Path Parsing
+ * --------------------------------------------------------------------
+ */
+static REGERR nr_NextName(const char *pPath, char *buf, uint32 bufsize, const char **newPath);
+static REGERR nr_RemoveName(char *path);
+static REGERR nr_CatName(REGFILE *reg, REGOFF node, char *path, uint32 bufsize,
+                    REGDESC *desc);
+static REGERR nr_ReplaceName(REGFILE *reg, REGOFF node, char *path,
+                    uint32 bufsize, REGDESC *desc);
+/* -------------------------------------------------------------------- */
+
+
+/* Scans path at 'pPath' and copies next name segment into 'buf'.
+ * Also sets 'newPath' to point at the next segment of pPath.
+ */
+static REGERR nr_NextName(const char *pPath, char *buf, uint32 bufsize, const char **newPath)
+{
+    uint32 len = 0;
+    REGERR err = REGERR_OK;
+
+    /* initialization and validation */
+    XP_ASSERT(buf);
+
+    *newPath = NULL;
+    *buf = '\0';
+
+    if ( pPath==NULL || *pPath=='\0' )
+        return REGERR_NOMORE;
+
+    /* ... skip an initial path delimiter */
+    if ( *pPath == PATHDEL ) {
+        pPath++;
+
+        if ( *pPath == '\0' )
+            return REGERR_NOMORE;
+    }
+
+    /* ... missing name segment or initial blank are errors*/
+    if ( *pPath == PATHDEL || *pPath == ' ' )
+        return REGERR_BADNAME;
+
+    /* copy first path segment into return buf */
+    while ( *pPath != '\0' && *pPath != PATHDEL )
+    {
+        if ( len == bufsize ) {
+            err = REGERR_NAMETOOLONG;
+            break;
+        }
+        if ( *pPath < ' ' && *pPath > 0 )
+            return REGERR_BADNAME;
+
+        *buf++ = *pPath++;
+        len++;
+    }
+    *buf = '\0';
+
+    /* ... name segment can't end with blanks, either */
+    if ( ' ' == *(buf-1) )
+        return REGERR_BADNAME;
+
+    /* return a pointer to the start of the next segment */
+    *newPath = pPath;
+
+    return err;
+
+}   /* nr_NextName */
+
+
+
+
+static REGERR nr_CatName(REGFILE *reg, REGOFF node, char *path, uint32 bufsize, REGDESC *desc)
+{
+    REGERR err = REGERR_OK;
+
+    char   *p;
+    uint32 len = XP_STRLEN(path);
+
+    if (len > 0)
+    {
+        p = &path[len-1];
+        if (*p != PATHDEL)
+        {
+            if ( len < bufsize ) {
+                p++;
+                *p = PATHDEL;
+                len++;
+            }
+            else
+                err = REGERR_BUFTOOSMALL;
+        }
+        p++;    /* point one past PATHDEL */
+    }
+    else
+        p = path;
+
+    if ( err == REGERR_OK ) {
+        err = nr_ReadDesc( reg, node, desc );
+        if ( err == REGERR_OK ) {
+            err = nr_ReadName( reg, desc, bufsize-len, p );
+        }
+    }
+
+    return err;
+
+}   /* CatName */
+
+
+
+static REGERR nr_ReplaceName(REGFILE *reg, REGOFF node, char *path, uint32 bufsize, REGDESC *desc)
+{
+    /* NOTE! It is EXREMELY important that names be in UTF-8; otherwise
+     * the backwards path search will fail for multi-byte/Unicode names
+     */
+
+    char   *p;
+    uint32 len;
+    REGERR err;
+
+    XP_ASSERT(path);
+
+    len = XP_STRLEN(path);
+    if ( len > bufsize )
+        return REGERR_PARAM;
+
+    if ( len > 0 ) {
+        p = &path[len-1];
+
+        while ((p > path) && (*p != PATHDEL)) {
+            --p;
+            --len;
+        }
+        if ( *p == PATHDEL ) {
+            p++; 
+            len++;
+        }
+    }
+    else
+        p = path;
+
+
+    err = nr_ReadDesc( reg, node, desc );
+    if ( err == REGERR_OK ) {
+        err = nr_ReadName( reg, desc, bufsize-len, p );
+    }
+
+    return err;
+
+}   /* ReplaceName */
+
+
+static REGERR nr_RemoveName(char *path)
+{
+    /* Typical inputs:
+     * path = "/Machine/4.0/"   output = "/Machine"
+     * path = "/Machine"        output = ""
+     * path = ""                output = REGERR_NOMORE
+     *
+     * NOTE! It is EXREMELY important that names be in UTF-8; otherwise
+     * the backwards path search will fail for multi-byte/Unicode names
+     */
+
+    int len = XP_STRLEN(path);
+    char *p;
+    if (len < 1)
+        return REGERR_NOMORE;
+
+    p = &path[len-1];
+    /* if last char is '/', ignore it */
+    if (*p == PATHDEL)
+        p--;
+
+    while ((p > path) && (*p != PATHDEL))
+        p--;
+
+/*  if (*p != PATHDEL)
+        return REGERR_NOMORE;
+*/
+
+    *p = '\0';
+    return REGERR_OK;
+
+}   /* RemoveName */
+
+
+
+/* --------------------------------------------------------------------
+ * Key/Entry Management
+ * --------------------------------------------------------------------
+ */
+static REGERR nr_Find(REGFILE *reg, REGOFF offParent, const char *pPath,
+    REGDESC *pDesc, REGOFF *pPrev, REGOFF *pParent, XP_Bool raw);
+
+static REGERR nr_FindAtLevel(REGFILE *reg, REGOFF offFirst, const char *pName,
+    REGDESC *pDesc, REGOFF *pOffPrev);
+
+static REGERR nr_CreateSubKey(REGFILE *reg, REGOFF parent, REGDESC *pDesc,
+                              char *name);
+static REGERR nr_CreateEntryString(REGFILE *reg, REGDESC *pParent, 
+    char *name, char *value);
+static REGERR nr_CreateEntry(REGFILE *reg, REGDESC *pParent, char *name,
+    uint16 type, char *buffer, uint32 length);
+/* -------------------------------------------------------------------- */
+
+
+
+static REGERR nr_Find(REGFILE *reg,
+            REGOFF offParent,
+            const char *pPath,
+            REGDESC *pDesc,
+            REGOFF *pPrev,
+            REGOFF *pParent,
+            XP_Bool raw)
+{
+
+    REGERR  err;
+    REGDESC desc;
+    REGOFF  offPrev = 0;
+    char    namebuf[MAXREGNAMELEN];
+    const char    *p;
+
+    XP_ASSERT( pPath != NULL );
+    XP_ASSERT( offParent >= HDRRESERVE );
+    XP_ASSERT( VALID_FILEHANDLE( reg->fh ) );
+
+    if (pPrev)
+        *pPrev = 0;
+    if (pParent)
+        *pParent = 0;
+
+    /* read starting desc */
+    err = nr_ReadDesc( reg, offParent, &desc);
+
+    if (raw == TRUE) {
+        if ( err == REGERR_OK ) {
+            /* save current location as parent of next segment */
+            offParent = desc.location;
+            /* look for name at next level down */
+            err = nr_FindAtLevel(reg, desc.down, pPath, &desc, &offPrev);
+        }
+    }
+    else {
+        /* Walk 'path', reading keys into 'desc' */
+        p = pPath;
+        while ( err == REGERR_OK ) 
+        {
+            err = nr_NextName(p, namebuf, sizeof(namebuf), &p);
+
+            if ( err == REGERR_OK ) {
+                /* save current location as parent of next segment */
+                offParent = desc.location;
+                /* look for name at next level down */
+                err = nr_FindAtLevel(reg, desc.down, namebuf, &desc, &offPrev);
+            }
+        }
+    }
+
+    if ( (raw == FALSE && err == REGERR_NOMORE) ||
+            (raw == TRUE && err == REGERR_OK) ) {
+        /* we found all the segments of the path--success! */
+        err = REGERR_OK;
+
+        if (pDesc) {
+            COPYDESC(pDesc, &desc);
+        }
+        if (pPrev) {
+            *pPrev = offPrev;
+        }
+        if (pParent) {
+            *pParent = offParent;
+        }
+    }
+    
+    return err;
+
+}   /* nr_Find */
+
+
+
+
+/* nr_FindAtLevel -- looks for a node matching "pName" on the level starting
+ *                   with "offset".  Returns REGERR_OK if found, REGERR_NOFIND
+ *                   if not (plus other error conditions).
+ *
+ *                   If pDesc and pOffPrev are valid pointers *AND* the name is
+ *                   found then pDesc will point at the REGDESC of the node and
+ *                   pOffPrev will be the offset of the desc for the previous
+ *                   node at the same level.  
+ *
+ *                   If the node is *NOT* found (REGERR_NOFIND is returned)
+ *                   pDesc will point at the REGDESC of the last found node
+ *                   (as will pOffPrev). If some other error is returned then
+ *                   THese values must not be used.
+ */
+static REGERR nr_FindAtLevel(REGFILE *reg,
+                             REGOFF offset,
+                             const char *pName,
+                             REGDESC *pDesc,
+                             REGOFF *pOffPrev)
+{
+    char    namebuf[MAXREGNAMELEN];
+    REGDESC desc;
+    REGERR  err;
+    REGOFF  prev = 0;
+
+    /* Note: offset=0 when there's no 'down' or 'left' */
+    XP_ASSERT(reg);
+    XP_ASSERT(offset < reg->hdr.avail);
+    XP_ASSERT(pName);
+    XP_ASSERT(*pName);
+
+    while ( offset != 0 )
+    {
+        /* get name of next node */
+        err = nr_ReadDesc(reg, offset, &desc);
+        if (err != REGERR_OK)
+            return err;
+
+        err = nr_ReadName(reg, &desc, sizeof(namebuf), namebuf);
+        if (err != REGERR_OK)
+            return err;
+
+        /* check to see if it's the one we want */
+        if (XP_STRCMP(namebuf, pName) == 0) {
+            /* Found it! Signaled by non-zero offset */
+            break;
+        }
+
+        /* advance to the next node */
+        prev = offset;
+        offset = desc.left;
+    }
+
+    if ( pDesc != NULL && (prev || offset)) {
+        /* prev and offset BOTH null means we never loaded a desc */
+        COPYDESC( pDesc, &desc );
+    }
+    if ( pOffPrev != NULL ) {
+        *pOffPrev = prev;
+    }
+
+    if ( offset != 0 ) /* if we found one */
+        return REGERR_OK;
+    else
+        return REGERR_NOFIND;
+}   /* FindAtLevel */
+
+
+
+static REGERR nr_CreateSubKey(REGFILE *reg,
+                              REGOFF parent,
+                              REGDESC *pDesc,
+                              char *name)
+{
+    /* nr_CreateSubKey does NO error checking--callers *MUST*
+     * ensure that there are no duplicates
+     */
+    REGDESC desc;
+    REGERR err;
+
+    XP_ASSERT(reg);
+    XP_ASSERT(pDesc);
+    XP_ASSERT(name);
+
+    err = nr_AppendName(reg, name, &desc);
+    if (err != REGERR_OK)
+        return err;
+
+    desc.type = REGTYPE_KEY;
+    desc.left = 0;
+    desc.down = 0;
+    desc.value = 0;
+    desc.valuelen = 0;
+    desc.valuebuf = 0;
+    desc.parent   = parent;
+
+    if ( parent == pDesc->location ) {
+        /* It's a parent desc, so no siblings */
+        err = nr_AppendDesc(reg, &desc, &pDesc->down);
+    }
+    else {
+        /* It's a sibling desc */
+        XP_ASSERT( pDesc->left == 0 ); /* not the end of chain! */
+        err = nr_AppendDesc(reg, &desc, &pDesc->left);
+    }
+    if (err != REGERR_OK)
+        return err;
+
+    /* write out the fixed up parent/sibling desc */
+    err = nr_WriteDesc(reg, pDesc);
+    COPYDESC(pDesc, &desc);
+
+    return err;
+
+}   /* nr_CreateSubKey */
+
+
+
+static REGERR nr_CreateEntryString(REGFILE *reg, REGDESC *pParent, char *name, char *value)
+{
+    REGDESC desc;
+    REGERR  err;
+
+    XP_ASSERT(reg);
+    XP_ASSERT(pParent);
+    XP_ASSERT(name);
+    XP_ASSERT(value);
+
+    XP_MEMSET( &desc, 0, sizeof(REGDESC) );
+
+    err = nr_AppendName(reg, name, &desc);
+    if (err != REGERR_OK)
+        return err;
+
+    err = nr_AppendString(reg, value, &desc);
+    if (err != REGERR_OK)
+        return err;
+
+    desc.type = REGTYPE_ENTRY_STRING_UTF;
+    desc.left = pParent->value;
+    desc.down = 0;
+    desc.parent = pParent->location;
+
+    err = nr_AppendDesc(reg, &desc, &pParent->value);
+    if (err != REGERR_OK)
+        return err;
+
+    /* printf("nr_AddEntry: %s=%s @0x%lx\n", name, value, pParent->value); */
+
+    return nr_WriteDesc(reg, pParent);
+
+}   /* nr_CreateEntryString */
+
+
+
+static REGERR nr_CreateEntry(REGFILE *reg, REGDESC *pParent, char *name,
+    uint16 type, char *value, uint32 length)
+{
+    REGDESC desc;
+    REGERR  err;
+
+    XP_ASSERT(reg);
+    XP_ASSERT(pParent);
+    XP_ASSERT(name);
+    XP_ASSERT(value);
+
+    XP_MEMSET( &desc, 0, sizeof(REGDESC) );
+
+    err = nr_AppendName(reg, name, &desc);
+    if (err != REGERR_OK)
+        return err;
+
+    err = nr_AppendData(reg, value, length, &desc);
+    if (err != REGERR_OK)
+        return err;
+
+    desc.type = type;
+    desc.left = pParent->value;
+    desc.down = 0;
+    desc.parent = pParent->location;
+
+    err = nr_AppendDesc(reg, &desc, &pParent->value);
+    if (err != REGERR_OK)
+        return err;
+
+    /* printf("nr_AddEntry: %s=%s @0x%lx\n", name, value, pParent->value); */
+
+    return nr_WriteDesc(reg, pParent);
+
+}   /* nr_CreateEntry */
+
+
+
+
+/* ---------------------------------------------------------------------
+ * Intermediate API
+ * ---------------------------------------------------------------------
+ */
+static REGOFF  nr_TranslateKey( REGFILE *reg, RKEY key );
+static REGERR  nr_InitStdRkeys( REGFILE *reg );
+static XP_Bool nr_ProtectedNode( REGFILE *reg, REGOFF key );
+static REGERR  nr_RegAddKey( REGFILE *reg, RKEY key, char *path, RKEY *newKey, XP_Bool raw );
+static REGERR  nr_RegDeleteKey( REGFILE *reg, RKEY key, char *path, XP_Bool raw );
+static REGERR  nr_RegOpen( const char *filename, HREG *hReg );
+static REGERR  nr_RegClose( HREG hReg );
+static char*   nr_GetUsername();
+static const char* nr_GetRegName (const char *name);
+static int     nr_RegSetBufferSize( HREG hReg, int bufsize );
+
+/* --------------------------------------------------------------------- */
+
+
+static REGOFF nr_TranslateKey( REGFILE *reg, RKEY key )
+{
+    REGOFF retKey = 0;
+
+    /* if it's a special key  */
+    if ( key < HDRRESERVE )  {
+        /* ...translate it */
+        switch (key)
+        {
+            case ROOTKEY:
+                retKey = reg->hdr.root;
+                break;
+
+            case ROOTKEY_VERSIONS:
+                retKey = reg->rkeys.versions;
+                break;
+
+            case ROOTKEY_USERS:
+                retKey = reg->rkeys.users;
+                break;
+
+            case ROOTKEY_COMMON:
+                retKey = reg->rkeys.common;
+                break;
+
+#ifndef STANDALONE_REGISTRY
+            case ROOTKEY_CURRENT_USER:
+                if ( reg->rkeys.current_user == 0 ) {
+                    /* not initialized--find the current user key */
+                    RKEY    userkey = 0;
+                    REGERR  err;
+                    char*   profName;
+
+                    profName = nr_GetUsername();
+                    if ( NULL != profName ) {
+                        /* Don't assign a slot for missing or magic profile */
+                        if ( '\0' == *profName ||
+                            0 == XP_STRCMP(ASW_MAGIC_PROFILE_NAME, profName)) 
+                        {
+                            err = REGERR_FAIL;
+                        } else {
+                            err = nr_RegAddKey( reg, reg->rkeys.users, profName, &userkey, FALSE );
+                        }
+                        XP_FREE(profName);
+                    }
+                    else {
+                        err = nr_RegAddKey( reg, reg->rkeys.users, "default", &userkey, FALSE );
+                    }
+
+                    if ( err == REGERR_OK ) {
+                        reg->rkeys.current_user = userkey;
+                    }
+                }
+                retKey = reg->rkeys.current_user;
+                break;
+#endif /* !STANDALONE_REGISTRY */
+
+            case ROOTKEY_PRIVATE:
+                retKey = reg->rkeys.privarea;
+                break;
+
+            default:
+                /* not a valid key */
+                retKey = 0;
+                break;
+        }
+    }
+    else {
+        /* ...otherwise it's fine as-is */
+        retKey = (REGOFF)key;
+    }
+    return ( retKey );
+}  /* nr_TranslateKey */
+
+
+
+static REGERR nr_InitStdRkeys( REGFILE *reg )
+{
+    REGERR      err = REGERR_OK;
+    RKEY        key;
+
+    XP_ASSERT( reg != NULL );
+
+    /* initialize to invalid key values */
+    XP_MEMSET( &reg->rkeys, 0, sizeof(STDNODES) );
+
+    /* Add each key before looking it up.  Adding an already
+     * existing key is harmless, and these MUST exist.
+     */
+
+    /* ROOTKEY_USERS */
+    err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_USERS_STR, &key, FALSE );
+    if ( err != REGERR_OK )
+        return err;
+    reg->rkeys.users = key;
+
+    /* ROOTKEY_COMMON */
+    err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_COMMON_STR, &key, FALSE );
+    if ( err != REGERR_OK ) 
+        return err;
+    reg->rkeys.common = key;
+
+    /* ROOTKEY_VERSIONS */
+    err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_VERSIONS_STR, &key, FALSE );
+    if ( err != REGERR_OK )
+        return err;
+    reg->rkeys.versions = key;
+
+    /* ROOTKEY_CURRENT_USER */
+    /* delay until first use -- see nr_TranslateKey */
+
+    /* ROOTKEY_PRIVATE */
+    err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_PRIVATE_STR, &key, FALSE );
+    if ( err != REGERR_OK ) 
+        return err;
+    reg->rkeys.privarea = key;
+
+    return err;
+}   /* nr_InitStdRkeys */
+
+
+
+static XP_Bool nr_ProtectedNode( REGFILE *reg, REGOFF key )
+{
+    if ( (key == reg->hdr.root) ||
+         (key == reg->rkeys.users) ||
+         (key == reg->rkeys.versions) ||
+         (key == reg->rkeys.common) ||
+         (key == reg->rkeys.current_user) )
+    {
+        return TRUE;
+    }
+    else
+        return FALSE;
+}
+
+
+
+static REGERR nr_RegAddKey( REGFILE *reg, RKEY key, char *path, RKEY *newKey, XP_Bool raw )
+{
+    REGERR      err;
+    REGDESC     desc;
+    REGOFF      start;
+    REGOFF      parent;
+    char        namebuf[MAXREGNAMELEN];
+    char        *p;
+
+    XP_ASSERT( regStartCount > 0 );
+    XP_ASSERT( reg != NULL );
+    XP_ASSERT( path != NULL );
+    XP_ASSERT( *path != '\0' );
+    XP_ASSERT( VALID_FILEHANDLE( reg->fh ) );
+
+    /* have to translate again in case this is an internal call */
+    start = nr_TranslateKey( reg, key );
+    if ( start == 0 )
+        return REGERR_PARAM;
+
+    /* Get starting desc */
+    err = nr_ReadDesc( reg, start, &desc );
+
+    if (raw == TRUE) {
+        if ( err == REGERR_OK) {
+            /* look for name at next level down */
+            parent = desc.location;
+            err = nr_FindAtLevel(reg, desc.down, path, &desc, 0);
+
+            /* if key is not found */
+            if ( err == REGERR_NOFIND ) {
+                /* add it as a sub-key to the last found key */
+                err = nr_CreateSubKey(reg, parent, &desc, path);
+            }
+        }
+    }
+    else {
+        /* Walk 'path', reading keys into 'desc' */
+        p = path;
+        while ( err == REGERR_OK ) {
+
+            /* get next name on the path */
+            err = nr_NextName(p, namebuf, sizeof(namebuf), (const char**)&p);
+            if ( err == REGERR_OK ) {
+                /* look for name at next level down */
+                parent = desc.location;
+                err = nr_FindAtLevel(reg, desc.down, namebuf, &desc, 0);
+
+                /* if key is not found */
+                if ( err == REGERR_NOFIND ) {
+                    /* add it as a sub-key to the last found key */
+                    err = nr_CreateSubKey(reg, parent, &desc, namebuf);
+                }
+            }
+        }
+    }
+
+    /* it's good to have processed the whole path */
+    if ( (raw == FALSE && err == REGERR_NOMORE) ||
+         (raw == TRUE && err == REGERR_OK) ) 
+    {
+        err = REGERR_OK;
+
+        /* return new key if the caller wants it */
+        if ( newKey != NULL ) {
+            *newKey = desc.location;
+        }
+    }
+
+    return err;
+
+}   /* nr_RegAddKey */
+
+
+
+
+static REGERR nr_RegDeleteKey( REGFILE *reg, RKEY key, char *path, XP_Bool raw )
+{
+    REGERR      err;
+    REGOFF      start;
+    REGDESC     desc;
+    REGDESC     predecessor;
+    REGOFF      offPrev;
+    REGOFF      offParent;
+    REGOFF*     link;
+
+    XP_ASSERT( regStartCount > 0 );
+    XP_ASSERT( reg != NULL );
+    XP_ASSERT( VALID_FILEHANDLE( reg->fh ) );
+
+    start = nr_TranslateKey( reg, key );
+    if ( path == NULL || *path == '\0' || start == 0 )
+        return REGERR_PARAM;
+
+    /* find the specified key */
+    err = nr_Find( reg, start, path, &desc, &offPrev, &offParent, raw );
+    if ( err == REGERR_OK ) {
+
+        XP_ASSERT( !TYPE_IS_ENTRY( desc.type ) );
+
+        /* make sure it's childless and not a top-level key */
+        if ( (desc.down == 0) && !nr_ProtectedNode( reg, desc.location ) ) {
+
+            /* Are we the first on our level? */
+            if ( offPrev == 0 ) {
+                /* Yes: link to parent's "down" pointer */
+                err = nr_ReadDesc( reg, offParent, &predecessor );
+                link = &(predecessor.down);
+            }
+            else {
+                /* No: link using predecessor's "left" pointer */
+                err = nr_ReadDesc( reg, offPrev, &predecessor );
+                link = &(predecessor.left);
+            }
+
+            /* If we read the predecessor desc OK */
+            if (err == REGERR_OK) {
+                XP_ASSERT( *link == desc.location );
+
+                /* link predecessor to next, removing current node from chain */
+                *link = desc.left;
+
+                /* Write the updated predecessor */
+                err = nr_WriteDesc( reg, &predecessor );
+                if ( err == REGERR_OK ) {
+                    /* Mark key deleted to prevent bogus use by anyone
+                     * who is holding an RKEY for that node
+                     */
+                    desc.type |= REGTYPE_DELETED;
+                    err = nr_WriteDesc( reg, &desc );
+                }
+            }
+        }
+        else {
+            /* specified node is protected from deletion */
+            err = REGERR_FAIL;
+        }
+    }
+
+    return err;
+
+}   /* nr_RegDeleteKey */
+
+
+
+static int nr_RegSetBufferSize( HREG hReg, int bufsize )
+{
+    REGERR      err = REGERR_OK;
+    REGHANDLE*  reghnd = (REGHANDLE*)hReg;
+    REGFILE*    reg;
+    XP_Bool     needDelete = FALSE;
+    int         newSize;
+
+    /* verify handle */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return -1;
+
+    reg = reghnd->pReg;
+
+    PR_Lock( reg->lock );
+ 
+    newSize = XP_FileSetBufferSize( reg->fh, bufsize );
+
+    PR_Unlock( reg->lock );
+
+    return newSize;
+}
+
+
+
+static REGERR nr_RegOpen( const char *filename, HREG *hReg )
+{
+    REGERR    status = REGERR_OK;
+    REGFILE   *pReg;
+    REGHANDLE *pHandle;
+
+    XP_ASSERT( regStartCount > 0 );
+
+    /* initialize output handle in case of error */
+    if ( hReg == NULL ) {
+        return REGERR_PARAM;
+    }
+    *hReg = NULL;
+    
+    /* Look for named file in list of open registries */
+    filename = nr_GetRegName( filename );
+    if (filename == NULL) {
+        filename = "";
+    }
+    pReg = vr_findRegFile( filename );
+
+    /* if registry not already open */
+    if (pReg == NULL) {
+
+        /* ...then open it */
+        pReg = (REGFILE*)XP_ALLOC( sizeof(REGFILE) );
+        if ( pReg == NULL ) {
+            status = REGERR_MEMORY;
+            goto bail;
+        }
+        XP_MEMSET(pReg, 0, sizeof(REGFILE));
+
+        pReg->inInit = TRUE;
+        pReg->filename = XP_STRDUP(filename);
+        if (pReg->filename == NULL) {
+            XP_FREE( pReg );
+            status = REGERR_MEMORY;
+            goto bail;
+        }
+
+        status = nr_OpenFile( filename, &(pReg->fh) );
+        if (status == REGERR_READONLY) {
+            /* Open, but read only */
+            pReg->readOnly = TRUE;
+            status = REGERR_OK;
+        }
+        if ( status != REGERR_OK ) {
+            XP_FREE( pReg->filename );
+            XP_FREE( pReg );
+
+            goto bail;
+        }
+
+        /* ...read and validate the header */
+        status = nr_ReadHdr( pReg );
+        if ( status != REGERR_OK ) {
+            nr_CloseFile( &(pReg->fh) );
+            XP_FREE( pReg->filename );
+            XP_FREE( pReg );
+            goto bail;
+        }
+
+        /* ...other misc initialization */
+        pReg->refCount = 0;
+
+#ifndef STANDALONE_REGISTRY
+        pReg->uniqkey = PR_Now();
+#endif
+
+        status = nr_InitStdRkeys( pReg );
+        if ( status == REGERR_OK ) {
+            /* ...and add it to the list */
+            nr_AddNode( pReg );
+        }
+        else {
+            nr_CloseFile( &(pReg->fh) );
+            XP_FREE( pReg->filename );
+            XP_FREE( pReg );
+            goto bail;
+        }
+
+#ifndef STANDALONE_REGISTRY
+        pReg->lock = PR_NewLock();
+#endif
+
+        /* now done with everything that needs to protect the header */
+        pReg->inInit = FALSE;
+    }
+
+    /* create a new handle to the regfile */
+    pHandle = (REGHANDLE*)XP_ALLOC( sizeof(REGHANDLE) );
+    if ( pHandle == NULL ) {
+        /* we can't create the handle */
+        if ( pReg->refCount == 0 ) {
+            /* we've just opened it so close it and remove node */
+            nr_CloseFile( &(pReg->fh) );
+            nr_DeleteNode( pReg );
+        }
+
+        status = REGERR_MEMORY;
+        goto bail;
+    }
+
+    pHandle->magic   = MAGIC_NUMBER;
+    pHandle->pReg    = pReg;
+
+    /* success: bump the reference count and return the handle */
+    pReg->refCount++;
+    *hReg = (void*)pHandle;
+
+bail:
+    return status;
+
+}   /* nr_RegOpen */
+
+
+
+static REGERR nr_RegClose( HREG hReg )
+{
+    REGERR      err = REGERR_OK;
+    REGHANDLE*  reghnd = (REGHANDLE*)hReg;
+    REGFILE*    reg;
+    XP_Bool     needDelete = FALSE;
+
+    XP_ASSERT( regStartCount > 0 );
+
+    /* verify handle */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    reg = reghnd->pReg;
+
+    PR_Lock( reg->lock );
+    if ( err == REGERR_OK )
+    {
+        XP_ASSERT( VALID_FILEHANDLE(reg->fh) );
+
+        /* save changed header info */
+        if ( reg->hdrDirty ) {
+            nr_WriteHdr( reg );
+        }
+
+        /* lower REGFILE user count */
+        reg->refCount--;
+
+        /* if registry is no longer in use */
+        if ( reg->refCount < 1 ) 
+        {
+            /* ...then close the file */
+            nr_CloseFile( &(reg->fh) );
+
+            /* ...and mark REGFILE node for deletion from list */
+            needDelete = TRUE;
+        }
+        else
+        {
+            /* ...otherwise make sure any writes are flushed */
+            XP_FileFlush( reg->fh );
+        }
+
+        reghnd->magic = 0;    /* prevent accidental re-use */  
+        PR_Unlock( reg->lock );
+
+        if ( needDelete )
+            nr_DeleteNode( reg );
+
+        XP_FREE( reghnd );
+    }
+
+    return err;
+
+}   /* nr_RegClose */
+
+
+
+static char *nr_GetUsername()
+{
+  if (NULL == user_name) {
+    return "default";
+  } else {
+    return user_name;
+  }
+}
+
+static const char* nr_GetRegName (const char *name)
+{
+    if (name == NULL || *name == '\0') {
+        XP_ASSERT( globalRegName != NULL );
+        return globalRegName;
+    } else {
+        return name;
+    }
+}
+
+
+
+
+/* ---------------------------------------------------------------------
+ * Public API
+ * --------------------------------------------------------------------- */
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegGetUsername - Gets a copy of the current username
+ *
+ * Parameters:
+ *   A variable which, on exit will contain an alloc'ed string which is a
+ *   copy of the current username.
+ *
+ * DO NOT USE -- OBSOLETE
+ * ---------------------------------------------------------------------
+ */
+
+VR_INTERFACE(REGERR) NR_RegGetUsername(char **name)
+{
+    /* XXX: does this need locking? */
+
+    if ( name == NULL )
+        return REGERR_PARAM;
+
+    *name = XP_STRDUP(nr_GetUsername());
+
+    if ( NULL == *name )
+        return REGERR_MEMORY;
+
+    return REGERR_OK;
+}
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegSetBufferSize - Set the buffer size
+ *
+ * Parameters:
+ *     name     - name of the current user
+ *
+ * Output:
+ * ---------------------------------------------------------------------
+ */
+
+VR_INTERFACE(int) NR_RegSetBufferSize( HREG hReg, int bufsize )
+{
+    int      newSize;
+
+    PR_Lock( reglist_lock );
+
+    newSize = nr_RegSetBufferSize( hReg, bufsize );
+
+    PR_Unlock(reglist_lock);
+
+    return newSize;
+}
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegSetUsername - Set the current username
+ * 
+ * If the current user profile name is not set then trying to use
+ * HKEY_CURRENT_USER will result in an error.
+ *
+ * Parameters:
+ *     name     - name of the current user
+ *
+ * Output:
+ * ---------------------------------------------------------------------
+ */
+
+VR_INTERFACE(REGERR) NR_RegSetUsername(const char *name)
+{
+    char *tmp;
+
+    if ( name == NULL || *name == '\0' )
+        return REGERR_PARAM;
+
+    tmp = XP_STRDUP(name);
+    if (NULL == tmp) {
+        return REGERR_MEMORY;
+    }
+
+    PR_Lock( reglist_lock );
+
+    XP_FREEIF(user_name);
+    user_name = tmp;
+
+/* XXX: changing the username should go through and clear out the current.user
+   for each open registry. */
+
+    PR_Unlock( reglist_lock );
+  
+    return REGERR_OK;
+}
+
+
+
+
+#ifndef STANDALONE_REGISTRY
+/* ---------------------------------------------------------------------
+ * NR_RegGetUniqueName
+ * 
+ * Returns a unique name that can be used for anonymous key/value names
+ *
+ * Parameters:
+ *     hReg     - handle of open registry
+ *     outbuf   - where to put the string
+ *     buflen   - how big the buffer is
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegGetUniqueName(HREG hReg, char* outbuf, uint32 buflen)
+{
+    PRUint64    one;
+    REGERR      err;
+    REGFILE*    reg;
+    static PRUint64 uniqkey;
+
+    /* verify parameters */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    if ( !outbuf )
+        return REGERR_PARAM;
+
+    if ( buflen <= (sizeof(PRUint64)*2) )
+        return REGERR_BUFTOOSMALL;
+
+    if ( LL_IS_ZERO(uniqkey) )
+        uniqkey = PR_Now();
+
+    PR_snprintf(outbuf,buflen,"%llx",uniqkey);
+
+    /* increment counter for next time */
+    LL_I2L(one,1);
+    LL_ADD(uniqkey, uniqkey, one);
+
+    return REGERR_OK;
+}
+#endif
+
+
+       
+       
+/* ---------------------------------------------------------------------
+ * NR_RegOpen - Open a netscape XP registry
+ *
+ * Parameters:
+ *    filename   - registry file to open. NULL or ""  opens the standard
+ *                 local registry.
+ *    hReg       - OUT: handle to opened registry
+ *
+ * Output:
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegOpen( const char *filename, HREG *hReg )
+{
+    REGERR    status = REGERR_OK;
+
+#if !defined(STANDALONE_REGISTRY)
+    /* you must call NR_StartupRegistry() first */
+    if ( regStartCount <= 0 )
+        return REGERR_FAIL;
+#endif
+
+    PR_Lock(reglist_lock);
+
+    status = nr_RegOpen( filename, hReg );
+
+    PR_Unlock(reglist_lock);
+
+    return status;
+
+}   /* NR_RegOpen */
+
+
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegClose - Close a netscape XP registry
+ *
+ * Parameters:
+ *    hReg     - handle of open registry to be closed.
+ *
+ * After calling this routine the handle is no longer valid
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegClose( HREG hReg )
+{
+    REGERR      err = REGERR_OK;
+
+    PR_Lock( reglist_lock );
+
+    err = nr_RegClose( hReg );
+
+    PR_Unlock(reglist_lock);
+
+    return err;
+
+}   /* NR_RegClose */
+
+
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegFlush - Manually flush data in a netscape XP registry
+ *
+ * Parameters:
+ *    hReg     - handle of open registry to be flushed.
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegFlush( HREG hReg )
+{
+    REGERR      err;
+    REGFILE*    reg;
+
+    /* verify parameters */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    /* can't flush a read-only registry */
+    if ( reg->readOnly )
+        return REGERR_READONLY;
+
+    /* lock the registry file */
+    err = nr_Lock( reg );
+    if ( err == REGERR_OK )
+    {
+        if ( reg->hdrDirty ) {
+            nr_WriteHdr( reg );
+        }
+
+        XP_FileFlush( reg->fh );
+
+        /* unlock the registry */
+        nr_Unlock( reg );
+    }
+
+    return err;
+
+} /* NR_RegFlush */
+
+
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegIsWritable - Check read/write status of open registry
+ *
+ * Parameters:
+ *    hReg     - handle of open registry to query
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegIsWritable( HREG hReg )
+{
+    REGERR      err;
+    REGFILE*    reg;
+
+    /* verify parameters */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    if ( reg->readOnly )
+        return REGERR_READONLY;
+    else
+        return REGERR_OK;
+
+}   /* NR_RegIsWritable */
+
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegAddKey - Add a key node to the registry
+ *
+ *      This routine is simply a wrapper to perform user input
+ *      validation and translation from HREG and standard key
+ *      values into the internal format
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - registry key obtained from NR_RegGetKey(),
+ *               or one of the standard top-level keys
+ *    path     - relative path of key to be added.  Intermediate
+ *               nodes will also be added if necessary.
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegAddKey( HREG hReg, RKEY key, char *path, RKEY *newKey )
+{
+    REGERR      err;
+    REGOFF      start;
+    REGFILE*    reg;
+
+    /* prevent use of return value in case errors aren't checked */
+    if ( newKey != NULL )
+        *newKey = 0;
+
+    /* verify parameters */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    if ( path == NULL || *path == '\0' || reg == NULL )
+        return REGERR_PARAM;
+
+    /* lock the registry file */
+    err = nr_Lock( reg );
+    if ( err == REGERR_OK )
+    {
+        /* ... don't allow additional children of ROOTKEY */
+        start = nr_TranslateKey( reg, key );
+        if ( start != 0 && start != reg->hdr.root )
+        {
+            err = nr_RegAddKey( reg, start, path, newKey, FALSE );
+        }
+        else
+            err = REGERR_PARAM;
+
+        /* unlock the registry */
+        nr_Unlock( reg );
+    }
+
+    return err;
+}   /* NR_RegAddKey */
+
+
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegAddKeyRaw - Add a key node to the registry
+ *
+ *      This routine is different from NR_RegAddKey() in that it takes 
+ *      a keyname rather than a path.
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - registry key obtained from NR_RegGetKey(),
+ *               or one of the standard top-level keys
+ *    keyname  - name of key to be added. No parsing of this
+ *               name happens.
+ *    newkey   - if not null the RKEY of the new key is returned
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegAddKeyRaw( HREG hReg, RKEY key, char *keyname, RKEY *newKey )
+{
+    REGERR      err;
+    REGOFF      start;
+    REGFILE*    reg;
+
+    /* prevent use of return value in case errors aren't checked */
+    if ( newKey != NULL )
+        *newKey = 0;
+
+    /* verify parameters */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    if ( keyname == NULL || *keyname == '\0' || reg == NULL )
+        return REGERR_PARAM;
+
+    /* lock the registry file */
+    err = nr_Lock( reg );
+    if ( err == REGERR_OK )
+    {
+        /* ... don't allow additional children of ROOTKEY */
+        start = nr_TranslateKey( reg, key );
+        if ( start != 0 && start != reg->hdr.root ) 
+        {
+            err = nr_RegAddKey( reg, start, keyname, newKey, TRUE );
+        }
+        else
+            err = REGERR_PARAM;
+
+        /* unlock the registry */
+        nr_Unlock( reg );
+    }
+
+    return err;
+}   /* NR_RegAddKeyRaw */
+
+
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegDeleteKey - Delete the specified key
+ *
+ * Note that delete simply orphans blocks and makes no attempt
+ * to reclaim space in the file. Use NR_RegPack()
+ *
+ * Cannot be used to delete keys with child keys
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - starting node RKEY, typically one of the standard ones.
+ *    path     - relative path of key to delete
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegDeleteKey( HREG hReg, RKEY key, char *path )
+{
+    REGERR      err;
+    REGFILE*    reg;
+
+    /* verify parameters */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    /* lock registry */
+    err = nr_Lock( reg );
+    if ( err == REGERR_OK )
+    {
+        err = nr_RegDeleteKey( reg, key, path, FALSE );
+        nr_Unlock( reg );
+    }
+
+    return err;
+}   /* NR_RegDeleteKey */
+
+
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegDeleteKeyRaw - Delete the specified raw key
+ *
+ * Note that delete simply orphans blocks and makes no attempt
+ * to reclaim space in the file. Use NR_RegPack()
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY or parent to the raw key you wish to delete
+ *    keyname  - name of child key to delete
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegDeleteKeyRaw( HREG hReg, RKEY key, char *keyname )
+{
+    REGERR      err;
+    REGFILE*    reg;
+
+    /* verify parameters */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    /* lock registry */
+    err = nr_Lock( reg );
+    if ( err == REGERR_OK )
+    {
+        err = nr_RegDeleteKey( reg, key, keyname, TRUE );
+        nr_Unlock( reg );
+    }
+
+    return err;
+}   /* NR_RegDeleteKeyRaw */
+
+
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegGetKey - Get the RKEY value of a node from its path
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - starting node RKEY, typically one of the standard ones.
+ *    path     - relative path of key to find.  (a blank path just gives you
+ *               the starting key--useful for verification, VersionRegistry)
+ *    result   - if successful the RKEY of the specified sub-key
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegGetKey( HREG hReg, RKEY key, const char *path, RKEY *result )
+{
+    REGERR      err;
+    REGOFF      start;
+    REGFILE*    reg;
+    REGDESC     desc;
+
+    XP_ASSERT( regStartCount > 0 );
+
+    /* prevent use of return value in case errors aren't checked */
+    if ( result != NULL )
+        *result = (RKEY)0;
+
+    /* verify parameters */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    if ( path == NULL || result == NULL )
+        return REGERR_PARAM;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    /* lock registry */
+    err = nr_Lock( reg );
+    if ( err == REGERR_OK )
+    {
+        start = nr_TranslateKey( reg, key );
+        if ( start != 0 )
+        {
+            /* find the specified key ( if it's valid )*/
+            err = nr_Find( reg, start, path, &desc, 0, 0, FALSE );
+            if ( err == REGERR_OK ) {
+                *result = (RKEY)desc.location;
+            }
+        }
+        else {
+            err = REGERR_PARAM;
+        }
+
+        nr_Unlock( reg );
+    }
+
+    return err;
+
+}   /* NR_RegGetKey */
+
+
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegGetKeyRaw - Get the RKEY value of a node from its keyname
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - starting node RKEY, typically one of the standard ones.
+ *    keyname  - keyname of key to find.  (a blank keyname just gives you
+ *               the starting key--useful for verification, VersionRegistry)
+ *    result   - if successful the RKEY of the specified sub-key
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegGetKeyRaw( HREG hReg, RKEY key, char *keyname, RKEY *result )
+{
+    REGERR      err;
+    REGOFF      start;
+    REGFILE*    reg;
+    REGDESC     desc;
+
+    XP_ASSERT( regStartCount > 0 );
+
+    /* prevent use of return value in case errors aren't checked */
+    if ( result != NULL )
+        *result = (RKEY)0;
+
+    /* verify parameters */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    if ( keyname == NULL || result == NULL )
+        return REGERR_PARAM;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    /* lock registry */
+    err = nr_Lock( reg );
+    if ( err == REGERR_OK )
+    {
+        start = nr_TranslateKey( reg, key );
+        if ( start != 0 )
+        {
+            /* find the specified key ( if it's valid )*/
+            err = nr_Find( reg, start, keyname, &desc, 0, 0, TRUE );
+            if ( err == REGERR_OK ) {
+                *result = (RKEY)desc.location;
+            }
+        }
+        else {
+            err = REGERR_PARAM;
+        }
+
+        nr_Unlock( reg );
+    }
+
+    return err;
+
+}   /* NR_RegGetKeyRaw */
+
+
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegGetEntryInfo - Get some basic info about the entry data
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
+ *    name     - name of entry
+ *    info     - return: Entry info object
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegGetEntryInfo( HREG hReg, RKEY key, char *name, 
+                            REGINFO *info )
+{
+    REGERR      err;
+    REGFILE*    reg;
+    REGDESC     desc;
+    
+    XP_ASSERT( regStartCount > 0 );
+
+    /* verify parameters */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    if ( name == NULL || *name == '\0' || info == NULL || key == 0 )
+        return REGERR_PARAM;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    err = nr_Lock( reg );
+    if ( err == REGERR_OK )
+    {
+        /* read starting desc */
+        err = nr_ReadDesc( reg, key, &desc);
+        if ( err == REGERR_OK ) 
+        {
+            /* if the named entry exists */
+            err = nr_FindAtLevel( reg, desc.value, name, &desc, NULL );
+            if ( err == REGERR_OK ) 
+            {
+                /* ... return the values */
+                if ( info->size == sizeof(REGINFO) )
+                {
+                    info->entryType   = desc.type;
+                    info->entryLength = desc.valuelen;
+                }
+                else
+                {
+                    /* uninitialized (maybe invalid) REGINFO structure */
+                    err = REGERR_PARAM;
+                }
+            }
+        }
+
+        nr_Unlock( reg );
+    }
+
+    return err;
+
+}   /* NR_RegGetEntryInfo */
+
+
+
+       
+/* ---------------------------------------------------------------------
+ * NR_RegGetEntryString - Get the UTF string value associated with the
+ *                       named entry of the specified key.
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
+ *    name     - name of entry
+ *    buffer   - destination for string
+ *    bufsize  - size of buffer
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegGetEntryString( HREG  hReg, RKEY  key, const char *name,
+                            char  *buffer, uint32 bufsize)
+{
+    REGERR      err;
+    REGFILE*    reg;
+    REGDESC     desc;
+
+    XP_ASSERT( regStartCount > 0 );
+
+    /* verify parameters */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    if ( name==NULL || *name=='\0' || buffer==NULL || bufsize==0 || key==0 )
+        return REGERR_PARAM;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    err = nr_Lock( reg );
+    if ( err == REGERR_OK )
+    {
+        /* read starting desc */
+        err = nr_ReadDesc( reg, key, &desc);
+        if ( err == REGERR_OK ) 
+        {
+            /* if the named entry exists */
+            err = nr_FindAtLevel( reg, desc.value, name, &desc, NULL );
+            if ( err == REGERR_OK ) 
+            {
+                /* read the string */
+                if ( desc.type == REGTYPE_ENTRY_STRING_UTF ) 
+                {
+                    err = nr_ReadData( reg, &desc, bufsize, buffer );
+                    /* prevent run-away strings */
+                    buffer[bufsize-1] = '\0';
+                }
+                else {
+                    err = REGERR_BADTYPE;
+                }
+            }
+        }
+
+        nr_Unlock( reg );
+    }
+
+    return err;
+
+}   /* NR_RegGetEntryString */
+
+
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegGetEntry - Get the value data associated with the
+ *                  named entry of the specified key.
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
+ *    name     - name of entry
+ *    buffer   - destination for data
+ *    size     - in:  size of buffer
+ *               out: size of actual data (incl. \0 term. for strings)
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegGetEntry( HREG hReg, RKEY key, char *name,
+    void *buffer, uint32 *size )
+{
+    REGERR      err;
+    REGFILE*    reg;
+    REGDESC     desc;
+    char        *tmpbuf = NULL;  /* malloc a tmp buffer to convert XP int arrays */
+    uint32      nInt;
+    uint32      *pISrc;
+    uint32      *pIDest;
+    XP_Bool     needFree = FALSE;
+
+    XP_ASSERT( regStartCount > 0 );
+
+    /* verify parameters */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    if ( name==NULL || *name=='\0' || buffer==NULL || size==NULL || key==0 )
+        return REGERR_PARAM;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    err = nr_Lock( reg );
+    if ( err == REGERR_OK )
+    {
+        /* read starting desc */
+        err = nr_ReadDesc( reg, key, &desc);
+        if ( err == REGERR_OK )
+        {
+            /* if the named entry exists */
+            err = nr_FindAtLevel( reg, desc.value, name, &desc, NULL );
+            if ( err == REGERR_OK )
+            {
+                if ( desc.valuelen > *size ) {
+                    err = REGERR_BUFTOOSMALL;
+                }
+                else if ( desc.valuelen == 0 ) {
+                    err = REGERR_FAIL;
+                }
+                else switch (desc.type)
+                {
+                /* platform independent array of 32-bit integers */
+                case REGTYPE_ENTRY_INT32_ARRAY:
+                    tmpbuf = (char*)XP_ALLOC( desc.valuelen );
+                    if ( tmpbuf != NULL ) 
+                    {
+                        needFree = TRUE;
+                        err = nr_ReadData( reg, &desc, desc.valuelen, tmpbuf );
+                        if ( REGERR_OK == err )
+                        {
+                            /* convert int array */
+                            nInt = (desc.valuelen / INTSIZE);
+                            pISrc = (uint32*)tmpbuf;
+                            pIDest = (uint32*)buffer;
+                            for(; nInt > 0; nInt--, pISrc++, pIDest++) {
+                                *pIDest = nr_ReadLong((char*)pISrc);
+                            }
+                        }
+                    }
+                    else
+                        err = REGERR_MEMORY;
+                    break;
+
+                case REGTYPE_ENTRY_STRING_UTF:
+                    tmpbuf = (char*)buffer;
+                    err = nr_ReadData( reg, &desc, *size, tmpbuf );
+                    /* prevent run-away strings */
+                    tmpbuf[(*size)-1] = '\0';
+                    break;
+
+                case REGTYPE_ENTRY_FILE:
+                    err = nr_ReadData( reg, &desc, *size, (char*)buffer );
+                    break;
+                
+                case REGTYPE_ENTRY_BYTES:
+                default:              /* return raw data for unknown types */
+                    err = nr_ReadData( reg, &desc, *size, (char*)buffer );
+                    break;
+                }
+
+                /* return the actual data size */
+                *size = desc.valuelen;
+            }
+        }
+
+        nr_Unlock( reg );
+    }
+
+    if (needFree)
+        XP_FREE(tmpbuf);
+
+    return err;
+
+}   /* NR_RegGetEntry */
+
+
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegSetEntryString - Store a UTF-8 string value associated with the
+ *                       named entry of the specified key.  Used for
+ *                       both creation and update.
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
+ *    name     - name of entry
+ *    buffer   - UTF-8 String to store
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegSetEntryString( HREG hReg, RKEY key, char *name,
+                                     char *buffer )
+{
+    REGERR      err;
+    REGFILE*    reg;
+    REGDESC     desc;
+    REGDESC     parent;
+
+    XP_ASSERT( regStartCount > 0 );
+
+    /* verify parameters */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    if ( name == NULL || *name == '\0' || buffer == NULL || key == 0 )
+        return REGERR_PARAM;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    /* lock registry */
+    err = nr_Lock( reg );
+    if ( err != REGERR_OK )
+        return err;
+
+    /* read starting desc */
+    err = nr_ReadDesc( reg, key, &parent);
+    if ( err == REGERR_OK ) {
+
+        /* if the named entry already exists */
+        err = nr_FindAtLevel( reg, parent.value, name, &desc, NULL );
+        if ( err == REGERR_OK ) {
+            /* then update the existing one */
+            err = nr_WriteString( reg, buffer, &desc );
+            if ( err == REGERR_OK ) {
+                desc.type = REGTYPE_ENTRY_STRING_UTF;
+                err = nr_WriteDesc( reg, &desc );
+            }
+        }
+        else if ( err == REGERR_NOFIND ) {
+            /* otherwise create a new entry */
+            err = nr_CreateEntryString( reg, &parent, name, buffer );
+        }
+        /* other errors fall through */
+    }
+
+    /* unlock registry */
+    nr_Unlock( reg );
+
+    return err;
+
+}   /* NR_RegSetEntryString */
+
+
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegSetEntry - Store value data associated with the named entry
+ *                  of the specified key.  Used for both creation and update.
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
+ *    name     - name of entry
+ *    type     - type of data to be stored
+ *    buffer   - data to store
+ *    size     - length of data to store in bytes
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegSetEntry( HREG hReg, RKEY key, char *name, uint16 type,
+    void *buffer, uint32 size )
+{
+    REGERR      err;
+    REGFILE*    reg;
+    REGDESC     desc;
+    REGDESC     parent;
+    char        *data = NULL;
+    uint32      nInt;
+    uint32      *pIDest;
+    uint32      *pISrc;
+    XP_Bool     needFree = FALSE;
+    int32       datalen = size;
+
+    XP_ASSERT( regStartCount > 0 );
+
+    /* verify parameters */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    if ( name==NULL || *name=='\0' || buffer==NULL || size==0 || key==0 )
+        return REGERR_PARAM;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    /* validate type and convert numerics to XP format */
+    switch (type)
+    {
+        case REGTYPE_ENTRY_BYTES:
+            data = (char*)buffer;
+            break;
+
+        case REGTYPE_ENTRY_FILE:
+            data = (char*)buffer;   
+            break;
+
+
+        case REGTYPE_ENTRY_STRING_UTF:
+            data = (char*)buffer;
+            /* string must be null terminated */
+            if ( data[size-1] != '\0' )
+                return REGERR_PARAM;
+            break;
+
+
+        case REGTYPE_ENTRY_INT32_ARRAY:
+            /* verify no partial integers */
+            if ( (size % INTSIZE) != 0 )
+                return REGERR_PARAM;
+
+            /* get a conversion buffer */
+            data = (char*)XP_ALLOC(size);
+            if ( data == NULL )
+                return REGERR_MEMORY;
+            else
+                needFree = TRUE;
+
+            /* convert array to XP format */
+            nInt = ( size / INTSIZE );
+            pIDest = (uint32*)data;
+            pISrc  = (uint32*)buffer;
+
+            for( ; nInt > 0; nInt--, pIDest++, pISrc++) {
+                nr_WriteLong( *pISrc, (char*)pIDest );
+            }
+            break;
+
+
+        default:
+            return REGERR_BADTYPE;
+    }
+
+    /* lock registry */
+    err = nr_Lock( reg );
+    if ( REGERR_OK == err )
+    {
+        /* read starting desc */
+        err = nr_ReadDesc( reg, key, &parent);
+        if ( err == REGERR_OK ) 
+        {
+            /* if the named entry already exists */
+            err = nr_FindAtLevel( reg, parent.value, name, &desc, NULL );
+            if ( err == REGERR_OK ) 
+            {
+                /* then update the existing one */
+                err = nr_WriteData( reg, data, datalen, &desc );
+                if ( err == REGERR_OK ) 
+                {
+                    desc.type = type;
+                    err = nr_WriteDesc( reg, &desc );
+                }
+            }
+            else if ( err == REGERR_NOFIND ) 
+            {
+                /* otherwise create a new entry */
+                err = nr_CreateEntry( reg, &parent, name, type, data, datalen );
+            }
+            else {
+                /* other errors fall through */
+            }
+        }
+
+        /* unlock registry */
+        nr_Unlock( reg );
+    }
+
+    if (needFree)
+        XP_FREE(data);
+
+    return err;
+
+}   /* NR_RegSetEntry */
+
+
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegDeleteEntry - Delete the named entry
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
+ *    name     - name of entry
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegDeleteEntry( HREG hReg, RKEY key, char *name )
+{
+    REGERR      err;
+    REGFILE*    reg;
+    REGDESC     desc;
+    REGDESC     parent;
+    REGOFF      offPrev;
+
+    XP_ASSERT( regStartCount > 0 );
+
+    /* verify parameters */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    if ( name == NULL || *name == '\0' || key == 0)
+        return REGERR_PARAM;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    /* lock registry */
+    err = nr_Lock( reg );
+    if ( err != REGERR_OK )
+        return err;
+
+    /* read starting desc */
+    err = nr_ReadDesc( reg, key, &parent);
+    if ( err == REGERR_OK ) {
+
+        /* look up the named entry */
+        err = nr_FindAtLevel( reg, parent.value, name, &desc, &offPrev );
+        if ( err == REGERR_OK ) {
+
+            XP_ASSERT( TYPE_IS_ENTRY( desc.type ) );
+
+            /* if entry is the head of a chain */
+            if ( offPrev == 0 ) {
+                /* hook parent key to next entry */
+                XP_ASSERT( parent.value == desc.location );
+                parent.value = desc.left;
+            }
+            else {
+                /* otherwise hook previous entry to next */
+                err = nr_ReadDesc( reg, offPrev, &parent );
+                parent.left = desc.left;
+            }
+            /* write out changed desc for previous node */
+            if ( err == REGERR_OK ) {
+                err = nr_WriteDesc( reg, &parent );
+                /* zap the deleted desc because an enum state may contain a
+                 * reference to a specific entry node
+                 */
+                if ( err == REGERR_OK ) {
+                    desc.type |= REGTYPE_DELETED;
+                    err = nr_WriteDesc( reg, &desc );
+                }
+            }
+        }
+    }
+
+    /* unlock registry */
+    nr_Unlock( reg );
+
+    return err;
+
+}   /* NR_RegDeleteEntry */
+
+
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegEnumSubkeys - Enumerate the subkey names for the specified key
+ *
+ * Returns REGERR_NOMORE at end of enumeration.
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY of key to enumerate--obtain with NR_RegGetKey()
+ *    eState   - enumerations state, must contain NULL to start
+ *    buffer   - location to store subkey names.  Once an enumeration
+ *               is started user must not modify contents since values
+ *               are built using the previous contents.
+ *    bufsize  - size of buffer for names
+ *    style    - 0 returns direct child keys only, REGENUM_DESCEND
+ *               returns entire sub-tree
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegEnumSubkeys( HREG hReg, RKEY key, REGENUM *state,
+                                    char *buffer, uint32 bufsize, uint32 style)
+{
+    REGERR      err;
+    REGFILE*    reg;
+    REGDESC     desc;
+
+    XP_ASSERT( regStartCount > 0 );
+
+    /* verify parameters */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    if ( key == 0 || state == NULL || buffer == NULL )
+        return REGERR_PARAM;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    /* lock registry */
+    err = nr_Lock( reg );
+    if ( err != REGERR_OK )
+        return err;
+
+    desc.down     = 0; /* initialize to quiet warnings */
+    desc.location = 0;
+
+    /* verify starting key */
+    key = nr_TranslateKey( reg, key );
+    if ( key == 0 )
+        err = REGERR_PARAM;
+    else if ( *state == 0 )
+        err = nr_ReadDesc( reg, key, &desc);
+    else
+        err = REGERR_OK;
+
+    if ( err == REGERR_OK )
+    {
+        /* if in initial state and no children return now */
+        if ( *state == 0 && desc.down == 0 ) 
+        {
+            err = REGERR_NOMORE;
+        }
+        else switch ( style )
+        {
+          case REGENUM_CHILDREN:
+            *buffer = '\0';
+            if ( *state == 0 ) 
+            {
+                /* initial state: get first child (.down) */
+                err = nr_ReplaceName( reg, desc.down, buffer, bufsize, &desc );
+            }
+            else 
+            {
+                /* get sibling (.left) of current key */
+                err = nr_ReadDesc( reg, *state, &desc );
+                if ( err == REGERR_OK || REGERR_DELETED == err )
+                {
+                    /* it's OK for the current (state) node to be deleted */
+                    if ( desc.left != 0 ) 
+                    {
+                        err = nr_ReplaceName( reg, desc.left, 
+                                    buffer, bufsize, &desc );
+                    }
+                    else
+                        err = REGERR_NOMORE;
+                }
+            }
+            break;
+
+
+          case REGENUM_DESCEND:
+            if ( *state == 0 ) 
+            {
+                /* initial state */
+                *buffer = '\0';
+                err = nr_ReplaceName( reg, desc.down, buffer, bufsize, &desc );
+            }
+            else 
+            {
+                /* get last position */
+                err = nr_ReadDesc( reg, *state, &desc );
+                if ( REGERR_OK != err && REGERR_DELETED != err ) 
+                {
+                    /* it is OK for the state node to be deleted
+                     * (the *next* node MUST be "live", though).
+                     * bail out on any other error */
+                    break;
+                }
+
+                if ( desc.down != 0 ) {
+                    /* append name of first child key */
+                    err = nr_CatName( reg, desc.down, buffer, bufsize, &desc );
+                }
+                else if ( desc.left != 0 ) {
+                    /* replace last segment with next sibling */
+                    err = nr_ReplaceName( reg, desc.left, 
+                                buffer, bufsize, &desc );
+                }
+                else {
+                  /* done with level, pop up as many times as necessary */
+                    while ( err == REGERR_OK ) 
+                    {
+                        if ( desc.parent != key && desc.parent != 0 ) 
+                        {
+                            err = nr_RemoveName( buffer );
+                            if ( err == REGERR_OK ) 
+                            {
+                                err = nr_ReadDesc( reg, desc.parent, &desc );
+                                if ( err == REGERR_OK && desc.left != 0 ) 
+                                {
+                                    err = nr_ReplaceName( reg, desc.left, 
+                                                buffer, bufsize, &desc );
+                                    break;  /* found a node */
+                                }
+                            }
+                        }
+                        else
+                            err = REGERR_NOMORE;
+                    }
+                }
+            }
+            break;
+
+
+          case REGENUM_DEPTH_FIRST:
+            if ( *state == 0 ) 
+            {
+                /* initial state */
+
+                *buffer = '\0';
+                err = nr_ReplaceName( reg, desc.down, buffer, bufsize, &desc );
+                while ( REGERR_OK == err && desc.down != 0 )
+                {
+                    /* start as far down the tree as possible */
+                    err = nr_CatName( reg, desc.down, buffer, bufsize, &desc );
+                }
+            }
+            else 
+            {
+                /* get last position */
+                err = nr_ReadDesc( reg, *state, &desc );
+                if ( REGERR_OK != err && REGERR_DELETED != err ) 
+                {
+                    /* it is OK for the state node to be deleted
+                     * (the *next* node MUST be "live", though).
+                     * bail out on any other error */
+                    break;
+                }
+
+                if ( desc.left != 0 )
+                {
+                    /* get sibling, then descend as far as possible */
+                    err = nr_ReplaceName(reg, desc.left, buffer,bufsize,&desc);
+
+                    while ( REGERR_OK == err && desc.down != 0 ) 
+                    {
+                        err = nr_CatName(reg, desc.down, buffer,bufsize,&desc);
+                    }
+                }
+                else 
+                {
+                    /* pop up to parent */
+                    if ( desc.parent != key && desc.parent != 0 )
+                    {
+                        err = nr_RemoveName( buffer );
+                        if ( REGERR_OK == err )
+                        {
+                            /* validate parent key */
+                            err = nr_ReadDesc( reg, desc.parent, &desc );
+                        }
+                    }
+                    else 
+                        err = REGERR_NOMORE;
+                }
+            }
+            break;
+
+
+          default:
+            err = REGERR_PARAM;
+            break;
+        }
+    }
+
+    /* set enum state to current key */
+    if ( err == REGERR_OK ) {
+        *state = desc.location;
+    }
+
+    /* unlock registry */
+    nr_Unlock( reg );
+
+    return err;
+
+}   /* NR_RegEnumSubkeys */
+
+
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegEnumEntries - Enumerate the entry names for the specified key
+ *
+ * Returns REGERR_NOMORE at end of enumeration.
+ *
+ * Parameters:
+ *    hReg     - handle of open registry
+ *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
+ *    eState   - enumerations state, must contain NULL to start
+ *    buffer   - location to store entry names
+ *    bufsize  - size of buffer for names
+ *    info     - optional REGINFO for the entry. If not NULL must be 
+ *               initialized as in NR_RegGetEntryInfo()
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegEnumEntries( HREG hReg, RKEY key, REGENUM *state,
+                            char *buffer, uint32 bufsize, REGINFO *info )
+{
+    REGERR      err;
+    REGFILE*    reg;
+    REGDESC     desc;
+
+    XP_ASSERT( regStartCount > 0 );
+
+    /* verify parameters */
+    err = VERIFY_HREG( hReg );
+    if ( err != REGERR_OK )
+        return err;
+
+    if ( key == 0 || state == NULL || buffer == NULL )
+        return REGERR_PARAM;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    /* lock registry */
+    err = nr_Lock( reg );
+    if ( err != REGERR_OK )
+        return err;
+    
+    /* verify starting key */
+    err = nr_ReadDesc( reg, key, &desc);
+    if ( err == REGERR_OK )
+    {
+        if ( *state == 0 ) 
+        {
+            /* initial state--get first entry */
+            if ( desc.value != 0 ) 
+            {
+                *buffer = '\0';
+                err =  nr_ReplaceName( reg, desc.value, buffer, bufsize, &desc );
+            }
+            else  
+            { 
+                /* there *are* no entries */
+                err = REGERR_NOMORE;
+            }
+        }
+        else 
+        {
+            /* 'state' stores previous entry */
+            err = nr_ReadDesc( reg, *state, &desc );
+            if ( err == REGERR_OK  || err == REGERR_DELETED ) 
+            {
+                /* get next entry in chain */
+                if ( desc.left != 0 ) 
+                {
+                    *buffer = '\0';
+                    err =  nr_ReplaceName( reg, desc.left, buffer, bufsize, &desc );
+                }
+                else 
+                {
+                    /* at end of chain */
+                    err = REGERR_NOMORE;
+                }
+            }
+        }
+
+        /* if we found an entry */
+        if ( err == REGERR_OK ) 
+        {
+            /* set enum state to current entry */
+            *state = desc.location;
+
+            /* return REGINFO if requested */
+            if ( info != NULL && info->size >= sizeof(REGINFO) ) 
+            {
+                info->entryType   = desc.type;
+                info->entryLength = desc.valuelen;
+            }
+        }
+    }
+
+    /* unlock registry */
+    nr_Unlock( reg );
+
+    return err;
+
+}   /* NR_RegEnumEntries */
+
+
+
+
+
+/* --------------------------------------------------------------------
+ * Registry Packing
+ * --------------------------------------------------------------------
+ */
+#ifndef STANDALONE_REGISTRY
+#include "VerReg.h"
+
+#ifdef RESURRECT_LATER
+static REGERR nr_createTempRegName( char *filename, uint32 filesize );
+static REGERR nr_addNodesToNewReg( HREG hReg, RKEY rootkey, HREG hRegNew, void *userData, nr_RegPackCallbackFunc fn );
+/* -------------------------------------------------------------------- */
+static REGERR nr_createTempRegName( char *filename, uint32 filesize )
+{
+    struct stat statbuf;
+    XP_Bool nameFound = FALSE;
+    char tmpname[MAX_PATH+1];
+    uint32 len;
+    int err;
+
+    XP_STRCPY( tmpname, filename );
+    len = XP_STRLEN(tmpname);
+    if (len < filesize) {
+        tmpname[len-1] = '~';
+        tmpname[len] = '\0';
+        remove(tmpname);
+        if ( stat(tmpname, &statbuf) != 0 )
+            nameFound = TRUE;
+    }
+    len++;
+    while (!nameFound && len < filesize ) {
+        tmpname[len-1] = '~';
+        tmpname[len] = '\0';
+        remove(tmpname);
+        if ( stat(tmpname, &statbuf) != 0 )
+            nameFound = TRUE;
+        else
+            len++;
+    }  
+    if (nameFound) {
+        XP_STRCPY(filename, tmpname);
+        err = REGERR_OK;
+    } else {
+        err = REGERR_FAIL;
+    }
+   return err;
+}
+
+static REGERR nr_addNodesToNewReg( HREG hReg, RKEY rootkey, HREG hRegNew, void *userData, nr_RegPackCallbackFunc fn )
+{
+    char keyname[MAXREGPATHLEN+1] = {0};
+    char entryname[MAXREGPATHLEN+1] = {0};
+    void *buffer;
+    uint32 bufsize = 2024;
+    uint32 datalen;
+    REGENUM state = 0;
+    REGENUM entrystate = 0;
+    REGINFO info;
+    int err = REGERR_OK;
+    int status = REGERR_OK;
+    RKEY key;
+    RKEY newKey;
+    REGFILE* reg;
+    REGFILE* regNew;
+    static int32 cnt = 0;
+    static int32 prevCnt = 0;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+    regNew = ((REGHANDLE*)hRegNew)->pReg;
+
+    buffer = XP_ALLOC(bufsize);
+    if ( buffer == NULL ) {
+        err = REGERR_MEMORY;
+        return err;
+    }
+
+    while (err == REGERR_OK)
+    {
+        err = NR_RegEnumSubkeys( hReg, rootkey, &state, keyname, sizeof(keyname), REGENUM_DESCEND );
+        if ( err != REGERR_OK )
+            break;
+        err = NR_RegAddKey( hRegNew, rootkey, keyname, &newKey );
+        if ( err != REGERR_OK )
+            break;
+        cnt++;
+        if (cnt >= prevCnt + 15) 
+        {
+            fn(userData, regNew->hdr.avail, reg->hdr.avail);
+            prevCnt = cnt;
+        }
+        err = NR_RegGetKey( hReg, rootkey, keyname, &key );
+        if ( err != REGERR_OK )
+            break;
+        entrystate = 0;
+        status = REGERR_OK;
+        while (status == REGERR_OK) {
+            info.size = sizeof(REGINFO);
+            status = NR_RegEnumEntries( hReg, key, &entrystate, entryname, 
+                                        sizeof(entryname), &info );
+            if ( status == REGERR_OK ) {
+                XP_ASSERT( bufsize >= info.entryLength );
+                datalen = bufsize;
+                status = NR_RegGetEntry( hReg, key, entryname, buffer, &datalen );
+                XP_ASSERT( info.entryLength == datalen );
+                if ( status == REGERR_OK ) {
+                    /* copy entry */
+                    status = NR_RegSetEntry( hRegNew, newKey, entryname, 
+                                info.entryType, buffer, info.entryLength );
+                }
+            } 
+        }
+        if ( status != REGERR_NOMORE ) {
+            /* pass real error to outer loop */
+            err = status;
+        }
+    }
+
+    if ( err == REGERR_NOMORE )
+        err = REGERR_OK;
+
+    XP_FREEIF(buffer);
+    return err;
+}
+#endif /* RESURRECT_LATER */
+
+
+
+/* ---------------------------------------------------------------------
+ * NR_RegPack    - Pack an open registry.  
+ *                Registry is locked the entire time.
+ *
+ * Parameters:
+ *    hReg     - handle of open registry to pack
+ * ---------------------------------------------------------------------
+ */
+VR_INTERFACE(REGERR) NR_RegPack( HREG hReg, void *userData, nr_RegPackCallbackFunc fn)
+{
+    return REGERR_FAIL; /* XXX resurrect after mozilla beta 1 */
+#if RESURRECT_LATER
+    XP_File  fh;
+    REGFILE* reg;
+    HREG hRegTemp;
+    char tempfilename[MAX_PATH+1] = {0};
+    char oldfilename[MAX_PATH+1] = {0};
+
+    XP_Bool bCloseTempFile = FALSE;
+
+    int err = REGERR_OK;
+    RKEY key;
+
+    XP_ASSERT( regStartCount > 0 );
+    if ( regStartCount <= 0 )
+        return REGERR_FAIL;
+
+    reg = ((REGHANDLE*)hReg)->pReg;
+
+    /* lock registry */
+    err = nr_Lock( reg );
+    if ( err != REGERR_OK )
+        return err; 
+
+    PR_Lock(reglist_lock); 
+    XP_STRCPY(tempfilename, reg->filename);
+    err = nr_createTempRegName(tempfilename, sizeof(tempfilename));
+    if ( err != REGERR_OK )
+        goto safe_exit; 
+     
+    /* force file creation */
+    fh = vr_fileOpen(tempfilename, XP_FILE_WRITE_BIN);
+    if ( !VALID_FILEHANDLE(fh) ) {
+        err = REGERR_FAIL;
+        goto safe_exit;
+    }
+    XP_FileClose(fh);
+
+    err = NR_RegOpen(tempfilename, &hRegTemp);
+    if ( err != REGERR_OK )
+        goto safe_exit;
+    bCloseTempFile = TRUE;
+
+    /* must open temp file first or we get the same name twice */
+    XP_STRCPY(oldfilename, reg->filename);
+    err = nr_createTempRegName(oldfilename, sizeof(oldfilename));
+    if ( err != REGERR_OK )
+        goto safe_exit; 
+     
+    key = ROOTKEY_PRIVATE;
+    err = nr_addNodesToNewReg( hReg, key, hRegTemp, userData, fn);
+    if ( err != REGERR_OK  )
+        goto safe_exit;
+    key = ROOTKEY_VERSIONS;
+    err = nr_addNodesToNewReg( hReg, key, hRegTemp, userData, fn);
+    if ( err != REGERR_OK  )
+        goto safe_exit;
+    key = ROOTKEY_COMMON;
+    err = nr_addNodesToNewReg( hReg, key, hRegTemp, userData, fn);
+    if ( err != REGERR_OK  )
+        goto safe_exit;
+    key = ROOTKEY_USERS;
+    err = nr_addNodesToNewReg( hReg, key, hRegTemp, userData, fn);
+    if ( err != REGERR_OK  )
+        goto safe_exit;
+
+    err = NR_RegClose(hRegTemp);
+    bCloseTempFile = FALSE;
+  
+    /* close current reg file so we can rename it */
+    XP_FileClose(reg->fh);
+   
+    /* rename current reg file out of the way */
+    err = nr_RenameFile(reg->filename, oldfilename);
+    if ( err == -1 ) {
+        /* rename failed, get rid of the new registry and reopen the old one*/
+        remove(tempfilename);
+        reg->fh = vr_fileOpen(reg->filename, XP_FILE_UPDATE_BIN);
+        goto safe_exit;
+    }
+
+    /* rename packed registry to the correct name */
+    err = nr_RenameFile(tempfilename, reg->filename);
+    if ( err == -1 ) {
+        /* failure, recover original registry */
+        err = nr_RenameFile(oldfilename, reg->filename);
+        remove(tempfilename);
+        reg->fh = vr_fileOpen(reg->filename, XP_FILE_UPDATE_BIN);
+        goto safe_exit;
+    
+    } else {
+        remove(oldfilename); 
+    }
+    reg->fh = vr_fileOpen(reg->filename, XP_FILE_UPDATE_BIN);
+
+safe_exit:
+    if ( bCloseTempFile ) {
+        NR_RegClose(hRegTemp);
+    }
+    PR_Unlock( reglist_lock );
+    nr_Unlock(reg);
+    return err;
+#endif /* RESURRECT_LATER */
+}
+
+#endif /* STANDALONE_REGISTRY */
+
+
+
+
+
+
+/* ---------------------------------------------------------------------
+ * ---------------------------------------------------------------------
+ * Registry initialization and shut-down
+ * ---------------------------------------------------------------------
+ * ---------------------------------------------------------------------
+ */
+
+#include "VerReg.h"
+
+#ifndef STANDALONE_REGISTRY
+extern PRLock *vr_lock;
+#endif 
+
+
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(STANDALONE_REGISTRY)
+extern XP_Bool bGlobalRegistry;
+#endif
+
+VR_INTERFACE(REGERR) NR_StartupRegistry(void)
+{
+    REGERR status = REGERR_OK;
+
+#ifndef STANDALONE_REGISTRY
+    if ( reglist_lock == NULL ) {
+        reglist_lock = PR_NewLock();
+    }
+
+    if ( reglist_lock != NULL ) {
+        PR_Lock( reglist_lock );
+    }
+    else {
+        XP_ASSERT( reglist_lock );
+        status = REGERR_FAIL;
+    }
+#endif
+
+    if ( status == REGERR_OK )
+    {
+        ++regStartCount;
+        if ( regStartCount == 1 )
+        {
+            /* first time only initialization */
+            vr_findGlobalRegName();
+
+#ifndef STANDALONE_REGISTRY
+            /* initialization for version registry */
+            vr_lock = PR_NewLock();
+            XP_ASSERT( vr_lock != NULL );
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+            bGlobalRegistry = ( getenv(UNIX_GLOBAL_FLAG) != NULL );
+#endif
+#endif 
+        } /* if ( regStartCount == 1 ) */
+
+        PR_Unlock( reglist_lock );
+    }
+
+    return status;
+}   /* NR_StartupRegistry */
+
+VR_INTERFACE(void) NR_ShutdownRegistry(void)
+{
+    REGFILE* pReg;
+    XP_Bool  bDestroyLocks = FALSE;
+
+    /* people should track whether NR_StartupRegistry() was successful
+     * and not call this if it fails... but they won't so we'll try to
+     * handle that case gracefully.
+     */
+#ifndef STANDALONE_REGISTRY
+    if ( reglist_lock == NULL ) 
+        return;  /* was not started successfully */
+#else
+    if ( regStartCount == 0 )
+        return;  /* was not started successfully */
+#endif
+
+    PR_Lock( reglist_lock );
+
+    --regStartCount;
+    if ( regStartCount == 0 )
+    {
+        /* shutdown for real. */
+
+        /* close any forgotten open registries */
+        while ( RegList != NULL ) 
+        {
+            pReg = RegList;
+            if ( pReg->hdrDirty ) {
+                nr_WriteHdr( pReg );
+            }
+            nr_CloseFile( &(pReg->fh) );
+            nr_DeleteNode( pReg );
+        }
+    
+        XP_FREEIF(user_name);
+        XP_FREEIF(globalRegName);
+        XP_FREEIF(verRegName);
+
+        bDestroyLocks = TRUE;
+    }
+
+    PR_Unlock( reglist_lock );
+
+#ifndef STANDALONE_REGISTRY    
+    if ( bDestroyLocks ) 
+    {
+        PR_DestroyLock( reglist_lock );
+        reglist_lock = NULL;
+
+        PR_DestroyLock(vr_lock);
+        vr_lock = NULL;
+    }
+#endif
+
+}   /* NR_ShutdownRegistry */
+
+/* EOF: reg.c */
new file mode 100644
--- /dev/null
+++ b/modules/libreg/src/reg.h
@@ -0,0 +1,196 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Daniel Veditz <dveditz@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/* reg.h
+ * XP Registry functions (prototype)
+ */
+
+#ifndef _REG_H_
+#define _REG_H_
+
+#include "vr_stubs.h"
+
+#ifndef STANDALONE_REGISTRY
+#include "prlock.h"
+#endif
+
+/* --------------------------------------------------------------------
+ * Miscellaneous Definitions
+ * --------------------------------------------------------------------
+ */
+#define MAGIC_NUMBER    0x76644441L
+#define MAJOR_VERSION   2          /* major version for incompatible changes */
+#define MINOR_VERSION   2          /* minor ver for new (compatible) features */
+#define PATHDEL         '/'
+#define HDRRESERVE      128        /* number of bytes reserved for hdr */
+#define INTSIZE         4
+#define DOUBLESIZE      8
+
+#define PACKBUFFERSIZE  2048
+
+
+/* Node types */
+#define REGTYPE_KEY                   (1)
+#define REGTYPE_DELETED               (0x0080)
+
+/* Private standard keys */
+#define ROOTKEY                       (0x20)
+#define ROOTKEY_VERSIONS              (0x21)
+
+/* strings for standard keys */
+#define ROOTKEY_STR             "/"
+#define ROOTKEY_VERSIONS_STR    "Version Registry"
+#define ROOTKEY_USERS_STR       "Users"
+#define ROOTKEY_COMMON_STR      "Common"
+#define ROOTKEY_PRIVATE_STR     "Private Arenas"
+
+#define OLD_VERSIONS_STR        "ROOTKEY_VERSIONS"
+#define OLD_USERS_STR           "ROOTKEY_USERS"
+#define OLD_COMMON_STR          "ROOTKEY_COMMON"
+
+/* needs to be kept in sync with PE. see ns/cmd/winfe/profile.h */
+/* and ns/cmd/macfe/central/profile.cp */
+#define ASW_MAGIC_PROFILE_NAME "User1"
+
+/* macros */
+#define COPYDESC(dest,src)  memcpy((dest),(src),sizeof(REGDESC))
+
+#define VALID_FILEHANDLE(fh)    ((fh) != NULL)
+
+#define INVALID_NAME_CHAR(p)    ( ((unsigned char)(p) < 0x20) )
+
+#define TYPE_IS_ENTRY(type)       ( (type) & REGTYPE_ENTRY )
+#define TYPE_IS_KEY(type)         ( !((type) & REGTYPE_ENTRY) )
+
+#define VERIFY_HREG(h)\
+    ( ((h) == NULL) ? REGERR_PARAM : \
+    ( (((REGHANDLE*)(h))->magic == MAGIC_NUMBER) ? REGERR_OK : REGERR_BADMAGIC ) )
+
+
+
+/* --------------------------------------------------------------------
+ * Types and Objects
+ * --------------------------------------------------------------------
+ */
+#undef REGOFF
+typedef int32 REGOFF;   /* offset into registry file */
+
+typedef struct _desc
+{
+    REGOFF  location;   /* this object's offset (for verification) */
+    REGOFF  name;       /* name string */
+    uint16  namelen;    /* length of name string (including terminator) */
+    uint16  type;       /* node type (key, or entry style) */
+    REGOFF  left;       /* next object at this level (0 if none) */
+    REGOFF  down;       /* KEY: first subkey        VALUE: 0 */
+    REGOFF  value;      /* KEY: first entry object  VALUE: value string */
+    uint32  valuelen;   /* KEY: 0  VALUE: length of value data */
+    uint32  valuebuf;   /* KEY: 0  VALUE: length available */
+    REGOFF  parent;     /* the node on the immediate level above */
+} REGDESC;
+
+/* offsets into structure on disk */
+#define DESC_LOCATION   0
+#define DESC_NAME       4
+#define DESC_NAMELEN    8
+#define DESC_TYPE       10
+#define DESC_LEFT       12
+#define DESC_DOWN       16
+#define DESC_VALUE      20
+#define DESC_VALUELEN   24
+#define DESC_VALUEBUF   16    /* stored in place of "down" for entries */
+#define DESC_PARENT     28
+
+#define DESC_SIZE       32    /* size of desc on disk */
+
+typedef struct _hdr
+{
+    uint32  magic;      /* must equal MAGIC_NUMBER */
+    uint16  verMajor;   /* major version number */
+    uint16  verMinor;   /* minor version number */
+    REGOFF  avail;      /* next available offset */
+    REGOFF  root;       /* root object */
+} REGHDR;
+
+/* offsets into structure on disk*/
+#define HDR_MAGIC       0
+#define HDR_VERMAJOR    4
+#define HDR_VERMINOR    6
+#define HDR_AVAIL       8
+#define HDR_ROOT        12
+
+typedef XP_File FILEHANDLE; /* platform-specific file reference */
+
+typedef struct _stdnodes {
+    REGOFF          versions;
+    REGOFF          users;
+    REGOFF          common;
+    REGOFF          current_user;
+    REGOFF          privarea;
+} STDNODES;
+
+typedef struct _regfile
+{
+    FILEHANDLE      fh;
+    REGHDR          hdr;
+    int             refCount;
+    int             hdrDirty;
+    int             inInit;
+    int             readOnly;
+    char *          filename;
+    STDNODES        rkeys;
+    struct _regfile *next;
+    struct _regfile *prev;
+#ifndef STANDALONE_REGISTRY
+    PRLock          *lock;
+    PRUint64        uniqkey;
+#endif
+} REGFILE;
+
+typedef struct _reghandle
+{
+    uint32          magic;     /* for validating reg handles */
+    REGFILE         *pReg;     /* the real registry file object */
+} REGHANDLE;
+
+
+#endif  /* _REG_H_ */
+
+/* EOF: reg.h */
+
new file mode 100644
--- /dev/null
+++ b/modules/libreg/src/vr_stubs.c
@@ -0,0 +1,507 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* this file contains stubs needed to build the registry routines
+ * into a stand-alone library for use with our installers
+ */
+
+#ifdef STANDALONE_REGISTRY
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#else
+
+#include "prtypes.h"
+#include "plstr.h"
+
+#endif /*STANDALONE_REGISTRY*/
+
+#include "vr_stubs.h"
+
+#ifdef XP_MACOSX
+#include <Carbon/Carbon.h>
+#include <stdlib.h>
+#endif
+
+#ifdef XP_BEOS
+#include <FindDirectory.h>
+#endif 
+
+#ifdef XP_MACOSX
+/* So that we're not dependent on the size of chars in a wide string literal */
+static const UniChar kOSXRegParentName[] =
+  { 'M', 'o', 'z', 'i', 'l', 'l', 'a' };
+static const UniChar kOSXRegName[] =
+  { 'G', 'l', 'o', 'b', 'a', 'l', '.', 'r', 'e', 'g', 's' };
+static const UniChar kOSXVersRegName[] =
+  { 'V', 'e', 'r', 's', 'i', 'o', 'n', 's', '.', 'r', 'e', 'g', 's' };
+
+#define UNICHAR_ARRAY_LEN(s) (sizeof(s) / sizeof(UniChar))
+#endif
+
+#define DEF_REG "/.mozilla/registry"
+#define WIN_REG "\\mozregistry.dat"
+#define MAC_REG "\pMozilla Registry"
+#define BEOS_REG "/mozilla/registry"
+
+#define DEF_VERREG "/.mozilla/mozver.dat"
+#define WIN_VERREG "\\mozver.dat"
+#define MAC_VERREG "\pMozilla Versions"
+#define BEOS_VERREG "/mozilla/mozver.dat"
+
+
+/* ------------------------------------------------------------------
+ *  OS/2 STUBS
+ * ------------------------------------------------------------------
+ */
+#ifdef XP_OS2
+#define INCL_DOS
+#include <os2.h>
+
+#ifdef STANDALONE_REGISTRY
+extern XP_File vr_fileOpen (const char *name, const char * mode)
+{
+    XP_File fh = NULL;
+    struct stat st;
+
+    if ( name != NULL ) {
+        if ( stat( name, &st ) == 0 )
+            fh = fopen( name, XP_FILE_UPDATE_BIN );
+        else
+            fh = fopen( name, XP_FILE_TRUNCATE_BIN );
+    }
+
+    return fh;
+}
+#endif /*STANDALONE_REGISTRY*/
+
+extern void vr_findGlobalRegName ()
+{
+    char    path[ CCHMAXPATH ];
+    int     pathlen;
+    XP_File fh = NULL;
+    struct stat st;
+
+    XP_STRCPY(path, ".");
+    pathlen = strlen(path);
+
+    if ( pathlen > 0 ) {
+        XP_STRCPY( path+pathlen, WIN_REG );
+        globalRegName = XP_STRDUP(path);
+    }
+}
+
+char* vr_findVerRegName()
+{
+    /* need to find a global place for the version registry */
+    if ( verRegName == NULL )
+    {
+        if ( globalRegName == NULL)
+            vr_findGlobalRegName();
+        verRegName = XP_STRDUP(globalRegName);
+    }
+
+    return verRegName;
+}
+
+#endif /* XP_OS2 */
+
+
+/* ------------------------------------------------------------------
+ *  WINDOWS STUBS
+ * ------------------------------------------------------------------
+ */
+#if defined(XP_WIN)
+#include "windows.h"
+#define PATHLEN 260
+
+#ifdef STANDALONE_REGISTRY
+extern XP_File vr_fileOpen (const char *name, const char * mode)
+{
+    XP_File fh = NULL;
+    struct stat st;
+
+    if ( name != NULL ) {
+        if ( stat( name, &st ) == 0 )
+            fh = fopen( name, XP_FILE_UPDATE_BIN );
+        else
+            fh = fopen( name, XP_FILE_TRUNCATE_BIN );
+    }
+
+    return fh;
+}
+#endif /*STANDALONE_REGISTRY*/
+
+extern void vr_findGlobalRegName ()
+{
+    char    path[ PATHLEN ];
+    int     pathlen;
+   
+    pathlen = GetWindowsDirectory(path, PATHLEN);
+    if ( pathlen > 0 ) {
+        XP_FREEIF(globalRegName);
+        XP_STRCPY( path+pathlen, WIN_REG );
+        globalRegName = XP_STRDUP(path);
+    }
+}
+
+char* vr_findVerRegName()
+{
+    char    path[ PATHLEN ];
+    int     pathlen;
+   
+    if ( verRegName == NULL )
+    {
+        pathlen = GetWindowsDirectory(path, PATHLEN);
+        if ( pathlen > 0 ) {
+            XP_STRCPY( path+pathlen, WIN_VERREG );
+            verRegName = XP_STRDUP(path);
+        }
+    }
+
+    return verRegName;
+}
+
+#if !defined(WIN32) && !defined(__BORLANDC__)
+int FAR PASCAL _export WEP(int);
+
+int FAR PASCAL LibMain(HANDLE hInst, WORD wDataSeg, WORD wHeapSize, LPSTR lpszCmdLine)
+{
+    if ( wHeapSize > 0 )
+        UnlockData(0);
+    return 1;
+}
+
+int FAR PASCAL _export WEP(int nParam)
+{ 
+    return 1; 
+}
+#endif /* not WIN32 */
+
+#endif /* XP_WIN */
+
+
+/* ------------------------------------------------------------------
+ *  MACINTOSH STUBS
+ * ------------------------------------------------------------------
+ */
+
+#if defined(XP_MACOSX)
+
+#ifdef STANDALONE_REGISTRY
+extern XP_File vr_fileOpen(const char *name, const char * mode)
+{
+    XP_File fh = NULL;
+    struct stat st;
+
+    errno = 0; /* reset errno (only if we're using stdio) */
+
+    if ( name != NULL ) {
+        if ( stat( name, &st ) == 0 )
+            fh = fopen( name, XP_FILE_UPDATE_BIN );
+        else 
+        {
+            /* should never get here! */
+            fh = fopen( name, XP_FILE_TRUNCATE_BIN );
+        }
+    }
+    return fh;
+}
+#endif /*STANDALONE_REGISTRY*/
+
+extern void vr_findGlobalRegName()
+{
+    OSErr   err;
+    FSRef   foundRef;
+    
+    err = FSFindFolder(kLocalDomain, kDomainLibraryFolderType, kDontCreateFolder, &foundRef);
+    if (err == noErr)
+    {
+        FSRef parentRef;
+        err = FSMakeFSRefUnicode(&foundRef, UNICHAR_ARRAY_LEN(kOSXRegParentName), kOSXRegParentName,
+                                 kTextEncodingUnknown, &parentRef);
+        if (err == fnfErr)
+        {
+            err = FSCreateDirectoryUnicode(&foundRef, UNICHAR_ARRAY_LEN(kOSXRegParentName), kOSXRegParentName,
+                                           kFSCatInfoNone, NULL, &parentRef, NULL, NULL);
+        }
+        if (err == noErr)
+        {
+            FSRef regRef;
+            err = FSMakeFSRefUnicode(&parentRef, UNICHAR_ARRAY_LEN(kOSXRegName), kOSXRegName,
+                                     kTextEncodingUnknown, &regRef);
+            if (err == fnfErr)
+            {
+                FSCatalogInfo catalogInfo;
+                FileInfo fileInfo = { 'REGS', 'MOSS', 0, { 0, 0 }, 0 };
+                memmove(&(catalogInfo.finderInfo), &fileInfo, sizeof(FileInfo));
+                err = FSCreateFileUnicode(&parentRef, UNICHAR_ARRAY_LEN(kOSXRegName), kOSXRegName,
+                                               kFSCatInfoFinderInfo, &catalogInfo, &regRef, NULL);
+            }
+            if (err == noErr)
+            {
+                UInt8 pathBuf[PATH_MAX];
+                err = FSRefMakePath(&regRef, pathBuf, sizeof(pathBuf));
+                if (err == noErr)
+                    globalRegName = XP_STRDUP((const char*)pathBuf);
+            }
+        }
+    }
+}
+
+extern char* vr_findVerRegName()
+{
+    OSErr   err;
+    FSRef   foundRef;
+    
+    err = FSFindFolder(kLocalDomain, kDomainLibraryFolderType, kDontCreateFolder, &foundRef);
+    if (err == noErr)
+    {
+        FSRef parentRef;
+        err = FSMakeFSRefUnicode(&foundRef, UNICHAR_ARRAY_LEN(kOSXRegParentName), kOSXRegParentName,
+                                 kTextEncodingUnknown, &parentRef);
+        if (err == fnfErr)
+        {
+            err = FSCreateDirectoryUnicode(&foundRef, UNICHAR_ARRAY_LEN(kOSXRegParentName), kOSXRegParentName,
+                                           kFSCatInfoNone, NULL, &parentRef, NULL, NULL);
+        }
+        if (err == noErr)
+        {
+            FSRef regRef;
+            err = FSMakeFSRefUnicode(&parentRef, UNICHAR_ARRAY_LEN(kOSXVersRegName), kOSXVersRegName,
+                                     kTextEncodingUnknown, &regRef);
+            if (err == fnfErr)
+            {
+                FSCatalogInfo catalogInfo;
+                FileInfo fileInfo = { 'REGS', 'MOSS', 0, { 0, 0 }, 0 };
+                memmove(&(catalogInfo.finderInfo), &fileInfo, sizeof(FileInfo));
+                err = FSCreateFileUnicode(&parentRef, UNICHAR_ARRAY_LEN(kOSXVersRegName), kOSXVersRegName,
+                                               kFSCatInfoFinderInfo, &catalogInfo, &regRef, NULL);
+            }
+            if (err == noErr)
+            {
+                UInt8 pathBuf[PATH_MAX];
+                err = FSRefMakePath(&regRef, pathBuf, sizeof(pathBuf));
+                if (err == noErr)
+                    verRegName = XP_STRDUP((const char*)pathBuf);
+            }
+        }
+    }
+    return verRegName;
+}
+
+#endif /* XP_MACOSX */
+
+
+/* ------------------------------------------------------------------
+ *  UNIX STUBS
+ * ------------------------------------------------------------------
+ */
+
+#if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_BEOS)
+
+#include <stdlib.h>
+
+#ifdef XP_OS2
+#include <io.h>
+#define W_OK 0x02 /*evil hack from the docs...*/
+#else
+#include <unistd.h>
+#endif
+
+#include "NSReg.h"
+#include "VerReg.h"
+
+char *TheRegistry = "registry"; 
+char *Flist;
+
+REGERR vr_ParseVersion(char *verstr, VERSION *result);
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+
+#ifdef STANDALONE_REGISTRY
+extern XP_File vr_fileOpen (const char *name, const char * mode)
+{
+    XP_File fh = NULL;
+    struct stat st;
+
+    if ( name != NULL ) {
+        if ( stat( name, &st ) == 0 )
+            fh = fopen( name, XP_FILE_UPDATE_BIN );
+        else
+            fh = fopen( name, XP_FILE_TRUNCATE_BIN );
+    }
+
+    return fh;
+}
+#endif /*STANDALONE_REGISTRY*/
+
+extern void vr_findGlobalRegName ()
+{
+#ifndef STANDALONE_REGISTRY
+    char *def = NULL;
+    char *home = getenv("HOME");
+    if (home != NULL) {
+        def = (char *) XP_ALLOC(XP_STRLEN(home) + XP_STRLEN(DEF_REG)+1);
+        if (def != NULL) {
+          XP_STRCPY(def, home);
+          XP_STRCAT(def, DEF_REG);
+        }
+    }
+    if (def != NULL) {
+        globalRegName = XP_STRDUP(def);
+    } else {
+        globalRegName = XP_STRDUP(TheRegistry);
+    }
+    XP_FREEIF(def);
+#else
+    globalRegName = XP_STRDUP(TheRegistry);
+#endif /*STANDALONE_REGISTRY*/
+}
+
+char* vr_findVerRegName ()
+{
+    if ( verRegName != NULL )
+        return verRegName;
+
+#ifndef STANDALONE_REGISTRY
+    {
+        char *def = NULL;
+        char *home = getenv("HOME");
+        if (home != NULL) {
+            def = (char *) XP_ALLOC(XP_STRLEN(home) + XP_STRLEN(DEF_VERREG)+1);
+            if (def != NULL) {
+                XP_STRCPY(def, home);
+                XP_STRCAT(def, DEF_VERREG);
+            }
+        }
+        if (def != NULL) {
+            verRegName = XP_STRDUP(def);
+        }
+        XP_FREEIF(def);
+    }
+#else
+    verRegName = XP_STRDUP(TheRegistry);
+#endif /*STANDALONE_REGISTRY*/
+
+    return verRegName;
+}
+
+#endif /*XP_UNIX*/
+
+ /* ------------------------------------------------------------------
+ *  BeOS STUBS
+ * ------------------------------------------------------------------
+ */
+
+#ifdef XP_BEOS
+
+#ifdef STANDALONE_REGISTRY
+extern XP_File vr_fileOpen (const char *name, const char * mode)
+{
+    XP_File fh = NULL;
+    struct stat st;
+
+    if ( name != NULL ) {
+        if ( stat( name, &st ) == 0 )
+            fh = fopen( name, XP_FILE_UPDATE_BIN );
+        else
+            fh = fopen( name, XP_FILE_TRUNCATE_BIN );
+    }
+
+    return fh;
+}
+#endif /*STANDALONE_REGISTRY*/
+
+extern void vr_findGlobalRegName ()
+{
+#ifndef STANDALONE_REGISTRY
+    char *def = NULL;
+      char settings[1024];
+      find_directory(B_USER_SETTINGS_DIRECTORY, -1, false, settings, sizeof(settings));
+    if (settings != NULL) {
+        def = (char *) XP_ALLOC(XP_STRLEN(settings) + XP_STRLEN(BEOS_REG)+1);
+        if (def != NULL) {
+          XP_STRCPY(def, settings);
+          XP_STRCAT(def, BEOS_REG);
+        }
+    }
+    if (def != NULL) {
+        globalRegName = XP_STRDUP(def);
+    } else {
+        globalRegName = XP_STRDUP(TheRegistry);
+    }
+    XP_FREEIF(def);
+#else
+    globalRegName = XP_STRDUP(TheRegistry);
+#endif /*STANDALONE_REGISTRY*/
+}
+
+char* vr_findVerRegName ()
+{
+    if ( verRegName != NULL )
+        return verRegName;
+
+#ifndef STANDALONE_REGISTRY
+    {
+        char *def = NULL;
+        char settings[1024];
+        find_directory(B_USER_SETTINGS_DIRECTORY, -1, false, settings, sizeof(settings));
+        if (settings != NULL) {
+            def = (char *) XP_ALLOC(XP_STRLEN(settings) + XP_STRLEN(BEOS_VERREG)+1);
+            if (def != NULL) {
+                XP_STRCPY(def, settings);
+                XP_STRCAT(def, BEOS_VERREG);
+            }
+        }
+        if (def != NULL) {
+            verRegName = XP_STRDUP(def);
+        }
+        XP_FREEIF(def);
+    }
+#else
+    verRegName = XP_STRDUP(TheRegistry);
+#endif /*STANDALONE_REGISTRY*/
+
+    return verRegName;
+}
+
+#endif /*XP_BEOS*/
+
+#endif /* XP_UNIX || XP_OS2 */
new file mode 100644
--- /dev/null
+++ b/modules/libreg/src/vr_stubs.h
@@ -0,0 +1,283 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Daniel Veditz <dveditz@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/* vr_stubs.h
+ *
+ * XP code stubs for stand-alone registry library
+ *
+ */
+
+#ifndef _VR_STUBS_H_
+#define _VR_STUBS_H_
+
+#ifdef STANDALONE_REGISTRY
+
+#include <errno.h>
+#include <string.h>
+
+#else
+
+#include "prio.h"
+#include "prlog.h"
+#include "prmem.h"
+#include "plstr.h"
+
+#endif /* STANDALONE_REGISTRY*/
+
+#if ( defined(BSDI) && !defined(BSDI_2) ) || defined(XP_OS2)
+#include <sys/types.h>
+#endif
+#include <sys/stat.h>
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#if defined(__cplusplus)
+# define XP_CPLUSPLUS
+# define XP_IS_CPLUSPLUS 1
+#else
+# define XP_IS_CPLUSPLUS 0
+#endif
+
+#if defined(XP_CPLUSPLUS)
+# define XP_BEGIN_PROTOS extern "C" {
+# define XP_END_PROTOS }
+#else
+# define XP_BEGIN_PROTOS
+# define XP_END_PROTOS
+#endif
+
+
+#ifdef STANDALONE_REGISTRY
+
+#define USE_STDIO_MODES
+
+#define XP_FileSeek(file,offset,whence) fseek((file), (offset), (whence))
+#define XP_FileRead(dest,count,file)    fread((dest), 1, (count), (file))
+#define XP_FileWrite(src,count,file)    fwrite((src), 1, (count), (file))
+#define XP_FileTell(file)               ftell(file)
+#define XP_FileFlush(file)              fflush(file)
+#define XP_FileClose(file)              fclose(file)
+#define XP_FileSetBufferSize(file,bufsize) (-1)
+
+#define XP_ASSERT(x)        ((void)0)
+
+#define XP_STRCAT(a,b)      strcat((a),(b))
+#define XP_ATOI             atoi
+#define XP_STRNCPY(a,b,n)   strncpy((a),(b),(n))
+#define XP_STRCPY(a,b)      strcpy((a),(b))
+#define XP_STRLEN(x)        strlen(x)
+#define XP_SPRINTF          sprintf
+#define XP_FREE(x)          free((x))
+#define XP_ALLOC(x)         malloc((x))
+#define XP_FREEIF(x)        if ((x)) free((x))
+#define XP_STRCMP(x,y)      strcmp((x),(y))
+#define XP_STRNCMP(x,y,n)   strncmp((x),(y),(n))
+#define XP_STRDUP(s)        strdup((s))
+#define XP_MEMCPY(d, s, l)  memcpy((d), (s), (l))
+#define XP_MEMSET(d, c, l)  memset((d), (c), (l))
+
+#define PR_Lock(a)          ((void)0)
+#define PR_Unlock(a)        ((void)0)
+
+#if defined(XP_WIN) || defined(XP_OS2)
+  #define XP_STRCASECMP(x,y)  stricmp((x),(y))
+  #define XP_STRNCASECMP(x,y,n) strnicmp((x),(y),(n))
+#else
+  #define XP_STRCASECMP(x,y)  strcasecmp((x),(y))
+  #define XP_STRNCASECMP(x,y,n) strncasecmp((x),(y),(n))
+#endif /* XP_WIN || XP_OS2 */
+
+typedef FILE          * XP_File;
+
+#else /* not standalone, use NSPR */
+
+
+/*-------------------------------------*/
+/* Alternate fileI/O function mappings */
+/*-------------------------------------*/
+
+#if USE_BUFFERED_REGISTRY_IO
+  /*-----------------------------------------------*/
+  /* home-grown XP buffering                       */
+  /* writes are buffered too so use flush!         */
+  /*-----------------------------------------------*/
+#define USE_STDIO_MODES
+
+#include "nr_bufio.h"
+#define XP_FileSeek(file,offset,whence) bufio_Seek((file),(offset),(whence))
+#define XP_FileRead(dest,count,file)    bufio_Read((file), (dest), (count))
+#define XP_FileWrite(src,count,file)    bufio_Write((file), (src), (count))
+#define XP_FileTell(file)               bufio_Tell(file)
+#define XP_FileClose(file)              bufio_Close(file)
+#define XP_FileOpen(path, mode)         bufio_Open((path), (mode))
+#define XP_FileFlush(file)              bufio_Flush(file)
+#define XP_FileSetBufferSize(file,bufsize) bufio_SetBufferSize(file,bufsize)
+
+
+typedef BufioFile* XP_File;
+
+#else
+  /*-----------------------------------------------*/
+  /* standard NSPR file I/O                        */
+  /*-----------------------------------------------*/
+#define USE_NSPR_MODES
+/*
+** Note that PR_Seek returns the offset (if successful) and -1 otherwise.  So
+** to make this code work
+**           if (XP_FileSeek(fh, offset, SEEK_SET) != 0)  { error handling }
+** we return 1 if PR_Seek() returns a negative value, and 0 otherwise
+*/
+#define XP_FileSeek(file,offset,whence) (PR_Seek((file), (offset), (whence)) < 0)
+#define XP_FileRead(dest,count,file)    PR_Read((file), (dest), (count))
+#define XP_FileWrite(src,count,file)    PR_Write((file), (src), (count))
+#define XP_FileTell(file)               PR_Seek(file, 0, PR_SEEK_CUR)
+#define XP_FileOpen(path, mode)         PR_Open((path), mode )
+#define XP_FileClose(file)              PR_Close(file)
+#define XP_FileFlush(file)              PR_Sync(file)
+#define XP_FileSetBufferSize(file,bufsize) (-1)
+
+typedef PRFileDesc* XP_File;
+
+#endif /*USE_MMAP_REGISTRY_IO*/
+
+
+
+#define XP_ASSERT(x)        PR_ASSERT((x))
+
+#define XP_STRCAT(a,b)      PL_strcat((a),(b))
+#define XP_ATOI             PL_atoi
+#define XP_STRCPY(a,b)      PL_strcpy((a),(b))
+#define XP_STRNCPY(a,b,n)   PL_strncpy((a),(b),(n))
+#define XP_STRLEN(x)        PL_strlen(x)
+#define XP_SPRINTF          sprintf
+#define XP_FREE(x)          PR_Free((x))
+#define XP_ALLOC(x)         PR_Malloc((x))
+#define XP_FREEIF(x)        PR_FREEIF(x)
+#define XP_STRCMP(x,y)      PL_strcmp((x),(y))
+#define XP_STRNCMP(x,y,n)   PL_strncmp((x),(y),(n))
+#define XP_STRDUP(s)        PL_strdup((s))
+#define XP_MEMCPY(d, s, l)  memcpy((d), (s), (l))
+#define XP_MEMSET(d, c, l)  memset((d), (c), (l))
+
+#define XP_STRCASECMP(x,y)  PL_strcasecmp((x),(y))
+#define XP_STRNCASECMP(x,y,n) PL_strncasecmp((x),(y),(n))
+
+
+#endif /*STANDALONE_REGISTRY*/
+
+/*--- file open modes for stdio ---*/
+#ifdef USE_STDIO_MODES
+#define XP_FILE_READ             "r"
+#define XP_FILE_READ_BIN         "rb"
+#define XP_FILE_WRITE            "w"
+#define XP_FILE_WRITE_BIN        "wb"
+#define XP_FILE_UPDATE           "r+"
+#define XP_FILE_TRUNCATE         "w+"
+#ifdef SUNOS4
+/* XXX SunOS4 hack -- make this universal by using r+b and w+b */
+#define XP_FILE_UPDATE_BIN       "r+"
+#define XP_FILE_TRUNCATE_BIN     "w+"
+#else
+#define XP_FILE_UPDATE_BIN       "rb+"
+#define XP_FILE_TRUNCATE_BIN     "wb+"
+#endif
+#endif /* USE_STDIO_MODES */
+
+/*--- file open modes for NSPR file I/O ---*/
+#ifdef USE_NSPR_MODES
+#define XP_FILE_READ             PR_RDONLY, 0644
+#define XP_FILE_READ_BIN         PR_RDONLY, 0644
+#define XP_FILE_WRITE            PR_WRONLY, 0644
+#define XP_FILE_WRITE_BIN        PR_WRONLY, 0644
+#define XP_FILE_UPDATE           (PR_RDWR|PR_CREATE_FILE), 0644
+#define XP_FILE_TRUNCATE         (PR_RDWR | PR_TRUNCATE), 0644
+#define XP_FILE_UPDATE_BIN       PR_RDWR|PR_CREATE_FILE, 0644
+#define XP_FILE_TRUNCATE_BIN     (PR_RDWR | PR_TRUNCATE), 0644
+
+#ifdef SEEK_SET
+    #undef SEEK_SET
+    #undef SEEK_CUR
+    #undef SEEK_END
+    #define SEEK_SET PR_SEEK_SET
+    #define SEEK_CUR PR_SEEK_CUR
+    #define SEEK_END PR_SEEK_END
+#endif
+#endif /* USE_NSPR_MODES */
+
+
+
+
+
+#ifdef STANDALONE_REGISTRY /* included from prmon.h otherwise */
+#include "prtypes.h"
+#endif /*STANDALONE_REGISTRY*/
+
+typedef int XP_Bool;
+
+typedef struct stat    XP_StatStruct;
+#define  XP_Stat(file,data)     stat((file),(data))
+
+XP_BEGIN_PROTOS
+
+#define nr_RenameFile(from, to)    rename((from), (to))
+
+extern char* globalRegName;
+extern char* verRegName;
+
+extern void vr_findGlobalRegName();
+extern char* vr_findVerRegName();
+
+
+#ifdef STANDALONE_REGISTRY /* included from prmon.h otherwise */
+
+extern XP_File vr_fileOpen(const char *name, const char * mode);
+
+
+#else
+#define vr_fileOpen PR_Open
+#endif /* STANDALONE_REGISTRY */
+
+XP_END_PROTOS
+
+#endif /* _VR_STUBS_H_ */
new file mode 100644
--- /dev/null
+++ b/modules/libreg/tests/interp.c
@@ -0,0 +1,293 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/* Registry interpreter */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "VerReg.h"
+#include "NSReg.h"
+
+extern char *errstr(REGERR err);
+extern int DumpTree(void);
+
+
+int error(char *func, int err)
+{
+	if (err == REGERR_OK)
+	{
+    	printf("\t%s -- OK\n", func);
+	}
+	else
+	{
+		printf("\t%s -- %s\n", func, errstr(err));
+	}
+
+	return err;
+
+}	/* error */
+
+static char  *GetNextWord(char *cmd, char *buf)
+{
+	/* copies until ',' or eos, then skips spaces */
+	if (!cmd || !buf)
+		return 0;
+	while (*cmd && *cmd != ',')
+		*buf++ = *cmd++;
+	*buf = '\0';
+	if (*cmd == ',')
+	{
+		cmd++;
+		while(*cmd && *cmd == ' ')
+			cmd++;
+	}
+	return cmd;
+
+}	/* GetNextWord */
+
+static int vr_ParseVersion(char *verstr, VERSION *result)
+{
+
+	result->major = result->minor = result->release = result->build = 0;
+	result->major = atoi(verstr);
+	while (*verstr && *verstr != '.')
+		verstr++;
+	if (*verstr)
+	{
+		verstr++;
+		result->minor = atoi(verstr);
+		while (*verstr && *verstr != '.')
+			verstr++;
+		if (*verstr)
+		{
+			verstr++;
+			result->release = atoi(verstr);
+			while (*verstr && *verstr != '.')
+				verstr++;
+			if (*verstr)
+			{
+				verstr++;
+				result->build = atoi(verstr);
+				while (*verstr && *verstr != '.')
+					verstr++;
+			}
+		}
+	}
+
+	return REGERR_OK;
+
+}	/* ParseVersion */
+
+
+void vCreate(char *cmd)
+{
+
+	/* Syntax: Create [new,] 5.0b1 */
+	char buf[512];
+    
+	int flag = 0;
+	cmd = GetNextWord(cmd, buf);
+    
+	error("VR_CreateRegistry", VR_CreateRegistry("Communicator", buf, cmd));
+
+}	/* vCreate */
+
+
+
+void vFind(char *cmd)
+{
+
+	VERSION ver;
+	char path[MAXREGPATHLEN];
+
+	if (error("VR_GetVersion", VR_GetVersion(cmd, &ver)) == REGERR_OK)
+	{
+		if (error("VR_GetPath", VR_GetPath(cmd, sizeof(path), path)) == REGERR_OK)
+		{
+			printf("%s found: ver=%d.%d.%d.%d, check=0x%04x, path=%s\n", 
+				cmd, ver.major, ver.minor, ver.release, ver.build, ver.check,
+				path);
+			return;
+		}
+	}
+
+	printf("%s not found.\n", cmd);
+	return;
+
+}	/* vFind */
+
+
+void vHelp(char *cmd)
+{
+
+	puts("Enter a command:");
+    puts("\tN)ew <dir> [, <ver>] - create a new registry");
+    puts("\tA)pp <dir>       - set application directory");
+    puts("\tC)lose           - close the registry");
+    puts("");
+	puts("\tI)nstall <name>, <version>, <path> - install a new component");
+	puts("\tR)emove <name>   - deletes a component from the Registry");
+    puts("\tX)ists <name>    - checks for existence in registry");
+    puts("\tT)est <name>     - validates physical existence");
+    puts("\tE)num <name>     - dumps named subtree");
+    puts("");
+    puts("\tV)ersion <name>  - gets component version");
+    puts("\tP)ath <name>     - gets component path");
+    puts("\treF)count <name> - gets component refcount");
+    puts("\tD)ir <name>      - gets component directory");
+    puts("\tSR)efcount <name>- sets component refcount");
+    puts("\tSD)ir <name>     - sets component directory");
+    puts("");
+	puts("\tQ)uit            - end the program");
+
+}	/* vHelp */
+
+
+void vInstall(char *cmd)
+{
+
+	char name[MAXREGPATHLEN+1];
+	char path[MAXREGPATHLEN+1];
+    char ver[MAXREGPATHLEN+1];
+
+    char *pPath, *pVer;
+
+	cmd = GetNextWord(cmd, name);
+    cmd = GetNextWord(cmd, ver);
+    cmd = GetNextWord(cmd, path);
+
+    pVer  = ( ver[0]  != '*' ) ? ver  : NULL;
+    pPath = ( path[0] != '*' ) ? path : NULL;
+
+	error("VR_Install", VR_Install(name, pPath, pVer, FALSE));
+
+}	/* vInstall */
+
+
+
+
+			
+
+void interp(void)
+{
+
+	char line[256];
+	char *p;
+
+	while(1)
+	{
+		putchar('>');
+		putchar(' ');
+		fflush(stdin); fflush(stdout); fflush(stderr);
+		gets(line);
+
+		/* p points to next word after verb on command line */
+		p = line;
+		while (*p && *p!=' ')
+			p++;
+		if (!*p)
+			p = 0;
+		else
+		{
+			while(*p && *p==' ')
+				p++;
+		}
+
+		switch(toupper(line[0]))
+		{
+		case 'N':
+			vCreate(p);
+			break;
+        case 'A':
+            error("VR_SetRegDirectory", VR_SetRegDirectory(p));
+            break;
+        case 'C':
+            error("VR_Close", VR_Close());
+            break;
+
+		case 'I':
+			vInstall(p);
+			break;
+		case 'R':
+        	error("VR_Remove", VR_Remove(p));
+			break;
+        case 'X':
+        	error("VR_InRegistry", VR_InRegistry(p));
+            break;
+        case 'T':
+        	error("VR_ValidateComponent", VR_ValidateComponent(p));
+            break;
+
+#if  LATER
+        case 'E':
+            vEnum(p);
+            break;
+
+        case 'V':
+            vVersion(p);
+            break;
+        case 'P':
+            vPath(p);
+            break;
+        case 'F':
+            vGetRefCount(p);
+            break;
+        case 'D':
+            vGetDir(p);
+            break;
+        
+        case 'S':
+            puts("--Unsupported--");
+#endif
+
+		case 'H':
+		default:
+			vHelp(line);
+			break;
+		case 'Q':
+			return;
+		}	/* switch */
+	}	/* while */
+
+	assert(0);
+	return;	/* shouldn't get here */
+
+}	/* interp */
+
+/* EOF: interp.c */
new file mode 100644
--- /dev/null
+++ b/modules/libreg/tests/regtest.c
@@ -0,0 +1,117 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <io.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include "NSReg.h"
+#include "VerReg.h"
+
+extern void interp(void);
+
+#define REGFILE "c:\\temp\\reg.dat"
+
+char *gRegistry;
+
+int main(int argc, char *argv[]);
+
+char *errstr(REGERR err)
+{
+
+	switch( err )
+	{
+	case REGERR_OK:
+		return "REGERR_OK";
+	case REGERR_FAIL:
+		return "REGERR_FAIL";
+	case REGERR_NOMORE:
+		return "REGERR_MORE";
+	case REGERR_NOFIND:
+		return "REGERR_NOFIND";
+	case REGERR_BADREAD:
+		return "REGERR_BADREAD";
+	case REGERR_BADLOCN:
+		return "REGERR_BADLOCN";
+	case REGERR_PARAM:
+		return "REGERR_PARAM";
+	case REGERR_BADMAGIC:
+		return "REGERR_BADMAGIC";
+    case REGERR_BADCHECK:
+        return "REGERR_BADCHECK";
+    case REGERR_NOFILE:
+        return "REGERR_NOFILE";
+    case REGERR_MEMORY:
+        return "REGERR_MEMORY";
+    case REGERR_BUFTOOSMALL:
+        return "REGERR_BUFTOOSMALL";
+    case REGERR_NAMETOOLONG:
+        return "REGERR_NAMETOOLONG";
+    case REGERR_REGVERSION:
+        return "REGERR_REGVERSION";
+    case REGERR_DELETED:
+        return "REGERR_DELETED";
+    case REGERR_BADTYPE:
+        return "REGERR_BADTYPE";
+    case REGERR_NOPATH:
+        return "REGERR_NOPATH";
+    case REGERR_BADNAME:
+        return "REGERR_BADNAME";
+    case REGERR_READONLY:
+        return "REGERR_READONLY";
+    case REGERR_BADUTF8:
+        return "REGERR_BADUTF8";
+	default:
+		return "<Unknown>";
+	}
+
+}	// errstr
+
+
+int main(int argc, char *argv[])
+{
+	printf("Registry Test 4/10/99.\n");
+
+    interp();
+
+	return 0;
+}
+
+
--- a/toolkit/library/libxul-config.mk
+++ b/toolkit/library/libxul-config.mk
@@ -59,16 +59,17 @@ ifndef MOZ_NATIVE_ZLIB
 CPPSRCS += dlldeps-zlib.cpp
 endif
 
 LOCAL_INCLUDES += -I$(topsrcdir)/widget/src/windows
 LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/base
 endif
 
 ifneq (,$(filter WINNT OS2,$(OS_ARCH)))
+REQUIRES += libreg
 DEFINES	+= -DZLIB_DLL=1
 endif
 
 ifeq ($(OS_ARCH),OS2)
 REQUIRES += widget gfx
 
 CPPSRCS += \
 	nsGFXDeps.cpp \
@@ -106,16 +107,17 @@ ifneq (Android,$(OS_TARGET))
 OS_LIBS += -lrt
 endif
 endif
 
 STATIC_LIBS += \
 	xpcom_core \
 	ucvutil_s \
 	chromium_s \
+	mozreg_s \
 	$(NULL)
 
 # component libraries
 COMPONENT_LIBS += \
 	necko \
 	uconv \
 	i18n \
 	chardet \
--- a/toolkit/profile/nsIProfileMigrator.idl
+++ b/toolkit/profile/nsIProfileMigrator.idl
@@ -68,16 +68,29 @@ interface nsIProfileStartup : nsISupport
  * @provider Application (Profile-migration code)
  * @client   Toolkit (Startup code)
  * @obtainable service, contractid("@mozilla.org/toolkit/profile-migrator;1")
  */
 [scriptable, uuid(24ce8b9d-b7ff-4279-aef4-26e158f03e34)]
 interface nsIProfileMigrator : nsISupports 
 {
   /**
+   * Import existing profile paths.  When the app is started the first
+   * time, if there are no INI-style profiles, appstartup will call
+   * this method to import any registry- style profiles that may
+   * exist. When this method is called, there is no event queue
+   * service and this method should not attempt to use the network or
+   * show any GUI.
+   *
+   * @note You don't actually have to move the profile data. Just call
+   *       nsIToolkitProfileService.create on the existing profile path(s).
+   */
+  void import();
+
+  /**
    * Do profile migration.
    *
    * When this method is called, a default profile has been created;
    * XPCOM has been initialized such that compreg.dat is in the
    * profile; the directory service does *not* return a key for
    * NS_APP_USER_PROFILE_50_DIR or any of the keys depending on an active
    * profile. To figure out the directory of the "current" profile, use
    * aStartup.directory.
--- a/toolkit/toolkit-makefiles.sh
+++ b/toolkit/toolkit-makefiles.sh
@@ -290,16 +290,22 @@ MAKEFILES_layout="
   layout/xul/base/src/tree/public/Makefile
 "
 
 MAKEFILES_libjar="
   modules/libjar/Makefile
   modules/libjar/test/Makefile
 "
 
+MAKEFILES_libreg="
+  modules/libreg/Makefile
+  modules/libreg/include/Makefile
+  modules/libreg/src/Makefile
+"
+
 MAKEFILES_libpref="
   modules/libpref/Makefile
   modules/libpref/public/Makefile
   modules/libpref/src/Makefile
 "
 
 MAKEFILES_libvorbis="
   media/libvorbis/Makefile
@@ -740,16 +746,17 @@ add_makefiles "
   $MAKEFILES_jsipc
   $MAKEFILES_jsdebugger
   $MAKEFILES_jsctypes
   $MAKEFILES_jsreflect
   $MAKEFILES_jsductwork
   $MAKEFILES_content
   $MAKEFILES_layout
   $MAKEFILES_libjar
+  $MAKEFILES_libreg
   $MAKEFILES_libpref
   $MAKEFILES_mathml
   $MAKEFILES_plugin
   $MAKEFILES_netwerk
   $MAKEFILES_storage
   $MAKEFILES_uriloader
   $MAKEFILES_profile
   $MAKEFILES_rdf
--- a/toolkit/toolkit-tiers.mk
+++ b/toolkit/toolkit-tiers.mk
@@ -54,16 +54,17 @@ endif
 
 tier_platform_dirs += xpcom
 
 ifndef MOZ_NATIVE_ZLIB
 tier_platform_dirs += modules/zlib
 endif
 
 tier_platform_dirs += \
+		modules/libreg \
 		modules/libpref \
 		intl \
 		netwerk \
 		$(NULL)
 
 ifdef MOZ_AUTH_EXTENSION
 tier_platform_dirs += extensions/auth
 endif
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -1856,16 +1856,45 @@ ShowProfileManager(nsIToolkitProfileServ
   aProfileSvc->GetStartOffline(&offline);
   if (offline) {
     SaveToEnv("XRE_START_OFFLINE=1");
   }
 
   return LaunchChild(aNative);
 }
 
+static nsresult
+ImportProfiles(nsIToolkitProfileService* aPService,
+               nsINativeAppSupport* aNative)
+{
+  nsresult rv;
+
+  SaveToEnv("XRE_IMPORT_PROFILES=1");
+
+  // try to import old-style profiles
+  { // scope XPCOM
+    ScopedXPCOMStartup xpcom;
+    rv = xpcom.Initialize();
+    if (NS_SUCCEEDED(rv)) {
+#ifdef XP_MACOSX
+      CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, PR_TRUE);
+#endif
+
+      nsCOMPtr<nsIProfileMigrator> migrator
+        (do_GetService(NS_PROFILEMIGRATOR_CONTRACTID));
+      if (migrator) {
+        migrator->Import();
+      }
+    }
+  }
+
+  aPService->Flush();
+  return LaunchChild(aNative);
+}
+
 // Pick a profile. We need to end up with a profile lock.
 //
 // 1) check for -profile <path>
 // 2) check for -P <name>
 // 3) check for -ProfileManager
 // 4) use the default profile, if there is one
 // 5) if there are *no* profiles, set up profile-migration
 // 6) display the profile-manager UI
@@ -2007,16 +2036,22 @@ SelectProfile(nsIProfileLock* *aResult, 
 
     return rv;
   }
 
   PRUint32 count;
   rv = profileSvc->GetProfileCount(&count);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (gAppData->flags & NS_XRE_ENABLE_PROFILE_MIGRATOR) {
+    if (!count && !EnvHasValue("XRE_IMPORT_PROFILES")) {
+      return ImportProfiles(profileSvc, aNative);
+    }
+  }
+
   ar = CheckArg("p", PR_FALSE, &arg);
   if (ar == ARG_BAD) {
     ar = CheckArg("osint");
     if (ar == ARG_FOUND) {
       PR_fprintf(PR_STDERR, "Error: argument -p is invalid when argument -osint is specified\n");
       return NS_ERROR_FAILURE;
     }
     return ShowProfileManager(profileSvc, aNative);
@@ -3428,16 +3463,17 @@ XRE_main(int argc, char* argv[], const n
         SaveStateForAppInitiatedRestart();
 
         // clear out any environment variables which may have been set 
         // during the relaunch process now that we know we won't be relaunching.
         SaveToEnv("XRE_PROFILE_PATH=");
         SaveToEnv("XRE_PROFILE_LOCAL_PATH=");
         SaveToEnv("XRE_PROFILE_NAME=");
         SaveToEnv("XRE_START_OFFLINE=");
+        SaveToEnv("XRE_IMPORT_PROFILES=");
         SaveToEnv("NO_EM_RESTART=");
         SaveToEnv("XUL_APP_FILE=");
         SaveToEnv("XRE_BINARY_PATH=");
 
         NS_TIME_FUNCTION_MARK("env munging");
 
         if (!shuttingDown) {
           NS_TIME_FUNCTION_MARK("Next: CreateHiddenWindow");