Back to index

lightning-sunbird  0.9+nobinonly
reg.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code, released
00017  * March 31, 1998.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1998
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *   Daniel Veditz <dveditz@netscape.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 /* ====================================================================
00041  * reg.c
00042  * XP Registry functions
00043  * ====================================================================
00044  */
00045 
00046 /* TODO:
00047  *  - Replace 'malloc' in NR_RegPack with the Netscape XP equivalent
00048  *  - Solve DOS 'errno' problem mentioned below
00049  *  - Solve rename across volume problem described in VR_PackRegistry
00050  */
00051 
00052 /* Preprocessor Defines
00053  *  STANDALONE_REGISTRY - define if not linking with Navigator
00054  *  NOCACHE_HDR         - define if multi-process access to registry
00055  *  SELF_REPAIR         - undefine to skip header update on open
00056  *  VERIFY_READ         - define TRUE to double-check short reads
00057  *
00058 #define NOCACHE_HDR     1
00059  */
00060 #define SELF_REPAIR     1
00061 #ifdef DEBUG
00062 #define VERIFY_READ     1
00063 #endif
00064 
00065 #include <stdio.h>
00066 #include <string.h>
00067 
00068 #ifdef STANDALONE_REGISTRY
00069 #include <stdlib.h>
00070 #include <assert.h>
00071 #include <errno.h>
00072 
00073 #if defined(XP_MAC) || defined(XP_MACOSX)
00074   #include <Errors.h>
00075 #endif
00076 
00077 #else
00078 
00079 #include "prtypes.h"
00080 #include "prlog.h"
00081 #include "prerror.h"
00082 #include "prprf.h"
00083 
00084 #endif /*STANDALONE_REGISTRY*/
00085 
00086 #if defined(SUNOS4)
00087 #include <unistd.h>  /* for SEEK_SET */
00088 #endif /* SUNOS4 */
00089 
00090 #include "reg.h"
00091 #include "NSReg.h"
00092 
00093 #if defined(XP_MAC)
00094 #define MAX_PATH 512
00095 #elif defined(XP_MACOSX)
00096 #define MAX_PATH PATH_MAX
00097 #elif defined(XP_UNIX)
00098 #ifndef MAX_PATH
00099 #define MAX_PATH 1024
00100 #endif
00101 #elif defined(XP_OS2)
00102 #ifndef MAX_PATH
00103 #define MAX_PATH 260
00104 #endif
00105 #elif defined(WIN32)
00106 #define MAX_PATH _MAX_PATH
00107 #elif defined(XP_BEOS)
00108 #include <limits.h>
00109 #define MAX_PATH PATH_MAX
00110 #endif
00111 
00112  
00113  /* NOTE! It is EXREMELY important that node names be in UTF-8; otherwise
00114  * backwards path search for delim char will fail for multi-byte/Unicode names
00115  */
00116 
00117 /* ====================================================================
00118  * Overview
00119  * --------------------------------------------------------------------
00120  *
00121  *  Layers:
00122  *      Interface
00123  *          Path Parsing
00124  *              Key/Entry Management
00125  *                  Block I/O
00126  *                      Virtual I/O
00127  *
00128  * The functions in this file search and add to a binary Registry file
00129  * quite efficiently.  So efficiently, squeezing out space left by
00130  * deleted and updated objects requires a separate "pack" operation.
00131  *
00132  * Terms:
00133  * As used here, a 'key' is a node in the tree. The root of the tree
00134  * exists in an otherwise empty Registry as is itself a key.  Every key
00135  * has 0 or more sub-keys. Every key also has 0 or more 'entry's. Both
00136  * entries and keys have names. Entries also have values associated.
00137  * Names and values are simply strings of characters. These strings
00138  * may be quoted so that they can include path delimiter and equals
00139  * sign characters which are otherwise reserved.
00140  * ====================================================================
00141  */
00142 
00143 /* --------------------------------------------------------------------
00144  * Module Global Data
00145  *
00146  * use of this data must be protected by the reglist lock
00147  * --------------------------------------------------------------------
00148  */
00149 
00150 #if !defined(STANDALONE_REGISTRY)
00151 static PRLock   *reglist_lock = NULL;
00152 #endif
00153 
00154 static REGFILE  *RegList = NULL;
00155 static int32    regStartCount = 0;
00156 char            *globalRegName = NULL;
00157 static char     *user_name = NULL;
00158 
00159 
00160 
00161 
00162 #if defined(XP_MAC) || defined(XP_MACOSX)
00163 
00164 void nr_MacAliasFromPath(const char * fileName, void ** alias, int32 * length);
00165 char * nr_PathFromMacAlias(const void * alias, uint32 aliasLength);
00166 
00167 #include <Aliases.h>
00168 #include <TextUtils.h>
00169 #include <Memory.h>
00170 #include <Folders.h>
00171 
00172 #ifdef XP_MACOSX
00173   #include "MoreFilesX.h"
00174 #else
00175   #include "FullPath.h"
00176 #endif
00177 
00178 static void copyCStringToPascal(Str255 dest, const char *src)
00179 {
00180     size_t copyLen = strlen(src);
00181     if (copyLen > 255)
00182         copyLen = 255;
00183     BlockMoveData(src, &dest[1], copyLen);
00184     dest[0] = copyLen;
00185 }
00186 
00187 #ifdef XP_MACOSX
00188 static OSErr isFileInTrash(FSRef *fsRef, PRBool *inTrash)
00189 {
00190     OSErr err;
00191     FSCatalogInfo catalogInfo;
00192 
00193     if (fsRef == NULL || inTrash == NULL)
00194         return paramErr;
00195     *inTrash = PR_FALSE;
00196 
00197     err = FSGetCatalogInfo(fsRef, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL);
00198     if (err == noErr)
00199     {
00200         FSRef trashFSRef, currFSRef, parentFSRef;
00201         err = FSFindFolder(catalogInfo.volume, kTrashFolderType, false, &trashFSRef);
00202         if (err == noErr)
00203         {
00204             /* FSRefGetParentRef returns noErr and a zeroed FSRef when it reaches the top */
00205             for (currFSRef = *fsRef;
00206                  (FSGetParentRef(&currFSRef, &parentFSRef) == noErr && FSRefValid(&parentFSRef));
00207                  currFSRef = parentFSRef)
00208             {
00209                 if (FSCompareFSRefs(&parentFSRef, &trashFSRef) == noErr)
00210                 {
00211                     *inTrash = PR_TRUE;
00212                     break;
00213                 }
00214             }
00215         }
00216     }
00217     return err;
00218 }
00219 #else
00220 static OSErr isFileInTrash(FSSpec *fileSpec, PRBool *inTrash)
00221 {
00222     OSErr err;
00223     short vRefNum;
00224     long dirID;
00225     
00226     if (fileSpec == NULL || inTrash == NULL)
00227         return paramErr;
00228     *inTrash = PR_FALSE;
00229     
00230     /* XXX - Only works if the file is in the top level of the trash dir */
00231     err = FindFolder(fileSpec->vRefNum, kTrashFolderType, false, &vRefNum, &dirID);
00232     if (err == noErr)
00233         if (dirID == fileSpec->parID)  /* File is inside the trash */
00234             *inTrash = PR_TRUE;
00235     
00236     return err;
00237 }
00238 #endif
00239 
00240 /* returns an alias as a malloc'd pointer.
00241  * On failure, *alias is NULL
00242  */
00243 void nr_MacAliasFromPath(const char * fileName, void ** alias, int32 * length)
00244 {
00245     OSErr err;
00246     Str255 pascalName;
00247     FSRef fsRef;
00248     FSSpec fs;
00249     AliasHandle macAlias;
00250     *alias = NULL;
00251     *length = 0;
00252     
00253 #ifdef XP_MACOSX
00254     err = FSPathMakeRef((const UInt8*)fileName, &fsRef, NULL);
00255     if ( err != noErr )
00256         return;
00257     err = FSNewAlias(NULL, &fsRef, &macAlias);
00258 #else
00259     copyCStringToPascal(pascalName, fileName);
00260     err = FSMakeFSSpec(0, 0, pascalName, &fs);
00261     if ( err != noErr )
00262         return;
00263     err = NewAlias(NULL, &fs, &macAlias);
00264 #endif
00265     
00266     if ( (err != noErr) || ( macAlias == NULL ))
00267         return;
00268     *length = GetHandleSize( (Handle) macAlias );
00269     *alias = XP_ALLOC( *length );
00270     if ( *alias == NULL )
00271     {
00272         DisposeHandle((Handle)macAlias);
00273         return;
00274     }
00275     HLock( (Handle) macAlias );
00276     XP_MEMCPY(*alias, *macAlias , *length);
00277     HUnlock( (Handle) macAlias );
00278     DisposeHandle( (Handle) macAlias);
00279     return;
00280 }
00281 
00282 /* resolves an alias, and returns a full path to the Mac file
00283  * If the alias changed, it would be nice to update our alias pointers
00284  */
00285 char * nr_PathFromMacAlias(const void * alias, uint32 aliasLength)
00286 {
00287     OSErr           err;
00288     AliasHandle     h           = NULL;
00289     Handle          fullPath    = NULL;
00290     short           fullPathLength;
00291     char *          cpath       = NULL;
00292     PRBool          inTrash;
00293     FSRef           fsRef;
00294     FSCatalogInfo   catalogInfo;
00295     UInt8           pathBuf[MAX_PATH];
00296     FSSpec          fs;
00297     Boolean         wasChanged; /* Change flag, it would be nice to change the alias on disk 
00298                         if the file location changed */
00299     
00300     
00301     XP_MEMSET( &fs, '\0', sizeof(FSSpec) );
00302     
00303     
00304     /* Copy the alias to a handle and resolve it */
00305     h = (AliasHandle) NewHandle(aliasLength);
00306     if ( h == NULL)
00307         goto fail;
00308         
00309         
00310     HLock( (Handle) h);
00311     XP_MEMCPY( *h, alias, aliasLength );
00312     HUnlock( (Handle) h);
00313     
00314 #ifdef XP_MACOSX
00315     err = FSResolveAlias(NULL, h, &fsRef, &wasChanged);
00316     if (err != noErr)
00317         goto fail;
00318 
00319     /* if the alias has changed and the file is now in the trash,
00320        assume that user has deleted it and that we do not want to look at it */
00321     if (wasChanged && (isFileInTrash(&fsRef, &inTrash) == noErr) && inTrash)
00322         goto fail;
00323     err = FSRefMakePath(&fsRef, pathBuf, sizeof(pathBuf));
00324     if (err != noErr)
00325         goto fail;
00326     fullPathLength = XP_STRLEN(pathBuf);
00327     cpath = (char*) XP_ALLOC(fullPathLength + 1);
00328     if ( cpath == NULL)
00329         goto fail;
00330     XP_MEMCPY(cpath, pathBuf, fullPathLength + 1);
00331 #else    
00332     err = ResolveAlias(NULL, h, &fs, &wasChanged);
00333     if (err != noErr)
00334         goto fail;
00335     
00336     /* if the alias has changed and the file is now in the trash,
00337        assume that user has deleted it and that we do not want to look at it */
00338     if (wasChanged && (isFileInTrash(&fs, &inTrash) == noErr) && inTrash)
00339         goto fail;
00340     
00341     /* Get the full path and create a char * out of it */
00342 
00343     err = GetFullPath(fs.vRefNum, fs.parID,fs.name, &fullPathLength, &fullPath);
00344     if ( (err != noErr) || (fullPath == NULL) )
00345         goto fail;
00346     
00347     cpath = (char*) XP_ALLOC(fullPathLength + 1);
00348     if ( cpath == NULL)
00349         goto fail;
00350     
00351     HLock( fullPath );
00352     XP_MEMCPY(cpath, *fullPath, fullPathLength);
00353     cpath[fullPathLength] = 0;
00354     HUnlock( fullPath );
00355 #endif    
00356     /* Drop through */
00357 fail:
00358     if (h != NULL)
00359         DisposeHandle( (Handle) h);
00360     if (fullPath != NULL)
00361         DisposeHandle( fullPath);
00362     return cpath;
00363 }
00364 
00365 #endif
00366 
00367 
00368 /* --------------------------------------------------------------------
00369  * Registry List management
00370  * --------------------------------------------------------------------
00371  */
00372 static void nr_AddNode(REGFILE* pReg);
00373 static void nr_DeleteNode(REGFILE *pReg);
00374 static REGFILE* vr_findRegFile(const char *filename);
00375 
00376 /* -------------------------------------------------------------------- */
00377 
00378 static void nr_AddNode(REGFILE* pReg)
00379 {
00380     /* add node to head of list */
00381     pReg->next = RegList;
00382     pReg->prev = NULL;
00383 
00384     RegList = pReg;
00385 
00386     if ( pReg->next != NULL ) {
00387         pReg->next->prev = pReg;
00388     }
00389 }
00390 
00391 static void nr_DeleteNode(REGFILE* pReg)
00392 {
00393     /* if at head of list... */
00394     if ( pReg->prev == NULL ) {
00395         RegList = pReg->next;
00396     }
00397     else {
00398         pReg->prev->next = pReg->next;
00399     }
00400 
00401     if ( pReg->next != NULL ) {
00402         pReg->next->prev = pReg->prev;
00403     }
00404 
00405     /* free memory */
00406 #ifndef STANDALONE_REGISTRY
00407     if ( pReg->lock != NULL )
00408         PR_DestroyLock( pReg->lock );
00409 #endif
00410     XP_FREEIF( pReg->filename );
00411     XP_FREE( pReg );
00412 }
00413 
00414 static REGFILE* vr_findRegFile(const char *filename)
00415 {
00416     REGFILE *pReg;
00417 
00418     pReg = RegList;
00419     while( pReg != NULL ) {
00420 #if defined(XP_UNIX) && !defined(XP_MACOSX) || defined XP_BEOS
00421         if ( 0 == XP_STRCMP( filename, pReg->filename ) ) {
00422 #else
00423         if ( 0 == XP_STRCASECMP( filename, pReg->filename ) ) {
00424 #endif
00425             break;
00426         }
00427         pReg = pReg->next;
00428     }
00429 
00430     return pReg;
00431 }
00432 
00433 
00434 /* --------------------------------------------------------------------
00435  * Virtual I/O
00436  *  Platform-specifics go in this section
00437  * --------------------------------------------------------------------
00438  */
00439 static REGERR nr_OpenFile(const char *path, FILEHANDLE *fh);
00440 static REGERR nr_CloseFile(FILEHANDLE *fh); /* Note: fh is a pointer */
00441 static REGERR nr_ReadFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer);
00442 static REGERR nr_WriteFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer);
00443 static REGERR nr_LockRange(FILEHANDLE fh, REGOFF offset, int32 len);
00444 static REGERR nr_UnlockRange(FILEHANDLE fh, REGOFF offset, int32 len);
00445 static int32  nr_GetFileLength(FILEHANDLE fh);
00446 /* -------------------------------------------------------------------- */
00447 
00448 #ifdef STANDALONE_REGISTRY
00449 static REGERR nr_OpenFile(const char *path, FILEHANDLE *fh)
00450 {
00451     XP_ASSERT( path != NULL );
00452     XP_ASSERT( fh != NULL );
00453 
00454     /* Open the file for exclusive random read/write */
00455     (*fh) = vr_fileOpen(path, XP_FILE_UPDATE_BIN);
00456     if ( !VALID_FILEHANDLE(*fh) )
00457     {
00458         switch (errno)
00459         {
00460 #if defined(XP_MAC) || defined(XP_MACOSX)
00461         case fnfErr:
00462 #else
00463         case ENOENT:    /* file not found */
00464 #endif
00465             return REGERR_NOFILE;
00466 
00467 #if defined(XP_MAC) || defined(XP_MACOSX)
00468         case opWrErr:
00469 #else
00470         case EROFS:     /* read-only file system */
00471         case EACCES:    /* file in use or read-only file*/
00472 #endif
00473             /* try read only */
00474             (*fh) = vr_fileOpen(path, XP_FILE_READ_BIN);
00475             if ( VALID_FILEHANDLE(*fh) )
00476                 return REGERR_READONLY;
00477             else
00478                 return REGERR_FAIL;
00479 
00480         default:
00481             return REGERR_FAIL;
00482         }
00483     }
00484 
00485     return REGERR_OK;
00486 
00487 }   /* OpenFile */
00488 #else
00489 static REGERR nr_OpenFile(const char *path, FILEHANDLE *fh)
00490 {
00491     PR_ASSERT( path != NULL );
00492     PR_ASSERT( fh != NULL );
00493 
00494     /* Open the file for exclusive random read/write */
00495     *fh = XP_FileOpen(path, XP_FILE_UPDATE_BIN);
00496     if ( !VALID_FILEHANDLE(*fh) )
00497     {
00498         XP_StatStruct st;
00499         if ( XP_Stat( path, &st ) != 0 )
00500         {
00501             /* file doesn't exist, so create */
00502             *fh = XP_FileOpen(path, XP_FILE_TRUNCATE_BIN);
00503         }
00504     }
00505 
00506     if ( !VALID_FILEHANDLE(*fh) )
00507     {
00508       /* For whatever reason we failed every attempt of getting */
00509       /* a read/write registry. Let's try a read-only registry. */
00510       (*fh) = XP_FileOpen(path, XP_FILE_READ_BIN);
00511       if ( VALID_FILEHANDLE(*fh) )
00512         return REGERR_READONLY;
00513       else
00514         /* we are in big trouble now */
00515         return REGERR_FAIL;
00516     }
00517 
00518     /* succeded in getting a read/write registry */
00519     return REGERR_OK;
00520 
00521 }   /* OpenFile */
00522 #endif
00523 
00524 
00525 static REGERR nr_CloseFile(FILEHANDLE *fh)
00526 {
00527     /* NOTE: 'fh' is a pointer, unlike other Close functions
00528      *       This is necessary so that nr_CloseFile can set it to NULL
00529      */
00530 
00531     XP_ASSERT( fh != NULL );
00532     if ( VALID_FILEHANDLE(*fh) )
00533         XP_FileClose(*fh);
00534     (*fh) = NULL;
00535     return REGERR_OK;
00536 
00537 }   /* CloseFile */
00538 
00539 
00540 
00541 static REGERR nr_ReadFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer)
00542 {
00543 #if VERIFY_READ
00544     #define        FILLCHAR  0xCC
00545     unsigned char* p;
00546     unsigned char* dbgend = (unsigned char*)buffer+len;
00547 #endif
00548 
00549     int32 readlen;
00550     REGERR err = REGERR_OK;
00551 
00552     XP_ASSERT(len > 0);
00553     XP_ASSERT(buffer != NULL);
00554     XP_ASSERT(fh != NULL);
00555 
00556 #if VERIFY_READ
00557     XP_MEMSET(buffer, FILLCHAR, len);
00558 #endif
00559 
00560     if (XP_FileSeek(fh, offset, SEEK_SET) != 0 ) {
00561         err = REGERR_FAIL;
00562     }
00563     else {
00564         readlen = XP_FileRead(buffer, len, fh );
00565         /* PR_READ() returns an unreliable length, check EOF separately */
00566         if (readlen < 0) {
00567 #if !defined(STANDALONE_REGISTRY) || (!defined(XP_MAC) && !defined(XP_MACOSX))
00568     #if defined(STANDALONE_REGISTRY)
00569             if (errno == EBADF) /* bad file handle, not open for read, etc. */
00570     #else
00571             if (PR_GetError() == PR_BAD_DESCRIPTOR_ERROR)
00572     #endif
00573                 err = REGERR_FAIL;
00574             else
00575 #endif
00576                 err = REGERR_BADREAD;
00577         }
00578         else if (readlen < len) {
00579 #if VERIFY_READ
00580             /* PR_READ() says we hit EOF but return length is unreliable. */
00581             /* If buffer has new data beyond what PR_READ() says it got */
00582             /* we'll assume the read was OK--this is a gamble but */
00583             /* missing errors will cause fewer problems than too many. */
00584             p = (unsigned char*)buffer + readlen;
00585             while ( (p < dbgend) && (*p == (unsigned char)FILLCHAR) ) {
00586                 p++;
00587             }
00588 
00589             /* really was EOF if it's all FILLCHAR's */
00590             if ( p == dbgend ) {
00591                 err = REGERR_BADREAD;
00592             }
00593 #else
00594             err = REGERR_BADREAD;
00595 #endif
00596         }
00597     }
00598 
00599     return err;
00600 
00601 }   /* ReadFile */
00602 
00603 
00604 
00605 static REGERR nr_WriteFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer)
00606 {
00607 
00608     /* Note: 'offset' will commonly be the end of the file, in which
00609      * case this function extends the file to 'offset'+'len'. This may
00610      * be a two-step operation on some platforms.
00611      */
00612     XP_ASSERT(len > 0);
00613     XP_ASSERT(buffer);
00614     XP_ASSERT(fh != NULL);
00615 
00616     if (XP_FileSeek(fh, offset, SEEK_SET) != 0)
00617         return REGERR_FAIL;
00618 
00619     if ((int32)XP_FileWrite(buffer, len, fh) != len)
00620     {
00621         /* disk full or some other catastrophic error */
00622         return REGERR_FAIL;
00623     }
00624 
00625     return REGERR_OK;
00626 
00627 }   /* WriteFile */
00628 
00629 
00630 
00631 static REGERR nr_LockRange(FILEHANDLE fh, REGOFF offset, int32 len)
00632 {
00633     /* TODO: Implement XP lock function with built-in retry. */
00634 
00635     return REGERR_OK;
00636 
00637 }   /* LockRange */
00638 
00639 
00640 
00641 static REGERR nr_UnlockRange(FILEHANDLE fh, REGOFF offset, int32 len)
00642 {
00643     /* TODO: Implement XP unlock function with built-in retry. */
00644 
00645     return REGERR_OK;
00646 
00647 }   /* UnlockRange */
00648 
00649 
00650 
00651 #if SELF_REPAIR
00652 static int32 nr_GetFileLength(FILEHANDLE fh)
00653 {
00654     int32 length;
00655     int32 curpos;
00656 
00657     curpos = XP_FileTell(fh);
00658     XP_FileSeek(fh, 0, SEEK_END);
00659     length = XP_FileTell(fh);
00660     XP_FileSeek(fh, curpos, SEEK_SET);
00661     return length;
00662 
00663 }   /* GetFileLength */
00664 #endif
00665 
00666 
00667 
00668 /* --------------------------------------------------------------------
00669  * Numeric converters
00670  * --------------------------------------------------------------------
00671  * The converters read and write integers in a common format so we
00672  * can transport registries without worrying about endian problems.
00673  *
00674  * The buffers *MUST* be the appropriate size!
00675  * --------------------------------------------------------------------
00676  */
00677 static uint32 nr_ReadLong(char *buffer);
00678 static uint16 nr_ReadShort(char *buffer);
00679 static void   nr_WriteLong(uint32 num, char *buffer);
00680 static void   nr_WriteShort(uint16 num, char *buffer);
00681 /* -------------------------------------------------------------------- */
00682 
00683 
00684 
00685 static uint16 nr_ReadShort(char *buffer)
00686 {
00687     uint16 val;
00688     uint8 *p = (uint8*)buffer;
00689  
00690     val = (uint16)(*p + (uint16)( *(p+1) * 0x100 ));
00691 
00692     return val;
00693 }
00694 
00695 
00696 
00697 static uint32 nr_ReadLong(char *buffer)
00698 {
00699     uint32 val;
00700     uint8 *p = (uint8*)buffer;
00701 
00702     val = *p
00703         + (uint32)(*(p+1) * 0x100L)
00704         + (uint32)(*(p+2) * 0x10000L )
00705         + (uint32)(*(p+3) * 0x1000000L );
00706 
00707     return val;
00708 }
00709 
00710 
00711 
00712 static void  nr_WriteLong(uint32 num, char *buffer)
00713 {
00714     uint8 *p = (uint8*)buffer;
00715     *p++ = (uint8)(num & 0x000000FF);
00716     num /= 0x100;
00717     *p++ = (uint8)(num & 0x000000FF);
00718     num /= 0x100;
00719     *p++ = (uint8)(num & 0x000000FF);
00720     num /= 0x100;
00721     *p   = (uint8)(num & 0x000000FF);
00722 }
00723 
00724 
00725 
00726 static void  nr_WriteShort(uint16 num, char *buffer)
00727 {
00728     uint8 *p = (uint8*)buffer;
00729 
00730     *p = (uint8)(num & 0x00FF);
00731     *(p+1) = (uint8)(num / 0x100);
00732 }
00733 
00734 
00735 
00736 /* --------------------------------------------------------------------
00737  * Block I/O
00738  * --------------------------------------------------------------------
00739  */
00740 static REGERR nr_ReadHdr(REGFILE *reg); /* Reads the file header, creates file if empty */
00741 static REGERR nr_WriteHdr(REGFILE *reg);    /* Writes the file header */
00742 static REGERR nr_CreateRoot(REGFILE *reg);
00743 
00744 static REGERR nr_Lock(REGFILE *reg);
00745 static REGERR nr_Unlock(REGFILE *reg);
00746 
00747 static REGERR nr_ReadDesc(REGFILE *reg, REGOFF offset, REGDESC *desc);      /* reads a desc */
00748 static REGERR nr_ReadName(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf);
00749 static REGERR nr_ReadData(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf);
00750 
00751 static REGERR nr_WriteDesc(REGFILE *reg, REGDESC *desc);                    /* writes a desc */
00752 static REGERR nr_WriteString(REGFILE *reg, char *string, REGDESC *desc);    /* writes a string */
00753 static REGERR nr_WriteData(REGFILE *reg, char *string, uint32 len, REGDESC *desc);  /* writes a string */
00754 
00755 static REGERR nr_AppendDesc(REGFILE *reg, REGDESC *desc, REGOFF *result);   /* adds a desc */
00756 static REGERR nr_AppendName(REGFILE *reg, char *name, REGDESC *desc);       /* adds a name */
00757 static REGERR nr_AppendString(REGFILE *reg, char *string, REGDESC *desc);   /* adds a string */
00758 static REGERR nr_AppendData(REGFILE *reg, char *string, uint32 len, REGDESC *desc); /* adds a string */
00759 
00760 static XP_Bool nr_IsValidUTF8(char *string);    /* checks if a string is UTF-8 encoded */
00761 /* -------------------------------------------------------------------- */
00762 
00763 
00764 
00765 static REGERR nr_ReadHdr(REGFILE *reg)
00766 {
00767 
00768     int err;
00769     long filelength;
00770     char hdrBuf[sizeof(REGHDR)];
00771 
00772     XP_ASSERT(reg);
00773     reg->hdrDirty = 0;
00774 
00775     err = nr_ReadFile(reg->fh, 0, sizeof(REGHDR), &hdrBuf);
00776 
00777     switch (err)
00778     {
00779     case REGERR_BADREAD:
00780         /* header doesn't exist, so create one */
00781         err = nr_CreateRoot(reg);
00782         break;
00783 
00784     case REGERR_OK:
00785         /* header read successfully -- convert */
00786         reg->hdr.magic    = nr_ReadLong ( hdrBuf + HDR_MAGIC );
00787         reg->hdr.verMajor = nr_ReadShort( hdrBuf + HDR_VERMAJOR );
00788         reg->hdr.verMinor = nr_ReadShort( hdrBuf + HDR_VERMINOR );
00789         reg->hdr.avail    = nr_ReadLong ( hdrBuf + HDR_AVAIL );
00790         reg->hdr.root     = nr_ReadLong ( hdrBuf + HDR_ROOT );
00791 
00792         /* check to see if it's the right file type */
00793         if (reg->hdr.magic != MAGIC_NUMBER) {
00794             err = REGERR_BADMAGIC;
00795             break;
00796         }
00797 
00798         /* Check registry version
00799          * If the major version is bumped we're incompatible
00800          * (minor version just means some new features were added)
00801          *
00802          * Upgrade code will go here in the future...
00803          */
00804         if ( reg->hdr.verMajor > MAJOR_VERSION ) {
00805             err = REGERR_REGVERSION;
00806             break;
00807         }
00808 
00809 #if SELF_REPAIR
00810         if ( reg->inInit && !(reg->readOnly) ) {
00811             filelength = nr_GetFileLength(reg->fh);
00812             if (reg->hdr.avail != filelength)
00813             {
00814                 reg->hdr.avail = filelength;
00815                 reg->hdrDirty = 1;
00816 #if NOCACHE_HDR
00817                 err = nr_WriteHdr(reg);
00818 #endif
00819             }
00820         }
00821 #endif  /* SELF_REPAIR */
00822         break;
00823 
00824     default:
00825         /* unexpected error from nr_ReadFile()*/
00826         XP_ASSERT(FALSE);
00827         err = REGERR_FAIL;
00828         break;
00829     }   /* switch */
00830 
00831     return err;
00832 
00833 }   /* ReadHdr */
00834 
00835 
00836 
00837 static REGERR nr_WriteHdr(REGFILE *reg)
00838 {
00839     REGERR err;
00840     char hdrBuf[sizeof(REGHDR)];
00841 
00842     XP_ASSERT(reg);
00843 
00844     if (reg->readOnly)
00845         return REGERR_READONLY;
00846 
00847     /* convert to XP int format */
00848     nr_WriteLong ( reg->hdr.magic,    hdrBuf + HDR_MAGIC );
00849     nr_WriteShort( reg->hdr.verMajor, hdrBuf + HDR_VERMAJOR );
00850     nr_WriteShort( reg->hdr.verMinor, hdrBuf + HDR_VERMINOR );
00851     nr_WriteLong ( reg->hdr.avail,    hdrBuf + HDR_AVAIL );
00852     nr_WriteLong ( reg->hdr.root,     hdrBuf + HDR_ROOT );
00853 
00854     /* err = nr_WriteFile(reg->fh, 0, sizeof(REGHDR), &reg->hdr); */
00855     err = nr_WriteFile(reg->fh, 0, sizeof(hdrBuf), &hdrBuf);
00856 
00857     if (err == REGERR_OK)
00858         reg->hdrDirty = 0;
00859 
00860     return err;
00861 
00862 }   /* WriteHdr */
00863 
00864 
00865 
00866 static REGERR nr_CreateRoot(REGFILE *reg)
00867 {
00868     /* Called when an empty file is detected by ReadHdr */
00869     REGERR err;
00870     REGDESC root;
00871 
00872     XP_ASSERT(reg);
00873 
00874     /* Create 'hdr' */
00875     reg->hdr.magic      = MAGIC_NUMBER;
00876     reg->hdr.verMajor   = MAJOR_VERSION;
00877     reg->hdr.verMinor   = MINOR_VERSION;
00878     reg->hdr.root       = 0;
00879     reg->hdr.avail      = HDRRESERVE;
00880 
00881     /* Create root descriptor */
00882     root.location   = 0;
00883     root.left       = 0;
00884     root.value      = 0;
00885     root.down       = 0;
00886     root.type       = REGTYPE_KEY;
00887     root.valuelen   = 0;
00888     root.valuebuf   = 0;
00889     root.parent     = 0;
00890 
00891     err = nr_AppendName(reg, ROOTKEY_STR, &root);
00892     if (err != REGERR_OK)
00893         return err;
00894 
00895     err = nr_AppendDesc(reg, &root, &reg->hdr.root);
00896     if (err != REGERR_OK)
00897         return err;
00898 
00899     return nr_WriteHdr(reg);    /* actually commit to disk */
00900 
00901     /* Create standard top-level nodes */
00902 
00903 }   /* CreateRoot */
00904 
00905 
00906 
00907 static REGERR nr_Lock(REGFILE *reg)
00908 {
00909     REGERR status;
00910 
00911     /* lock file */
00912     status = nr_LockRange(reg->fh, 0, sizeof(REGHDR));
00913 
00914     if (status == REGERR_OK)
00915     {
00916         /* lock the object */
00917         PR_Lock( reg->lock );
00918 
00919 #if NOCACHE_HDR
00920         /* try to refresh header info */
00921         status = nr_ReadHdr(reg);
00922         if ( status != REGERR_OK ) {
00923             PR_Unlock( reg->lock );
00924         }
00925 #endif
00926     }
00927 
00928     return status;
00929 }   /* Lock */
00930 
00931 
00932 
00933 static REGERR nr_Unlock(REGFILE *reg)
00934 {
00935     PR_Unlock( reg->lock );
00936 
00937     return nr_UnlockRange(reg->fh, 0, sizeof(REGHDR));
00938 }   /* Unlock */
00939 
00940 
00941 
00942 static REGERR nr_ReadDesc(REGFILE *reg, REGOFF offset, REGDESC *desc)
00943 {
00944 
00945     REGERR err;
00946     char descBuf[ DESC_SIZE ];
00947 
00948     XP_ASSERT(reg);
00949     XP_ASSERT(offset >= HDRRESERVE);
00950     XP_ASSERT(offset < reg->hdr.avail);
00951     XP_ASSERT(desc);
00952 
00953     err = nr_ReadFile(reg->fh, offset, DESC_SIZE, &descBuf);
00954     if (err == REGERR_OK)
00955     {
00956         desc->location  = nr_ReadLong ( descBuf + DESC_LOCATION );
00957         desc->name      = nr_ReadLong ( descBuf + DESC_NAME );
00958         desc->namelen   = nr_ReadShort( descBuf + DESC_NAMELEN );
00959         desc->type      = nr_ReadShort( descBuf + DESC_TYPE );
00960         desc->left      = nr_ReadLong ( descBuf + DESC_LEFT );
00961         desc->value     = nr_ReadLong ( descBuf + DESC_VALUE );
00962         desc->valuelen  = nr_ReadLong ( descBuf + DESC_VALUELEN );
00963         desc->parent    = nr_ReadLong ( descBuf + DESC_PARENT );
00964 
00965         if ( TYPE_IS_ENTRY(desc->type) ) {
00966             desc->down = 0;
00967             desc->valuebuf  = nr_ReadLong( descBuf + DESC_VALUEBUF );
00968         }
00969         else {  /* TYPE is KEY */
00970             desc->down      = nr_ReadLong( descBuf + DESC_DOWN );
00971             desc->valuebuf  = 0;
00972         }
00973 
00974         if (desc->location != offset)
00975             err = REGERR_BADLOCN;
00976         else if ( desc->type & REGTYPE_DELETED )
00977             err = REGERR_DELETED;
00978     }
00979 
00980     return err;
00981 
00982 }   /* ReadDesc */
00983 
00984 
00985 
00986 static REGERR nr_ReadName(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf)
00987 {
00988 
00989     REGERR err;
00990 
00991     XP_ASSERT(reg);
00992     XP_ASSERT(desc->name > 0);
00993     XP_ASSERT(desc->name < reg->hdr.avail);
00994     XP_ASSERT(buflen > 0);
00995     XP_ASSERT(buf);
00996 
00997     if ( desc->namelen > buflen )
00998         return REGERR_BUFTOOSMALL;
00999 
01000     err = nr_ReadFile(reg->fh, desc->name, desc->namelen, buf);
01001 
01002     buf[buflen-1] = '\0';   /* avoid runaways */
01003 
01004     return err;
01005 
01006 }   /* ReadName */
01007 
01008 
01009 
01010 static REGERR nr_ReadData(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf)
01011 {
01012 
01013     REGERR err;
01014 
01015     XP_ASSERT(reg);
01016     XP_ASSERT(desc->value > 0);
01017     XP_ASSERT(desc->value < reg->hdr.avail);
01018     XP_ASSERT(buflen > 0);
01019     XP_ASSERT(buf);
01020 
01021     if ( desc->valuelen > buflen )
01022         return REGERR_BUFTOOSMALL;
01023 
01024     err = nr_ReadFile(reg->fh, desc->value, desc->valuelen, buf);
01025 
01026     return err;
01027 
01028 }   /* nr_ReadData */
01029 
01030 
01031 
01032 static REGERR nr_WriteDesc(REGFILE *reg, REGDESC *desc)
01033 {
01034     char descBuf[ DESC_SIZE ];
01035 
01036     XP_ASSERT(reg);
01037     XP_ASSERT(desc);
01038     XP_ASSERT( desc->location >= HDRRESERVE );
01039     XP_ASSERT( desc->location < reg->hdr.avail );
01040 
01041     if (reg->readOnly)
01042         return REGERR_READONLY;
01043 
01044     /* convert to XP int format */
01045     nr_WriteLong ( desc->location,  descBuf + DESC_LOCATION );
01046     nr_WriteLong ( desc->name,      descBuf + DESC_NAME );
01047     nr_WriteShort( desc->namelen,   descBuf + DESC_NAMELEN );
01048     nr_WriteShort( desc->type,      descBuf + DESC_TYPE );
01049     nr_WriteLong ( desc->left,      descBuf + DESC_LEFT );
01050     nr_WriteLong ( desc->value,     descBuf + DESC_VALUE );
01051     nr_WriteLong ( desc->valuelen,  descBuf + DESC_VALUELEN );
01052     nr_WriteLong ( desc->parent,    descBuf + DESC_PARENT );
01053 
01054     if ( TYPE_IS_ENTRY(desc->type) ) {
01055         XP_ASSERT( 0 == desc->down );
01056         nr_WriteLong( desc->valuebuf,  descBuf + DESC_VALUEBUF );
01057     }
01058     else {  /* TYPE is KEY */
01059         XP_ASSERT( 0 == desc->valuebuf );
01060         nr_WriteLong( desc->down,      descBuf + DESC_DOWN );
01061     }
01062 
01063     return nr_WriteFile(reg->fh, desc->location, DESC_SIZE, descBuf);
01064 }   /* nr_WriteDesc */
01065 
01066 
01067 
01068 static REGERR nr_AppendDesc(REGFILE *reg, REGDESC *desc, REGOFF *result)
01069 {
01070 
01071     REGERR err;
01072     char descBuf[ DESC_SIZE ];
01073 
01074     XP_ASSERT(reg);
01075     XP_ASSERT(desc);
01076     XP_ASSERT(result);
01077 
01078     *result = 0;
01079 
01080     if (reg->readOnly)
01081         return REGERR_READONLY;
01082 
01083     desc->location = reg->hdr.avail;
01084 
01085     /* convert to XP int format */
01086     nr_WriteLong ( desc->location,  descBuf + DESC_LOCATION );
01087     nr_WriteLong ( desc->name,      descBuf + DESC_NAME );
01088     nr_WriteShort( desc->namelen,   descBuf + DESC_NAMELEN );
01089     nr_WriteShort( desc->type,      descBuf + DESC_TYPE );
01090     nr_WriteLong ( desc->left,      descBuf + DESC_LEFT );
01091     nr_WriteLong ( desc->value,     descBuf + DESC_VALUE );
01092     nr_WriteLong ( desc->valuelen,  descBuf + DESC_VALUELEN );
01093     nr_WriteLong ( desc->parent,    descBuf + DESC_PARENT );
01094 
01095     if ( TYPE_IS_ENTRY(desc->type) ) {
01096         XP_ASSERT( 0 == desc->down );
01097         nr_WriteLong( desc->valuebuf,  descBuf + DESC_VALUEBUF );
01098     }
01099     else {  /* TYPE is KEY */
01100         XP_ASSERT( 0 == desc->valuebuf );
01101         nr_WriteLong( desc->down,      descBuf + DESC_DOWN );
01102     }
01103 
01104     err = nr_WriteFile(reg->fh, reg->hdr.avail, DESC_SIZE, descBuf);
01105 
01106     if (err == REGERR_OK)
01107     {
01108         *result = reg->hdr.avail;
01109         reg->hdr.avail += DESC_SIZE;
01110         reg->hdrDirty = 1;
01111 #if NOCACHE_HDR
01112         err = nr_WriteHdr(reg);
01113 #endif
01114     }
01115 
01116     return err;
01117 
01118 }   /* AppendDesc */
01119 
01120 
01121 
01122 static REGERR nr_AppendName(REGFILE *reg, char *name, REGDESC *desc)
01123 {
01124     REGERR err;
01125     int len;
01126     char *p;
01127 
01128     XP_ASSERT(reg);
01129     XP_ASSERT(name);
01130     XP_ASSERT(desc);
01131 
01132     if (!nr_IsValidUTF8(name))
01133         return REGERR_BADUTF8;
01134     if (reg->readOnly)
01135         return REGERR_READONLY;
01136 
01137     len = XP_STRLEN(name) + 1;
01138 
01139     /* check for valid name parameter */
01140     if ( len == 1 )
01141         return REGERR_PARAM;
01142 
01143     if ( len > MAXREGNAMELEN )
01144         return REGERR_NAMETOOLONG;
01145 
01146     for ( p = name; (*p != 0); p++ ) {
01147         if ( INVALID_NAME_CHAR(*p) )
01148             return REGERR_BADNAME;
01149     }
01150 
01151     /* save the name */
01152     err = nr_WriteFile(reg->fh, reg->hdr.avail, len, name);
01153 
01154     /* if write successful update the desc and hdr */
01155     if (err == REGERR_OK)
01156     {
01157         desc->namelen = (uint16)len;
01158         desc->name = reg->hdr.avail;
01159         reg->hdr.avail += len;
01160         reg->hdrDirty = 1;
01161 #if NOCACHE_HDR
01162         err = nr_WriteHdr(reg);
01163 #endif
01164     }
01165 
01166     return err;
01167 
01168 }   /* nr_AppendName */
01169 
01170 
01171 
01172 static REGERR nr_WriteString(REGFILE *reg, char *string, REGDESC *desc)
01173 {
01174     uint32 len;
01175 
01176     XP_ASSERT(string);
01177     if (!nr_IsValidUTF8(string))
01178         return REGERR_BADUTF8;
01179     if (reg->readOnly)
01180         return REGERR_READONLY;
01181     len = XP_STRLEN(string) + 1;
01182 
01183     return nr_WriteData( reg, string, len, desc );
01184 
01185 }   /* nr_WriteString */
01186 
01187 
01188 
01189 static REGERR nr_WriteData(REGFILE *reg, char *string, uint32 len, REGDESC *desc)
01190 {
01191     REGERR err;
01192 
01193     XP_ASSERT(reg);
01194     XP_ASSERT(string);
01195     XP_ASSERT(desc);
01196 
01197     if (reg->readOnly)
01198         return REGERR_READONLY;
01199 
01200     if ( len == 0 )
01201         return REGERR_PARAM;
01202 
01203     if ( len > MAXREGVALUELEN )
01204         return REGERR_NAMETOOLONG;
01205 
01206     /* save the data in the same place if it fits */
01207     if ( len <= desc->valuebuf ) {
01208         err = nr_WriteFile( reg->fh, desc->value, len, string );
01209         if ( err == REGERR_OK ) {
01210             desc->valuelen = len;
01211         }
01212     }
01213     else {
01214         /* otherwise append new data */
01215         err = nr_AppendData( reg, string, len, desc );
01216     }
01217 
01218     return err;
01219 
01220 }   /* nr_WriteData */
01221 
01222 
01223 
01224 static REGERR nr_AppendString(REGFILE *reg, char *string, REGDESC *desc)
01225 {
01226     uint32 len;
01227 
01228     XP_ASSERT(string);
01229     if (!nr_IsValidUTF8(string))
01230         return REGERR_BADUTF8;
01231     if (reg->readOnly)
01232         return REGERR_READONLY;
01233     len = XP_STRLEN(string) + 1;
01234 
01235     return nr_AppendData( reg, string, len, desc );
01236 
01237 }   /* nr_AppendString */
01238 
01239 
01240 
01241 static REGERR nr_AppendData(REGFILE *reg, char *string, uint32 len, REGDESC *desc)
01242 {
01243     REGERR err;
01244 
01245     XP_ASSERT(reg);
01246     XP_ASSERT(string);
01247     XP_ASSERT(desc);
01248 
01249     if (reg->readOnly)
01250         return REGERR_READONLY;
01251 
01252     if ( len == 0 )
01253         return REGERR_PARAM;
01254 
01255     if ( len > MAXREGVALUELEN )
01256         return REGERR_NAMETOOLONG;
01257 
01258     /* save the string */
01259     err = nr_WriteFile(reg->fh, reg->hdr.avail, len, string);
01260     if (err == REGERR_OK)
01261     {
01262         desc->value     = reg->hdr.avail;
01263         desc->valuelen  = len;
01264         desc->valuebuf  = len;
01265 
01266         reg->hdr.avail += len;
01267         reg->hdrDirty   = 1;
01268 #if NOCACHE_HDR
01269         err = nr_WriteHdr(reg);
01270 #endif
01271     }
01272 
01273     return err;
01274 
01275 }   /* nr_AppendData */
01276 
01277 static XP_Bool nr_IsValidUTF8(char *string)
01278 {
01279     int follow = 0;
01280     char *c;
01281     unsigned char ch;
01282 
01283     XP_ASSERT(string);
01284     if ( !string )
01285         return FALSE;
01286 
01287     for ( c = string; *c != '\0'; c++ )
01288     {
01289         ch = (unsigned char)*c;
01290         if( follow == 0 )
01291         {
01292             /* expecting an initial byte */
01293             if ( ch <= 0x7F )
01294             {
01295                 /* standard byte -- do nothing */
01296             }
01297             else if ((0xC0 & ch) == 0x80)
01298             {
01299                 /* follow byte illegal here */
01300                 return FALSE;
01301             }
01302             else if ((0xE0 & ch) == 0xC0)
01303             {
01304                 follow = 1;
01305             }
01306             else if ((0xF0 & ch) == 0xE0)
01307             {
01308                 follow = 2;
01309             }
01310             else
01311             { 
01312                 /* unexpected (unsupported) initial byte */
01313                 return FALSE;
01314             }
01315         }
01316         else 
01317         {
01318             XP_ASSERT( follow > 0 );
01319             if ((0xC0 & ch) == 0x80)
01320             {
01321                 /* expecting follow byte and found one */
01322                 follow--;
01323             }
01324             else 
01325             {
01326                 /* invalid state */
01327                 return FALSE;
01328             }
01329         }
01330     } /* for */
01331 
01332     if ( follow != 0 )
01333     {
01334         /* invalid state -- interrupted character */
01335         return FALSE;
01336     }
01337     
01338     return TRUE;
01339 }   /* checks if a string is UTF-8 encoded */
01340 
01341 /* --------------------------------------------------------------------
01342  * Path Parsing
01343  * --------------------------------------------------------------------
01344  */
01345 static REGERR nr_NextName(const char *pPath, char *buf, uint32 bufsize, const char **newPath);
01346 static REGERR nr_RemoveName(char *path);
01347 static REGERR nr_CatName(REGFILE *reg, REGOFF node, char *path, uint32 bufsize,
01348                     REGDESC *desc);
01349 static REGERR nr_ReplaceName(REGFILE *reg, REGOFF node, char *path,
01350                     uint32 bufsize, REGDESC *desc);
01351 /* -------------------------------------------------------------------- */
01352 
01353 
01354 /* Scans path at 'pPath' and copies next name segment into 'buf'.
01355  * Also sets 'newPath' to point at the next segment of pPath.
01356  */
01357 static REGERR nr_NextName(const char *pPath, char *buf, uint32 bufsize, const char **newPath)
01358 {
01359     uint32 len = 0;
01360     REGERR err = REGERR_OK;
01361 
01362     /* initialization and validation */
01363     XP_ASSERT(buf);
01364 
01365     *newPath = NULL;
01366     *buf = '\0';
01367 
01368     if ( pPath==NULL || *pPath=='\0' )
01369         return REGERR_NOMORE;
01370 
01371     /* ... skip an initial path delimiter */
01372     if ( *pPath == PATHDEL ) {
01373         pPath++;
01374 
01375         if ( *pPath == '\0' )
01376             return REGERR_NOMORE;
01377     }
01378 
01379     /* ... missing name segment or initial blank are errors*/
01380     if ( *pPath == PATHDEL || *pPath == ' ' )
01381         return REGERR_BADNAME;
01382 
01383     /* copy first path segment into return buf */
01384     while ( *pPath != '\0' && *pPath != PATHDEL )
01385     {
01386         if ( len == bufsize ) {
01387             err = REGERR_NAMETOOLONG;
01388             break;
01389         }
01390         if ( *pPath < ' ' && *pPath > 0 )
01391             return REGERR_BADNAME;
01392 
01393         *buf++ = *pPath++;
01394         len++;
01395     }
01396     *buf = '\0';
01397 
01398     /* ... name segment can't end with blanks, either */
01399     if ( ' ' == *(buf-1) )
01400         return REGERR_BADNAME;
01401 
01402     /* return a pointer to the start of the next segment */
01403     *newPath = pPath;
01404 
01405     return err;
01406 
01407 }   /* nr_NextName */
01408 
01409 
01410 
01411 
01412 static REGERR nr_CatName(REGFILE *reg, REGOFF node, char *path, uint32 bufsize, REGDESC *desc)
01413 {
01414     REGERR err = REGERR_OK;
01415 
01416     char   *p;
01417     uint32 len = XP_STRLEN(path);
01418 
01419     if (len > 0)
01420     {
01421         p = &path[len-1];
01422         if (*p != PATHDEL)
01423         {
01424             if ( len < bufsize ) {
01425                 p++;
01426                 *p = PATHDEL;
01427                 len++;
01428             }
01429             else
01430                 err = REGERR_BUFTOOSMALL;
01431         }
01432         p++;    /* point one past PATHDEL */
01433     }
01434     else
01435         p = path;
01436 
01437     if ( err == REGERR_OK ) {
01438         err = nr_ReadDesc( reg, node, desc );
01439         if ( err == REGERR_OK ) {
01440             err = nr_ReadName( reg, desc, bufsize-len, p );
01441         }
01442     }
01443 
01444     return err;
01445 
01446 }   /* CatName */
01447 
01448 
01449 
01450 static REGERR nr_ReplaceName(REGFILE *reg, REGOFF node, char *path, uint32 bufsize, REGDESC *desc)
01451 {
01452     /* NOTE! It is EXREMELY important that names be in UTF-8; otherwise
01453      * the backwards path search will fail for multi-byte/Unicode names
01454      */
01455 
01456     char   *p;
01457     uint32 len;
01458     REGERR err;
01459 
01460     XP_ASSERT(path);
01461 
01462     len = XP_STRLEN(path);
01463     if ( len > bufsize )
01464         return REGERR_PARAM;
01465 
01466     if ( len > 0 ) {
01467         p = &path[len-1];
01468 
01469         while ((p > path) && (*p != PATHDEL)) {
01470             --p;
01471             --len;
01472         }
01473         if ( *p == PATHDEL ) {
01474             p++; 
01475             len++;
01476         }
01477     }
01478     else
01479         p = path;
01480 
01481 
01482     err = nr_ReadDesc( reg, node, desc );
01483     if ( err == REGERR_OK ) {
01484         err = nr_ReadName( reg, desc, bufsize-len, p );
01485     }
01486 
01487     return err;
01488 
01489 }   /* ReplaceName */
01490 
01491 
01492 static REGERR nr_RemoveName(char *path)
01493 {
01494     /* Typical inputs:
01495      * path = "/Machine/4.0/"   output = "/Machine"
01496      * path = "/Machine"        output = ""
01497      * path = ""                output = REGERR_NOMORE
01498      *
01499      * NOTE! It is EXREMELY important that names be in UTF-8; otherwise
01500      * the backwards path search will fail for multi-byte/Unicode names
01501      */
01502 
01503     int len = XP_STRLEN(path);
01504     char *p;
01505     if (len < 1)
01506         return REGERR_NOMORE;
01507 
01508     p = &path[len-1];
01509     /* if last char is '/', ignore it */
01510     if (*p == PATHDEL)
01511         p--;
01512 
01513     while ((p > path) && (*p != PATHDEL))
01514         p--;
01515 
01516 /*  if (*p != PATHDEL)
01517         return REGERR_NOMORE;
01518 */
01519 
01520     *p = '\0';
01521     return REGERR_OK;
01522 
01523 }   /* RemoveName */
01524 
01525 
01526 
01527 /* --------------------------------------------------------------------
01528  * Key/Entry Management
01529  * --------------------------------------------------------------------
01530  */
01531 static REGERR nr_Find(REGFILE *reg, REGOFF offParent, const char *pPath,
01532     REGDESC *pDesc, REGOFF *pPrev, REGOFF *pParent, XP_Bool raw);
01533 
01534 static REGERR nr_FindAtLevel(REGFILE *reg, REGOFF offFirst, const char *pName,
01535     REGDESC *pDesc, REGOFF *pOffPrev);
01536 
01537 static REGERR nr_CreateSubKey(REGFILE *reg, REGOFF parent, REGDESC *pDesc,
01538                               char *name);
01539 static REGERR nr_CreateEntryString(REGFILE *reg, REGDESC *pParent, 
01540     char *name, char *value);
01541 static REGERR nr_CreateEntry(REGFILE *reg, REGDESC *pParent, char *name,
01542     uint16 type, char *buffer, uint32 length);
01543 /* -------------------------------------------------------------------- */
01544 
01545 
01546 
01547 static REGERR nr_Find(REGFILE *reg,
01548             REGOFF offParent,
01549             const char *pPath,
01550             REGDESC *pDesc,
01551             REGOFF *pPrev,
01552             REGOFF *pParent,
01553             XP_Bool raw)
01554 {
01555 
01556     REGERR  err;
01557     REGDESC desc;
01558     REGOFF  offPrev = 0;
01559     char    namebuf[MAXREGNAMELEN];
01560     const char    *p;
01561 
01562     XP_ASSERT( pPath != NULL );
01563     XP_ASSERT( offParent >= HDRRESERVE );
01564     XP_ASSERT( VALID_FILEHANDLE( reg->fh ) );
01565 
01566     if (pPrev)
01567         *pPrev = 0;
01568     if (pParent)
01569         *pParent = 0;
01570 
01571     /* read starting desc */
01572     err = nr_ReadDesc( reg, offParent, &desc);
01573 
01574     if (raw == TRUE) {
01575         if ( err == REGERR_OK ) {
01576             /* save current location as parent of next segment */
01577             offParent = desc.location;
01578             /* look for name at next level down */
01579             err = nr_FindAtLevel(reg, desc.down, pPath, &desc, &offPrev);
01580         }
01581     }
01582     else {
01583         /* Walk 'path', reading keys into 'desc' */
01584         p = pPath;
01585         while ( err == REGERR_OK ) 
01586         {
01587             err = nr_NextName(p, namebuf, sizeof(namebuf), &p);
01588 
01589             if ( err == REGERR_OK ) {
01590                 /* save current location as parent of next segment */
01591                 offParent = desc.location;
01592                 /* look for name at next level down */
01593                 err = nr_FindAtLevel(reg, desc.down, namebuf, &desc, &offPrev);
01594             }
01595         }
01596     }
01597 
01598     if ( (raw == FALSE && err == REGERR_NOMORE) ||
01599             (raw == TRUE && err == REGERR_OK) ) {
01600         /* we found all the segments of the path--success! */
01601         err = REGERR_OK;
01602 
01603         if (pDesc) {
01604             COPYDESC(pDesc, &desc);
01605         }
01606         if (pPrev) {
01607             *pPrev = offPrev;
01608         }
01609         if (pParent) {
01610             *pParent = offParent;
01611         }
01612     }
01613     
01614     return err;
01615 
01616 }   /* nr_Find */
01617 
01618 
01619 
01620 
01621 /* nr_FindAtLevel -- looks for a node matching "pName" on the level starting
01622  *                   with "offset".  Returns REGERR_OK if found, REGERR_NOFIND
01623  *                   if not (plus other error conditions).
01624  *
01625  *                   If pDesc and pOffPrev are valid pointers *AND* the name is
01626  *                   found then pDesc will point at the REGDESC of the node and
01627  *                   pOffPrev will be the offset of the desc for the previous
01628  *                   node at the same level.  
01629  *
01630  *                   If the node is *NOT* found (REGERR_NOFIND is returned)
01631  *                   pDesc will point at the REGDESC of the last found node
01632  *                   (as will pOffPrev). If some other error is returned then
01633  *                   THese values must not be used.
01634  */
01635 static REGERR nr_FindAtLevel(REGFILE *reg,
01636                              REGOFF offset,
01637                              const char *pName,
01638                              REGDESC *pDesc,
01639                              REGOFF *pOffPrev)
01640 {
01641     char    namebuf[MAXREGNAMELEN];
01642     REGDESC desc;
01643     REGERR  err;
01644     REGOFF  prev = 0;
01645 
01646     /* Note: offset=0 when there's no 'down' or 'left' */
01647     XP_ASSERT(reg);
01648     XP_ASSERT(offset < reg->hdr.avail);
01649     XP_ASSERT(pName);
01650     XP_ASSERT(*pName);
01651 
01652     while ( offset != 0 )
01653     {
01654         /* get name of next node */
01655         err = nr_ReadDesc(reg, offset, &desc);
01656         if (err != REGERR_OK)
01657             return err;
01658 
01659         err = nr_ReadName(reg, &desc, sizeof(namebuf), namebuf);
01660         if (err != REGERR_OK)
01661             return err;
01662 
01663         /* check to see if it's the one we want */
01664         if (XP_STRCMP(namebuf, pName) == 0) {
01665             /* Found it! Signaled by non-zero offset */
01666             break;
01667         }
01668 
01669         /* advance to the next node */
01670         prev = offset;
01671         offset = desc.left;
01672     }
01673 
01674     if ( pDesc != NULL && (prev || offset)) {
01675         /* prev and offset BOTH null means we never loaded a desc */
01676         COPYDESC( pDesc, &desc );
01677     }
01678     if ( pOffPrev != NULL ) {
01679         *pOffPrev = prev;
01680     }
01681 
01682     if ( offset != 0 ) /* if we found one */
01683         return REGERR_OK;
01684     else
01685         return REGERR_NOFIND;
01686 }   /* FindAtLevel */
01687 
01688 
01689 
01690 static REGERR nr_CreateSubKey(REGFILE *reg,
01691                               REGOFF parent,
01692                               REGDESC *pDesc,
01693                               char *name)
01694 {
01695     /* nr_CreateSubKey does NO error checking--callers *MUST*
01696      * ensure that there are no duplicates
01697      */
01698     REGDESC desc;
01699     REGERR err;
01700 
01701     XP_ASSERT(reg);
01702     XP_ASSERT(pDesc);
01703     XP_ASSERT(name);
01704 
01705     err = nr_AppendName(reg, name, &desc);
01706     if (err != REGERR_OK)
01707         return err;
01708 
01709     desc.type = REGTYPE_KEY;
01710     desc.left = 0;
01711     desc.down = 0;
01712     desc.value = 0;
01713     desc.valuelen = 0;
01714     desc.valuebuf = 0;
01715     desc.parent   = parent;
01716 
01717     if ( parent == pDesc->location ) {
01718         /* It's a parent desc, so no siblings */
01719         err = nr_AppendDesc(reg, &desc, &pDesc->down);
01720     }
01721     else {
01722         /* It's a sibling desc */
01723         XP_ASSERT( pDesc->left == 0 ); /* not the end of chain! */
01724         err = nr_AppendDesc(reg, &desc, &pDesc->left);
01725     }
01726     if (err != REGERR_OK)
01727         return err;
01728 
01729     /* write out the fixed up parent/sibling desc */
01730     err = nr_WriteDesc(reg, pDesc);
01731     COPYDESC(pDesc, &desc);
01732 
01733     return err;
01734 
01735 }   /* nr_CreateSubKey */
01736 
01737 
01738 
01739 static REGERR nr_CreateEntryString(REGFILE *reg, REGDESC *pParent, char *name, char *value)
01740 {
01741     REGDESC desc;
01742     REGERR  err;
01743 
01744     XP_ASSERT(reg);
01745     XP_ASSERT(pParent);
01746     XP_ASSERT(name);
01747     XP_ASSERT(value);
01748 
01749     XP_MEMSET( &desc, 0, sizeof(REGDESC) );
01750 
01751     err = nr_AppendName(reg, name, &desc);
01752     if (err != REGERR_OK)
01753         return err;
01754 
01755     err = nr_AppendString(reg, value, &desc);
01756     if (err != REGERR_OK)
01757         return err;
01758 
01759     desc.type = REGTYPE_ENTRY_STRING_UTF;
01760     desc.left = pParent->value;
01761     desc.down = 0;
01762     desc.parent = pParent->location;
01763 
01764     err = nr_AppendDesc(reg, &desc, &pParent->value);
01765     if (err != REGERR_OK)
01766         return err;
01767 
01768     /* printf("nr_AddEntry: %s=%s @0x%lx\n", name, value, pParent->value); */
01769 
01770     return nr_WriteDesc(reg, pParent);
01771 
01772 }   /* nr_CreateEntryString */
01773 
01774 
01775 
01776 static REGERR nr_CreateEntry(REGFILE *reg, REGDESC *pParent, char *name,
01777     uint16 type, char *value, uint32 length)
01778 {
01779     REGDESC desc;
01780     REGERR  err;
01781 
01782     XP_ASSERT(reg);
01783     XP_ASSERT(pParent);
01784     XP_ASSERT(name);
01785     XP_ASSERT(value);
01786 
01787     XP_MEMSET( &desc, 0, sizeof(REGDESC) );
01788 
01789     err = nr_AppendName(reg, name, &desc);
01790     if (err != REGERR_OK)
01791         return err;
01792 
01793     err = nr_AppendData(reg, value, length, &desc);
01794     if (err != REGERR_OK)
01795         return err;
01796 
01797     desc.type = type;
01798     desc.left = pParent->value;
01799     desc.down = 0;
01800     desc.parent = pParent->location;
01801 
01802     err = nr_AppendDesc(reg, &desc, &pParent->value);
01803     if (err != REGERR_OK)
01804         return err;
01805 
01806     /* printf("nr_AddEntry: %s=%s @0x%lx\n", name, value, pParent->value); */
01807 
01808     return nr_WriteDesc(reg, pParent);
01809 
01810 }   /* nr_CreateEntry */
01811 
01812 
01813 
01814 
01815 /* ---------------------------------------------------------------------
01816  * Intermediate API
01817  * ---------------------------------------------------------------------
01818  */
01819 static REGOFF  nr_TranslateKey( REGFILE *reg, RKEY key );
01820 static REGERR  nr_InitStdRkeys( REGFILE *reg );
01821 static XP_Bool nr_ProtectedNode( REGFILE *reg, REGOFF key );
01822 static REGERR  nr_RegAddKey( REGFILE *reg, RKEY key, char *path, RKEY *newKey, XP_Bool raw );
01823 static REGERR  nr_RegDeleteKey( REGFILE *reg, RKEY key, char *path, XP_Bool raw );
01824 static REGERR  nr_RegOpen( const char *filename, HREG *hReg );
01825 static REGERR  nr_RegClose( HREG hReg );
01826 static char*   nr_GetUsername();
01827 static const char* nr_GetRegName (const char *name);
01828 static int     nr_RegSetBufferSize( HREG hReg, int bufsize );
01829 
01830 /* --------------------------------------------------------------------- */
01831 
01832 
01833 static REGOFF nr_TranslateKey( REGFILE *reg, RKEY key )
01834 {
01835     REGOFF retKey = 0;
01836 
01837     /* if it's a special key  */
01838     if ( key < HDRRESERVE )  {
01839         /* ...translate it */
01840         switch (key)
01841         {
01842             case ROOTKEY:
01843                 retKey = reg->hdr.root;
01844                 break;
01845 
01846             case ROOTKEY_VERSIONS:
01847                 retKey = reg->rkeys.versions;
01848                 break;
01849 
01850             case ROOTKEY_USERS:
01851                 retKey = reg->rkeys.users;
01852                 break;
01853 
01854             case ROOTKEY_COMMON:
01855                 retKey = reg->rkeys.common;
01856                 break;
01857 
01858 #ifndef STANDALONE_REGISTRY
01859             case ROOTKEY_CURRENT_USER:
01860                 if ( reg->rkeys.current_user == 0 ) {
01861                     /* not initialized--find the current user key */
01862                     RKEY    userkey = 0;
01863                     REGERR  err;
01864                     char*   profName;
01865 
01866                     profName = nr_GetUsername();
01867                     if ( NULL != profName ) {
01868                         /* Don't assign a slot for missing or magic profile */
01869                         if ( '\0' == *profName ||
01870                             0 == XP_STRCMP(ASW_MAGIC_PROFILE_NAME, profName)) 
01871                         {
01872                             err = REGERR_FAIL;
01873                         } else {
01874                             err = nr_RegAddKey( reg, reg->rkeys.users, profName, &userkey, FALSE );
01875                         }
01876                         XP_FREE(profName);
01877                     }
01878                     else {
01879                         err = nr_RegAddKey( reg, reg->rkeys.users, "default", &userkey, FALSE );
01880                     }
01881 
01882                     if ( err == REGERR_OK ) {
01883                         reg->rkeys.current_user = userkey;
01884                     }
01885                 }
01886                 retKey = reg->rkeys.current_user;
01887                 break;
01888 #endif /* !STANDALONE_REGISTRY */
01889 
01890             case ROOTKEY_PRIVATE:
01891                 retKey = reg->rkeys.privarea;
01892                 break;
01893 
01894             default:
01895                 /* not a valid key */
01896                 retKey = 0;
01897                 break;
01898         }
01899     }
01900     else {
01901         /* ...otherwise it's fine as-is */
01902         retKey = (REGOFF)key;
01903     }
01904     return ( retKey );
01905 }  /* nr_TranslateKey */
01906 
01907 
01908 
01909 static REGERR nr_InitStdRkeys( REGFILE *reg )
01910 {
01911     REGERR      err = REGERR_OK;
01912     RKEY        key;
01913 
01914     XP_ASSERT( reg != NULL );
01915 
01916     /* initialize to invalid key values */
01917     XP_MEMSET( &reg->rkeys, 0, sizeof(STDNODES) );
01918 
01919     /* Add each key before looking it up.  Adding an already
01920      * existing key is harmless, and these MUST exist.
01921      */
01922 
01923     /* ROOTKEY_USERS */
01924     err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_USERS_STR, &key, FALSE );
01925     if ( err != REGERR_OK )
01926         return err;
01927     reg->rkeys.users = key;
01928 
01929     /* ROOTKEY_COMMON */
01930     err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_COMMON_STR, &key, FALSE );
01931     if ( err != REGERR_OK ) 
01932         return err;
01933     reg->rkeys.common = key;
01934 
01935     /* ROOTKEY_VERSIONS */
01936     err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_VERSIONS_STR, &key, FALSE );
01937     if ( err != REGERR_OK )
01938         return err;
01939     reg->rkeys.versions = key;
01940 
01941     /* ROOTKEY_CURRENT_USER */
01942     /* delay until first use -- see nr_TranslateKey */
01943 
01944     /* ROOTKEY_PRIVATE */
01945     err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_PRIVATE_STR, &key, FALSE );
01946     if ( err != REGERR_OK ) 
01947         return err;
01948     reg->rkeys.privarea = key;
01949 
01950     return err;
01951 }   /* nr_InitStdRkeys */
01952 
01953 
01954 
01955 static XP_Bool nr_ProtectedNode( REGFILE *reg, REGOFF key )
01956 {
01957     if ( (key == reg->hdr.root) ||
01958          (key == reg->rkeys.users) ||
01959          (key == reg->rkeys.versions) ||
01960          (key == reg->rkeys.common) ||
01961          (key == reg->rkeys.current_user) )
01962     {
01963         return TRUE;
01964     }
01965     else
01966         return FALSE;
01967 }
01968 
01969 
01970 
01971 static REGERR nr_RegAddKey( REGFILE *reg, RKEY key, char *path, RKEY *newKey, XP_Bool raw )
01972 {
01973     REGERR      err;
01974     REGDESC     desc;
01975     REGOFF      start;
01976     REGOFF      parent;
01977     char        namebuf[MAXREGNAMELEN];
01978     char        *p;
01979 
01980     XP_ASSERT( regStartCount > 0 );
01981     XP_ASSERT( reg != NULL );
01982     XP_ASSERT( path != NULL );
01983     XP_ASSERT( *path != '\0' );
01984     XP_ASSERT( VALID_FILEHANDLE( reg->fh ) );
01985 
01986     /* have to translate again in case this is an internal call */
01987     start = nr_TranslateKey( reg, key );
01988     if ( start == 0 )
01989         return REGERR_PARAM;
01990 
01991     /* Get starting desc */
01992     err = nr_ReadDesc( reg, start, &desc );
01993 
01994     if (raw == TRUE) {
01995         if ( err == REGERR_OK) {
01996             /* look for name at next level down */
01997             parent = desc.location;
01998             err = nr_FindAtLevel(reg, desc.down, path, &desc, 0);
01999 
02000             /* if key is not found */
02001             if ( err == REGERR_NOFIND ) {
02002                 /* add it as a sub-key to the last found key */
02003                 err = nr_CreateSubKey(reg, parent, &desc, path);
02004             }
02005         }
02006     }
02007     else {
02008         /* Walk 'path', reading keys into 'desc' */
02009         p = path;
02010         while ( err == REGERR_OK ) {
02011 
02012             /* get next name on the path */
02013             err = nr_NextName(p, namebuf, sizeof(namebuf), &p);
02014             if ( err == REGERR_OK ) {
02015                 /* look for name at next level down */
02016                 parent = desc.location;
02017                 err = nr_FindAtLevel(reg, desc.down, namebuf, &desc, 0);
02018 
02019                 /* if key is not found */
02020                 if ( err == REGERR_NOFIND ) {
02021                     /* add it as a sub-key to the last found key */
02022                     err = nr_CreateSubKey(reg, parent, &desc, namebuf);
02023                 }
02024             }
02025         }
02026     }
02027 
02028     /* it's good to have processed the whole path */
02029     if ( (raw == FALSE && err == REGERR_NOMORE) ||
02030          (raw == TRUE && err == REGERR_OK) ) 
02031     {
02032         err = REGERR_OK;
02033 
02034         /* return new key if the caller wants it */
02035         if ( newKey != NULL ) {
02036             *newKey = desc.location;
02037         }
02038     }
02039 
02040     return err;
02041 
02042 }   /* nr_RegAddKey */
02043 
02044 
02045 
02046 
02047 static REGERR nr_RegDeleteKey( REGFILE *reg, RKEY key, char *path, XP_Bool raw )
02048 {
02049     REGERR      err;
02050     REGOFF      start;
02051     REGDESC     desc;
02052     REGDESC     predecessor;
02053     REGOFF      offPrev;
02054     REGOFF      offParent;
02055     REGOFF*     link;
02056 
02057     XP_ASSERT( regStartCount > 0 );
02058     XP_ASSERT( reg != NULL );
02059     XP_ASSERT( VALID_FILEHANDLE( reg->fh ) );
02060 
02061     start = nr_TranslateKey( reg, key );
02062     if ( path == NULL || *path == '\0' || start == 0 )
02063         return REGERR_PARAM;
02064 
02065     /* find the specified key */
02066     err = nr_Find( reg, start, path, &desc, &offPrev, &offParent, raw );
02067     if ( err == REGERR_OK ) {
02068 
02069         XP_ASSERT( !TYPE_IS_ENTRY( desc.type ) );
02070 
02071         /* make sure it's childless and not a top-level key */
02072         if ( (desc.down == 0) && !nr_ProtectedNode( reg, desc.location ) ) {
02073 
02074             /* Are we the first on our level? */
02075             if ( offPrev == 0 ) {
02076                 /* Yes: link to parent's "down" pointer */
02077                 err = nr_ReadDesc( reg, offParent, &predecessor );
02078                 link = &(predecessor.down);
02079             }
02080             else {
02081                 /* No: link using predecessor's "left" pointer */
02082                 err = nr_ReadDesc( reg, offPrev, &predecessor );
02083                 link = &(predecessor.left);
02084             }
02085 
02086             /* If we read the predecessor desc OK */
02087             if (err == REGERR_OK) {
02088                 XP_ASSERT( *link == desc.location );
02089 
02090                 /* link predecessor to next, removing current node from chain */
02091                 *link = desc.left;
02092 
02093                 /* Write the updated predecessor */
02094                 err = nr_WriteDesc( reg, &predecessor );
02095                 if ( err == REGERR_OK ) {
02096                     /* Mark key deleted to prevent bogus use by anyone
02097                      * who is holding an RKEY for that node
02098                      */
02099                     desc.type |= REGTYPE_DELETED;
02100                     err = nr_WriteDesc( reg, &desc );
02101                 }
02102             }
02103         }
02104         else {
02105             /* specified node is protected from deletion */
02106             err = REGERR_FAIL;
02107         }
02108     }
02109 
02110     return err;
02111 
02112 }   /* nr_RegDeleteKey */
02113 
02114 
02115 
02116 static int nr_RegSetBufferSize( HREG hReg, int bufsize )
02117 {
02118     REGERR      err = REGERR_OK;
02119     REGHANDLE*  reghnd = (REGHANDLE*)hReg;
02120     REGFILE*    reg;
02121     XP_Bool     needDelete = FALSE;
02122     int         newSize;
02123 
02124     /* verify handle */
02125     err = VERIFY_HREG( hReg );
02126     if ( err != REGERR_OK )
02127         return -1;
02128 
02129     reg = reghnd->pReg;
02130 
02131     PR_Lock( reg->lock );
02132  
02133     newSize = XP_FileSetBufferSize( reg->fh, bufsize );
02134 
02135     PR_Unlock( reg->lock );
02136 
02137     return newSize;
02138 }
02139 
02140 
02141 
02142 static REGERR nr_RegOpen( const char *filename, HREG *hReg )
02143 {
02144     REGERR    status = REGERR_OK;
02145     REGFILE   *pReg;
02146     REGHANDLE *pHandle;
02147 
02148     XP_ASSERT( regStartCount > 0 );
02149 
02150     /* initialize output handle in case of error */
02151     if ( hReg == NULL ) {
02152         return REGERR_PARAM;
02153     }
02154     *hReg = NULL;
02155     
02156     /* Look for named file in list of open registries */
02157     filename = nr_GetRegName( filename );
02158     if (filename == NULL) {
02159         filename = "";
02160     }
02161     pReg = vr_findRegFile( filename );
02162 
02163     /* if registry not already open */
02164     if (pReg == NULL) {
02165 
02166         /* ...then open it */
02167         pReg = (REGFILE*)XP_ALLOC( sizeof(REGFILE) );
02168         if ( pReg == NULL ) {
02169             status = REGERR_MEMORY;
02170             goto bail;
02171         }
02172         XP_MEMSET(pReg, 0, sizeof(REGFILE));
02173 
02174         pReg->inInit = TRUE;
02175         pReg->filename = XP_STRDUP(filename);
02176         if (pReg->filename == NULL) {
02177             XP_FREE( pReg );
02178             status = REGERR_MEMORY;
02179             goto bail;
02180         }
02181 
02182         status = nr_OpenFile( filename, &(pReg->fh) );
02183         if (status == REGERR_READONLY) {
02184             /* Open, but read only */
02185             pReg->readOnly = TRUE;
02186             status = REGERR_OK;
02187         }
02188         if ( status != REGERR_OK ) {
02189             XP_FREE( pReg->filename );
02190             XP_FREE( pReg );
02191 
02192             goto bail;
02193         }
02194 
02195         /* ...read and validate the header */
02196         status = nr_ReadHdr( pReg );
02197         if ( status != REGERR_OK ) {
02198             nr_CloseFile( &(pReg->fh) );
02199             XP_FREE( pReg->filename );
02200             XP_FREE( pReg );
02201             goto bail;
02202         }
02203 
02204         /* ...other misc initialization */
02205         pReg->refCount = 0;
02206 
02207 #ifndef STANDALONE_REGISTRY
02208         pReg->uniqkey = PR_Now();
02209 #endif
02210 
02211         status = nr_InitStdRkeys( pReg );
02212         if ( status == REGERR_OK ) {
02213             /* ...and add it to the list */
02214             nr_AddNode( pReg );
02215         }
02216         else {
02217             nr_CloseFile( &(pReg->fh) );
02218             XP_FREE( pReg->filename );
02219             XP_FREE( pReg );
02220             goto bail;
02221         }
02222 
02223 #ifndef STANDALONE_REGISTRY
02224         pReg->lock = PR_NewLock();
02225 #endif
02226 
02227         /* now done with everything that needs to protect the header */
02228         pReg->inInit = FALSE;
02229     }
02230 
02231     /* create a new handle to the regfile */
02232     pHandle = (REGHANDLE*)XP_ALLOC( sizeof(REGHANDLE) );
02233     if ( pHandle == NULL ) {
02234         /* we can't create the handle */
02235         if ( pReg->refCount == 0 ) {
02236             /* we've just opened it so close it and remove node */
02237             nr_CloseFile( &(pReg->fh) );
02238             nr_DeleteNode( pReg );
02239         }
02240 
02241         status = REGERR_MEMORY;
02242         goto bail;
02243     }
02244 
02245     pHandle->magic   = MAGIC_NUMBER;
02246     pHandle->pReg    = pReg;
02247 
02248     /* success: bump the reference count and return the handle */
02249     pReg->refCount++;
02250     *hReg = (void*)pHandle;
02251 
02252 bail:
02253     return status;
02254 
02255 }   /* nr_RegOpen */
02256 
02257 
02258 
02259 static REGERR nr_RegClose( HREG hReg )
02260 {
02261     REGERR      err = REGERR_OK;
02262     REGHANDLE*  reghnd = (REGHANDLE*)hReg;
02263     REGFILE*    reg;
02264     XP_Bool     needDelete = FALSE;
02265 
02266     XP_ASSERT( regStartCount > 0 );
02267 
02268     /* verify handle */
02269     err = VERIFY_HREG( hReg );
02270     if ( err != REGERR_OK )
02271         return err;
02272 
02273     reg = reghnd->pReg;
02274 
02275     PR_Lock( reg->lock );
02276     if ( err == REGERR_OK )
02277     {
02278         XP_ASSERT( VALID_FILEHANDLE(reg->fh) );
02279 
02280         /* save changed header info */
02281         if ( reg->hdrDirty ) {
02282             nr_WriteHdr( reg );
02283         }
02284 
02285         /* lower REGFILE user count */
02286         reg->refCount--;
02287 
02288         /* if registry is no longer in use */
02289         if ( reg->refCount < 1 ) 
02290         {
02291             /* ...then close the file */
02292             nr_CloseFile( &(reg->fh) );
02293 
02294             /* ...and mark REGFILE node for deletion from list */
02295             needDelete = TRUE;
02296         }
02297         else
02298         {
02299             /* ...otherwise make sure any writes are flushed */
02300             XP_FileFlush( reg->fh );
02301         }
02302 
02303         reghnd->magic = 0;    /* prevent accidental re-use */  
02304         PR_Unlock( reg->lock );
02305 
02306         if ( needDelete )
02307             nr_DeleteNode( reg );
02308 
02309         XP_FREE( reghnd );
02310     }
02311 
02312     return err;
02313 
02314 }   /* nr_RegClose */
02315 
02316 
02317 
02318 static char *nr_GetUsername()
02319 {
02320   if (NULL == user_name) {
02321     return "default";
02322   } else {
02323     return user_name;
02324   }
02325 }
02326 
02327 static const char* nr_GetRegName (const char *name)
02328 {
02329     if (name == NULL || *name == '\0') {
02330         XP_ASSERT( globalRegName != NULL );
02331         return globalRegName;
02332     } else {
02333         return name;
02334     }
02335 }
02336 
02337 
02338 
02339 
02340 /* ---------------------------------------------------------------------
02341  * Public API
02342  * --------------------------------------------------------------------- */
02343 
02344 
02345 /* ---------------------------------------------------------------------
02346  * NR_RegGetUsername - Gets a copy of the current username
02347  *
02348  * Parameters:
02349  *   A variable which, on exit will contain an alloc'ed string which is a
02350  *   copy of the current username.
02351  *
02352  * DO NOT USE -- OBSOLETE
02353  * ---------------------------------------------------------------------
02354  */
02355 
02356 VR_INTERFACE(REGERR) NR_RegGetUsername(char **name)
02357 {
02358     /* XXX: does this need locking? */
02359 
02360     if ( name == NULL )
02361         return REGERR_PARAM;
02362 
02363     *name = XP_STRDUP(nr_GetUsername());
02364 
02365     if ( NULL == *name )
02366         return REGERR_MEMORY;
02367 
02368     return REGERR_OK;
02369 }
02370 
02371 
02372 /* ---------------------------------------------------------------------
02373  * NR_RegSetBufferSize - Set the buffer size
02374  *
02375  * Parameters:
02376  *     name     - name of the current user
02377  *
02378  * Output:
02379  * ---------------------------------------------------------------------
02380  */
02381 
02382 VR_INTERFACE(int) NR_RegSetBufferSize( HREG hReg, int bufsize )
02383 {
02384     int      newSize;
02385 
02386     PR_Lock( reglist_lock );
02387 
02388     newSize = nr_RegSetBufferSize( hReg, bufsize );
02389 
02390     PR_Unlock(reglist_lock);
02391 
02392     return newSize;
02393 }
02394 
02395 
02396 /* ---------------------------------------------------------------------
02397  * NR_RegSetUsername - Set the current username
02398  * 
02399  * If the current user profile name is not set then trying to use
02400  * HKEY_CURRENT_USER will result in an error.
02401  *
02402  * Parameters:
02403  *     name     - name of the current user
02404  *
02405  * Output:
02406  * ---------------------------------------------------------------------
02407  */
02408 
02409 VR_INTERFACE(REGERR) NR_RegSetUsername(const char *name)
02410 {
02411     char *tmp;
02412 
02413     if ( name == NULL || *name == '\0' )
02414         return REGERR_PARAM;
02415 
02416     tmp = XP_STRDUP(name);
02417     if (NULL == tmp) {
02418         return REGERR_MEMORY;
02419     }
02420 
02421     PR_Lock( reglist_lock );
02422 
02423     XP_FREEIF(user_name);
02424     user_name = tmp;
02425 
02426 /* XXX: changing the username should go through and clear out the current.user
02427    for each open registry. */
02428 
02429     PR_Unlock( reglist_lock );
02430   
02431     return REGERR_OK;
02432 }
02433 
02434 
02435 
02436 
02437 #ifndef STANDALONE_REGISTRY
02438 /* ---------------------------------------------------------------------
02439  * NR_RegGetUniqueName
02440  * 
02441  * Returns a unique name that can be used for anonymous key/value names
02442  *
02443  * Parameters:
02444  *     hReg     - handle of open registry
02445  *     outbuf   - where to put the string
02446  *     buflen   - how big the buffer is
02447  * ---------------------------------------------------------------------
02448  */
02449 VR_INTERFACE(REGERR) NR_RegGetUniqueName(HREG hReg, char* outbuf, uint32 buflen)
02450 {
02451     PRUint64    one;
02452     REGERR      err;
02453     REGFILE*    reg;
02454     static PRUint64 uniqkey;
02455 
02456     /* verify parameters */
02457     err = VERIFY_HREG( hReg );
02458     if ( err != REGERR_OK )
02459         return err;
02460 
02461     reg = ((REGHANDLE*)hReg)->pReg;
02462 
02463     if ( !outbuf )
02464         return REGERR_PARAM;
02465 
02466     if ( buflen <= (sizeof(PRUint64)*2) )
02467         return REGERR_BUFTOOSMALL;
02468 
02469     if ( LL_IS_ZERO(uniqkey) )
02470         uniqkey = PR_Now();
02471 
02472     PR_snprintf(outbuf,buflen,"%llx",uniqkey);
02473 
02474     /* increment counter for next time */
02475     LL_I2L(one,1);
02476     LL_ADD(uniqkey, uniqkey, one);
02477 
02478     return REGERR_OK;
02479 }
02480 #endif
02481 
02482 
02483        
02484        
02485 /* ---------------------------------------------------------------------
02486  * NR_RegOpen - Open a netscape XP registry
02487  *
02488  * Parameters:
02489  *    filename   - registry file to open. NULL or ""  opens the standard
02490  *                 local registry.
02491  *    hReg       - OUT: handle to opened registry
02492  *
02493  * Output:
02494  * ---------------------------------------------------------------------
02495  */
02496 VR_INTERFACE(REGERR) NR_RegOpen( const char *filename, HREG *hReg )
02497 {
02498     REGERR    status = REGERR_OK;
02499 
02500 #if !defined(STANDALONE_REGISTRY)
02501     /* you must call NR_StartupRegistry() first */
02502     if ( regStartCount <= 0 )
02503         return REGERR_FAIL;
02504 #endif
02505 
02506     PR_Lock(reglist_lock);
02507 
02508     status = nr_RegOpen( filename, hReg );
02509 
02510     PR_Unlock(reglist_lock);
02511 
02512     return status;
02513 
02514 }   /* NR_RegOpen */
02515 
02516 
02517 
02518 
02519 /* ---------------------------------------------------------------------
02520  * NR_RegClose - Close a netscape XP registry
02521  *
02522  * Parameters:
02523  *    hReg     - handle of open registry to be closed.
02524  *
02525  * After calling this routine the handle is no longer valid
02526  * ---------------------------------------------------------------------
02527  */
02528 VR_INTERFACE(REGERR) NR_RegClose( HREG hReg )
02529 {
02530     REGERR      err = REGERR_OK;
02531 
02532     PR_Lock( reglist_lock );
02533 
02534     err = nr_RegClose( hReg );
02535 
02536     PR_Unlock(reglist_lock);
02537 
02538     return err;
02539 
02540 }   /* NR_RegClose */
02541 
02542 
02543 
02544 
02545 /* ---------------------------------------------------------------------
02546  * NR_RegFlush - Manually flush data in a netscape XP registry
02547  *
02548  * Parameters:
02549  *    hReg     - handle of open registry to be flushed.
02550  * ---------------------------------------------------------------------
02551  */
02552 VR_INTERFACE(REGERR) NR_RegFlush( HREG hReg )
02553 {
02554     REGERR      err;
02555     REGFILE*    reg;
02556 
02557     /* verify parameters */
02558     err = VERIFY_HREG( hReg );
02559     if ( err != REGERR_OK )
02560         return err;
02561 
02562     reg = ((REGHANDLE*)hReg)->pReg;
02563 
02564     /* can't flush a read-only registry */
02565     if ( reg->readOnly )
02566         return REGERR_READONLY;
02567 
02568     /* lock the registry file */
02569     err = nr_Lock( reg );
02570     if ( err == REGERR_OK )
02571     {
02572         if ( reg->hdrDirty ) {
02573             nr_WriteHdr( reg );
02574         }
02575 
02576         XP_FileFlush( reg->fh );
02577 
02578         /* unlock the registry */
02579         nr_Unlock( reg );
02580     }
02581 
02582     return err;
02583 
02584 } /* NR_RegFlush */
02585 
02586 
02587 
02588 
02589 /* ---------------------------------------------------------------------
02590  * NR_RegIsWritable - Check read/write status of open registry
02591  *
02592  * Parameters:
02593  *    hReg     - handle of open registry to query
02594  * ---------------------------------------------------------------------
02595  */
02596 VR_INTERFACE(REGERR) NR_RegIsWritable( HREG hReg )
02597 {
02598     REGERR      err;
02599     REGFILE*    reg;
02600 
02601     /* verify parameters */
02602     err = VERIFY_HREG( hReg );
02603     if ( err != REGERR_OK )
02604         return err;
02605 
02606     reg = ((REGHANDLE*)hReg)->pReg;
02607 
02608     if ( reg->readOnly )
02609         return REGERR_READONLY;
02610     else
02611         return REGERR_OK;
02612 
02613 }   /* NR_RegIsWritable */
02614 
02615 
02616 
02617 /* ---------------------------------------------------------------------
02618  * NR_RegAddKey - Add a key node to the registry
02619  *
02620  *      This routine is simply a wrapper to perform user input
02621  *      validation and translation from HREG and standard key
02622  *      values into the internal format
02623  *
02624  * Parameters:
02625  *    hReg     - handle of open registry
02626  *    key      - registry key obtained from NR_RegGetKey(),
02627  *               or one of the standard top-level keys
02628  *    path     - relative path of key to be added.  Intermediate
02629  *               nodes will also be added if necessary.
02630  * ---------------------------------------------------------------------
02631  */
02632 VR_INTERFACE(REGERR) NR_RegAddKey( HREG hReg, RKEY key, char *path, RKEY *newKey )
02633 {
02634     REGERR      err;
02635     REGOFF      start;
02636     REGFILE*    reg;
02637 
02638     /* prevent use of return value in case errors aren't checked */
02639     if ( newKey != NULL )
02640         *newKey = 0;
02641 
02642     /* verify parameters */
02643     err = VERIFY_HREG( hReg );
02644     if ( err != REGERR_OK )
02645         return err;
02646 
02647     reg = ((REGHANDLE*)hReg)->pReg;
02648 
02649     if ( path == NULL || *path == '\0' || reg == NULL )
02650         return REGERR_PARAM;
02651 
02652     /* lock the registry file */
02653     err = nr_Lock( reg );
02654     if ( err == REGERR_OK )
02655     {
02656         /* ... don't allow additional children of ROOTKEY */
02657         start = nr_TranslateKey( reg, key );
02658         if ( start != 0 && start != reg->hdr.root )
02659         {
02660             err = nr_RegAddKey( reg, start, path, newKey, FALSE );
02661         }
02662         else
02663             err = REGERR_PARAM;
02664 
02665         /* unlock the registry */
02666         nr_Unlock( reg );
02667     }
02668 
02669     return err;
02670 }   /* NR_RegAddKey */
02671 
02672 
02673 
02674 
02675 /* ---------------------------------------------------------------------
02676  * NR_RegAddKeyRaw - Add a key node to the registry
02677  *
02678  *      This routine is different from NR_RegAddKey() in that it takes 
02679  *      a keyname rather than a path.
02680  *
02681  * Parameters:
02682  *    hReg     - handle of open registry
02683  *    key      - registry key obtained from NR_RegGetKey(),
02684  *               or one of the standard top-level keys
02685  *    keyname  - name of key to be added. No parsing of this
02686  *               name happens.
02687  *    newkey   - if not null the RKEY of the new key is returned
02688  * ---------------------------------------------------------------------
02689  */
02690 VR_INTERFACE(REGERR) NR_RegAddKeyRaw( HREG hReg, RKEY key, char *keyname, RKEY *newKey )
02691 {
02692     REGERR      err;
02693     REGOFF      start;
02694     REGFILE*    reg;
02695 
02696     /* prevent use of return value in case errors aren't checked */
02697     if ( newKey != NULL )
02698         *newKey = 0;
02699 
02700     /* verify parameters */
02701     err = VERIFY_HREG( hReg );
02702     if ( err != REGERR_OK )
02703         return err;
02704 
02705     reg = ((REGHANDLE*)hReg)->pReg;
02706 
02707     if ( keyname == NULL || *keyname == '\0' || reg == NULL )
02708         return REGERR_PARAM;
02709 
02710     /* lock the registry file */
02711     err = nr_Lock( reg );
02712     if ( err == REGERR_OK )
02713     {
02714         /* ... don't allow additional children of ROOTKEY */
02715         start = nr_TranslateKey( reg, key );
02716         if ( start != 0 && start != reg->hdr.root ) 
02717         {
02718             err = nr_RegAddKey( reg, start, keyname, newKey, TRUE );
02719         }
02720         else
02721             err = REGERR_PARAM;
02722 
02723         /* unlock the registry */
02724         nr_Unlock( reg );
02725     }
02726 
02727     return err;
02728 }   /* NR_RegAddKeyRaw */
02729 
02730 
02731 
02732 
02733 /* ---------------------------------------------------------------------
02734  * NR_RegDeleteKey - Delete the specified key
02735  *
02736  * Note that delete simply orphans blocks and makes no attempt
02737  * to reclaim space in the file. Use NR_RegPack()
02738  *
02739  * Cannot be used to delete keys with child keys
02740  *
02741  * Parameters:
02742  *    hReg     - handle of open registry
02743  *    key      - starting node RKEY, typically one of the standard ones.
02744  *    path     - relative path of key to delete
02745  * ---------------------------------------------------------------------
02746  */
02747 VR_INTERFACE(REGERR) NR_RegDeleteKey( HREG hReg, RKEY key, char *path )
02748 {
02749     REGERR      err;
02750     REGFILE*    reg;
02751 
02752     /* verify parameters */
02753     err = VERIFY_HREG( hReg );
02754     if ( err != REGERR_OK )
02755         return err;
02756 
02757     reg = ((REGHANDLE*)hReg)->pReg;
02758 
02759     /* lock registry */
02760     err = nr_Lock( reg );
02761     if ( err == REGERR_OK )
02762     {
02763         err = nr_RegDeleteKey( reg, key, path, FALSE );
02764         nr_Unlock( reg );
02765     }
02766 
02767     return err;
02768 }   /* NR_RegDeleteKey */
02769 
02770 
02771 
02772 
02773 /* ---------------------------------------------------------------------
02774  * NR_RegDeleteKeyRaw - Delete the specified raw key
02775  *
02776  * Note that delete simply orphans blocks and makes no attempt
02777  * to reclaim space in the file. Use NR_RegPack()
02778  *
02779  * Parameters:
02780  *    hReg     - handle of open registry
02781  *    key      - RKEY or parent to the raw key you wish to delete
02782  *    keyname  - name of child key to delete
02783  * ---------------------------------------------------------------------
02784  */
02785 VR_INTERFACE(REGERR) NR_RegDeleteKeyRaw( HREG hReg, RKEY key, char *keyname )
02786 {
02787     REGERR      err;
02788     REGFILE*    reg;
02789 
02790     /* verify parameters */
02791     err = VERIFY_HREG( hReg );
02792     if ( err != REGERR_OK )
02793         return err;
02794 
02795     reg = ((REGHANDLE*)hReg)->pReg;
02796 
02797     /* lock registry */
02798     err = nr_Lock( reg );
02799     if ( err == REGERR_OK )
02800     {
02801         err = nr_RegDeleteKey( reg, key, keyname, TRUE );
02802         nr_Unlock( reg );
02803     }
02804 
02805     return err;
02806 }   /* NR_RegDeleteKeyRaw */
02807 
02808 
02809 
02810 
02811 /* ---------------------------------------------------------------------
02812  * NR_RegGetKey - Get the RKEY value of a node from its path
02813  *
02814  * Parameters:
02815  *    hReg     - handle of open registry
02816  *    key      - starting node RKEY, typically one of the standard ones.
02817  *    path     - relative path of key to find.  (a blank path just gives you
02818  *               the starting key--useful for verification, VersionRegistry)
02819  *    result   - if successful the RKEY of the specified sub-key
02820  * ---------------------------------------------------------------------
02821  */
02822 VR_INTERFACE(REGERR) NR_RegGetKey( HREG hReg, RKEY key, const char *path, RKEY *result )
02823 {
02824     REGERR      err;
02825     REGOFF      start;
02826     REGFILE*    reg;
02827     REGDESC     desc;
02828 
02829     XP_ASSERT( regStartCount > 0 );
02830 
02831     /* prevent use of return value in case errors aren't checked */
02832     if ( result != NULL )
02833         *result = (RKEY)0;
02834 
02835     /* verify parameters */
02836     err = VERIFY_HREG( hReg );
02837     if ( err != REGERR_OK )
02838         return err;
02839 
02840     if ( path == NULL || result == NULL )
02841         return REGERR_PARAM;
02842 
02843     reg = ((REGHANDLE*)hReg)->pReg;
02844 
02845     /* lock registry */
02846     err = nr_Lock( reg );
02847     if ( err == REGERR_OK )
02848     {
02849         start = nr_TranslateKey( reg, key );
02850         if ( start != 0 )
02851         {
02852             /* find the specified key ( if it's valid )*/
02853             err = nr_Find( reg, start, path, &desc, 0, 0, FALSE );
02854             if ( err == REGERR_OK ) {
02855                 *result = (RKEY)desc.location;
02856             }
02857         }
02858         else {
02859             err = REGERR_PARAM;
02860         }
02861 
02862         nr_Unlock( reg );
02863     }
02864 
02865     return err;
02866 
02867 }   /* NR_RegGetKey */
02868 
02869 
02870 
02871 
02872 /* ---------------------------------------------------------------------
02873  * NR_RegGetKeyRaw - Get the RKEY value of a node from its keyname
02874  *
02875  * Parameters:
02876  *    hReg     - handle of open registry
02877  *    key      - starting node RKEY, typically one of the standard ones.
02878  *    keyname  - keyname of key to find.  (a blank keyname just gives you
02879  *               the starting key--useful for verification, VersionRegistry)
02880  *    result   - if successful the RKEY of the specified sub-key
02881  * ---------------------------------------------------------------------
02882  */
02883 VR_INTERFACE(REGERR) NR_RegGetKeyRaw( HREG hReg, RKEY key, char *keyname, RKEY *result )
02884 {
02885     REGERR      err;
02886     REGOFF      start;
02887     REGFILE*    reg;
02888     REGDESC     desc;
02889 
02890     XP_ASSERT( regStartCount > 0 );
02891 
02892     /* prevent use of return value in case errors aren't checked */
02893     if ( result != NULL )
02894         *result = (RKEY)0;
02895 
02896     /* verify parameters */
02897     err = VERIFY_HREG( hReg );
02898     if ( err != REGERR_OK )
02899         return err;
02900 
02901     if ( keyname == NULL || result == NULL )
02902         return REGERR_PARAM;
02903 
02904     reg = ((REGHANDLE*)hReg)->pReg;
02905 
02906     /* lock registry */
02907     err = nr_Lock( reg );
02908     if ( err == REGERR_OK )
02909     {
02910         start = nr_TranslateKey( reg, key );
02911         if ( start != 0 )
02912         {
02913             /* find the specified key ( if it's valid )*/
02914             err = nr_Find( reg, start, keyname, &desc, 0, 0, TRUE );
02915             if ( err == REGERR_OK ) {
02916                 *result = (RKEY)desc.location;
02917             }
02918         }
02919         else {
02920             err = REGERR_PARAM;
02921         }
02922 
02923         nr_Unlock( reg );
02924     }
02925 
02926     return err;
02927 
02928 }   /* NR_RegGetKeyRaw */
02929 
02930 
02931 
02932 
02933 /* ---------------------------------------------------------------------
02934  * NR_RegGetEntryInfo - Get some basic info about the entry data
02935  *
02936  * Parameters:
02937  *    hReg     - handle of open registry
02938  *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
02939  *    name     - name of entry
02940  *    info     - return: Entry info object
02941  * ---------------------------------------------------------------------
02942  */
02943 VR_INTERFACE(REGERR) NR_RegGetEntryInfo( HREG hReg, RKEY key, char *name, 
02944                             REGINFO *info )
02945 {
02946     REGERR      err;
02947     REGFILE*    reg;
02948     REGDESC     desc;
02949     
02950     XP_ASSERT( regStartCount > 0 );
02951 
02952     /* verify parameters */
02953     err = VERIFY_HREG( hReg );
02954     if ( err != REGERR_OK )
02955         return err;
02956 
02957     if ( name == NULL || *name == '\0' || info == NULL || key == 0 )
02958         return REGERR_PARAM;
02959 
02960     reg = ((REGHANDLE*)hReg)->pReg;
02961 
02962     err = nr_Lock( reg );
02963     if ( err == REGERR_OK )
02964     {
02965         /* read starting desc */
02966         err = nr_ReadDesc( reg, key, &desc);
02967         if ( err == REGERR_OK ) 
02968         {
02969             /* if the named entry exists */
02970             err = nr_FindAtLevel( reg, desc.value, name, &desc, NULL );
02971             if ( err == REGERR_OK ) 
02972             {
02973                 /* ... return the values */
02974                 if ( info->size == sizeof(REGINFO) )
02975                 {
02976                     info->entryType   = desc.type;
02977                     info->entryLength = desc.valuelen;
02978                 }
02979                 else
02980                 {
02981                     /* uninitialized (maybe invalid) REGINFO structure */
02982                     err = REGERR_PARAM;
02983                 }
02984             }
02985         }
02986 
02987         nr_Unlock( reg );
02988     }
02989 
02990     return err;
02991 
02992 }   /* NR_RegGetEntryInfo */
02993 
02994 
02995 
02996        
02997 /* ---------------------------------------------------------------------
02998  * NR_RegGetEntryString - Get the UTF string value associated with the
02999  *                       named entry of the specified key.
03000  *
03001  * Parameters:
03002  *    hReg     - handle of open registry
03003  *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
03004  *    name     - name of entry
03005  *    buffer   - destination for string
03006  *    bufsize  - size of buffer
03007  * ---------------------------------------------------------------------
03008  */
03009 VR_INTERFACE(REGERR) NR_RegGetEntryString( HREG  hReg, RKEY  key, char  *name,
03010                             char  *buffer, uint32 bufsize)
03011 {
03012     REGERR      err;
03013     REGFILE*    reg;
03014     REGDESC     desc;
03015 
03016     XP_ASSERT( regStartCount > 0 );
03017 
03018     /* verify parameters */
03019     err = VERIFY_HREG( hReg );
03020     if ( err != REGERR_OK )
03021         return err;
03022 
03023     if ( name==NULL || *name=='\0' || buffer==NULL || bufsize==0 || key==0 )
03024         return REGERR_PARAM;
03025 
03026     reg = ((REGHANDLE*)hReg)->pReg;
03027 
03028     err = nr_Lock( reg );
03029     if ( err == REGERR_OK )
03030     {
03031         /* read starting desc */
03032         err = nr_ReadDesc( reg, key, &desc);
03033         if ( err == REGERR_OK ) 
03034         {
03035             /* if the named entry exists */
03036             err = nr_FindAtLevel( reg, desc.value, name, &desc, NULL );
03037             if ( err == REGERR_OK ) 
03038             {
03039                 /* read the string */
03040                 if ( desc.type == REGTYPE_ENTRY_STRING_UTF ) 
03041                 {
03042                     err = nr_ReadData( reg, &desc, bufsize, buffer );
03043                     /* prevent run-away strings */
03044                     buffer[bufsize-1] = '\0';
03045                 }
03046                 else {
03047                     err = REGERR_BADTYPE;
03048                 }
03049             }
03050         }
03051 
03052         nr_Unlock( reg );
03053     }
03054 
03055     return err;
03056 
03057 }   /* NR_RegGetEntryString */
03058 
03059 
03060 
03061 
03062 /* ---------------------------------------------------------------------
03063  * NR_RegGetEntry - Get the value data associated with the
03064  *                  named entry of the specified key.
03065  *
03066  * Parameters:
03067  *    hReg     - handle of open registry
03068  *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
03069  *    name     - name of entry
03070  *    buffer   - destination for data
03071  *    size     - in:  size of buffer
03072  *               out: size of actual data (incl. \0 term. for strings)
03073  * ---------------------------------------------------------------------
03074  */
03075 VR_INTERFACE(REGERR) NR_RegGetEntry( HREG hReg, RKEY key, char *name,
03076     void *buffer, uint32 *size )
03077 {
03078     REGERR      err;
03079     REGFILE*    reg;
03080     REGDESC     desc;
03081     char        *tmpbuf = NULL;  /* malloc a tmp buffer to convert XP int arrays */
03082     uint32      nInt;
03083     uint32      *pISrc;
03084     uint32      *pIDest;
03085     XP_Bool     needFree = FALSE;
03086 
03087     XP_ASSERT( regStartCount > 0 );
03088 
03089     /* verify parameters */
03090     err = VERIFY_HREG( hReg );
03091     if ( err != REGERR_OK )
03092         return err;
03093 
03094     if ( name==NULL || *name=='\0' || buffer==NULL || size==NULL || key==0 )
03095         return REGERR_PARAM;
03096 
03097     reg = ((REGHANDLE*)hReg)->pReg;
03098 
03099     err = nr_Lock( reg );
03100     if ( err == REGERR_OK )
03101     {
03102         /* read starting desc */
03103         err = nr_ReadDesc( reg, key, &desc);
03104         if ( err == REGERR_OK )
03105         {
03106             /* if the named entry exists */
03107             err = nr_FindAtLevel( reg, desc.value, name, &desc, NULL );
03108             if ( err == REGERR_OK )
03109             {
03110                 if ( desc.valuelen > *size ) {
03111                     err = REGERR_BUFTOOSMALL;
03112                 }
03113                 else if ( desc.valuelen == 0 ) {
03114                     err = REGERR_FAIL;
03115                 }
03116                 else switch (desc.type)
03117                 {
03118                 /* platform independent array of 32-bit integers */
03119                 case REGTYPE_ENTRY_INT32_ARRAY:
03120                     tmpbuf = (char*)XP_ALLOC( desc.valuelen );
03121                     if ( tmpbuf != NULL ) 
03122                     {
03123                         needFree = TRUE;
03124                         err = nr_ReadData( reg, &desc, desc.valuelen, tmpbuf );
03125                         if ( REGERR_OK == err )
03126                         {
03127                             /* convert int array */
03128                             nInt = (desc.valuelen / INTSIZE);
03129                             pISrc = (uint32*)tmpbuf;
03130                             pIDest = (uint32*)buffer;
03131                             for(; nInt > 0; nInt--, pISrc++, pIDest++) {
03132                                 *pIDest = nr_ReadLong((char*)pISrc);
03133                             }
03134                         }
03135                     }
03136                     else
03137                         err = REGERR_MEMORY;
03138                     break;
03139 
03140                 case REGTYPE_ENTRY_STRING_UTF:
03141                     tmpbuf = (char*)buffer;
03142                     err = nr_ReadData( reg, &desc, *size, tmpbuf );
03143                     /* prevent run-away strings */
03144                     tmpbuf[(*size)-1] = '\0';
03145                     break;
03146 
03147                 case REGTYPE_ENTRY_FILE:
03148 
03149                     err = nr_ReadData( reg, &desc, *size, (char*)buffer );
03150 #if defined(XP_MAC) || defined(XP_MACOSX)
03151                     if (err == 0)
03152                     {
03153                         tmpbuf = nr_PathFromMacAlias(buffer, *size);
03154                         if (tmpbuf == NULL) 
03155                         {
03156                             buffer = NULL;
03157                             err = REGERR_NOFILE; /* must match nr_GetPathname() in VerReg.c */
03158                         }
03159                         else 
03160                         {
03161                             needFree = TRUE;
03162 
03163                             if (XP_STRLEN(tmpbuf) < *size) /* leave room for \0 */
03164                                 XP_STRCPY(buffer, tmpbuf);
03165                             else 
03166                                 err = REGERR_BUFTOOSMALL;
03167                         }
03168                     }
03169 #endif
03170                     break;
03171                 
03172                 case REGTYPE_ENTRY_BYTES:
03173                 default:              /* return raw data for unknown types */
03174                     err = nr_ReadData( reg, &desc, *size, (char*)buffer );
03175                     break;
03176                 }
03177 
03178                 /* return the actual data size */
03179                 *size = desc.valuelen;
03180             }
03181         }
03182 
03183         nr_Unlock( reg );
03184     }
03185 
03186     if (needFree)
03187         XP_FREE(tmpbuf);
03188 
03189     return err;
03190 
03191 }   /* NR_RegGetEntry */
03192 
03193 
03194 
03195 
03196 /* ---------------------------------------------------------------------
03197  * NR_RegSetEntryString - Store a UTF-8 string value associated with the
03198  *                       named entry of the specified key.  Used for
03199  *                       both creation and update.
03200  *
03201  * Parameters:
03202  *    hReg     - handle of open registry
03203  *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
03204  *    name     - name of entry
03205  *    buffer   - UTF-8 String to store
03206  * ---------------------------------------------------------------------
03207  */
03208 VR_INTERFACE(REGERR) NR_RegSetEntryString( HREG hReg, RKEY key, char *name,
03209                                      char *buffer )
03210 {
03211     REGERR      err;
03212     REGFILE*    reg;
03213     REGDESC     desc;
03214     REGDESC     parent;
03215 
03216     XP_ASSERT( regStartCount > 0 );
03217 
03218     /* verify parameters */
03219     err = VERIFY_HREG( hReg );
03220     if ( err != REGERR_OK )
03221         return err;
03222 
03223     if ( name == NULL || *name == '\0' || buffer == NULL || key == 0 )
03224         return REGERR_PARAM;
03225 
03226     reg = ((REGHANDLE*)hReg)->pReg;
03227 
03228     /* lock registry */
03229     err = nr_Lock( reg );
03230     if ( err != REGERR_OK )
03231         return err;
03232 
03233     /* read starting desc */
03234     err = nr_ReadDesc( reg, key, &parent);
03235     if ( err == REGERR_OK ) {
03236 
03237         /* if the named entry already exists */
03238         err = nr_FindAtLevel( reg, parent.value, name, &desc, NULL );
03239         if ( err == REGERR_OK ) {
03240             /* then update the existing one */
03241             err = nr_WriteString( reg, buffer, &desc );
03242             if ( err == REGERR_OK ) {
03243                 desc.type = REGTYPE_ENTRY_STRING_UTF;
03244                 err = nr_WriteDesc( reg, &desc );
03245             }
03246         }
03247         else if ( err == REGERR_NOFIND ) {
03248             /* otherwise create a new entry */
03249             err = nr_CreateEntryString( reg, &parent, name, buffer );
03250         }
03251         /* other errors fall through */
03252     }
03253 
03254     /* unlock registry */
03255     nr_Unlock( reg );
03256 
03257     return err;
03258 
03259 }   /* NR_RegSetEntryString */
03260 
03261 
03262 
03263 
03264 /* ---------------------------------------------------------------------
03265  * NR_RegSetEntry - Store value data associated with the named entry
03266  *                  of the specified key.  Used for both creation and update.
03267  *
03268  * Parameters:
03269  *    hReg     - handle of open registry
03270  *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
03271  *    name     - name of entry
03272  *    type     - type of data to be stored
03273  *    buffer   - data to store
03274  *    size     - length of data to store in bytes
03275  * ---------------------------------------------------------------------
03276  */
03277 VR_INTERFACE(REGERR) NR_RegSetEntry( HREG hReg, RKEY key, char *name, uint16 type,
03278     void *buffer, uint32 size )
03279 {
03280     REGERR      err;
03281     REGFILE*    reg;
03282     REGDESC     desc;
03283     REGDESC     parent;
03284     char        *data = NULL;
03285     uint32      nInt;
03286     uint32      *pIDest;
03287     uint32      *pISrc;
03288     XP_Bool     needFree = FALSE;
03289     int32       datalen = size;
03290 
03291     XP_ASSERT( regStartCount > 0 );
03292 
03293     /* verify parameters */
03294     err = VERIFY_HREG( hReg );
03295     if ( err != REGERR_OK )
03296         return err;
03297 
03298     if ( name==NULL || *name=='\0' || buffer==NULL || size==0 || key==0 )
03299         return REGERR_PARAM;
03300 
03301     reg = ((REGHANDLE*)hReg)->pReg;
03302 
03303     /* validate type and convert numerics to XP format */
03304     switch (type)
03305     {
03306         case REGTYPE_ENTRY_BYTES:
03307             data = (char*)buffer;
03308             break;
03309 
03310         case REGTYPE_ENTRY_FILE:
03311 
03312 #if defined(XP_MAC) || defined(XP_MACOSX)
03313             nr_MacAliasFromPath(buffer, (void **)&data, &datalen);
03314             if (data)
03315                 needFree = TRUE;
03316 #else
03317             data = (char*)buffer;   
03318 #endif
03319             break;
03320 
03321 
03322         case REGTYPE_ENTRY_STRING_UTF:
03323             data = (char*)buffer;
03324             /* string must be null terminated */
03325             if ( data[size-1] != '\0' )
03326                 return REGERR_PARAM;
03327             break;
03328 
03329 
03330         case REGTYPE_ENTRY_INT32_ARRAY:
03331             /* verify no partial integers */
03332             if ( (size % INTSIZE) != 0 )
03333                 return REGERR_PARAM;
03334 
03335             /* get a conversion buffer */
03336             data = (char*)XP_ALLOC(size);
03337             if ( data == NULL )
03338                 return REGERR_MEMORY;
03339             else
03340                 needFree = TRUE;
03341 
03342             /* convert array to XP format */
03343             nInt = ( size / INTSIZE );
03344             pIDest = (uint32*)data;
03345             pISrc  = (uint32*)buffer;
03346 
03347             for( ; nInt > 0; nInt--, pIDest++, pISrc++) {
03348                 nr_WriteLong( *pISrc, (char*)pIDest );
03349             }
03350             break;
03351 
03352 
03353         default:
03354             return REGERR_BADTYPE;
03355     }
03356 
03357     /* lock registry */
03358     err = nr_Lock( reg );
03359     if ( REGERR_OK == err )
03360     {
03361         /* read starting desc */
03362         err = nr_ReadDesc( reg, key, &parent);
03363         if ( err == REGERR_OK ) 
03364         {
03365             /* if the named entry already exists */
03366             err = nr_FindAtLevel( reg, parent.value, name, &desc, NULL );
03367             if ( err == REGERR_OK ) 
03368             {
03369                 /* then update the existing one */
03370                 err = nr_WriteData( reg, data, datalen, &desc );
03371                 if ( err == REGERR_OK ) 
03372                 {
03373                     desc.type = type;
03374                     err = nr_WriteDesc( reg, &desc );
03375                 }
03376             }
03377             else if ( err == REGERR_NOFIND ) 
03378             {
03379                 /* otherwise create a new entry */
03380                 err = nr_CreateEntry( reg, &parent, name, type, data, datalen );
03381             }
03382             else {
03383                 /* other errors fall through */
03384             }
03385         }
03386 
03387         /* unlock registry */
03388         nr_Unlock( reg );
03389     }
03390 
03391     if (needFree)
03392         XP_FREE(data);
03393 
03394     return err;
03395 
03396 }   /* NR_RegSetEntry */
03397 
03398 
03399 
03400 
03401 /* ---------------------------------------------------------------------
03402  * NR_RegDeleteEntry - Delete the named entry
03403  *
03404  * Parameters:
03405  *    hReg     - handle of open registry
03406  *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
03407  *    name     - name of entry
03408  * ---------------------------------------------------------------------
03409  */
03410 VR_INTERFACE(REGERR) NR_RegDeleteEntry( HREG hReg, RKEY key, char *name )
03411 {
03412     REGERR      err;
03413     REGFILE*    reg;
03414     REGDESC     desc;
03415     REGDESC     parent;
03416     REGOFF      offPrev;
03417 
03418     XP_ASSERT( regStartCount > 0 );
03419 
03420     /* verify parameters */
03421     err = VERIFY_HREG( hReg );
03422     if ( err != REGERR_OK )
03423         return err;
03424 
03425     if ( name == NULL || *name == '\0' || key == 0)
03426         return REGERR_PARAM;
03427 
03428     reg = ((REGHANDLE*)hReg)->pReg;
03429 
03430     /* lock registry */
03431     err = nr_Lock( reg );
03432     if ( err != REGERR_OK )
03433         return err;
03434 
03435     /* read starting desc */
03436     err = nr_ReadDesc( reg, key, &parent);
03437     if ( err == REGERR_OK ) {
03438 
03439         /* look up the named entry */
03440         err = nr_FindAtLevel( reg, parent.value, name, &desc, &offPrev );
03441         if ( err == REGERR_OK ) {
03442 
03443             XP_ASSERT( TYPE_IS_ENTRY( desc.type ) );
03444 
03445             /* if entry is the head of a chain */
03446             if ( offPrev == 0 ) {
03447                 /* hook parent key to next entry */
03448                 XP_ASSERT( parent.value == desc.location );
03449                 parent.value = desc.left;
03450             }
03451             else {
03452                 /* otherwise hook previous entry to next */
03453                 err = nr_ReadDesc( reg, offPrev, &parent );
03454                 parent.left = desc.left;
03455             }
03456             /* write out changed desc for previous node */
03457             if ( err == REGERR_OK ) {
03458                 err = nr_WriteDesc( reg, &parent );
03459                 /* zap the deleted desc because an enum state may contain a
03460                  * reference to a specific entry node
03461                  */
03462                 if ( err == REGERR_OK ) {
03463                     desc.type |= REGTYPE_DELETED;
03464                     err = nr_WriteDesc( reg, &desc );
03465                 }
03466             }
03467         }
03468     }
03469 
03470     /* unlock registry */
03471     nr_Unlock( reg );
03472 
03473     return err;
03474 
03475 }   /* NR_RegDeleteEntry */
03476 
03477 
03478 
03479 
03480 /* ---------------------------------------------------------------------
03481  * NR_RegEnumSubkeys - Enumerate the subkey names for the specified key
03482  *
03483  * Returns REGERR_NOMORE at end of enumeration.
03484  *
03485  * Parameters:
03486  *    hReg     - handle of open registry
03487  *    key      - RKEY of key to enumerate--obtain with NR_RegGetKey()
03488  *    eState   - enumerations state, must contain NULL to start
03489  *    buffer   - location to store subkey names.  Once an enumeration
03490  *               is started user must not modify contents since values
03491  *               are built using the previous contents.
03492  *    bufsize  - size of buffer for names
03493  *    style    - 0 returns direct child keys only, REGENUM_DESCEND
03494  *               returns entire sub-tree
03495  * ---------------------------------------------------------------------
03496  */
03497 VR_INTERFACE(REGERR) NR_RegEnumSubkeys( HREG hReg, RKEY key, REGENUM *state,
03498                                     char *buffer, uint32 bufsize, uint32 style)
03499 {
03500     REGERR      err;
03501     REGFILE*    reg;
03502     REGDESC     desc;
03503 
03504     XP_ASSERT( regStartCount > 0 );
03505 
03506     /* verify parameters */
03507     err = VERIFY_HREG( hReg );
03508     if ( err != REGERR_OK )
03509         return err;
03510 
03511     if ( key == 0 || state == NULL || buffer == NULL )
03512         return REGERR_PARAM;
03513 
03514     reg = ((REGHANDLE*)hReg)->pReg;
03515 
03516     /* lock registry */
03517     err = nr_Lock( reg );
03518     if ( err != REGERR_OK )
03519         return err;
03520 
03521     desc.down     = 0; /* initialize to quiet warnings */
03522     desc.location = 0;
03523 
03524     /* verify starting key */
03525     key = nr_TranslateKey( reg, key );
03526     if ( key == 0 )
03527         err = REGERR_PARAM;
03528     else if ( *state == 0 )
03529         err = nr_ReadDesc( reg, key, &desc);
03530     else
03531         err = REGERR_OK;
03532 
03533     if ( err == REGERR_OK )
03534     {
03535         /* if in initial state and no children return now */
03536         if ( *state == 0 && desc.down == 0 ) 
03537         {
03538             err = REGERR_NOMORE;
03539         }
03540         else switch ( style )
03541         {
03542           case REGENUM_CHILDREN:
03543             *buffer = '\0';
03544             if ( *state == 0 ) 
03545             {
03546                 /* initial state: get first child (.down) */
03547                 err = nr_ReplaceName( reg, desc.down, buffer, bufsize, &desc );
03548             }
03549             else 
03550             {
03551                 /* get sibling (.left) of current key */
03552                 err = nr_ReadDesc( reg, *state, &desc );
03553                 if ( err == REGERR_OK || REGERR_DELETED == err )
03554                 {
03555                     /* it's OK for the current (state) node to be deleted */
03556                     if ( desc.left != 0 ) 
03557                     {
03558                         err = nr_ReplaceName( reg, desc.left, 
03559                                     buffer, bufsize, &desc );
03560                     }
03561                     else
03562                         err = REGERR_NOMORE;
03563                 }
03564             }
03565             break;
03566 
03567 
03568           case REGENUM_DESCEND:
03569             if ( *state == 0 ) 
03570             {
03571                 /* initial state */
03572                 *buffer = '\0';
03573                 err = nr_ReplaceName( reg, desc.down, buffer, bufsize, &desc );
03574             }
03575             else 
03576             {
03577                 /* get last position */
03578                 err = nr_ReadDesc( reg, *state, &desc );
03579                 if ( REGERR_OK != err && REGERR_DELETED != err ) 
03580                 {
03581                     /* it is OK for the state node to be deleted
03582                      * (the *next* node MUST be "live", though).
03583                      * bail out on any other error */
03584                     break;
03585                 }
03586 
03587                 if ( desc.down != 0 ) {
03588                     /* append name of first child key */
03589                     err = nr_CatName( reg, desc.down, buffer, bufsize, &desc );
03590                 }
03591                 else if ( desc.left != 0 ) {
03592                     /* replace last segment with next sibling */
03593                     err = nr_ReplaceName( reg, desc.left, 
03594                                 buffer, bufsize, &desc );
03595                 }
03596                 else {
03597                   /* done with level, pop up as many times as necessary */
03598                     while ( err == REGERR_OK ) 
03599                     {
03600                         if ( desc.parent != key && desc.parent != 0 ) 
03601                         {
03602                             err = nr_RemoveName( buffer );
03603                             if ( err == REGERR_OK ) 
03604                             {
03605                                 err = nr_ReadDesc( reg, desc.parent, &desc );
03606                                 if ( err == REGERR_OK && desc.left != 0 ) 
03607                                 {
03608                                     err = nr_ReplaceName( reg, desc.left, 
03609                                                 buffer, bufsize, &desc );
03610                                     break;  /* found a node */
03611                                 }
03612                             }
03613                         }
03614                         else
03615                             err = REGERR_NOMORE;
03616                     }
03617                 }
03618             }
03619             break;
03620 
03621 
03622           case REGENUM_DEPTH_FIRST:
03623             if ( *state == 0 ) 
03624             {
03625                 /* initial state */
03626 
03627                 *buffer = '\0';
03628                 err = nr_ReplaceName( reg, desc.down, buffer, bufsize, &desc );
03629                 while ( REGERR_OK == err && desc.down != 0 )
03630                 {
03631                     /* start as far down the tree as possible */
03632                     err = nr_CatName( reg, desc.down, buffer, bufsize, &desc );
03633                 }
03634             }
03635             else 
03636             {
03637                 /* get last position */
03638                 err = nr_ReadDesc( reg, *state, &desc );
03639                 if ( REGERR_OK != err && REGERR_DELETED != err ) 
03640                 {
03641                     /* it is OK for the state node to be deleted
03642                      * (the *next* node MUST be "live", though).
03643                      * bail out on any other error */
03644                     break;
03645                 }
03646 
03647                 if ( desc.left != 0 )
03648                 {
03649                     /* get sibling, then descend as far as possible */
03650                     err = nr_ReplaceName(reg, desc.left, buffer,bufsize,&desc);
03651 
03652                     while ( REGERR_OK == err && desc.down != 0 ) 
03653                     {
03654                         err = nr_CatName(reg, desc.down, buffer,bufsize,&desc);
03655                     }
03656                 }
03657                 else 
03658                 {
03659                     /* pop up to parent */
03660                     if ( desc.parent != key && desc.parent != 0 )
03661                     {
03662                         err = nr_RemoveName( buffer );
03663                         if ( REGERR_OK == err )
03664                         {
03665                             /* validate parent key */
03666                             err = nr_ReadDesc( reg, desc.parent, &desc );
03667                         }
03668                     }
03669                     else 
03670                         err = REGERR_NOMORE;
03671                 }
03672             }
03673             break;
03674 
03675 
03676           default:
03677             err = REGERR_PARAM;
03678             break;
03679         }
03680     }
03681 
03682     /* set enum state to current key */
03683     if ( err == REGERR_OK ) {
03684         *state = desc.location;
03685     }
03686 
03687     /* unlock registry */
03688     nr_Unlock( reg );
03689 
03690     return err;
03691 
03692 }   /* NR_RegEnumSubkeys */
03693 
03694 
03695 
03696 
03697 /* ---------------------------------------------------------------------
03698  * NR_RegEnumEntries - Enumerate the entry names for the specified key
03699  *
03700  * Returns REGERR_NOMORE at end of enumeration.
03701  *
03702  * Parameters:
03703  *    hReg     - handle of open registry
03704  *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
03705  *    eState   - enumerations state, must contain NULL to start
03706  *    buffer   - location to store entry names
03707  *    bufsize  - size of buffer for names
03708  *    info     - optional REGINFO for the entry. If not NULL must be 
03709  *               initialized as in NR_RegGetEntryInfo()
03710  * ---------------------------------------------------------------------
03711  */
03712 VR_INTERFACE(REGERR) NR_RegEnumEntries( HREG hReg, RKEY key, REGENUM *state,
03713                             char *buffer, uint32 bufsize, REGINFO *info )
03714 {
03715     REGERR      err;
03716     REGFILE*    reg;
03717     REGDESC     desc;
03718 
03719     XP_ASSERT( regStartCount > 0 );
03720 
03721     /* verify parameters */
03722     err = VERIFY_HREG( hReg );
03723     if ( err != REGERR_OK )
03724         return err;
03725 
03726     if ( key == 0 || state == NULL || buffer == NULL )
03727         return REGERR_PARAM;
03728 
03729     reg = ((REGHANDLE*)hReg)->pReg;
03730 
03731     /* lock registry */
03732     err = nr_Lock( reg );
03733     if ( err != REGERR_OK )
03734         return err;
03735     
03736     /* verify starting key */
03737     err = nr_ReadDesc( reg, key, &desc);
03738     if ( err == REGERR_OK )
03739     {
03740         if ( *state == 0 ) 
03741         {
03742             /* initial state--get first entry */
03743             if ( desc.value != 0 ) 
03744             {
03745                 *buffer = '\0';
03746                 err =  nr_ReplaceName( reg, desc.value, buffer, bufsize, &desc );
03747             }
03748             else  
03749             { 
03750                 /* there *are* no entries */
03751                 err = REGERR_NOMORE;
03752             }
03753         }
03754         else 
03755         {
03756             /* 'state' stores previous entry */
03757             err = nr_ReadDesc( reg, *state, &desc );
03758             if ( err == REGERR_OK  || err == REGERR_DELETED ) 
03759             {
03760                 /* get next entry in chain */
03761                 if ( desc.left != 0 ) 
03762                 {
03763                     *buffer = '\0';
03764                     err =  nr_ReplaceName( reg, desc.left, buffer, bufsize, &desc );
03765                 }
03766                 else 
03767                 {
03768                     /* at end of chain */
03769                     err = REGERR_NOMORE;
03770                 }
03771             }
03772         }
03773 
03774         /* if we found an entry */
03775         if ( err == REGERR_OK ) 
03776         {
03777             /* set enum state to current entry */
03778             *state = desc.location;
03779 
03780             /* return REGINFO if requested */
03781             if ( info != NULL && info->size >= sizeof(REGINFO) ) 
03782             {
03783                 info->entryType   = desc.type;
03784                 info->entryLength = desc.valuelen;
03785             }
03786         }
03787     }
03788 
03789     /* unlock registry */
03790     nr_Unlock( reg );
03791 
03792     return err;
03793 
03794 }   /* NR_RegEnumEntries */
03795 
03796 
03797 
03798 
03799 
03800 /* --------------------------------------------------------------------
03801  * Registry Packing
03802  * --------------------------------------------------------------------
03803  */
03804 #ifndef STANDALONE_REGISTRY
03805 #include "VerReg.h"
03806 
03807 #ifdef RESURRECT_LATER
03808 static REGERR nr_createTempRegName( char *filename, uint32 filesize );
03809 static REGERR nr_addNodesToNewReg( HREG hReg, RKEY rootkey, HREG hRegNew, void *userData, nr_RegPackCallbackFunc fn );
03810 /* -------------------------------------------------------------------- */
03811 static REGERR nr_createTempRegName( char *filename, uint32 filesize )
03812 {
03813     struct stat statbuf;
03814     XP_Bool nameFound = FALSE;
03815     char tmpname[MAX_PATH+1];
03816     uint32 len;
03817     int err;
03818 
03819     XP_STRCPY( tmpname, filename );
03820     len = XP_STRLEN(tmpname);
03821     if (len < filesize) {
03822         tmpname[len-1] = '~';
03823         tmpname[len] = '\0';
03824         remove(tmpname);
03825         if ( stat(tmpname, &statbuf) != 0 )
03826             nameFound = TRUE;
03827     }
03828     len++;
03829     while (!nameFound && len < filesize ) {
03830         tmpname[len-1] = '~';
03831         tmpname[len] = '\0';
03832         remove(tmpname);
03833         if ( stat(tmpname, &statbuf) != 0 )
03834             nameFound = TRUE;
03835         else
03836             len++;
03837     }  
03838     if (nameFound) {
03839         XP_STRCPY(filename, tmpname);
03840         err = REGERR_OK;
03841     } else {
03842         err = REGERR_FAIL;
03843     }
03844    return err;
03845 }
03846 
03847 static REGERR nr_addNodesToNewReg( HREG hReg, RKEY rootkey, HREG hRegNew, void *userData, nr_RegPackCallbackFunc fn )
03848 {
03849     char keyname[MAXREGPATHLEN+1] = {0};
03850     char entryname[MAXREGPATHLEN+1] = {0};
03851     void *buffer;
03852     uint32 bufsize = 2024;
03853     uint32 datalen;
03854     REGENUM state = 0;
03855     REGENUM entrystate = 0;
03856     REGINFO info;
03857     int err = REGERR_OK;
03858     int status = REGERR_OK;
03859     RKEY key;
03860     RKEY newKey;
03861     REGFILE* reg;
03862     REGFILE* regNew;
03863     static int32 cnt = 0;
03864     static int32 prevCnt = 0;
03865 
03866     reg = ((REGHANDLE*)hReg)->pReg;
03867     regNew = ((REGHANDLE*)hRegNew)->pReg;
03868 
03869     buffer = XP_ALLOC(bufsize);
03870     if ( buffer == NULL ) {
03871         err = REGERR_MEMORY;
03872         return err;
03873     }
03874 
03875     while (err == REGERR_OK)
03876     {
03877         err = NR_RegEnumSubkeys( hReg, rootkey, &state, keyname, sizeof(keyname), REGENUM_DESCEND );
03878         if ( err != REGERR_OK )
03879             break;
03880         err = NR_RegAddKey( hRegNew, rootkey, keyname, &newKey );
03881         if ( err != REGERR_OK )
03882             break;
03883         cnt++;
03884         if (cnt >= prevCnt + 15) 
03885         {
03886             fn(userData, regNew->hdr.avail, reg->hdr.avail);
03887             prevCnt = cnt;
03888         }
03889         err = NR_RegGetKey( hReg, rootkey, keyname, &key );
03890         if ( err != REGERR_OK )
03891             break;
03892         entrystate = 0;
03893         status = REGERR_OK;
03894         while (status == REGERR_OK) {
03895             info.size = sizeof(REGINFO);
03896             status = NR_RegEnumEntries( hReg, key, &entrystate, entryname, 
03897                                         sizeof(entryname), &info );
03898             if ( status == REGERR_OK ) {
03899                 XP_ASSERT( bufsize >= info.entryLength );
03900                 datalen = bufsize;
03901                 status = NR_RegGetEntry( hReg, key, entryname, buffer, &datalen );
03902                 XP_ASSERT( info.entryLength == datalen );
03903                 if ( status == REGERR_OK ) {
03904                     /* copy entry */
03905                     status = NR_RegSetEntry( hRegNew, newKey, entryname, 
03906                                 info.entryType, buffer, info.entryLength );
03907                 }
03908             } 
03909         }
03910         if ( status != REGERR_NOMORE ) {
03911             /* pass real error to outer loop */
03912             err = status;
03913         }
03914     }
03915 
03916     if ( err == REGERR_NOMORE )
03917         err = REGERR_OK;
03918 
03919     XP_FREEIF(buffer);
03920     return err;
03921 }
03922 #endif /* RESURRECT_LATER */
03923 
03924 
03925 
03926 /* ---------------------------------------------------------------------
03927  * NR_RegPack    - Pack an open registry.  
03928  *                Registry is locked the entire time.
03929  *
03930  * Parameters:
03931  *    hReg     - handle of open registry to pack
03932  * ---------------------------------------------------------------------
03933  */
03934 VR_INTERFACE(REGERR) NR_RegPack( HREG hReg, void *userData, nr_RegPackCallbackFunc fn)
03935 {
03936     return REGERR_FAIL; /* XXX resurrect after mozilla beta 1 */
03937 #if RESURRECT_LATER
03938     XP_File  fh;
03939     REGFILE* reg;
03940     HREG hRegTemp;
03941     char tempfilename[MAX_PATH+1] = {0};
03942     char oldfilename[MAX_PATH+1] = {0};
03943 
03944     XP_Bool bCloseTempFile = FALSE;
03945 
03946     int err = REGERR_OK;
03947     RKEY key;
03948 
03949     XP_ASSERT( regStartCount > 0 );
03950     if ( regStartCount <= 0 )
03951         return REGERR_FAIL;
03952 
03953     reg = ((REGHANDLE*)hReg)->pReg;
03954 
03955     /* lock registry */
03956     err = nr_Lock( reg );
03957     if ( err != REGERR_OK )
03958         return err; 
03959 
03960     PR_Lock(reglist_lock); 
03961     XP_STRCPY(tempfilename, reg->filename);
03962     err = nr_createTempRegName(tempfilename, sizeof(tempfilename));
03963     if ( err != REGERR_OK )
03964         goto safe_exit; 
03965      
03966     /* force file creation */
03967     fh = vr_fileOpen(tempfilename, XP_FILE_WRITE_BIN);
03968     if ( !VALID_FILEHANDLE(fh) ) {
03969         err = REGERR_FAIL;
03970         goto safe_exit;
03971     }
03972     XP_FileClose(fh);
03973 
03974     err = NR_RegOpen(tempfilename, &hRegTemp);
03975     if ( err != REGERR_OK )
03976         goto safe_exit;
03977     bCloseTempFile = TRUE;
03978 
03979     /* must open temp file first or we get the same name twice */
03980     XP_STRCPY(oldfilename, reg->filename);
03981     err = nr_createTempRegName(oldfilename, sizeof(oldfilename));
03982     if ( err != REGERR_OK )
03983         goto safe_exit; 
03984      
03985     key = ROOTKEY_PRIVATE;
03986     err = nr_addNodesToNewReg( hReg, key, hRegTemp, userData, fn);
03987     if ( err != REGERR_OK  )
03988         goto safe_exit;
03989     key = ROOTKEY_VERSIONS;
03990     err = nr_addNodesToNewReg( hReg, key, hRegTemp, userData, fn);
03991     if ( err != REGERR_OK  )
03992         goto safe_exit;
03993     key = ROOTKEY_COMMON;
03994     err = nr_addNodesToNewReg( hReg, key, hRegTemp, userData, fn);
03995     if ( err != REGERR_OK  )
03996         goto safe_exit;
03997     key = ROOTKEY_USERS;
03998     err = nr_addNodesToNewReg( hReg, key, hRegTemp, userData, fn);
03999     if ( err != REGERR_OK  )
04000         goto safe_exit;
04001 
04002     err = NR_RegClose(hRegTemp);
04003     bCloseTempFile = FALSE;
04004   
04005     /* close current reg file so we can rename it */
04006     XP_FileClose(reg->fh);
04007    
04008     /* rename current reg file out of the way */
04009     err = nr_RenameFile(reg->filename, oldfilename);
04010     if ( err == -1 ) {
04011         /* rename failed, get rid of the new registry and reopen the old one*/
04012         remove(tempfilename);
04013         reg->fh = vr_fileOpen(reg->filename, XP_FILE_UPDATE_BIN);
04014         goto safe_exit;
04015     }
04016 
04017     /* rename packed registry to the correct name */
04018     err = nr_RenameFile(tempfilename, reg->filename);
04019     if ( err == -1 ) {
04020         /* failure, recover original registry */
04021         err = nr_RenameFile(oldfilename, reg->filename);
04022         remove(tempfilename);
04023         reg->fh = vr_fileOpen(reg->filename, XP_FILE_UPDATE_BIN);
04024         goto safe_exit;
04025     
04026     } else {
04027         remove(oldfilename); 
04028     }
04029     reg->fh = vr_fileOpen(reg->filename, XP_FILE_UPDATE_BIN);
04030 
04031 safe_exit:
04032     if ( bCloseTempFile ) {
04033         NR_RegClose(hRegTemp);
04034     }
04035     PR_Unlock( reglist_lock );
04036     nr_Unlock(reg);
04037     return err;
04038 #endif /* RESURRECT_LATER */
04039 }
04040 
04041 
04042 #ifdef XP_MAC
04043 #pragma export reset
04044 #endif
04045 
04046 #endif /* STANDALONE_REGISTRY */
04047 
04048 
04049 
04050 
04051 
04052 
04053 /* ---------------------------------------------------------------------
04054  * ---------------------------------------------------------------------
04055  * Registry initialization and shut-down
04056  * ---------------------------------------------------------------------
04057  * ---------------------------------------------------------------------
04058  */
04059 
04060 #include "VerReg.h"
04061 
04062 #ifndef STANDALONE_REGISTRY
04063 extern PRLock *vr_lock;
04064 #endif 
04065 
04066 
04067 
04068 #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(STANDALONE_REGISTRY)
04069 extern XP_Bool bGlobalRegistry;
04070 #endif
04071 
04072 #ifdef XP_MAC
04073 #pragma export on
04074 #endif
04075 
04076 VR_INTERFACE(REGERR) NR_StartupRegistry(void)
04077 {
04078     REGERR status = REGERR_OK;
04079 
04080 #ifndef STANDALONE_REGISTRY
04081     if ( reglist_lock == NULL ) {
04082         reglist_lock = PR_NewLock();
04083     }
04084 
04085     if ( reglist_lock != NULL ) {
04086         PR_Lock( reglist_lock );
04087     }
04088     else {
04089         XP_ASSERT( reglist_lock );
04090         status = REGERR_FAIL;
04091     }
04092 #endif
04093 
04094     if ( status == REGERR_OK )
04095     {
04096         ++regStartCount;
04097         if ( regStartCount == 1 )
04098         {
04099             /* first time only initialization */
04100             vr_findGlobalRegName();
04101 
04102 #ifndef STANDALONE_REGISTRY
04103             /* initialization for version registry */
04104             vr_lock = PR_NewLock();
04105             XP_ASSERT( vr_lock != NULL );
04106 #if defined(XP_UNIX) && !defined(XP_MACOSX)
04107             bGlobalRegistry = ( getenv(UNIX_GLOBAL_FLAG) != NULL );
04108 #endif
04109 #endif 
04110         } /* if ( regStartCount == 1 ) */
04111 
04112         PR_Unlock( reglist_lock );
04113     }
04114 
04115     return status;
04116 }   /* NR_StartupRegistry */
04117 
04118 VR_INTERFACE(void) NR_ShutdownRegistry(void)
04119 {
04120     REGFILE* pReg;
04121     XP_Bool  bDestroyLocks = FALSE;
04122 
04123     /* people should track whether NR_StartupRegistry() was successful
04124      * and not call this if it fails... but they won't so we'll try to
04125      * handle that case gracefully.
04126      */
04127 #ifndef STANDALONE_REGISTRY
04128     if ( reglist_lock == NULL ) 
04129         return;  /* was not started successfully */
04130 #else
04131     if ( regStartCount == 0 )
04132         return;  /* was not started successfully */
04133 #endif
04134 
04135     PR_Lock( reglist_lock );
04136 
04137     --regStartCount;
04138     if ( regStartCount == 0 )
04139     {
04140         /* shutdown for real. */
04141 
04142         /* close any forgotten open registries */
04143         while ( RegList != NULL ) 
04144         {
04145             pReg = RegList;
04146             if ( pReg->hdrDirty ) {
04147                 nr_WriteHdr( pReg );
04148             }
04149             nr_CloseFile( &(pReg->fh) );
04150             nr_DeleteNode( pReg );
04151         }
04152     
04153         XP_FREEIF(user_name);
04154         XP_FREEIF(globalRegName);
04155         XP_FREEIF(verRegName);
04156 
04157         bDestroyLocks = TRUE;
04158     }
04159 
04160     PR_Unlock( reglist_lock );
04161 
04162 #ifndef STANDALONE_REGISTRY    
04163     if ( bDestroyLocks ) 
04164     {
04165         PR_DestroyLock( reglist_lock );
04166         reglist_lock = NULL;
04167 
04168         PR_DestroyLock(vr_lock);
04169         vr_lock = NULL;
04170     }
04171 #endif
04172 
04173 }   /* NR_ShutdownRegistry */
04174 
04175 #ifdef XP_MAC
04176 #pragma export reset
04177 #endif
04178 
04179 /* EOF: reg.c */