From ec547b0b85bb61877dd3191482ad8bba145a9a1d Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Sun, 25 Sep 2011 16:26:10 +0100 Subject: [PATCH] Backout changeset 971ed890c27d (bug 679352), since burning comm-central; a=callek --- browser/components/build/Makefile.in | 4 + modules/libreg/Makefile.in | 50 + modules/libreg/include/Makefile.in | 50 + modules/libreg/include/NSReg.h | 574 ++++ modules/libreg/include/VerReg.h | 105 + modules/libreg/src/Makefile.in | 82 + modules/libreg/src/VerReg.c | 1766 ++++++++++++ modules/libreg/src/nr_bufio.c | 758 +++++ modules/libreg/src/nr_bufio.h | 64 + modules/libreg/src/objs.mk | 47 + modules/libreg/src/reg.c | 3920 ++++++++++++++++++++++++++ modules/libreg/src/reg.h | 196 ++ modules/libreg/src/vr_stubs.c | 507 ++++ modules/libreg/src/vr_stubs.h | 283 ++ modules/libreg/tests/interp.c | 293 ++ modules/libreg/tests/regtest.c | 117 + toolkit/library/libxul-config.mk | 2 + toolkit/toolkit-makefiles.sh | 7 + toolkit/toolkit-tiers.mk | 1 + 19 files changed, 8826 insertions(+) create mode 100644 modules/libreg/Makefile.in create mode 100644 modules/libreg/include/Makefile.in create mode 100644 modules/libreg/include/NSReg.h create mode 100644 modules/libreg/include/VerReg.h create mode 100644 modules/libreg/src/Makefile.in create mode 100644 modules/libreg/src/VerReg.c create mode 100644 modules/libreg/src/nr_bufio.c create mode 100644 modules/libreg/src/nr_bufio.h create mode 100644 modules/libreg/src/objs.mk create mode 100644 modules/libreg/src/reg.c create mode 100644 modules/libreg/src/reg.h create mode 100644 modules/libreg/src/vr_stubs.c create mode 100644 modules/libreg/src/vr_stubs.h create mode 100644 modules/libreg/tests/interp.c create mode 100644 modules/libreg/tests/regtest.c diff --git a/browser/components/build/Makefile.in b/browser/components/build/Makefile.in index 48e5a27b26b5..e09dde0820c7 100644 --- a/browser/components/build/Makefile.in +++ b/browser/components/build/Makefile.in @@ -54,9 +54,13 @@ 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) \ diff --git a/modules/libreg/Makefile.in b/modules/libreg/Makefile.in new file mode 100644 index 000000000000..6bda65d5324b --- /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 + diff --git a/modules/libreg/include/Makefile.in b/modules/libreg/include/Makefile.in new file mode 100644 index 000000000000..92e2263c4d7c --- /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 + diff --git a/modules/libreg/include/NSReg.h b/modules/libreg/include/NSReg.h new file mode 100644 index 000000000000..c90b46b1e569 --- /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 + * + * 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 */ + diff --git a/modules/libreg/include/VerReg.h b/modules/libreg/include/VerReg.h new file mode 100644 index 000000000000..b6359e9909a2 --- /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 + * + * 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 */ + diff --git a/modules/libreg/src/Makefile.in b/modules/libreg/src/Makefile.in new file mode 100644 index 000000000000..58e5132fae28 --- /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 diff --git a/modules/libreg/src/VerReg.c b/modules/libreg/src/VerReg.c new file mode 100644 index 000000000000..d6372379a090 --- /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 + * + * 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 +#include + +#if defined(XP_WIN) +#include +#endif + +#if defined(XP_OS2) +#include +#include +#endif + +#include +#include + +#ifdef STANDALONE_REGISTRY +#include +#include +#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 + * Edward Kandrot + * + * 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 + * got using NSPR memory-mapped I/O for the registry. Unfortunately NSPR + * doesn't support mmapio on the Mac. + *-----------------------------------------------------------------------*/ + +#include +#include +#include + +#if defined(XP_MACOSX) +#include +#endif + +#if defined(SUNOS4) +#include /* 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 +#include +#include +#include + +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 */ diff --git a/modules/libreg/src/nr_bufio.h b/modules/libreg/src/nr_bufio.h new file mode 100644 index 000000000000..c7aff0174a48 --- /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 + * Edward Kandrot + * + * 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_ */ + diff --git a/modules/libreg/src/objs.mk b/modules/libreg/src/objs.mk new file mode 100644 index 000000000000..767f531aa7ee --- /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)) + + diff --git a/modules/libreg/src/reg.c b/modules/libreg/src/reg.c new file mode 100644 index 000000000000..a500d6189d1c --- /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 + * + * 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 +#include + +#ifdef XP_UNIX +#include +#endif + +#ifdef STANDALONE_REGISTRY +#include +#include +#include +#else +#include "prtypes.h" +#include "prlog.h" +#include "prerror.h" +#include "prprf.h" +#endif /*STANDALONE_REGISTRY*/ + +#if defined(SUNOS4) +#include /* 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 +#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), ®->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, ®->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( ®->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 */ diff --git a/modules/libreg/src/reg.h b/modules/libreg/src/reg.h new file mode 100644 index 000000000000..7a0408c403e8 --- /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 + * + * 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 */ + diff --git a/modules/libreg/src/vr_stubs.c b/modules/libreg/src/vr_stubs.c new file mode 100644 index 000000000000..f2e99f1574e5 --- /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 +#include +#include + +#else + +#include "prtypes.h" +#include "plstr.h" + +#endif /*STANDALONE_REGISTRY*/ + +#include "vr_stubs.h" + +#ifdef XP_MACOSX +#include +#include +#endif + +#ifdef XP_BEOS +#include +#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 + +#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, ®Ref); + 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, ®Ref, NULL); + } + if (err == noErr) + { + UInt8 pathBuf[PATH_MAX]; + err = FSRefMakePath(®Ref, 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, ®Ref); + 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, ®Ref, NULL); + } + if (err == noErr) + { + UInt8 pathBuf[PATH_MAX]; + err = FSRefMakePath(®Ref, 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 + +#ifdef XP_OS2 +#include +#define W_OK 0x02 /*evil hack from the docs...*/ +#else +#include +#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 */ diff --git a/modules/libreg/src/vr_stubs.h b/modules/libreg/src/vr_stubs.h new file mode 100644 index 000000000000..f6eb75507ef8 --- /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 + * + * 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 +#include + +#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 +#endif +#include + +#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_ */ diff --git a/modules/libreg/tests/interp.c b/modules/libreg/tests/interp.c new file mode 100644 index 000000000000..772884dca37d --- /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 +#include +#include +#include + +#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 [, ] - create a new registry"); + puts("\tA)pp - set application directory"); + puts("\tC)lose - close the registry"); + puts(""); + puts("\tI)nstall , , - install a new component"); + puts("\tR)emove - deletes a component from the Registry"); + puts("\tX)ists - checks for existence in registry"); + puts("\tT)est - validates physical existence"); + puts("\tE)num - dumps named subtree"); + puts(""); + puts("\tV)ersion - gets component version"); + puts("\tP)ath - gets component path"); + puts("\treF)count - gets component refcount"); + puts("\tD)ir - gets component directory"); + puts("\tSR)efcount - sets component refcount"); + puts("\tSD)ir - 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 */ diff --git a/modules/libreg/tests/regtest.c b/modules/libreg/tests/regtest.c new file mode 100644 index 000000000000..1f4b75d3190e --- /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 +#include +#include +#include +#include +#include + +#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 ""; + } + +} // errstr + + +int main(int argc, char *argv[]) +{ + printf("Registry Test 4/10/99.\n"); + + interp(); + + return 0; +} + + diff --git a/toolkit/library/libxul-config.mk b/toolkit/library/libxul-config.mk index 6a551482fbc2..1fbf005d67e6 100644 --- a/toolkit/library/libxul-config.mk +++ b/toolkit/library/libxul-config.mk @@ -64,6 +64,7 @@ LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/base endif ifneq (,$(filter WINNT OS2,$(OS_ARCH))) +REQUIRES += libreg DEFINES += -DZLIB_DLL=1 endif @@ -111,6 +112,7 @@ STATIC_LIBS += \ xpcom_core \ ucvutil_s \ chromium_s \ + mozreg_s \ $(NULL) # component libraries diff --git a/toolkit/toolkit-makefiles.sh b/toolkit/toolkit-makefiles.sh index dcde4cb3612c..a41728e8ba73 100644 --- a/toolkit/toolkit-makefiles.sh +++ b/toolkit/toolkit-makefiles.sh @@ -294,6 +294,12 @@ MAKEFILES_libjar=" 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 @@ -744,6 +750,7 @@ add_makefiles " $MAKEFILES_content $MAKEFILES_layout $MAKEFILES_libjar + $MAKEFILES_libreg $MAKEFILES_libpref $MAKEFILES_mathml $MAKEFILES_plugin diff --git a/toolkit/toolkit-tiers.mk b/toolkit/toolkit-tiers.mk index d1244812a84d..2ebc7ff1a08f 100644 --- a/toolkit/toolkit-tiers.mk +++ b/toolkit/toolkit-tiers.mk @@ -59,6 +59,7 @@ tier_platform_dirs += modules/zlib endif tier_platform_dirs += \ + modules/libreg \ modules/libpref \ intl \ netwerk \