1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3
* The contents of this file are subject to the Netscape Public
4
* License Version 1.1 (the "License"); you may not use this file
5
* except in compliance with the License. You may obtain a copy of
6
* the License at http://www.mozilla.org/NPL/
8
* Software distributed under the License is distributed on an "AS
9
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
10
* implied. See the License for the specific language governing
11
* rights and limitations under the License.
13
* The Original Code is Mozilla Communicator client code,
14
* released March 31, 1998.
16
* The Initial Developer of the Original Code is Netscape Communications
17
* Corporation. Portions created by Netscape are
18
* Copyright (C) 1998 Netscape Communications Corporation. All
22
* Daniel Veditz <dveditz@netscape.com>
24
/* ====================================================================
26
* XP Registry functions
27
* ====================================================================
31
* - Replace 'malloc' in NR_RegPack with the Netscape XP equivalent
32
* - Solve DOS 'errno' problem mentioned below
33
* - Solve rename across volume problem described in VR_PackRegistry
36
/* Preprocessor Defines
37
* STANDALONE_REGISTRY - define if not linking with Navigator
38
* NOCACHE_HDR - define if multi-process access to registry
39
* SELF_REPAIR - undefine to skip header update on open
40
* VERIFY_READ - define TRUE to double-check short reads
52
#ifdef STANDALONE_REGISTRY
57
#if defined(XP_MAC) || defined(XP_MACOSX)
68
#endif /*STANDALONE_REGISTRY*/
71
#include <unistd.h> /* for SEEK_SET */
79
#elif defined(XP_MACOSX)
80
#define MAX_PATH PATH_MAX
81
#elif defined(XP_UNIX)
90
#define MAX_PATH _MAX_PATH
91
#elif defined(XP_BEOS)
93
#define MAX_PATH PATH_MAX
97
/* NOTE! It is EXREMELY important that node names be in UTF-8; otherwise
98
* backwards path search for delim char will fail for multi-byte/Unicode names
101
/* ====================================================================
103
* --------------------------------------------------------------------
108
* Key/Entry Management
112
* The functions in this file search and add to a binary Registry file
113
* quite efficiently. So efficiently, squeezing out space left by
114
* deleted and updated objects requires a separate "pack" operation.
117
* As used here, a 'key' is a node in the tree. The root of the tree
118
* exists in an otherwise empty Registry as is itself a key. Every key
119
* has 0 or more sub-keys. Every key also has 0 or more 'entry's. Both
120
* entries and keys have names. Entries also have values associated.
121
* Names and values are simply strings of characters. These strings
122
* may be quoted so that they can include path delimiter and equals
123
* sign characters which are otherwise reserved.
124
* ====================================================================
127
/* --------------------------------------------------------------------
130
* use of this data must be protected by the reglist lock
131
* --------------------------------------------------------------------
134
#if !defined(STANDALONE_REGISTRY)
135
static PRLock *reglist_lock = NULL;
138
static REGFILE *RegList = NULL;
139
static int32 regStartCount = 0;
140
char *globalRegName = NULL;
141
static char *user_name = NULL;
146
#if defined(XP_MAC) || defined(XP_MACOSX)
148
void nr_MacAliasFromPath(const char * fileName, void ** alias, int32 * length);
149
char * nr_PathFromMacAlias(const void * alias, uint32 aliasLength);
152
#include <TextUtils.h>
157
#include "MoreFilesX.h"
159
#include "FullPath.h"
162
static void copyCStringToPascal(Str255 dest, const char *src)
164
size_t copyLen = strlen(src);
167
BlockMoveData(src, &dest[1], copyLen);
172
static OSErr isFileInTrash(FSRef *fsRef, PRBool *inTrash)
175
FSCatalogInfo catalogInfo;
177
if (fsRef == NULL || inTrash == NULL)
181
err = FSGetCatalogInfo(fsRef, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL);
184
FSRef trashFSRef, currFSRef, parentFSRef;
185
err = FSFindFolder(catalogInfo.volume, kTrashFolderType, false, &trashFSRef);
188
/* FSRefGetParentRef returns noErr and a zeroed FSRef when it reaches the top */
189
for (currFSRef = *fsRef;
190
(FSGetParentRef(&currFSRef, &parentFSRef) == noErr && FSRefValid(&parentFSRef));
191
currFSRef = parentFSRef)
193
if (FSCompareFSRefs(&parentFSRef, &trashFSRef) == noErr)
204
static OSErr isFileInTrash(FSSpec *fileSpec, PRBool *inTrash)
210
if (fileSpec == NULL || inTrash == NULL)
214
/* XXX - Only works if the file is in the top level of the trash dir */
215
err = FindFolder(fileSpec->vRefNum, kTrashFolderType, false, &vRefNum, &dirID);
217
if (dirID == fileSpec->parID) /* File is inside the trash */
224
/* returns an alias as a malloc'd pointer.
225
* On failure, *alias is NULL
227
void nr_MacAliasFromPath(const char * fileName, void ** alias, int32 * length)
233
AliasHandle macAlias;
238
err = FSPathMakeRef((const UInt8*)fileName, &fsRef, NULL);
241
err = FSNewAlias(NULL, &fsRef, &macAlias);
243
copyCStringToPascal(pascalName, fileName);
244
err = FSMakeFSSpec(0, 0, pascalName, &fs);
247
err = NewAlias(NULL, &fs, &macAlias);
250
if ( (err != noErr) || ( macAlias == NULL ))
252
*length = GetHandleSize( (Handle) macAlias );
253
*alias = XP_ALLOC( *length );
254
if ( *alias == NULL )
256
DisposeHandle((Handle)macAlias);
259
HLock( (Handle) macAlias );
260
XP_MEMCPY(*alias, *macAlias , *length);
261
HUnlock( (Handle) macAlias );
262
DisposeHandle( (Handle) macAlias);
266
/* resolves an alias, and returns a full path to the Mac file
267
* If the alias changed, it would be nice to update our alias pointers
269
char * nr_PathFromMacAlias(const void * alias, uint32 aliasLength)
272
AliasHandle h = NULL;
273
Handle fullPath = NULL;
274
short fullPathLength;
278
FSCatalogInfo catalogInfo;
279
UInt8 pathBuf[MAX_PATH];
281
Boolean wasChanged; /* Change flag, it would be nice to change the alias on disk
282
if the file location changed */
285
XP_MEMSET( &fs, '\0', sizeof(FSSpec) );
288
/* Copy the alias to a handle and resolve it */
289
h = (AliasHandle) NewHandle(aliasLength);
295
XP_MEMCPY( *h, alias, aliasLength );
296
HUnlock( (Handle) h);
299
err = FSResolveAlias(NULL, h, &fsRef, &wasChanged);
303
/* if the alias has changed and the file is now in the trash,
304
assume that user has deleted it and that we do not want to look at it */
305
if (wasChanged && (isFileInTrash(&fsRef, &inTrash) == noErr) && inTrash)
307
err = FSRefMakePath(&fsRef, pathBuf, sizeof(pathBuf));
310
fullPathLength = XP_STRLEN(pathBuf);
311
cpath = (char*) XP_ALLOC(fullPathLength + 1);
314
XP_MEMCPY(cpath, pathBuf, fullPathLength + 1);
316
err = ResolveAlias(NULL, h, &fs, &wasChanged);
320
/* if the alias has changed and the file is now in the trash,
321
assume that user has deleted it and that we do not want to look at it */
322
if (wasChanged && (isFileInTrash(&fs, &inTrash) == noErr) && inTrash)
325
/* Get the full path and create a char * out of it */
327
err = GetFullPath(fs.vRefNum, fs.parID,fs.name, &fullPathLength, &fullPath);
328
if ( (err != noErr) || (fullPath == NULL) )
331
cpath = (char*) XP_ALLOC(fullPathLength + 1);
336
XP_MEMCPY(cpath, *fullPath, fullPathLength);
337
cpath[fullPathLength] = 0;
343
DisposeHandle( (Handle) h);
344
if (fullPath != NULL)
345
DisposeHandle( fullPath);
352
/* --------------------------------------------------------------------
353
* Registry List management
354
* --------------------------------------------------------------------
356
static void nr_AddNode(REGFILE* pReg);
357
static void nr_DeleteNode(REGFILE *pReg);
358
static REGFILE* vr_findRegFile(const char *filename);
360
/* -------------------------------------------------------------------- */
362
static void nr_AddNode(REGFILE* pReg)
364
/* add node to head of list */
365
pReg->next = RegList;
370
if ( pReg->next != NULL ) {
371
pReg->next->prev = pReg;
375
static void nr_DeleteNode(REGFILE* pReg)
377
/* if at head of list... */
378
if ( pReg->prev == NULL ) {
379
RegList = pReg->next;
382
pReg->prev->next = pReg->next;
385
if ( pReg->next != NULL ) {
386
pReg->next->prev = pReg->prev;
390
#ifndef STANDALONE_REGISTRY
391
if ( pReg->lock != NULL )
392
PR_DestroyLock( pReg->lock );
394
XP_FREEIF( pReg->filename );
398
static REGFILE* vr_findRegFile(const char *filename)
403
while( pReg != NULL ) {
404
#if defined(XP_UNIX) && !defined(XP_MACOSX) || defined XP_BEOS
405
if ( 0 == XP_STRCMP( filename, pReg->filename ) ) {
407
if ( 0 == XP_STRCASECMP( filename, pReg->filename ) ) {
418
/* --------------------------------------------------------------------
420
* Platform-specifics go in this section
421
* --------------------------------------------------------------------
423
static REGERR nr_OpenFile(const char *path, FILEHANDLE *fh);
424
static REGERR nr_CloseFile(FILEHANDLE *fh); /* Note: fh is a pointer */
425
static REGERR nr_ReadFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer);
426
static REGERR nr_WriteFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer);
427
static REGERR nr_LockRange(FILEHANDLE fh, REGOFF offset, int32 len);
428
static REGERR nr_UnlockRange(FILEHANDLE fh, REGOFF offset, int32 len);
429
static int32 nr_GetFileLength(FILEHANDLE fh);
430
/* -------------------------------------------------------------------- */
432
#ifdef STANDALONE_REGISTRY
433
static REGERR nr_OpenFile(const char *path, FILEHANDLE *fh)
435
XP_ASSERT( path != NULL );
436
XP_ASSERT( fh != NULL );
438
/* Open the file for exclusive random read/write */
439
(*fh) = vr_fileOpen(path, XP_FILE_UPDATE_BIN);
440
if ( !VALID_FILEHANDLE(*fh) )
444
#if defined(XP_MAC) || defined(XP_MACOSX)
447
case ENOENT: /* file not found */
449
return REGERR_NOFILE;
451
#if defined(XP_MAC) || defined(XP_MACOSX)
454
case EROFS: /* read-only file system */
455
case EACCES: /* file in use or read-only file*/
458
(*fh) = vr_fileOpen(path, XP_FILE_READ_BIN);
459
if ( VALID_FILEHANDLE(*fh) )
460
return REGERR_READONLY;
473
static REGERR nr_OpenFile(const char *path, FILEHANDLE *fh)
475
PR_ASSERT( path != NULL );
476
PR_ASSERT( fh != NULL );
478
/* Open the file for exclusive random read/write */
479
*fh = XP_FileOpen(path, XP_FILE_UPDATE_BIN);
480
if ( !VALID_FILEHANDLE(*fh) )
483
if ( XP_Stat( path, &st ) != 0 )
485
/* file doesn't exist, so create */
486
*fh = XP_FileOpen(path, XP_FILE_TRUNCATE_BIN);
490
if ( !VALID_FILEHANDLE(*fh) )
492
/* For whatever reason we failed every attempt of getting */
493
/* a read/write registry. Let's try a read-only registry. */
494
(*fh) = XP_FileOpen(path, XP_FILE_READ_BIN);
495
if ( VALID_FILEHANDLE(*fh) )
496
return REGERR_READONLY;
498
/* we are in big trouble now */
502
/* succeded in getting a read/write registry */
509
static REGERR nr_CloseFile(FILEHANDLE *fh)
511
/* NOTE: 'fh' is a pointer, unlike other Close functions
512
* This is necessary so that nr_CloseFile can set it to NULL
515
XP_ASSERT( fh != NULL );
516
if ( VALID_FILEHANDLE(*fh) )
525
static REGERR nr_ReadFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer)
528
#define FILLCHAR 0xCC
530
unsigned char* dbgend = (unsigned char*)buffer+len;
534
REGERR err = REGERR_OK;
537
XP_ASSERT(buffer != NULL);
538
XP_ASSERT(fh != NULL);
541
XP_MEMSET(buffer, FILLCHAR, len);
544
if (XP_FileSeek(fh, offset, SEEK_SET) != 0 ) {
548
readlen = XP_FileRead(buffer, len, fh );
549
/* PR_READ() returns an unreliable length, check EOF separately */
551
#if !defined(STANDALONE_REGISTRY) || (!defined(XP_MAC) && !defined(XP_MACOSX))
552
#if defined(STANDALONE_REGISTRY)
553
if (errno == EBADF) /* bad file handle, not open for read, etc. */
555
if (PR_GetError() == PR_BAD_DESCRIPTOR_ERROR)
560
err = REGERR_BADREAD;
562
else if (readlen < len) {
564
/* PR_READ() says we hit EOF but return length is unreliable. */
565
/* If buffer has new data beyond what PR_READ() says it got */
566
/* we'll assume the read was OK--this is a gamble but */
567
/* missing errors will cause fewer problems than too many. */
568
p = (unsigned char*)buffer + readlen;
569
while ( (p < dbgend) && (*p == (unsigned char)FILLCHAR) ) {
573
/* really was EOF if it's all FILLCHAR's */
575
err = REGERR_BADREAD;
578
err = REGERR_BADREAD;
589
static REGERR nr_WriteFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer)
592
/* Note: 'offset' will commonly be the end of the file, in which
593
* case this function extends the file to 'offset'+'len'. This may
594
* be a two-step operation on some platforms.
598
XP_ASSERT(fh != NULL);
600
if (XP_FileSeek(fh, offset, SEEK_SET) != 0)
603
if ((int32)XP_FileWrite(buffer, len, fh) != len)
605
/* disk full or some other catastrophic error */
615
static REGERR nr_LockRange(FILEHANDLE fh, REGOFF offset, int32 len)
617
/* TODO: Implement XP lock function with built-in retry. */
625
static REGERR nr_UnlockRange(FILEHANDLE fh, REGOFF offset, int32 len)
627
/* TODO: Implement XP unlock function with built-in retry. */
636
static int32 nr_GetFileLength(FILEHANDLE fh)
641
curpos = XP_FileTell(fh);
642
XP_FileSeek(fh, 0, SEEK_END);
643
length = XP_FileTell(fh);
644
XP_FileSeek(fh, curpos, SEEK_SET);
647
} /* GetFileLength */
652
/* --------------------------------------------------------------------
654
* --------------------------------------------------------------------
655
* The converters read and write integers in a common format so we
656
* can transport registries without worrying about endian problems.
658
* The buffers *MUST* be the appropriate size!
659
* --------------------------------------------------------------------
661
static uint32 nr_ReadLong(char *buffer);
662
static uint16 nr_ReadShort(char *buffer);
663
static void nr_WriteLong(uint32 num, char *buffer);
664
static void nr_WriteShort(uint16 num, char *buffer);
665
/* -------------------------------------------------------------------- */
669
static uint16 nr_ReadShort(char *buffer)
672
uint8 *p = (uint8*)buffer;
674
val = (uint16)(*p + (uint16)( *(p+1) * 0x100 ));
681
static uint32 nr_ReadLong(char *buffer)
684
uint8 *p = (uint8*)buffer;
687
+ (uint32)(*(p+1) * 0x100L)
688
+ (uint32)(*(p+2) * 0x10000L )
689
+ (uint32)(*(p+3) * 0x1000000L );
696
static void nr_WriteLong(uint32 num, char *buffer)
698
uint8 *p = (uint8*)buffer;
699
*p++ = (uint8)(num & 0x000000FF);
701
*p++ = (uint8)(num & 0x000000FF);
703
*p++ = (uint8)(num & 0x000000FF);
705
*p = (uint8)(num & 0x000000FF);
710
static void nr_WriteShort(uint16 num, char *buffer)
712
uint8 *p = (uint8*)buffer;
714
*p = (uint8)(num & 0x00FF);
715
*(p+1) = (uint8)(num / 0x100);
720
/* --------------------------------------------------------------------
722
* --------------------------------------------------------------------
724
static REGERR nr_ReadHdr(REGFILE *reg); /* Reads the file header, creates file if empty */
725
static REGERR nr_WriteHdr(REGFILE *reg); /* Writes the file header */
726
static REGERR nr_CreateRoot(REGFILE *reg);
728
static REGERR nr_Lock(REGFILE *reg);
729
static REGERR nr_Unlock(REGFILE *reg);
731
static REGERR nr_ReadDesc(REGFILE *reg, REGOFF offset, REGDESC *desc); /* reads a desc */
732
static REGERR nr_ReadName(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf);
733
static REGERR nr_ReadData(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf);
735
static REGERR nr_WriteDesc(REGFILE *reg, REGDESC *desc); /* writes a desc */
736
static REGERR nr_WriteString(REGFILE *reg, char *string, REGDESC *desc); /* writes a string */
737
static REGERR nr_WriteData(REGFILE *reg, char *string, uint32 len, REGDESC *desc); /* writes a string */
739
static REGERR nr_AppendDesc(REGFILE *reg, REGDESC *desc, REGOFF *result); /* adds a desc */
740
static REGERR nr_AppendName(REGFILE *reg, char *name, REGDESC *desc); /* adds a name */
741
static REGERR nr_AppendString(REGFILE *reg, char *string, REGDESC *desc); /* adds a string */
742
static REGERR nr_AppendData(REGFILE *reg, char *string, uint32 len, REGDESC *desc); /* adds a string */
744
static XP_Bool nr_IsValidUTF8(char *string); /* checks if a string is UTF-8 encoded */
745
/* -------------------------------------------------------------------- */
749
static REGERR nr_ReadHdr(REGFILE *reg)
754
char hdrBuf[sizeof(REGHDR)];
759
err = nr_ReadFile(reg->fh, 0, sizeof(REGHDR), &hdrBuf);
764
/* header doesn't exist, so create one */
765
err = nr_CreateRoot(reg);
769
/* header read successfully -- convert */
770
reg->hdr.magic = nr_ReadLong ( hdrBuf + HDR_MAGIC );
771
reg->hdr.verMajor = nr_ReadShort( hdrBuf + HDR_VERMAJOR );
772
reg->hdr.verMinor = nr_ReadShort( hdrBuf + HDR_VERMINOR );
773
reg->hdr.avail = nr_ReadLong ( hdrBuf + HDR_AVAIL );
774
reg->hdr.root = nr_ReadLong ( hdrBuf + HDR_ROOT );
776
/* check to see if it's the right file type */
777
if (reg->hdr.magic != MAGIC_NUMBER) {
778
err = REGERR_BADMAGIC;
782
/* Check registry version
783
* If the major version is bumped we're incompatible
784
* (minor version just means some new features were added)
786
* Upgrade code will go here in the future...
788
if ( reg->hdr.verMajor > MAJOR_VERSION ) {
789
err = REGERR_REGVERSION;
794
if ( reg->inInit && !(reg->readOnly) ) {
795
filelength = nr_GetFileLength(reg->fh);
796
if (reg->hdr.avail != filelength)
798
reg->hdr.avail = filelength;
801
err = nr_WriteHdr(reg);
805
#endif /* SELF_REPAIR */
809
/* unexpected error from nr_ReadFile()*/
821
static REGERR nr_WriteHdr(REGFILE *reg)
824
char hdrBuf[sizeof(REGHDR)];
829
return REGERR_READONLY;
831
/* convert to XP int format */
832
nr_WriteLong ( reg->hdr.magic, hdrBuf + HDR_MAGIC );
833
nr_WriteShort( reg->hdr.verMajor, hdrBuf + HDR_VERMAJOR );
834
nr_WriteShort( reg->hdr.verMinor, hdrBuf + HDR_VERMINOR );
835
nr_WriteLong ( reg->hdr.avail, hdrBuf + HDR_AVAIL );
836
nr_WriteLong ( reg->hdr.root, hdrBuf + HDR_ROOT );
838
/* err = nr_WriteFile(reg->fh, 0, sizeof(REGHDR), ®->hdr); */
839
err = nr_WriteFile(reg->fh, 0, sizeof(hdrBuf), &hdrBuf);
841
if (err == REGERR_OK)
850
static REGERR nr_CreateRoot(REGFILE *reg)
852
/* Called when an empty file is detected by ReadHdr */
859
reg->hdr.magic = MAGIC_NUMBER;
860
reg->hdr.verMajor = MAJOR_VERSION;
861
reg->hdr.verMinor = MINOR_VERSION;
863
reg->hdr.avail = HDRRESERVE;
865
/* Create root descriptor */
870
root.type = REGTYPE_KEY;
875
err = nr_AppendName(reg, ROOTKEY_STR, &root);
876
if (err != REGERR_OK)
879
err = nr_AppendDesc(reg, &root, ®->hdr.root);
880
if (err != REGERR_OK)
883
return nr_WriteHdr(reg); /* actually commit to disk */
885
/* Create standard top-level nodes */
891
static REGERR nr_Lock(REGFILE *reg)
896
status = nr_LockRange(reg->fh, 0, sizeof(REGHDR));
898
if (status == REGERR_OK)
900
/* lock the object */
901
PR_Lock( reg->lock );
904
/* try to refresh header info */
905
status = nr_ReadHdr(reg);
906
if ( status != REGERR_OK ) {
907
PR_Unlock( reg->lock );
917
static REGERR nr_Unlock(REGFILE *reg)
919
PR_Unlock( reg->lock );
921
return nr_UnlockRange(reg->fh, 0, sizeof(REGHDR));
926
static REGERR nr_ReadDesc(REGFILE *reg, REGOFF offset, REGDESC *desc)
930
char descBuf[ DESC_SIZE ];
933
XP_ASSERT(offset >= HDRRESERVE);
934
XP_ASSERT(offset < reg->hdr.avail);
937
err = nr_ReadFile(reg->fh, offset, DESC_SIZE, &descBuf);
938
if (err == REGERR_OK)
940
desc->location = nr_ReadLong ( descBuf + DESC_LOCATION );
941
desc->name = nr_ReadLong ( descBuf + DESC_NAME );
942
desc->namelen = nr_ReadShort( descBuf + DESC_NAMELEN );
943
desc->type = nr_ReadShort( descBuf + DESC_TYPE );
944
desc->left = nr_ReadLong ( descBuf + DESC_LEFT );
945
desc->value = nr_ReadLong ( descBuf + DESC_VALUE );
946
desc->valuelen = nr_ReadLong ( descBuf + DESC_VALUELEN );
947
desc->parent = nr_ReadLong ( descBuf + DESC_PARENT );
949
if ( TYPE_IS_ENTRY(desc->type) ) {
951
desc->valuebuf = nr_ReadLong( descBuf + DESC_VALUEBUF );
953
else { /* TYPE is KEY */
954
desc->down = nr_ReadLong( descBuf + DESC_DOWN );
958
if (desc->location != offset)
959
err = REGERR_BADLOCN;
960
else if ( desc->type & REGTYPE_DELETED )
961
err = REGERR_DELETED;
970
static REGERR nr_ReadName(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf)
976
XP_ASSERT(desc->name > 0);
977
XP_ASSERT(desc->name < reg->hdr.avail);
978
XP_ASSERT(buflen > 0);
981
if ( desc->namelen > buflen )
982
return REGERR_BUFTOOSMALL;
984
err = nr_ReadFile(reg->fh, desc->name, desc->namelen, buf);
986
buf[buflen-1] = '\0'; /* avoid runaways */
994
static REGERR nr_ReadData(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf)
1000
XP_ASSERT(desc->value > 0);
1001
XP_ASSERT(desc->value < reg->hdr.avail);
1002
XP_ASSERT(buflen > 0);
1005
if ( desc->valuelen > buflen )
1006
return REGERR_BUFTOOSMALL;
1008
err = nr_ReadFile(reg->fh, desc->value, desc->valuelen, buf);
1016
static REGERR nr_WriteDesc(REGFILE *reg, REGDESC *desc)
1018
char descBuf[ DESC_SIZE ];
1022
XP_ASSERT( desc->location >= HDRRESERVE );
1023
XP_ASSERT( desc->location < reg->hdr.avail );
1026
return REGERR_READONLY;
1028
/* convert to XP int format */
1029
nr_WriteLong ( desc->location, descBuf + DESC_LOCATION );
1030
nr_WriteLong ( desc->name, descBuf + DESC_NAME );
1031
nr_WriteShort( desc->namelen, descBuf + DESC_NAMELEN );
1032
nr_WriteShort( desc->type, descBuf + DESC_TYPE );
1033
nr_WriteLong ( desc->left, descBuf + DESC_LEFT );
1034
nr_WriteLong ( desc->value, descBuf + DESC_VALUE );
1035
nr_WriteLong ( desc->valuelen, descBuf + DESC_VALUELEN );
1036
nr_WriteLong ( desc->parent, descBuf + DESC_PARENT );
1038
if ( TYPE_IS_ENTRY(desc->type) ) {
1039
XP_ASSERT( 0 == desc->down );
1040
nr_WriteLong( desc->valuebuf, descBuf + DESC_VALUEBUF );
1042
else { /* TYPE is KEY */
1043
XP_ASSERT( 0 == desc->valuebuf );
1044
nr_WriteLong( desc->down, descBuf + DESC_DOWN );
1047
return nr_WriteFile(reg->fh, desc->location, DESC_SIZE, descBuf);
1048
} /* nr_WriteDesc */
1052
static REGERR nr_AppendDesc(REGFILE *reg, REGDESC *desc, REGOFF *result)
1056
char descBuf[ DESC_SIZE ];
1065
return REGERR_READONLY;
1067
desc->location = reg->hdr.avail;
1069
/* convert to XP int format */
1070
nr_WriteLong ( desc->location, descBuf + DESC_LOCATION );
1071
nr_WriteLong ( desc->name, descBuf + DESC_NAME );
1072
nr_WriteShort( desc->namelen, descBuf + DESC_NAMELEN );
1073
nr_WriteShort( desc->type, descBuf + DESC_TYPE );
1074
nr_WriteLong ( desc->left, descBuf + DESC_LEFT );
1075
nr_WriteLong ( desc->value, descBuf + DESC_VALUE );
1076
nr_WriteLong ( desc->valuelen, descBuf + DESC_VALUELEN );
1077
nr_WriteLong ( desc->parent, descBuf + DESC_PARENT );
1079
if ( TYPE_IS_ENTRY(desc->type) ) {
1080
XP_ASSERT( 0 == desc->down );
1081
nr_WriteLong( desc->valuebuf, descBuf + DESC_VALUEBUF );
1083
else { /* TYPE is KEY */
1084
XP_ASSERT( 0 == desc->valuebuf );
1085
nr_WriteLong( desc->down, descBuf + DESC_DOWN );
1088
err = nr_WriteFile(reg->fh, reg->hdr.avail, DESC_SIZE, descBuf);
1090
if (err == REGERR_OK)
1092
*result = reg->hdr.avail;
1093
reg->hdr.avail += DESC_SIZE;
1096
err = nr_WriteHdr(reg);
1106
static REGERR nr_AppendName(REGFILE *reg, char *name, REGDESC *desc)
1116
if (!nr_IsValidUTF8(name))
1117
return REGERR_BADUTF8;
1119
return REGERR_READONLY;
1121
len = XP_STRLEN(name) + 1;
1123
/* check for valid name parameter */
1125
return REGERR_PARAM;
1127
if ( len > MAXREGNAMELEN )
1128
return REGERR_NAMETOOLONG;
1130
for ( p = name; (*p != 0); p++ ) {
1131
if ( INVALID_NAME_CHAR(*p) )
1132
return REGERR_BADNAME;
1136
err = nr_WriteFile(reg->fh, reg->hdr.avail, len, name);
1138
/* if write successful update the desc and hdr */
1139
if (err == REGERR_OK)
1141
desc->namelen = (uint16)len;
1142
desc->name = reg->hdr.avail;
1143
reg->hdr.avail += len;
1146
err = nr_WriteHdr(reg);
1152
} /* nr_AppendName */
1156
static REGERR nr_WriteString(REGFILE *reg, char *string, REGDESC *desc)
1161
if (!nr_IsValidUTF8(string))
1162
return REGERR_BADUTF8;
1164
return REGERR_READONLY;
1165
len = XP_STRLEN(string) + 1;
1167
return nr_WriteData( reg, string, len, desc );
1169
} /* nr_WriteString */
1173
static REGERR nr_WriteData(REGFILE *reg, char *string, uint32 len, REGDESC *desc)
1182
return REGERR_READONLY;
1185
return REGERR_PARAM;
1187
if ( len > MAXREGVALUELEN )
1188
return REGERR_NAMETOOLONG;
1190
/* save the data in the same place if it fits */
1191
if ( len <= desc->valuebuf ) {
1192
err = nr_WriteFile( reg->fh, desc->value, len, string );
1193
if ( err == REGERR_OK ) {
1194
desc->valuelen = len;
1198
/* otherwise append new data */
1199
err = nr_AppendData( reg, string, len, desc );
1204
} /* nr_WriteData */
1208
static REGERR nr_AppendString(REGFILE *reg, char *string, REGDESC *desc)
1213
if (!nr_IsValidUTF8(string))
1214
return REGERR_BADUTF8;
1216
return REGERR_READONLY;
1217
len = XP_STRLEN(string) + 1;
1219
return nr_AppendData( reg, string, len, desc );
1221
} /* nr_AppendString */
1225
static REGERR nr_AppendData(REGFILE *reg, char *string, uint32 len, REGDESC *desc)
1234
return REGERR_READONLY;
1237
return REGERR_PARAM;
1239
if ( len > MAXREGVALUELEN )
1240
return REGERR_NAMETOOLONG;
1242
/* save the string */
1243
err = nr_WriteFile(reg->fh, reg->hdr.avail, len, string);
1244
if (err == REGERR_OK)
1246
desc->value = reg->hdr.avail;
1247
desc->valuelen = len;
1248
desc->valuebuf = len;
1250
reg->hdr.avail += len;
1253
err = nr_WriteHdr(reg);
1259
} /* nr_AppendData */
1261
static XP_Bool nr_IsValidUTF8(char *string)
1271
for ( c = string; *c != '\0'; c++ )
1273
ch = (unsigned char)*c;
1276
/* expecting an initial byte */
1279
/* standard byte -- do nothing */
1281
else if ((0xC0 & ch) == 0x80)
1283
/* follow byte illegal here */
1286
else if ((0xE0 & ch) == 0xC0)
1290
else if ((0xF0 & ch) == 0xE0)
1296
/* unexpected (unsupported) initial byte */
1302
XP_ASSERT( follow > 0 );
1303
if ((0xC0 & ch) == 0x80)
1305
/* expecting follow byte and found one */
1318
/* invalid state -- interrupted character */
1323
} /* checks if a string is UTF-8 encoded */
1325
/* --------------------------------------------------------------------
1327
* --------------------------------------------------------------------
1329
static REGERR nr_NextName(const char *pPath, char *buf, uint32 bufsize, const char **newPath);
1330
static REGERR nr_RemoveName(char *path);
1331
static REGERR nr_CatName(REGFILE *reg, REGOFF node, char *path, uint32 bufsize,
1333
static REGERR nr_ReplaceName(REGFILE *reg, REGOFF node, char *path,
1334
uint32 bufsize, REGDESC *desc);
1335
/* -------------------------------------------------------------------- */
1338
/* Scans path at 'pPath' and copies next name segment into 'buf'.
1339
* Also sets 'newPath' to point at the next segment of pPath.
1341
static REGERR nr_NextName(const char *pPath, char *buf, uint32 bufsize, const char **newPath)
1344
REGERR err = REGERR_OK;
1346
/* initialization and validation */
1352
if ( pPath==NULL || *pPath=='\0' )
1353
return REGERR_NOMORE;
1355
/* ... skip an initial path delimiter */
1356
if ( *pPath == PATHDEL ) {
1359
if ( *pPath == '\0' )
1360
return REGERR_NOMORE;
1363
/* ... missing name segment or initial blank are errors*/
1364
if ( *pPath == PATHDEL || *pPath == ' ' )
1365
return REGERR_BADNAME;
1367
/* copy first path segment into return buf */
1368
while ( *pPath != '\0' && *pPath != PATHDEL )
1370
if ( len == bufsize ) {
1371
err = REGERR_NAMETOOLONG;
1374
if ( *pPath < ' ' && *pPath > 0 )
1375
return REGERR_BADNAME;
1382
/* ... name segment can't end with blanks, either */
1383
if ( ' ' == *(buf-1) )
1384
return REGERR_BADNAME;
1386
/* return a pointer to the start of the next segment */
1396
static REGERR nr_CatName(REGFILE *reg, REGOFF node, char *path, uint32 bufsize, REGDESC *desc)
1398
REGERR err = REGERR_OK;
1401
uint32 len = XP_STRLEN(path);
1408
if ( len < bufsize ) {
1414
err = REGERR_BUFTOOSMALL;
1416
p++; /* point one past PATHDEL */
1421
if ( err == REGERR_OK ) {
1422
err = nr_ReadDesc( reg, node, desc );
1423
if ( err == REGERR_OK ) {
1424
err = nr_ReadName( reg, desc, bufsize-len, p );
1434
static REGERR nr_ReplaceName(REGFILE *reg, REGOFF node, char *path, uint32 bufsize, REGDESC *desc)
1436
/* NOTE! It is EXREMELY important that names be in UTF-8; otherwise
1437
* the backwards path search will fail for multi-byte/Unicode names
1446
len = XP_STRLEN(path);
1447
if ( len > bufsize )
1448
return REGERR_PARAM;
1453
while ((p > path) && (*p != PATHDEL)) {
1457
if ( *p == PATHDEL ) {
1466
err = nr_ReadDesc( reg, node, desc );
1467
if ( err == REGERR_OK ) {
1468
err = nr_ReadName( reg, desc, bufsize-len, p );
1476
static REGERR nr_RemoveName(char *path)
1479
* path = "/Machine/4.0/" output = "/Machine"
1480
* path = "/Machine" output = ""
1481
* path = "" output = REGERR_NOMORE
1483
* NOTE! It is EXREMELY important that names be in UTF-8; otherwise
1484
* the backwards path search will fail for multi-byte/Unicode names
1487
int len = XP_STRLEN(path);
1490
return REGERR_NOMORE;
1493
/* if last char is '/', ignore it */
1497
while ((p > path) && (*p != PATHDEL))
1500
/* if (*p != PATHDEL)
1501
return REGERR_NOMORE;
1511
/* --------------------------------------------------------------------
1512
* Key/Entry Management
1513
* --------------------------------------------------------------------
1515
static REGERR nr_Find(REGFILE *reg, REGOFF offParent, const char *pPath,
1516
REGDESC *pDesc, REGOFF *pPrev, REGOFF *pParent, XP_Bool raw);
1518
static REGERR nr_FindAtLevel(REGFILE *reg, REGOFF offFirst, const char *pName,
1519
REGDESC *pDesc, REGOFF *pOffPrev);
1521
static REGERR nr_CreateSubKey(REGFILE *reg, REGOFF parent, REGDESC *pDesc,
1523
static REGERR nr_CreateEntryString(REGFILE *reg, REGDESC *pParent,
1524
char *name, char *value);
1525
static REGERR nr_CreateEntry(REGFILE *reg, REGDESC *pParent, char *name,
1526
uint16 type, char *buffer, uint32 length);
1527
/* -------------------------------------------------------------------- */
1531
static REGERR nr_Find(REGFILE *reg,
1543
char namebuf[MAXREGNAMELEN];
1546
XP_ASSERT( pPath != NULL );
1547
XP_ASSERT( offParent >= HDRRESERVE );
1548
XP_ASSERT( VALID_FILEHANDLE( reg->fh ) );
1555
/* read starting desc */
1556
err = nr_ReadDesc( reg, offParent, &desc);
1559
if ( err == REGERR_OK ) {
1560
/* save current location as parent of next segment */
1561
offParent = desc.location;
1562
/* look for name at next level down */
1563
err = nr_FindAtLevel(reg, desc.down, pPath, &desc, &offPrev);
1567
/* Walk 'path', reading keys into 'desc' */
1569
while ( err == REGERR_OK )
1571
err = nr_NextName(p, namebuf, sizeof(namebuf), &p);
1573
if ( err == REGERR_OK ) {
1574
/* save current location as parent of next segment */
1575
offParent = desc.location;
1576
/* look for name at next level down */
1577
err = nr_FindAtLevel(reg, desc.down, namebuf, &desc, &offPrev);
1582
if ( (raw == FALSE && err == REGERR_NOMORE) ||
1583
(raw == TRUE && err == REGERR_OK) ) {
1584
/* we found all the segments of the path--success! */
1588
COPYDESC(pDesc, &desc);
1594
*pParent = offParent;
1605
/* nr_FindAtLevel -- looks for a node matching "pName" on the level starting
1606
* with "offset". Returns REGERR_OK if found, REGERR_NOFIND
1607
* if not (plus other error conditions).
1609
* If pDesc and pOffPrev are valid pointers *AND* the name is
1610
* found then pDesc will point at the REGDESC of the node and
1611
* pOffPrev will be the offset of the desc for the previous
1612
* node at the same level.
1614
* If the node is *NOT* found (REGERR_NOFIND is returned)
1615
* pDesc will point at the REGDESC of the last found node
1616
* (as will pOffPrev). If some other error is returned then
1617
* THese values must not be used.
1619
static REGERR nr_FindAtLevel(REGFILE *reg,
1625
char namebuf[MAXREGNAMELEN];
1630
/* Note: offset=0 when there's no 'down' or 'left' */
1632
XP_ASSERT(offset < reg->hdr.avail);
1636
while ( offset != 0 )
1638
/* get name of next node */
1639
err = nr_ReadDesc(reg, offset, &desc);
1640
if (err != REGERR_OK)
1643
err = nr_ReadName(reg, &desc, sizeof(namebuf), namebuf);
1644
if (err != REGERR_OK)
1647
/* check to see if it's the one we want */
1648
if (XP_STRCMP(namebuf, pName) == 0) {
1649
/* Found it! Signaled by non-zero offset */
1653
/* advance to the next node */
1658
if ( pDesc != NULL && (prev || offset)) {
1659
/* prev and offset BOTH null means we never loaded a desc */
1660
COPYDESC( pDesc, &desc );
1662
if ( pOffPrev != NULL ) {
1666
if ( offset != 0 ) /* if we found one */
1669
return REGERR_NOFIND;
1674
static REGERR nr_CreateSubKey(REGFILE *reg,
1679
/* nr_CreateSubKey does NO error checking--callers *MUST*
1680
* ensure that there are no duplicates
1689
err = nr_AppendName(reg, name, &desc);
1690
if (err != REGERR_OK)
1693
desc.type = REGTYPE_KEY;
1699
desc.parent = parent;
1701
if ( parent == pDesc->location ) {
1702
/* It's a parent desc, so no siblings */
1703
err = nr_AppendDesc(reg, &desc, &pDesc->down);
1706
/* It's a sibling desc */
1707
XP_ASSERT( pDesc->left == 0 ); /* not the end of chain! */
1708
err = nr_AppendDesc(reg, &desc, &pDesc->left);
1710
if (err != REGERR_OK)
1713
/* write out the fixed up parent/sibling desc */
1714
err = nr_WriteDesc(reg, pDesc);
1715
COPYDESC(pDesc, &desc);
1719
} /* nr_CreateSubKey */
1723
static REGERR nr_CreateEntryString(REGFILE *reg, REGDESC *pParent, char *name, char *value)
1733
XP_MEMSET( &desc, 0, sizeof(REGDESC) );
1735
err = nr_AppendName(reg, name, &desc);
1736
if (err != REGERR_OK)
1739
err = nr_AppendString(reg, value, &desc);
1740
if (err != REGERR_OK)
1743
desc.type = REGTYPE_ENTRY_STRING_UTF;
1744
desc.left = pParent->value;
1746
desc.parent = pParent->location;
1748
err = nr_AppendDesc(reg, &desc, &pParent->value);
1749
if (err != REGERR_OK)
1752
/* printf("nr_AddEntry: %s=%s @0x%lx\n", name, value, pParent->value); */
1754
return nr_WriteDesc(reg, pParent);
1756
} /* nr_CreateEntryString */
1760
static REGERR nr_CreateEntry(REGFILE *reg, REGDESC *pParent, char *name,
1761
uint16 type, char *value, uint32 length)
1771
XP_MEMSET( &desc, 0, sizeof(REGDESC) );
1773
err = nr_AppendName(reg, name, &desc);
1774
if (err != REGERR_OK)
1777
err = nr_AppendData(reg, value, length, &desc);
1778
if (err != REGERR_OK)
1782
desc.left = pParent->value;
1784
desc.parent = pParent->location;
1786
err = nr_AppendDesc(reg, &desc, &pParent->value);
1787
if (err != REGERR_OK)
1790
/* printf("nr_AddEntry: %s=%s @0x%lx\n", name, value, pParent->value); */
1792
return nr_WriteDesc(reg, pParent);
1794
} /* nr_CreateEntry */
1799
/* ---------------------------------------------------------------------
1801
* ---------------------------------------------------------------------
1803
static REGOFF nr_TranslateKey( REGFILE *reg, RKEY key );
1804
static REGERR nr_InitStdRkeys( REGFILE *reg );
1805
static XP_Bool nr_ProtectedNode( REGFILE *reg, REGOFF key );
1806
static REGERR nr_RegAddKey( REGFILE *reg, RKEY key, char *path, RKEY *newKey, XP_Bool raw );
1807
static REGERR nr_RegDeleteKey( REGFILE *reg, RKEY key, char *path, XP_Bool raw );
1808
static REGERR nr_RegOpen( const char *filename, HREG *hReg );
1809
static REGERR nr_RegClose( HREG hReg );
1810
static char* nr_GetUsername();
1811
static const char* nr_GetRegName (const char *name);
1812
static int nr_RegSetBufferSize( HREG hReg, int bufsize );
1814
/* --------------------------------------------------------------------- */
1817
static REGOFF nr_TranslateKey( REGFILE *reg, RKEY key )
1821
/* if it's a special key */
1822
if ( key < HDRRESERVE ) {
1823
/* ...translate it */
1827
retKey = reg->hdr.root;
1830
case ROOTKEY_VERSIONS:
1831
retKey = reg->rkeys.versions;
1835
retKey = reg->rkeys.users;
1838
case ROOTKEY_COMMON:
1839
retKey = reg->rkeys.common;
1842
#ifndef STANDALONE_REGISTRY
1843
case ROOTKEY_CURRENT_USER:
1844
if ( reg->rkeys.current_user == 0 ) {
1845
/* not initialized--find the current user key */
1850
profName = nr_GetUsername();
1851
if ( NULL != profName ) {
1852
/* Don't assign a slot for missing or magic profile */
1853
if ( '\0' == *profName ||
1854
0 == XP_STRCMP(ASW_MAGIC_PROFILE_NAME, profName))
1858
err = nr_RegAddKey( reg, reg->rkeys.users, profName, &userkey, FALSE );
1863
err = nr_RegAddKey( reg, reg->rkeys.users, "default", &userkey, FALSE );
1866
if ( err == REGERR_OK ) {
1867
reg->rkeys.current_user = userkey;
1870
retKey = reg->rkeys.current_user;
1872
#endif /* !STANDALONE_REGISTRY */
1874
case ROOTKEY_PRIVATE:
1875
retKey = reg->rkeys.privarea;
1879
/* not a valid key */
1885
/* ...otherwise it's fine as-is */
1886
retKey = (REGOFF)key;
1889
} /* nr_TranslateKey */
1893
static REGERR nr_InitStdRkeys( REGFILE *reg )
1895
REGERR err = REGERR_OK;
1898
XP_ASSERT( reg != NULL );
1900
/* initialize to invalid key values */
1901
XP_MEMSET( ®->rkeys, 0, sizeof(STDNODES) );
1903
/* Add each key before looking it up. Adding an already
1904
* existing key is harmless, and these MUST exist.
1908
err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_USERS_STR, &key, FALSE );
1909
if ( err != REGERR_OK )
1911
reg->rkeys.users = key;
1913
/* ROOTKEY_COMMON */
1914
err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_COMMON_STR, &key, FALSE );
1915
if ( err != REGERR_OK )
1917
reg->rkeys.common = key;
1919
/* ROOTKEY_VERSIONS */
1920
err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_VERSIONS_STR, &key, FALSE );
1921
if ( err != REGERR_OK )
1923
reg->rkeys.versions = key;
1925
/* ROOTKEY_CURRENT_USER */
1926
/* delay until first use -- see nr_TranslateKey */
1928
/* ROOTKEY_PRIVATE */
1929
err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_PRIVATE_STR, &key, FALSE );
1930
if ( err != REGERR_OK )
1932
reg->rkeys.privarea = key;
1935
} /* nr_InitStdRkeys */
1939
static XP_Bool nr_ProtectedNode( REGFILE *reg, REGOFF key )
1941
if ( (key == reg->hdr.root) ||
1942
(key == reg->rkeys.users) ||
1943
(key == reg->rkeys.versions) ||
1944
(key == reg->rkeys.common) ||
1945
(key == reg->rkeys.current_user) )
1955
static REGERR nr_RegAddKey( REGFILE *reg, RKEY key, char *path, RKEY *newKey, XP_Bool raw )
1961
char namebuf[MAXREGNAMELEN];
1964
XP_ASSERT( regStartCount > 0 );
1965
XP_ASSERT( reg != NULL );
1966
XP_ASSERT( path != NULL );
1967
XP_ASSERT( *path != '\0' );
1968
XP_ASSERT( VALID_FILEHANDLE( reg->fh ) );
1970
/* have to translate again in case this is an internal call */
1971
start = nr_TranslateKey( reg, key );
1973
return REGERR_PARAM;
1975
/* Get starting desc */
1976
err = nr_ReadDesc( reg, start, &desc );
1979
if ( err == REGERR_OK) {
1980
/* look for name at next level down */
1981
parent = desc.location;
1982
err = nr_FindAtLevel(reg, desc.down, path, &desc, 0);
1984
/* if key is not found */
1985
if ( err == REGERR_NOFIND ) {
1986
/* add it as a sub-key to the last found key */
1987
err = nr_CreateSubKey(reg, parent, &desc, path);
1992
/* Walk 'path', reading keys into 'desc' */
1994
while ( err == REGERR_OK ) {
1996
/* get next name on the path */
1997
err = nr_NextName(p, namebuf, sizeof(namebuf), &p);
1998
if ( err == REGERR_OK ) {
1999
/* look for name at next level down */
2000
parent = desc.location;
2001
err = nr_FindAtLevel(reg, desc.down, namebuf, &desc, 0);
2003
/* if key is not found */
2004
if ( err == REGERR_NOFIND ) {
2005
/* add it as a sub-key to the last found key */
2006
err = nr_CreateSubKey(reg, parent, &desc, namebuf);
2012
/* it's good to have processed the whole path */
2013
if ( (raw == FALSE && err == REGERR_NOMORE) ||
2014
(raw == TRUE && err == REGERR_OK) )
2018
/* return new key if the caller wants it */
2019
if ( newKey != NULL ) {
2020
*newKey = desc.location;
2026
} /* nr_RegAddKey */
2031
static REGERR nr_RegDeleteKey( REGFILE *reg, RKEY key, char *path, XP_Bool raw )
2036
REGDESC predecessor;
2041
XP_ASSERT( regStartCount > 0 );
2042
XP_ASSERT( reg != NULL );
2043
XP_ASSERT( VALID_FILEHANDLE( reg->fh ) );
2045
start = nr_TranslateKey( reg, key );
2046
if ( path == NULL || *path == '\0' || start == 0 )
2047
return REGERR_PARAM;
2049
/* find the specified key */
2050
err = nr_Find( reg, start, path, &desc, &offPrev, &offParent, raw );
2051
if ( err == REGERR_OK ) {
2053
XP_ASSERT( !TYPE_IS_ENTRY( desc.type ) );
2055
/* make sure it's childless and not a top-level key */
2056
if ( (desc.down == 0) && !nr_ProtectedNode( reg, desc.location ) ) {
2058
/* Are we the first on our level? */
2059
if ( offPrev == 0 ) {
2060
/* Yes: link to parent's "down" pointer */
2061
err = nr_ReadDesc( reg, offParent, &predecessor );
2062
link = &(predecessor.down);
2065
/* No: link using predecessor's "left" pointer */
2066
err = nr_ReadDesc( reg, offPrev, &predecessor );
2067
link = &(predecessor.left);
2070
/* If we read the predecessor desc OK */
2071
if (err == REGERR_OK) {
2072
XP_ASSERT( *link == desc.location );
2074
/* link predecessor to next, removing current node from chain */
2077
/* Write the updated predecessor */
2078
err = nr_WriteDesc( reg, &predecessor );
2079
if ( err == REGERR_OK ) {
2080
/* Mark key deleted to prevent bogus use by anyone
2081
* who is holding an RKEY for that node
2083
desc.type |= REGTYPE_DELETED;
2084
err = nr_WriteDesc( reg, &desc );
2089
/* specified node is protected from deletion */
2096
} /* nr_RegDeleteKey */
2100
static int nr_RegSetBufferSize( HREG hReg, int bufsize )
2102
REGERR err = REGERR_OK;
2103
REGHANDLE* reghnd = (REGHANDLE*)hReg;
2105
XP_Bool needDelete = FALSE;
2109
err = VERIFY_HREG( hReg );
2110
if ( err != REGERR_OK )
2115
PR_Lock( reg->lock );
2117
newSize = XP_FileSetBufferSize( reg->fh, bufsize );
2119
PR_Unlock( reg->lock );
2126
static REGERR nr_RegOpen( const char *filename, HREG *hReg )
2128
REGERR status = REGERR_OK;
2132
XP_ASSERT( regStartCount > 0 );
2134
/* initialize output handle in case of error */
2135
if ( hReg == NULL ) {
2136
return REGERR_PARAM;
2140
/* Look for named file in list of open registries */
2141
filename = nr_GetRegName( filename );
2142
if (filename == NULL) {
2145
pReg = vr_findRegFile( filename );
2147
/* if registry not already open */
2150
/* ...then open it */
2151
pReg = (REGFILE*)XP_ALLOC( sizeof(REGFILE) );
2152
if ( pReg == NULL ) {
2153
status = REGERR_MEMORY;
2156
XP_MEMSET(pReg, 0, sizeof(REGFILE));
2158
pReg->inInit = TRUE;
2159
pReg->filename = XP_STRDUP(filename);
2160
if (pReg->filename == NULL) {
2162
status = REGERR_MEMORY;
2166
status = nr_OpenFile( filename, &(pReg->fh) );
2167
if (status == REGERR_READONLY) {
2168
/* Open, but read only */
2169
pReg->readOnly = TRUE;
2172
if ( status != REGERR_OK ) {
2173
XP_FREE( pReg->filename );
2179
/* ...read and validate the header */
2180
status = nr_ReadHdr( pReg );
2181
if ( status != REGERR_OK ) {
2182
nr_CloseFile( &(pReg->fh) );
2183
XP_FREE( pReg->filename );
2188
/* ...other misc initialization */
2191
#ifndef STANDALONE_REGISTRY
2192
pReg->uniqkey = PR_Now();
2195
status = nr_InitStdRkeys( pReg );
2196
if ( status == REGERR_OK ) {
2197
/* ...and add it to the list */
2201
nr_CloseFile( &(pReg->fh) );
2202
XP_FREE( pReg->filename );
2207
#ifndef STANDALONE_REGISTRY
2208
pReg->lock = PR_NewLock();
2211
/* now done with everything that needs to protect the header */
2212
pReg->inInit = FALSE;
2215
/* create a new handle to the regfile */
2216
pHandle = (REGHANDLE*)XP_ALLOC( sizeof(REGHANDLE) );
2217
if ( pHandle == NULL ) {
2218
/* we can't create the handle */
2219
if ( pReg->refCount == 0 ) {
2220
/* we've just opened it so close it and remove node */
2221
nr_CloseFile( &(pReg->fh) );
2222
nr_DeleteNode( pReg );
2225
status = REGERR_MEMORY;
2229
pHandle->magic = MAGIC_NUMBER;
2230
pHandle->pReg = pReg;
2232
/* success: bump the reference count and return the handle */
2234
*hReg = (void*)pHandle;
2243
static REGERR nr_RegClose( HREG hReg )
2245
REGERR err = REGERR_OK;
2246
REGHANDLE* reghnd = (REGHANDLE*)hReg;
2248
XP_Bool needDelete = FALSE;
2250
XP_ASSERT( regStartCount > 0 );
2253
err = VERIFY_HREG( hReg );
2254
if ( err != REGERR_OK )
2259
PR_Lock( reg->lock );
2260
if ( err == REGERR_OK )
2262
XP_ASSERT( VALID_FILEHANDLE(reg->fh) );
2264
/* save changed header info */
2265
if ( reg->hdrDirty ) {
2269
/* lower REGFILE user count */
2272
/* if registry is no longer in use */
2273
if ( reg->refCount < 1 )
2275
/* ...then close the file */
2276
nr_CloseFile( &(reg->fh) );
2278
/* ...and mark REGFILE node for deletion from list */
2283
/* ...otherwise make sure any writes are flushed */
2284
XP_FileFlush( reg->fh );
2287
reghnd->magic = 0; /* prevent accidental re-use */
2288
PR_Unlock( reg->lock );
2291
nr_DeleteNode( reg );
2302
static char *nr_GetUsername()
2304
if (NULL == user_name) {
2311
static const char* nr_GetRegName (const char *name)
2313
if (name == NULL || *name == '\0') {
2314
XP_ASSERT( globalRegName != NULL );
2315
return globalRegName;
2324
/* ---------------------------------------------------------------------
2326
* --------------------------------------------------------------------- */
2329
/* ---------------------------------------------------------------------
2330
* NR_RegGetUsername - Gets a copy of the current username
2333
* A variable which, on exit will contain an alloc'ed string which is a
2334
* copy of the current username.
2336
* DO NOT USE -- OBSOLETE
2337
* ---------------------------------------------------------------------
2340
VR_INTERFACE(REGERR) NR_RegGetUsername(char **name)
2342
/* XXX: does this need locking? */
2345
return REGERR_PARAM;
2347
*name = XP_STRDUP(nr_GetUsername());
2349
if ( NULL == *name )
2350
return REGERR_MEMORY;
2356
/* ---------------------------------------------------------------------
2357
* NR_RegSetBufferSize - Set the buffer size
2360
* name - name of the current user
2363
* ---------------------------------------------------------------------
2366
VR_INTERFACE(int) NR_RegSetBufferSize( HREG hReg, int bufsize )
2370
PR_Lock( reglist_lock );
2372
newSize = nr_RegSetBufferSize( hReg, bufsize );
2374
PR_Unlock(reglist_lock);
2380
/* ---------------------------------------------------------------------
2381
* NR_RegSetUsername - Set the current username
2383
* If the current user profile name is not set then trying to use
2384
* HKEY_CURRENT_USER will result in an error.
2387
* name - name of the current user
2390
* ---------------------------------------------------------------------
2393
VR_INTERFACE(REGERR) NR_RegSetUsername(const char *name)
2397
if ( name == NULL || *name == '\0' )
2398
return REGERR_PARAM;
2400
tmp = XP_STRDUP(name);
2402
return REGERR_MEMORY;
2405
PR_Lock( reglist_lock );
2407
XP_FREEIF(user_name);
2410
/* XXX: changing the username should go through and clear out the current.user
2411
for each open registry. */
2413
PR_Unlock( reglist_lock );
2421
#ifndef STANDALONE_REGISTRY
2422
/* ---------------------------------------------------------------------
2423
* NR_RegGetUniqueName
2425
* Returns a unique name that can be used for anonymous key/value names
2428
* hReg - handle of open registry
2429
* outbuf - where to put the string
2430
* buflen - how big the buffer is
2431
* ---------------------------------------------------------------------
2433
VR_INTERFACE(REGERR) NR_RegGetUniqueName(HREG hReg, char* outbuf, uint32 buflen)
2438
static PRUint64 uniqkey;
2440
/* verify parameters */
2441
err = VERIFY_HREG( hReg );
2442
if ( err != REGERR_OK )
2445
reg = ((REGHANDLE*)hReg)->pReg;
2448
return REGERR_PARAM;
2450
if ( buflen <= (sizeof(PRUint64)*2) )
2451
return REGERR_BUFTOOSMALL;
2453
if ( LL_IS_ZERO(uniqkey) )
2456
PR_snprintf(outbuf,buflen,"%llx",uniqkey);
2458
/* increment counter for next time */
2460
LL_ADD(uniqkey, uniqkey, one);
2469
/* ---------------------------------------------------------------------
2470
* NR_RegOpen - Open a netscape XP registry
2473
* filename - registry file to open. NULL or "" opens the standard
2475
* hReg - OUT: handle to opened registry
2478
* ---------------------------------------------------------------------
2480
VR_INTERFACE(REGERR) NR_RegOpen( const char *filename, HREG *hReg )
2482
REGERR status = REGERR_OK;
2484
#if !defined(STANDALONE_REGISTRY)
2485
/* you must call NR_StartupRegistry() first */
2486
if ( regStartCount <= 0 )
2490
PR_Lock(reglist_lock);
2492
status = nr_RegOpen( filename, hReg );
2494
PR_Unlock(reglist_lock);
2503
/* ---------------------------------------------------------------------
2504
* NR_RegClose - Close a netscape XP registry
2507
* hReg - handle of open registry to be closed.
2509
* After calling this routine the handle is no longer valid
2510
* ---------------------------------------------------------------------
2512
VR_INTERFACE(REGERR) NR_RegClose( HREG hReg )
2514
REGERR err = REGERR_OK;
2516
PR_Lock( reglist_lock );
2518
err = nr_RegClose( hReg );
2520
PR_Unlock(reglist_lock);
2529
/* ---------------------------------------------------------------------
2530
* NR_RegFlush - Manually flush data in a netscape XP registry
2533
* hReg - handle of open registry to be flushed.
2534
* ---------------------------------------------------------------------
2536
VR_INTERFACE(REGERR) NR_RegFlush( HREG hReg )
2541
/* verify parameters */
2542
err = VERIFY_HREG( hReg );
2543
if ( err != REGERR_OK )
2546
reg = ((REGHANDLE*)hReg)->pReg;
2548
/* can't flush a read-only registry */
2549
if ( reg->readOnly )
2550
return REGERR_READONLY;
2552
/* lock the registry file */
2553
err = nr_Lock( reg );
2554
if ( err == REGERR_OK )
2556
if ( reg->hdrDirty ) {
2560
XP_FileFlush( reg->fh );
2562
/* unlock the registry */
2573
/* ---------------------------------------------------------------------
2574
* NR_RegIsWritable - Check read/write status of open registry
2577
* hReg - handle of open registry to query
2578
* ---------------------------------------------------------------------
2580
VR_INTERFACE(REGERR) NR_RegIsWritable( HREG hReg )
2585
/* verify parameters */
2586
err = VERIFY_HREG( hReg );
2587
if ( err != REGERR_OK )
2590
reg = ((REGHANDLE*)hReg)->pReg;
2592
if ( reg->readOnly )
2593
return REGERR_READONLY;
2597
} /* NR_RegIsWritable */
2601
/* ---------------------------------------------------------------------
2602
* NR_RegAddKey - Add a key node to the registry
2604
* This routine is simply a wrapper to perform user input
2605
* validation and translation from HREG and standard key
2606
* values into the internal format
2609
* hReg - handle of open registry
2610
* key - registry key obtained from NR_RegGetKey(),
2611
* or one of the standard top-level keys
2612
* path - relative path of key to be added. Intermediate
2613
* nodes will also be added if necessary.
2614
* ---------------------------------------------------------------------
2616
VR_INTERFACE(REGERR) NR_RegAddKey( HREG hReg, RKEY key, char *path, RKEY *newKey )
2622
/* prevent use of return value in case errors aren't checked */
2623
if ( newKey != NULL )
2626
/* verify parameters */
2627
err = VERIFY_HREG( hReg );
2628
if ( err != REGERR_OK )
2631
reg = ((REGHANDLE*)hReg)->pReg;
2633
if ( path == NULL || *path == '\0' || reg == NULL )
2634
return REGERR_PARAM;
2636
/* lock the registry file */
2637
err = nr_Lock( reg );
2638
if ( err == REGERR_OK )
2640
/* ... don't allow additional children of ROOTKEY */
2641
start = nr_TranslateKey( reg, key );
2642
if ( start != 0 && start != reg->hdr.root )
2644
err = nr_RegAddKey( reg, start, path, newKey, FALSE );
2649
/* unlock the registry */
2654
} /* NR_RegAddKey */
2659
/* ---------------------------------------------------------------------
2660
* NR_RegAddKeyRaw - Add a key node to the registry
2662
* This routine is different from NR_RegAddKey() in that it takes
2663
* a keyname rather than a path.
2666
* hReg - handle of open registry
2667
* key - registry key obtained from NR_RegGetKey(),
2668
* or one of the standard top-level keys
2669
* keyname - name of key to be added. No parsing of this
2671
* newkey - if not null the RKEY of the new key is returned
2672
* ---------------------------------------------------------------------
2674
VR_INTERFACE(REGERR) NR_RegAddKeyRaw( HREG hReg, RKEY key, char *keyname, RKEY *newKey )
2680
/* prevent use of return value in case errors aren't checked */
2681
if ( newKey != NULL )
2684
/* verify parameters */
2685
err = VERIFY_HREG( hReg );
2686
if ( err != REGERR_OK )
2689
reg = ((REGHANDLE*)hReg)->pReg;
2691
if ( keyname == NULL || *keyname == '\0' || reg == NULL )
2692
return REGERR_PARAM;
2694
/* lock the registry file */
2695
err = nr_Lock( reg );
2696
if ( err == REGERR_OK )
2698
/* ... don't allow additional children of ROOTKEY */
2699
start = nr_TranslateKey( reg, key );
2700
if ( start != 0 && start != reg->hdr.root )
2702
err = nr_RegAddKey( reg, start, keyname, newKey, TRUE );
2707
/* unlock the registry */
2712
} /* NR_RegAddKeyRaw */
2717
/* ---------------------------------------------------------------------
2718
* NR_RegDeleteKey - Delete the specified key
2720
* Note that delete simply orphans blocks and makes no attempt
2721
* to reclaim space in the file. Use NR_RegPack()
2723
* Cannot be used to delete keys with child keys
2726
* hReg - handle of open registry
2727
* key - starting node RKEY, typically one of the standard ones.
2728
* path - relative path of key to delete
2729
* ---------------------------------------------------------------------
2731
VR_INTERFACE(REGERR) NR_RegDeleteKey( HREG hReg, RKEY key, char *path )
2736
/* verify parameters */
2737
err = VERIFY_HREG( hReg );
2738
if ( err != REGERR_OK )
2741
reg = ((REGHANDLE*)hReg)->pReg;
2744
err = nr_Lock( reg );
2745
if ( err == REGERR_OK )
2747
err = nr_RegDeleteKey( reg, key, path, FALSE );
2752
} /* NR_RegDeleteKey */
2757
/* ---------------------------------------------------------------------
2758
* NR_RegDeleteKeyRaw - Delete the specified raw key
2760
* Note that delete simply orphans blocks and makes no attempt
2761
* to reclaim space in the file. Use NR_RegPack()
2764
* hReg - handle of open registry
2765
* key - RKEY or parent to the raw key you wish to delete
2766
* keyname - name of child key to delete
2767
* ---------------------------------------------------------------------
2769
VR_INTERFACE(REGERR) NR_RegDeleteKeyRaw( HREG hReg, RKEY key, char *keyname )
2774
/* verify parameters */
2775
err = VERIFY_HREG( hReg );
2776
if ( err != REGERR_OK )
2779
reg = ((REGHANDLE*)hReg)->pReg;
2782
err = nr_Lock( reg );
2783
if ( err == REGERR_OK )
2785
err = nr_RegDeleteKey( reg, key, keyname, TRUE );
2790
} /* NR_RegDeleteKeyRaw */
2795
/* ---------------------------------------------------------------------
2796
* NR_RegGetKey - Get the RKEY value of a node from its path
2799
* hReg - handle of open registry
2800
* key - starting node RKEY, typically one of the standard ones.
2801
* path - relative path of key to find. (a blank path just gives you
2802
* the starting key--useful for verification, VersionRegistry)
2803
* result - if successful the RKEY of the specified sub-key
2804
* ---------------------------------------------------------------------
2806
VR_INTERFACE(REGERR) NR_RegGetKey( HREG hReg, RKEY key, const char *path, RKEY *result )
2813
XP_ASSERT( regStartCount > 0 );
2815
/* prevent use of return value in case errors aren't checked */
2816
if ( result != NULL )
2819
/* verify parameters */
2820
err = VERIFY_HREG( hReg );
2821
if ( err != REGERR_OK )
2824
if ( path == NULL || result == NULL )
2825
return REGERR_PARAM;
2827
reg = ((REGHANDLE*)hReg)->pReg;
2830
err = nr_Lock( reg );
2831
if ( err == REGERR_OK )
2833
start = nr_TranslateKey( reg, key );
2836
/* find the specified key ( if it's valid )*/
2837
err = nr_Find( reg, start, path, &desc, 0, 0, FALSE );
2838
if ( err == REGERR_OK ) {
2839
*result = (RKEY)desc.location;
2851
} /* NR_RegGetKey */
2856
/* ---------------------------------------------------------------------
2857
* NR_RegGetKeyRaw - Get the RKEY value of a node from its keyname
2860
* hReg - handle of open registry
2861
* key - starting node RKEY, typically one of the standard ones.
2862
* keyname - keyname of key to find. (a blank keyname just gives you
2863
* the starting key--useful for verification, VersionRegistry)
2864
* result - if successful the RKEY of the specified sub-key
2865
* ---------------------------------------------------------------------
2867
VR_INTERFACE(REGERR) NR_RegGetKeyRaw( HREG hReg, RKEY key, char *keyname, RKEY *result )
2874
XP_ASSERT( regStartCount > 0 );
2876
/* prevent use of return value in case errors aren't checked */
2877
if ( result != NULL )
2880
/* verify parameters */
2881
err = VERIFY_HREG( hReg );
2882
if ( err != REGERR_OK )
2885
if ( keyname == NULL || result == NULL )
2886
return REGERR_PARAM;
2888
reg = ((REGHANDLE*)hReg)->pReg;
2891
err = nr_Lock( reg );
2892
if ( err == REGERR_OK )
2894
start = nr_TranslateKey( reg, key );
2897
/* find the specified key ( if it's valid )*/
2898
err = nr_Find( reg, start, keyname, &desc, 0, 0, TRUE );
2899
if ( err == REGERR_OK ) {
2900
*result = (RKEY)desc.location;
2912
} /* NR_RegGetKeyRaw */
2917
/* ---------------------------------------------------------------------
2918
* NR_RegGetEntryInfo - Get some basic info about the entry data
2921
* hReg - handle of open registry
2922
* key - RKEY of key that contains entry--obtain with NR_RegGetKey()
2923
* name - name of entry
2924
* info - return: Entry info object
2925
* ---------------------------------------------------------------------
2927
VR_INTERFACE(REGERR) NR_RegGetEntryInfo( HREG hReg, RKEY key, char *name,
2934
XP_ASSERT( regStartCount > 0 );
2936
/* verify parameters */
2937
err = VERIFY_HREG( hReg );
2938
if ( err != REGERR_OK )
2941
if ( name == NULL || *name == '\0' || info == NULL || key == 0 )
2942
return REGERR_PARAM;
2944
reg = ((REGHANDLE*)hReg)->pReg;
2946
err = nr_Lock( reg );
2947
if ( err == REGERR_OK )
2949
/* read starting desc */
2950
err = nr_ReadDesc( reg, key, &desc);
2951
if ( err == REGERR_OK )
2953
/* if the named entry exists */
2954
err = nr_FindAtLevel( reg, desc.value, name, &desc, NULL );
2955
if ( err == REGERR_OK )
2957
/* ... return the values */
2958
if ( info->size == sizeof(REGINFO) )
2960
info->entryType = desc.type;
2961
info->entryLength = desc.valuelen;
2965
/* uninitialized (maybe invalid) REGINFO structure */
2976
} /* NR_RegGetEntryInfo */
2981
/* ---------------------------------------------------------------------
2982
* NR_RegGetEntryString - Get the UTF string value associated with the
2983
* named entry of the specified key.
2986
* hReg - handle of open registry
2987
* key - RKEY of key that contains entry--obtain with NR_RegGetKey()
2988
* name - name of entry
2989
* buffer - destination for string
2990
* bufsize - size of buffer
2991
* ---------------------------------------------------------------------
2993
VR_INTERFACE(REGERR) NR_RegGetEntryString( HREG hReg, RKEY key, char *name,
2994
char *buffer, uint32 bufsize)
3000
XP_ASSERT( regStartCount > 0 );
3002
/* verify parameters */
3003
err = VERIFY_HREG( hReg );
3004
if ( err != REGERR_OK )
3007
if ( name==NULL || *name=='\0' || buffer==NULL || bufsize==0 || key==0 )
3008
return REGERR_PARAM;
3010
reg = ((REGHANDLE*)hReg)->pReg;
3012
err = nr_Lock( reg );
3013
if ( err == REGERR_OK )
3015
/* read starting desc */
3016
err = nr_ReadDesc( reg, key, &desc);
3017
if ( err == REGERR_OK )
3019
/* if the named entry exists */
3020
err = nr_FindAtLevel( reg, desc.value, name, &desc, NULL );
3021
if ( err == REGERR_OK )
3023
/* read the string */
3024
if ( desc.type == REGTYPE_ENTRY_STRING_UTF )
3026
err = nr_ReadData( reg, &desc, bufsize, buffer );
3027
/* prevent run-away strings */
3028
buffer[bufsize-1] = '\0';
3031
err = REGERR_BADTYPE;
3041
} /* NR_RegGetEntryString */
3046
/* ---------------------------------------------------------------------
3047
* NR_RegGetEntry - Get the value data associated with the
3048
* named entry of the specified key.
3051
* hReg - handle of open registry
3052
* key - RKEY of key that contains entry--obtain with NR_RegGetKey()
3053
* name - name of entry
3054
* buffer - destination for data
3055
* size - in: size of buffer
3056
* out: size of actual data (incl. \0 term. for strings)
3057
* ---------------------------------------------------------------------
3059
VR_INTERFACE(REGERR) NR_RegGetEntry( HREG hReg, RKEY key, char *name,
3060
void *buffer, uint32 *size )
3065
char *tmpbuf = NULL; /* malloc a tmp buffer to convert XP int arrays */
3069
XP_Bool needFree = FALSE;
3071
XP_ASSERT( regStartCount > 0 );
3073
/* verify parameters */
3074
err = VERIFY_HREG( hReg );
3075
if ( err != REGERR_OK )
3078
if ( name==NULL || *name=='\0' || buffer==NULL || size==NULL || key==0 )
3079
return REGERR_PARAM;
3081
reg = ((REGHANDLE*)hReg)->pReg;
3083
err = nr_Lock( reg );
3084
if ( err == REGERR_OK )
3086
/* read starting desc */
3087
err = nr_ReadDesc( reg, key, &desc);
3088
if ( err == REGERR_OK )
3090
/* if the named entry exists */
3091
err = nr_FindAtLevel( reg, desc.value, name, &desc, NULL );
3092
if ( err == REGERR_OK )
3094
if ( desc.valuelen > *size ) {
3095
err = REGERR_BUFTOOSMALL;
3097
else if ( desc.valuelen == 0 ) {
3100
else switch (desc.type)
3102
/* platform independent array of 32-bit integers */
3103
case REGTYPE_ENTRY_INT32_ARRAY:
3104
tmpbuf = (char*)XP_ALLOC( desc.valuelen );
3105
if ( tmpbuf != NULL )
3108
err = nr_ReadData( reg, &desc, desc.valuelen, tmpbuf );
3109
if ( REGERR_OK == err )
3111
/* convert int array */
3112
nInt = (desc.valuelen / INTSIZE);
3113
pISrc = (uint32*)tmpbuf;
3114
pIDest = (uint32*)buffer;
3115
for(; nInt > 0; nInt--, pISrc++, pIDest++) {
3116
*pIDest = nr_ReadLong((char*)pISrc);
3121
err = REGERR_MEMORY;
3124
case REGTYPE_ENTRY_STRING_UTF:
3125
tmpbuf = (char*)buffer;
3126
err = nr_ReadData( reg, &desc, *size, tmpbuf );
3127
/* prevent run-away strings */
3128
tmpbuf[(*size)-1] = '\0';
3131
case REGTYPE_ENTRY_FILE:
3133
err = nr_ReadData( reg, &desc, *size, (char*)buffer );
3134
#if defined(XP_MAC) || defined(XP_MACOSX)
3137
tmpbuf = nr_PathFromMacAlias(buffer, *size);
3141
err = REGERR_NOFILE; /* must match nr_GetPathname() in VerReg.c */
3147
if (XP_STRLEN(tmpbuf) < *size) /* leave room for \0 */
3148
XP_STRCPY(buffer, tmpbuf);
3150
err = REGERR_BUFTOOSMALL;
3156
case REGTYPE_ENTRY_BYTES:
3157
default: /* return raw data for unknown types */
3158
err = nr_ReadData( reg, &desc, *size, (char*)buffer );
3162
/* return the actual data size */
3163
*size = desc.valuelen;
3175
} /* NR_RegGetEntry */
3180
/* ---------------------------------------------------------------------
3181
* NR_RegSetEntryString - Store a UTF-8 string value associated with the
3182
* named entry of the specified key. Used for
3183
* both creation and update.
3186
* hReg - handle of open registry
3187
* key - RKEY of key that contains entry--obtain with NR_RegGetKey()
3188
* name - name of entry
3189
* buffer - UTF-8 String to store
3190
* ---------------------------------------------------------------------
3192
VR_INTERFACE(REGERR) NR_RegSetEntryString( HREG hReg, RKEY key, char *name,
3200
XP_ASSERT( regStartCount > 0 );
3202
/* verify parameters */
3203
err = VERIFY_HREG( hReg );
3204
if ( err != REGERR_OK )
3207
if ( name == NULL || *name == '\0' || buffer == NULL || key == 0 )
3208
return REGERR_PARAM;
3210
reg = ((REGHANDLE*)hReg)->pReg;
3213
err = nr_Lock( reg );
3214
if ( err != REGERR_OK )
3217
/* read starting desc */
3218
err = nr_ReadDesc( reg, key, &parent);
3219
if ( err == REGERR_OK ) {
3221
/* if the named entry already exists */
3222
err = nr_FindAtLevel( reg, parent.value, name, &desc, NULL );
3223
if ( err == REGERR_OK ) {
3224
/* then update the existing one */
3225
err = nr_WriteString( reg, buffer, &desc );
3226
if ( err == REGERR_OK ) {
3227
desc.type = REGTYPE_ENTRY_STRING_UTF;
3228
err = nr_WriteDesc( reg, &desc );
3231
else if ( err == REGERR_NOFIND ) {
3232
/* otherwise create a new entry */
3233
err = nr_CreateEntryString( reg, &parent, name, buffer );
3235
/* other errors fall through */
3238
/* unlock registry */
3243
} /* NR_RegSetEntryString */
3248
/* ---------------------------------------------------------------------
3249
* NR_RegSetEntry - Store value data associated with the named entry
3250
* of the specified key. Used for both creation and update.
3253
* hReg - handle of open registry
3254
* key - RKEY of key that contains entry--obtain with NR_RegGetKey()
3255
* name - name of entry
3256
* type - type of data to be stored
3257
* buffer - data to store
3258
* size - length of data to store in bytes
3259
* ---------------------------------------------------------------------
3261
VR_INTERFACE(REGERR) NR_RegSetEntry( HREG hReg, RKEY key, char *name, uint16 type,
3262
void *buffer, uint32 size )
3272
XP_Bool needFree = FALSE;
3273
int32 datalen = size;
3275
XP_ASSERT( regStartCount > 0 );
3277
/* verify parameters */
3278
err = VERIFY_HREG( hReg );
3279
if ( err != REGERR_OK )
3282
if ( name==NULL || *name=='\0' || buffer==NULL || size==0 || key==0 )
3283
return REGERR_PARAM;
3285
reg = ((REGHANDLE*)hReg)->pReg;
3287
/* validate type and convert numerics to XP format */
3290
case REGTYPE_ENTRY_BYTES:
3291
data = (char*)buffer;
3294
case REGTYPE_ENTRY_FILE:
3296
#if defined(XP_MAC) || defined(XP_MACOSX)
3297
nr_MacAliasFromPath(buffer, (void **)&data, &datalen);
3301
data = (char*)buffer;
3306
case REGTYPE_ENTRY_STRING_UTF:
3307
data = (char*)buffer;
3308
/* string must be null terminated */
3309
if ( data[size-1] != '\0' )
3310
return REGERR_PARAM;
3314
case REGTYPE_ENTRY_INT32_ARRAY:
3315
/* verify no partial integers */
3316
if ( (size % INTSIZE) != 0 )
3317
return REGERR_PARAM;
3319
/* get a conversion buffer */
3320
data = (char*)XP_ALLOC(size);
3322
return REGERR_MEMORY;
3326
/* convert array to XP format */
3327
nInt = ( size / INTSIZE );
3328
pIDest = (uint32*)data;
3329
pISrc = (uint32*)buffer;
3331
for( ; nInt > 0; nInt--, pIDest++, pISrc++) {
3332
nr_WriteLong( *pISrc, (char*)pIDest );
3338
return REGERR_BADTYPE;
3342
err = nr_Lock( reg );
3343
if ( REGERR_OK == err )
3345
/* read starting desc */
3346
err = nr_ReadDesc( reg, key, &parent);
3347
if ( err == REGERR_OK )
3349
/* if the named entry already exists */
3350
err = nr_FindAtLevel( reg, parent.value, name, &desc, NULL );
3351
if ( err == REGERR_OK )
3353
/* then update the existing one */
3354
err = nr_WriteData( reg, data, datalen, &desc );
3355
if ( err == REGERR_OK )
3358
err = nr_WriteDesc( reg, &desc );
3361
else if ( err == REGERR_NOFIND )
3363
/* otherwise create a new entry */
3364
err = nr_CreateEntry( reg, &parent, name, type, data, datalen );
3367
/* other errors fall through */
3371
/* unlock registry */
3380
} /* NR_RegSetEntry */
3385
/* ---------------------------------------------------------------------
3386
* NR_RegDeleteEntry - Delete the named entry
3389
* hReg - handle of open registry
3390
* key - RKEY of key that contains entry--obtain with NR_RegGetKey()
3391
* name - name of entry
3392
* ---------------------------------------------------------------------
3394
VR_INTERFACE(REGERR) NR_RegDeleteEntry( HREG hReg, RKEY key, char *name )
3402
XP_ASSERT( regStartCount > 0 );
3404
/* verify parameters */
3405
err = VERIFY_HREG( hReg );
3406
if ( err != REGERR_OK )
3409
if ( name == NULL || *name == '\0' || key == 0)
3410
return REGERR_PARAM;
3412
reg = ((REGHANDLE*)hReg)->pReg;
3415
err = nr_Lock( reg );
3416
if ( err != REGERR_OK )
3419
/* read starting desc */
3420
err = nr_ReadDesc( reg, key, &parent);
3421
if ( err == REGERR_OK ) {
3423
/* look up the named entry */
3424
err = nr_FindAtLevel( reg, parent.value, name, &desc, &offPrev );
3425
if ( err == REGERR_OK ) {
3427
XP_ASSERT( TYPE_IS_ENTRY( desc.type ) );
3429
/* if entry is the head of a chain */
3430
if ( offPrev == 0 ) {
3431
/* hook parent key to next entry */
3432
XP_ASSERT( parent.value == desc.location );
3433
parent.value = desc.left;
3436
/* otherwise hook previous entry to next */
3437
err = nr_ReadDesc( reg, offPrev, &parent );
3438
parent.left = desc.left;
3440
/* write out changed desc for previous node */
3441
if ( err == REGERR_OK ) {
3442
err = nr_WriteDesc( reg, &parent );
3443
/* zap the deleted desc because an enum state may contain a
3444
* reference to a specific entry node
3446
if ( err == REGERR_OK ) {
3447
desc.type |= REGTYPE_DELETED;
3448
err = nr_WriteDesc( reg, &desc );
3454
/* unlock registry */
3459
} /* NR_RegDeleteEntry */
3464
/* ---------------------------------------------------------------------
3465
* NR_RegEnumSubkeys - Enumerate the subkey names for the specified key
3467
* Returns REGERR_NOMORE at end of enumeration.
3470
* hReg - handle of open registry
3471
* key - RKEY of key to enumerate--obtain with NR_RegGetKey()
3472
* eState - enumerations state, must contain NULL to start
3473
* buffer - location to store subkey names. Once an enumeration
3474
* is started user must not modify contents since values
3475
* are built using the previous contents.
3476
* bufsize - size of buffer for names
3477
* style - 0 returns direct child keys only, REGENUM_DESCEND
3478
* returns entire sub-tree
3479
* ---------------------------------------------------------------------
3481
VR_INTERFACE(REGERR) NR_RegEnumSubkeys( HREG hReg, RKEY key, REGENUM *state,
3482
char *buffer, uint32 bufsize, uint32 style)
3488
XP_ASSERT( regStartCount > 0 );
3490
/* verify parameters */
3491
err = VERIFY_HREG( hReg );
3492
if ( err != REGERR_OK )
3495
if ( key == 0 || state == NULL || buffer == NULL )
3496
return REGERR_PARAM;
3498
reg = ((REGHANDLE*)hReg)->pReg;
3501
err = nr_Lock( reg );
3502
if ( err != REGERR_OK )
3505
desc.down = 0; /* initialize to quiet warnings */
3508
/* verify starting key */
3509
key = nr_TranslateKey( reg, key );
3512
else if ( *state == 0 )
3513
err = nr_ReadDesc( reg, key, &desc);
3517
if ( err == REGERR_OK )
3519
/* if in initial state and no children return now */
3520
if ( *state == 0 && desc.down == 0 )
3522
err = REGERR_NOMORE;
3524
else switch ( style )
3526
case REGENUM_CHILDREN:
3530
/* initial state: get first child (.down) */
3531
err = nr_ReplaceName( reg, desc.down, buffer, bufsize, &desc );
3535
/* get sibling (.left) of current key */
3536
err = nr_ReadDesc( reg, *state, &desc );
3537
if ( err == REGERR_OK || REGERR_DELETED == err )
3539
/* it's OK for the current (state) node to be deleted */
3540
if ( desc.left != 0 )
3542
err = nr_ReplaceName( reg, desc.left,
3543
buffer, bufsize, &desc );
3546
err = REGERR_NOMORE;
3552
case REGENUM_DESCEND:
3557
err = nr_ReplaceName( reg, desc.down, buffer, bufsize, &desc );
3561
/* get last position */
3562
err = nr_ReadDesc( reg, *state, &desc );
3563
if ( REGERR_OK != err && REGERR_DELETED != err )
3565
/* it is OK for the state node to be deleted
3566
* (the *next* node MUST be "live", though).
3567
* bail out on any other error */
3571
if ( desc.down != 0 ) {
3572
/* append name of first child key */
3573
err = nr_CatName( reg, desc.down, buffer, bufsize, &desc );
3575
else if ( desc.left != 0 ) {
3576
/* replace last segment with next sibling */
3577
err = nr_ReplaceName( reg, desc.left,
3578
buffer, bufsize, &desc );
3581
/* done with level, pop up as many times as necessary */
3582
while ( err == REGERR_OK )
3584
if ( desc.parent != key && desc.parent != 0 )
3586
err = nr_RemoveName( buffer );
3587
if ( err == REGERR_OK )
3589
err = nr_ReadDesc( reg, desc.parent, &desc );
3590
if ( err == REGERR_OK && desc.left != 0 )
3592
err = nr_ReplaceName( reg, desc.left,
3593
buffer, bufsize, &desc );
3594
break; /* found a node */
3599
err = REGERR_NOMORE;
3606
case REGENUM_DEPTH_FIRST:
3612
err = nr_ReplaceName( reg, desc.down, buffer, bufsize, &desc );
3613
while ( REGERR_OK == err && desc.down != 0 )
3615
/* start as far down the tree as possible */
3616
err = nr_CatName( reg, desc.down, buffer, bufsize, &desc );
3621
/* get last position */
3622
err = nr_ReadDesc( reg, *state, &desc );
3623
if ( REGERR_OK != err && REGERR_DELETED != err )
3625
/* it is OK for the state node to be deleted
3626
* (the *next* node MUST be "live", though).
3627
* bail out on any other error */
3631
if ( desc.left != 0 )
3633
/* get sibling, then descend as far as possible */
3634
err = nr_ReplaceName(reg, desc.left, buffer,bufsize,&desc);
3636
while ( REGERR_OK == err && desc.down != 0 )
3638
err = nr_CatName(reg, desc.down, buffer,bufsize,&desc);
3643
/* pop up to parent */
3644
if ( desc.parent != key && desc.parent != 0 )
3646
err = nr_RemoveName( buffer );
3647
if ( REGERR_OK == err )
3649
/* validate parent key */
3650
err = nr_ReadDesc( reg, desc.parent, &desc );
3654
err = REGERR_NOMORE;
3666
/* set enum state to current key */
3667
if ( err == REGERR_OK ) {
3668
*state = desc.location;
3671
/* unlock registry */
3676
} /* NR_RegEnumSubkeys */
3681
/* ---------------------------------------------------------------------
3682
* NR_RegEnumEntries - Enumerate the entry names for the specified key
3684
* Returns REGERR_NOMORE at end of enumeration.
3687
* hReg - handle of open registry
3688
* key - RKEY of key that contains entry--obtain with NR_RegGetKey()
3689
* eState - enumerations state, must contain NULL to start
3690
* buffer - location to store entry names
3691
* bufsize - size of buffer for names
3692
* info - optional REGINFO for the entry. If not NULL must be
3693
* initialized as in NR_RegGetEntryInfo()
3694
* ---------------------------------------------------------------------
3696
VR_INTERFACE(REGERR) NR_RegEnumEntries( HREG hReg, RKEY key, REGENUM *state,
3697
char *buffer, uint32 bufsize, REGINFO *info )
3703
XP_ASSERT( regStartCount > 0 );
3705
/* verify parameters */
3706
err = VERIFY_HREG( hReg );
3707
if ( err != REGERR_OK )
3710
if ( key == 0 || state == NULL || buffer == NULL )
3711
return REGERR_PARAM;
3713
reg = ((REGHANDLE*)hReg)->pReg;
3716
err = nr_Lock( reg );
3717
if ( err != REGERR_OK )
3720
/* verify starting key */
3721
err = nr_ReadDesc( reg, key, &desc);
3722
if ( err == REGERR_OK )
3726
/* initial state--get first entry */
3727
if ( desc.value != 0 )
3730
err = nr_ReplaceName( reg, desc.value, buffer, bufsize, &desc );
3734
/* there *are* no entries */
3735
err = REGERR_NOMORE;
3740
/* 'state' stores previous entry */
3741
err = nr_ReadDesc( reg, *state, &desc );
3742
if ( err == REGERR_OK || err == REGERR_DELETED )
3744
/* get next entry in chain */
3745
if ( desc.left != 0 )
3748
err = nr_ReplaceName( reg, desc.left, buffer, bufsize, &desc );
3752
/* at end of chain */
3753
err = REGERR_NOMORE;
3758
/* if we found an entry */
3759
if ( err == REGERR_OK )
3761
/* set enum state to current entry */
3762
*state = desc.location;
3764
/* return REGINFO if requested */
3765
if ( info != NULL && info->size >= sizeof(REGINFO) )
3767
info->entryType = desc.type;
3768
info->entryLength = desc.valuelen;
3773
/* unlock registry */
3778
} /* NR_RegEnumEntries */
3784
/* --------------------------------------------------------------------
3786
* --------------------------------------------------------------------
3788
#ifndef STANDALONE_REGISTRY
3791
#ifdef RESURRECT_LATER
3792
static REGERR nr_createTempRegName( char *filename, uint32 filesize );
3793
static REGERR nr_addNodesToNewReg( HREG hReg, RKEY rootkey, HREG hRegNew, void *userData, nr_RegPackCallbackFunc fn );
3794
/* -------------------------------------------------------------------- */
3795
static REGERR nr_createTempRegName( char *filename, uint32 filesize )
3797
struct stat statbuf;
3798
XP_Bool nameFound = FALSE;
3799
char tmpname[MAX_PATH+1];
3803
XP_STRCPY( tmpname, filename );
3804
len = XP_STRLEN(tmpname);
3805
if (len < filesize) {
3806
tmpname[len-1] = '~';
3807
tmpname[len] = '\0';
3809
if ( stat(tmpname, &statbuf) != 0 )
3813
while (!nameFound && len < filesize ) {
3814
tmpname[len-1] = '~';
3815
tmpname[len] = '\0';
3817
if ( stat(tmpname, &statbuf) != 0 )
3823
XP_STRCPY(filename, tmpname);
3831
static REGERR nr_addNodesToNewReg( HREG hReg, RKEY rootkey, HREG hRegNew, void *userData, nr_RegPackCallbackFunc fn )
3833
char keyname[MAXREGPATHLEN+1] = {0};
3834
char entryname[MAXREGPATHLEN+1] = {0};
3836
uint32 bufsize = 2024;
3839
REGENUM entrystate = 0;
3841
int err = REGERR_OK;
3842
int status = REGERR_OK;
3847
static int32 cnt = 0;
3848
static int32 prevCnt = 0;
3850
reg = ((REGHANDLE*)hReg)->pReg;
3851
regNew = ((REGHANDLE*)hRegNew)->pReg;
3853
buffer = XP_ALLOC(bufsize);
3854
if ( buffer == NULL ) {
3855
err = REGERR_MEMORY;
3859
while (err == REGERR_OK)
3861
err = NR_RegEnumSubkeys( hReg, rootkey, &state, keyname, sizeof(keyname), REGENUM_DESCEND );
3862
if ( err != REGERR_OK )
3864
err = NR_RegAddKey( hRegNew, rootkey, keyname, &newKey );
3865
if ( err != REGERR_OK )
3868
if (cnt >= prevCnt + 15)
3870
fn(userData, regNew->hdr.avail, reg->hdr.avail);
3873
err = NR_RegGetKey( hReg, rootkey, keyname, &key );
3874
if ( err != REGERR_OK )
3878
while (status == REGERR_OK) {
3879
info.size = sizeof(REGINFO);
3880
status = NR_RegEnumEntries( hReg, key, &entrystate, entryname,
3881
sizeof(entryname), &info );
3882
if ( status == REGERR_OK ) {
3883
XP_ASSERT( bufsize >= info.entryLength );
3885
status = NR_RegGetEntry( hReg, key, entryname, buffer, &datalen );
3886
XP_ASSERT( info.entryLength == datalen );
3887
if ( status == REGERR_OK ) {
3889
status = NR_RegSetEntry( hRegNew, newKey, entryname,
3890
info.entryType, buffer, info.entryLength );
3894
if ( status != REGERR_NOMORE ) {
3895
/* pass real error to outer loop */
3900
if ( err == REGERR_NOMORE )
3906
#endif /* RESURRECT_LATER */
3910
/* ---------------------------------------------------------------------
3911
* NR_RegPack - Pack an open registry.
3912
* Registry is locked the entire time.
3915
* hReg - handle of open registry to pack
3916
* ---------------------------------------------------------------------
3918
VR_INTERFACE(REGERR) NR_RegPack( HREG hReg, void *userData, nr_RegPackCallbackFunc fn)
3920
return REGERR_FAIL; /* XXX resurrect after mozilla beta 1 */
3925
char tempfilename[MAX_PATH+1] = {0};
3926
char oldfilename[MAX_PATH+1] = {0};
3928
XP_Bool bCloseTempFile = FALSE;
3930
int err = REGERR_OK;
3933
XP_ASSERT( regStartCount > 0 );
3934
if ( regStartCount <= 0 )
3937
reg = ((REGHANDLE*)hReg)->pReg;
3940
err = nr_Lock( reg );
3941
if ( err != REGERR_OK )
3944
PR_Lock(reglist_lock);
3945
XP_STRCPY(tempfilename, reg->filename);
3946
err = nr_createTempRegName(tempfilename, sizeof(tempfilename));
3947
if ( err != REGERR_OK )
3950
/* force file creation */
3951
fh = vr_fileOpen(tempfilename, XP_FILE_WRITE_BIN);
3952
if ( !VALID_FILEHANDLE(fh) ) {
3958
err = NR_RegOpen(tempfilename, &hRegTemp);
3959
if ( err != REGERR_OK )
3961
bCloseTempFile = TRUE;
3963
/* must open temp file first or we get the same name twice */
3964
XP_STRCPY(oldfilename, reg->filename);
3965
err = nr_createTempRegName(oldfilename, sizeof(oldfilename));
3966
if ( err != REGERR_OK )
3969
key = ROOTKEY_PRIVATE;
3970
err = nr_addNodesToNewReg( hReg, key, hRegTemp, userData, fn);
3971
if ( err != REGERR_OK )
3973
key = ROOTKEY_VERSIONS;
3974
err = nr_addNodesToNewReg( hReg, key, hRegTemp, userData, fn);
3975
if ( err != REGERR_OK )
3977
key = ROOTKEY_COMMON;
3978
err = nr_addNodesToNewReg( hReg, key, hRegTemp, userData, fn);
3979
if ( err != REGERR_OK )
3981
key = ROOTKEY_USERS;
3982
err = nr_addNodesToNewReg( hReg, key, hRegTemp, userData, fn);
3983
if ( err != REGERR_OK )
3986
err = NR_RegClose(hRegTemp);
3987
bCloseTempFile = FALSE;
3989
/* close current reg file so we can rename it */
3990
XP_FileClose(reg->fh);
3992
/* rename current reg file out of the way */
3993
err = nr_RenameFile(reg->filename, oldfilename);
3995
/* rename failed, get rid of the new registry and reopen the old one*/
3996
remove(tempfilename);
3997
reg->fh = vr_fileOpen(reg->filename, XP_FILE_UPDATE_BIN);
4001
/* rename packed registry to the correct name */
4002
err = nr_RenameFile(tempfilename, reg->filename);
4004
/* failure, recover original registry */
4005
err = nr_RenameFile(oldfilename, reg->filename);
4006
remove(tempfilename);
4007
reg->fh = vr_fileOpen(reg->filename, XP_FILE_UPDATE_BIN);
4011
remove(oldfilename);
4013
reg->fh = vr_fileOpen(reg->filename, XP_FILE_UPDATE_BIN);
4016
if ( bCloseTempFile ) {
4017
NR_RegClose(hRegTemp);
4019
PR_Unlock( reglist_lock );
4022
#endif /* RESURRECT_LATER */
4027
#pragma export reset
4030
#endif /* STANDALONE_REGISTRY */
4037
/* ---------------------------------------------------------------------
4038
* ---------------------------------------------------------------------
4039
* Registry initialization and shut-down
4040
* ---------------------------------------------------------------------
4041
* ---------------------------------------------------------------------
4046
#ifndef STANDALONE_REGISTRY
4047
extern PRLock *vr_lock;
4052
#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(STANDALONE_REGISTRY)
4053
extern XP_Bool bGlobalRegistry;
4060
VR_INTERFACE(REGERR) NR_StartupRegistry(void)
4062
REGERR status = REGERR_OK;
4064
#ifndef STANDALONE_REGISTRY
4065
if ( reglist_lock == NULL ) {
4066
reglist_lock = PR_NewLock();
4069
if ( reglist_lock != NULL ) {
4070
PR_Lock( reglist_lock );
4073
XP_ASSERT( reglist_lock );
4074
status = REGERR_FAIL;
4078
if ( status == REGERR_OK )
4081
if ( regStartCount == 1 )
4083
/* first time only initialization */
4084
vr_findGlobalRegName();
4086
#ifndef STANDALONE_REGISTRY
4087
/* initialization for version registry */
4088
vr_lock = PR_NewLock();
4089
XP_ASSERT( vr_lock != NULL );
4090
#if defined(XP_UNIX) && !defined(XP_MACOSX)
4091
bGlobalRegistry = ( getenv(UNIX_GLOBAL_FLAG) != NULL );
4094
} /* if ( regStartCount == 1 ) */
4096
PR_Unlock( reglist_lock );
4100
} /* NR_StartupRegistry */
4102
VR_INTERFACE(void) NR_ShutdownRegistry(void)
4105
XP_Bool bDestroyLocks = FALSE;
4107
/* people should track whether NR_StartupRegistry() was successful
4108
* and not call this if it fails... but they won't so we'll try to
4109
* handle that case gracefully.
4111
#ifndef STANDALONE_REGISTRY
4112
if ( reglist_lock == NULL )
4113
return; /* was not started successfully */
4115
if ( regStartCount == 0 )
4116
return; /* was not started successfully */
4119
PR_Lock( reglist_lock );
4122
if ( regStartCount == 0 )
4124
/* shutdown for real. */
4126
/* close any forgotten open registries */
4127
while ( RegList != NULL )
4130
if ( pReg->hdrDirty ) {
4131
nr_WriteHdr( pReg );
4133
nr_CloseFile( &(pReg->fh) );
4134
nr_DeleteNode( pReg );
4137
XP_FREEIF(user_name);
4138
XP_FREEIF(globalRegName);
4139
XP_FREEIF(verRegName);
4141
bDestroyLocks = TRUE;
4144
PR_Unlock( reglist_lock );
4146
#ifndef STANDALONE_REGISTRY
4147
if ( bDestroyLocks )
4149
PR_DestroyLock( reglist_lock );
4150
reglist_lock = NULL;
4152
PR_DestroyLock(vr_lock);
4157
} /* NR_ShutdownRegistry */
4160
#pragma export reset