home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / modules / libreg / src / reg.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  81.0 KB  |  3,037 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18. /* ====================================================================
  19.  * reg.c
  20.  * XP Registry functions
  21.  * ====================================================================
  22.  */
  23.  
  24. /* Preprocessor Defines
  25.  *  STANDALONE_REGISTRY - define if not linking with Navigator
  26.  *  NOCACHE_HDR            - define if multi-threaded access to registry
  27.  *  SELF_REPAIR         - define to skip header update on open
  28.  *  VERIFY_READ         - define TRUE to double-check short reads
  29.  */
  30. #define NOCACHE_HDR     1
  31. #define SELF_REPAIR     1
  32. #ifdef DEBUG
  33. #define VERIFY_READ     1
  34. #endif
  35.  
  36. #ifndef STANDALONE_REGISTRY
  37.   #include "xp_mcom.h"
  38.   #include "xp_error.h"
  39.   #include "prmon.h"
  40.   #include "prefapi.h"
  41. #else
  42.  
  43. #include <stdlib.h>
  44. #include <stdio.h>
  45. #include <string.h>
  46. #include <assert.h>
  47. #include <errno.h>
  48.  
  49. #ifdef SUNOS4
  50.   #include <unistd.h>  /* for SEEK_SET */
  51. #endif /* SUNOS4 */
  52. #endif /* STANDALONE_REGISTRY */
  53.  
  54. #include "reg.h"
  55. #include "NSReg.h"
  56.  
  57.  
  58. /* NOTE! It is EXREMELY important that node names be in UTF-8; otherwise
  59.  * backwards path search for delim char will fail for multi-byte/Unicode names.
  60.  * SmartUpdate will also break, as it relies on Java's UTF-8 -> Unicode
  61.  * conversion, which will fail if the text is not actually valid UTF-8
  62.  */
  63.  
  64. /* ====================================================================
  65.  * Overview
  66.  * --------------------------------------------------------------------
  67.  *
  68.  * Layers:
  69.  *        Interface
  70.  *            Path Parsing
  71.  *            Key/Entry Management
  72.  *                Block I/O
  73.  *                    Virtual I/O
  74.  *
  75.  * The functions in this file search and add to a binary Registry file
  76.  * quite efficiently.  So efficiently, squeezing out space left by
  77.  * deleted and updated objects requires a separate "pack" operation.
  78.  *
  79.  * Terms:
  80.  * As used here, a 'key' is a node in the tree. The root of the tree
  81.  * exists in an otherwise empty Registry as is itself a key.  Every key
  82.  * has 0 or more sub-keys. Every key also has 0 or more 'entry's. Both
  83.  * entries and keys have names. Entries also have values associated.
  84.  * Names and values are simply strings of characters. These strings
  85.  * may be quoted so that they can include path delimiter and equals
  86.  * sign characters which are otherwise reserved.
  87.  * ====================================================================
  88.  */
  89.  
  90. /* --------------------------------------------------------------------
  91.  * Module Global Data
  92.  * --------------------------------------------------------------------
  93.  */
  94.  
  95. static REGFILE *RegList = NULL;
  96. static XP_Bool bRegStarted = FALSE;
  97. #if !defined(STANDALONE_REGISTRY)
  98. static PRMonitor *reglist_monitor;
  99. #endif
  100.  
  101.  
  102.  
  103. /* --------------------------------------------------------------------
  104.  * Registry List management
  105.  * --------------------------------------------------------------------
  106.  */
  107. static void nr_AddNode(REGFILE* pReg);
  108. static void nr_DeleteNode(REGFILE *pReg);
  109. static REGFILE* vr_findRegFile(char *filename);
  110. /* -------------------------------------------------------------------- */
  111.  
  112. static void nr_AddNode(REGFILE* pReg)
  113. {
  114.     /* add node to head of list */
  115.     pReg->next = RegList;
  116.     pReg->prev = NULL;
  117.  
  118.     RegList = pReg;
  119.  
  120.     if ( pReg->next != NULL ) {
  121.         pReg->next->prev = pReg;
  122.     }
  123. }
  124.  
  125. static void nr_DeleteNode(REGFILE* pReg)
  126. {
  127.     /* if at head of list... */
  128.     if ( pReg->prev == NULL ) {
  129.         RegList = pReg->next;
  130.     }
  131.     else {
  132.         pReg->prev->next = pReg->next;
  133.     }
  134.  
  135.     if ( pReg->next != NULL ) {
  136.         pReg->next->prev = pReg->prev;
  137.     }
  138.  
  139.     /* free memory */
  140. #ifndef STANDALONE_REGISTRY
  141.     if ( pReg->monitor != NULL )
  142.         PR_DestroyMonitor( pReg->monitor );
  143. #endif
  144.     XP_FREEIF( pReg->filename );
  145.     XP_FREE( pReg );
  146. }
  147.  
  148. static REGFILE* vr_findRegFile(char *filename)
  149. {
  150.     REGFILE *pReg;
  151.  
  152.     pReg = RegList;
  153.     while( pReg != NULL ) {
  154. #ifdef XP_UNIX
  155.         if ( 0 == XP_STRCMP( filename, pReg->filename ) ) {
  156. #else
  157.         if ( 0 == XP_STRCASECMP( filename, pReg->filename ) ) {
  158. #endif
  159.             break;
  160.         }
  161.         pReg = pReg->next;
  162.     }
  163.  
  164.     return pReg;
  165. }
  166.  
  167.  
  168. /* --------------------------------------------------------------------
  169.  * Virtual I/O
  170.  *    Platform-specifics go in this section
  171.  * --------------------------------------------------------------------
  172.  */
  173. static REGERR nr_OpenFile(char *path, FILEHANDLE *fh);
  174. static REGERR nr_CloseFile(FILEHANDLE *fh);    /* Note: fh is a pointer */
  175. static REGERR nr_ReadFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer);
  176. static REGERR nr_WriteFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer);
  177. static REGERR nr_LockRange(FILEHANDLE fh, REGOFF offset, int32 len);
  178. static REGERR nr_UnlockRange(FILEHANDLE fh, REGOFF offset, int32 len);
  179. static int32  nr_GetFileLength(FILEHANDLE fh);
  180. /* -------------------------------------------------------------------- */
  181.  
  182.  
  183.  
  184. static REGERR nr_OpenFile(char *path, FILEHANDLE *fh)
  185. {
  186.     XP_ASSERT( path != NULL );
  187.     XP_ASSERT( fh != NULL );
  188.  
  189.     /* Open the file for exclusive random read/write */
  190.     (*fh) = XP_FileOpen(path, xpRegistry, XP_FILE_UPDATE_BIN);
  191.     if ( !VALID_FILEHANDLE(*fh) )
  192.     {
  193.         switch (errno)
  194.         {
  195.         case ENOENT:    /* file not found */
  196.             return REGERR_NOFILE;
  197.  
  198.         case EACCES:    /* file in use */
  199.             /* DVNOTE: should we try read only? */
  200.             (*fh) = XP_FileOpen(path, xpRegistry, XP_FILE_READ_BIN);
  201.             if ( VALID_FILEHANDLE(*fh) )
  202.                 return REGERR_READONLY;
  203.             else
  204.                 return REGERR_FAIL;
  205.                 
  206.         case EMFILE:    /* too many files open */
  207.         default:
  208.             return REGERR_FAIL;
  209.         }
  210.     }
  211.  
  212.     return REGERR_OK;
  213.  
  214. }    /* OpenFile */
  215.  
  216.  
  217.  
  218. static REGERR nr_CloseFile(FILEHANDLE *fh)
  219. {
  220.     /* NOTE: 'fh' is a pointer, unlike other Close functions
  221.      *         This is necessary so that nr_CloseFile can set it to NULL
  222.      */
  223.  
  224.     XP_ASSERT( fh != NULL );
  225.     if ( VALID_FILEHANDLE(*fh) )
  226.         XP_FileClose(*fh);
  227.     (*fh) = NULL;
  228.     return REGERR_OK;
  229.  
  230. }    /* CloseFile */
  231.  
  232.  
  233.  
  234. static REGERR nr_ReadFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer)
  235. {
  236. #if VERIFY_READ
  237.     #define        FILLCHAR  0xCC
  238.     unsigned char* p;
  239.     unsigned char* dbgend = (unsigned char*)buffer+len;
  240. #endif
  241.  
  242.     int32 readlen;
  243.     REGERR err = REGERR_OK;
  244.  
  245.     XP_ASSERT(len > 0);
  246.     XP_ASSERT(buffer != NULL);
  247.     XP_ASSERT(fh != NULL);
  248.  
  249. #if VERIFY_READ
  250.     memset(buffer, FILLCHAR, len);
  251. #endif
  252.  
  253.     if (XP_FileSeek(fh, offset, SEEK_SET) != 0 ) {
  254.         err = REGERR_FAIL;
  255.     }
  256.     else {
  257.         readlen = XP_FileRead(buffer, len, fh );
  258.         /* PR_READ() returns an unreliable length, check EOF separately */
  259.         if (readlen < 0) {
  260.             if (errno == EBADF)    /* bad file handle, not open for read, etc. */
  261.                 err = REGERR_FAIL;
  262.             else
  263.                 err = REGERR_BADREAD;
  264.         }
  265.         else if (readlen < len) {
  266. #if VERIFY_READ
  267.             /* PR_READ() says we hit EOF but return length is unreliable. */
  268.             /* If buffer has new data beyond what PR_READ() says it got */
  269.             /* we'll assume the read was OK--this is a gamble but */
  270.             /* missing errors will cause fewer problems than too many. */
  271.             p = (unsigned char*)buffer+readlen;
  272.             while ( (*p == (unsigned char)FILLCHAR) && (p < dbgend) ) {
  273.                 p++;
  274.             }
  275.  
  276.             /* really was EOF if it's all FILLCHAR's */
  277.             if ( p == dbgend ) {
  278.                 err = REGERR_BADREAD;
  279.             }
  280. #else
  281.             err = REGERR_BADREAD;
  282. #endif
  283.         }
  284.     }
  285.  
  286.     return err;
  287.  
  288. }    /* ReadFile */
  289.  
  290.  
  291.  
  292. static REGERR nr_WriteFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer)
  293. {
  294.  
  295.     /* Note: 'offset' will commonly be the end of the file, in which
  296.      * case this function extends the file to 'offset'+'len'. This may
  297.      * be a two-step operation on some platforms.
  298.      */
  299.     XP_ASSERT(len > 0);
  300.     XP_ASSERT(buffer);
  301.     XP_ASSERT(fh != NULL);
  302.  
  303.     if (XP_FileSeek(fh, offset, SEEK_SET) != 0)
  304.         return REGERR_FAIL;
  305.  
  306.     if ((int32)XP_FileWrite(buffer, len, fh) != len)
  307.     {
  308.         /* disk full or some other catastrophic error */
  309.         return REGERR_FAIL;
  310.     }
  311.  
  312.     return REGERR_OK;
  313.  
  314. }    /* WriteFile */
  315.  
  316.  
  317.  
  318. static REGERR nr_LockRange(FILEHANDLE fh, REGOFF offset, int32 len)
  319. {
  320.     /* TODO: Implement XP lock function with built-in retry. */
  321.  
  322.     return REGERR_OK;
  323.  
  324. }    /* LockRange */
  325.  
  326.  
  327.  
  328. static REGERR nr_UnlockRange(FILEHANDLE fh, REGOFF offset, int32 len)
  329. {
  330.     /* TODO: Implement XP unlock function with built-in retry. */
  331.  
  332.     XP_FileFlush( fh );
  333.     return REGERR_OK;
  334.  
  335. }    /* UnlockRange */
  336.  
  337.  
  338.  
  339. #if SELF_REPAIR
  340. static int32 nr_GetFileLength(FILEHANDLE fh)
  341. {
  342.     int32 length;
  343.     int32 curpos;
  344.  
  345.     curpos = XP_FileTell(fh);
  346.     XP_FileSeek(fh, 0, SEEK_END);
  347.     length = XP_FileTell(fh);
  348.     XP_FileSeek(fh, curpos, SEEK_SET);
  349.     return length;
  350.  
  351. }    /* GetFileLength */
  352. #endif
  353.  
  354.  
  355.  
  356. /* --------------------------------------------------------------------
  357.  * Numeric converters
  358.  * --------------------------------------------------------------------
  359.  * The converters read and write integers in a common format so we
  360.  * can transport registries without worrying about endian problems.
  361.  *
  362.  * The buffers *MUST* be the appropriate size!
  363.  * --------------------------------------------------------------------
  364.  */
  365. static uint32 nr_ReadLong(char *buffer);
  366. static uint16 nr_ReadShort(char *buffer);
  367. static void   nr_WriteLong(uint32 num, char *buffer);
  368. static void   nr_WriteShort(uint16 num, char *buffer);
  369. /* -------------------------------------------------------------------- */
  370.  
  371.  
  372.  
  373. static uint16 nr_ReadShort(char *buffer)
  374. {
  375.     uint16 val;
  376.     uint8 *p = (uint8*)buffer;
  377.  
  378.     val = *p + (uint16)( *(p+1) * 0x100 );
  379.  
  380.     return val;
  381. }
  382.  
  383.  
  384.  
  385. static uint32 nr_ReadLong(char *buffer)
  386. {
  387.     uint32 val;
  388.     uint8 *p = (uint8*)buffer;
  389.  
  390.     val = *p
  391.         + (uint32)(*(p+1) * 0x100L)
  392.         + (uint32)(*(p+2) * 0x10000L )
  393.         + (uint32)(*(p+3) * 0x1000000L );
  394.  
  395.     return val;
  396. }
  397.  
  398.  
  399.  
  400. static void  nr_WriteLong(uint32 num, char *buffer)
  401. {
  402.     uint8 *p = (uint8*)buffer;
  403.     *p++ = (uint8)(num & 0x000000FF);
  404.     num /= 0x100;
  405.     *p++ = (uint8)(num & 0x000000FF);
  406.     num /= 0x100;
  407.     *p++ = (uint8)(num & 0x000000FF);
  408.     num /= 0x100;
  409.     *p   = (uint8)(num & 0x000000FF);
  410. }
  411.  
  412.  
  413.  
  414. static void  nr_WriteShort(uint16 num, char *buffer)
  415. {
  416.     uint8 *p = (uint8*)buffer;
  417.  
  418.     *p = (uint8)(num & 0x00FF);
  419.     *(p+1) = (uint8)(num / 0x100);
  420. }
  421.  
  422.  
  423.  
  424. /* --------------------------------------------------------------------
  425.  * Block I/O
  426.  * --------------------------------------------------------------------
  427.  */
  428. static REGERR nr_ReadHdr(REGFILE *reg);    /* Reads the file header, creates file if empty */
  429. static REGERR nr_WriteHdr(REGFILE *reg);    /* Writes the file header */
  430. static REGERR nr_CreateRoot(REGFILE *reg);
  431.  
  432. static REGERR nr_Lock(REGFILE *reg);
  433. static REGERR nr_Unlock(REGFILE *reg);
  434.  
  435. static REGERR nr_ReadDesc(REGFILE *reg, REGOFF offset, REGDESC *desc);        /* reads a desc */
  436. static REGERR nr_ReadName(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf);
  437. static REGERR nr_ReadData(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf);
  438.  
  439. static REGERR nr_WriteDesc(REGFILE *reg, REGDESC *desc);                    /* writes a desc */
  440. static REGERR nr_WriteString(REGFILE *reg, char *string, REGDESC *desc);    /* writes a string */
  441. static REGERR nr_WriteData(REGFILE *reg, char *string, uint32 len, REGDESC *desc);    /* writes a string */
  442.  
  443. static REGERR nr_AppendDesc(REGFILE *reg, REGDESC *desc, REGOFF *result);    /* adds a desc */
  444. static REGERR nr_AppendName(REGFILE *reg, char *name, REGDESC *desc);        /* adds a name */
  445. static REGERR nr_AppendString(REGFILE *reg, char *string, REGDESC *desc);    /* adds a string */
  446. static REGERR nr_AppendData(REGFILE *reg, char *string, uint32 len, REGDESC *desc);    /* adds a string */
  447. /* -------------------------------------------------------------------- */
  448.  
  449.  
  450.  
  451. static REGERR nr_ReadHdr(REGFILE *reg)
  452. {
  453.  
  454.     int err;
  455.     long filelength;
  456.     char hdrBuf[sizeof(REGHDR)];
  457.  
  458.     XP_ASSERT(reg);
  459.     reg->hdrDirty = 0;
  460.  
  461.     err = nr_ReadFile(reg->fh, 0, sizeof(REGHDR), &hdrBuf);
  462.  
  463.     switch (err)
  464.     {
  465.     case REGERR_BADREAD:
  466.         /* header doesn't exist, so create one */
  467.         err = nr_CreateRoot(reg);
  468.         break;
  469.  
  470.     case REGERR_OK:
  471.         /* header read successfully -- convert */
  472.         reg->hdr.magic    = nr_ReadLong ( hdrBuf + HDR_MAGIC );
  473.         reg->hdr.verMajor = nr_ReadShort( hdrBuf + HDR_VERMAJOR );
  474.         reg->hdr.verMinor = nr_ReadShort( hdrBuf + HDR_VERMINOR );
  475.         reg->hdr.avail    = nr_ReadLong ( hdrBuf + HDR_AVAIL );
  476.         reg->hdr.root     = nr_ReadLong ( hdrBuf + HDR_ROOT );
  477.  
  478.         /* check to see if it's the right file type */
  479.         if (reg->hdr.magic != MAGIC_NUMBER) {
  480.             err = REGERR_BADMAGIC;
  481.             break;
  482.         }
  483.  
  484.         /* Check registry version
  485.          * If the major version is bumped we're incompatible
  486.          * (minor version just means some new features were added)
  487.          *
  488.          * Upgrade code will go here in the future...
  489.          */
  490.         if ( reg->hdr.verMajor > MAJOR_VERSION ) {
  491.             err = REGERR_REGVERSION;
  492.             break;
  493.         }
  494.  
  495. #if SELF_REPAIR
  496.         if ( reg->inInit && !(reg->readOnly) ) {
  497.             filelength = nr_GetFileLength(reg->fh);
  498.             if (reg->hdr.avail != filelength)
  499.             {
  500.                 reg->hdr.avail = filelength;
  501.                 reg->hdrDirty = 1;
  502. #if NOCACHE_HDR
  503.                 err = nr_WriteHdr(reg);
  504. #endif
  505.             }
  506.         }
  507. #endif    /* SELF_REPAIR */
  508.         break;
  509.  
  510.     default:
  511.         /* unexpected error from nr_ReadFile()*/
  512.         XP_ASSERT(FALSE);
  513.         err = REGERR_FAIL;
  514.         break;
  515.     }    /* switch */
  516.  
  517.     return err;
  518.  
  519. }    /* ReadHdr */
  520.  
  521.  
  522.  
  523. static REGERR nr_WriteHdr(REGFILE *reg)
  524. {
  525.     REGERR err;
  526.     char hdrBuf[sizeof(REGHDR)];
  527.  
  528.     XP_ASSERT(reg);
  529.  
  530.     if (reg->readOnly)
  531.         return REGERR_READONLY;
  532.  
  533.     /* convert to XP int format */
  534.     nr_WriteLong ( reg->hdr.magic,    hdrBuf + HDR_MAGIC );
  535.     nr_WriteShort( reg->hdr.verMajor, hdrBuf + HDR_VERMAJOR );
  536.     nr_WriteShort( reg->hdr.verMinor, hdrBuf + HDR_VERMINOR );
  537.     nr_WriteLong ( reg->hdr.avail,    hdrBuf + HDR_AVAIL );
  538.     nr_WriteLong ( reg->hdr.root,     hdrBuf + HDR_ROOT );
  539.  
  540.     /* err = nr_WriteFile(reg->fh, 0, sizeof(REGHDR), ®->hdr); */
  541.     err = nr_WriteFile(reg->fh, 0, sizeof(REGHDR), &hdrBuf);
  542.  
  543.     if (err == REGERR_OK)
  544.         reg->hdrDirty = 0;
  545.  
  546.     return err;
  547.  
  548. }    /* WriteHdr */
  549.  
  550.  
  551.  
  552. static REGERR nr_CreateRoot(REGFILE *reg)
  553. {
  554.     /* Called when an empty file is detected by ReadHdr */
  555.     REGERR err;
  556.     REGDESC root;
  557.  
  558.     XP_ASSERT(reg);
  559.  
  560.     /* Create 'hdr' */
  561.     reg->hdr.magic      = MAGIC_NUMBER;
  562.     reg->hdr.verMajor   = MAJOR_VERSION;
  563.     reg->hdr.verMinor   = MINOR_VERSION;
  564.     reg->hdr.root       = 0;
  565.     reg->hdr.avail      = HDRRESERVE;
  566.  
  567.     /* Create root descriptor */
  568.     root.location   = 0;
  569.     root.left       = 0;
  570.     root.value      = 0;
  571.     root.down       = 0;
  572.     root.type       = REGTYPE_KEY;
  573.     root.valuelen   = 0;
  574.     root.valuebuf   = 0;
  575.     root.parent     = 0;
  576.  
  577.     err = nr_AppendName(reg, ROOTKEY_STR, &root);
  578.     if (err != REGERR_OK)
  579.         return err;
  580.  
  581.     err = nr_AppendDesc(reg, &root, ®->hdr.root);
  582.     if (err != REGERR_OK)
  583.         return err;
  584.  
  585.     return nr_WriteHdr(reg);    /* actually commit to disk */
  586.  
  587.     /* Create standard top-level nodes */
  588.  
  589. }    /* CreateRoot */
  590.  
  591.  
  592.  
  593. static REGERR nr_Lock(REGFILE *reg)
  594. {
  595.  
  596.     REGERR err;
  597.  
  598.     /* lock header */
  599.     err = nr_LockRange(reg->fh, 0, sizeof(REGHDR));
  600.     if (err != REGERR_OK)
  601.         return err;
  602.  
  603. #ifndef STANDALONE_REGISTRY
  604.     PR_EnterMonitor( reg->monitor );
  605. #endif
  606.  
  607.     return nr_ReadHdr(reg);
  608.  
  609. }    /* Lock */
  610.  
  611.  
  612.  
  613. static REGERR nr_Unlock(REGFILE *reg)
  614. {
  615. #ifndef STANDALONE_REGISTRY
  616.     PR_ExitMonitor( reg->monitor );
  617. #endif
  618.  
  619.     return nr_UnlockRange(reg->fh, 0, sizeof(REGHDR));
  620. }    /* Unlock */
  621.  
  622.  
  623.  
  624. static REGERR nr_ReadDesc(REGFILE *reg, REGOFF offset, REGDESC *desc)
  625. {
  626.  
  627.     REGERR err;
  628.     char descBuf[ DESC_SIZE ];
  629.  
  630.     XP_ASSERT(reg);
  631.     XP_ASSERT(offset >= HDRRESERVE);
  632.     XP_ASSERT(offset < reg->hdr.avail);
  633.     XP_ASSERT(desc);
  634.  
  635.     err = nr_ReadFile(reg->fh, offset, DESC_SIZE, &descBuf);
  636.     if (err == REGERR_OK)
  637.     {
  638.         desc->location  = nr_ReadLong ( descBuf + DESC_LOCATION );
  639.         desc->name      = nr_ReadLong ( descBuf + DESC_NAME );
  640.         desc->namelen   = nr_ReadShort( descBuf + DESC_NAMELEN );
  641.         desc->type      = nr_ReadShort( descBuf + DESC_TYPE );
  642.         desc->left      = nr_ReadLong ( descBuf + DESC_LEFT );
  643.         desc->value     = nr_ReadLong ( descBuf + DESC_VALUE );
  644.         desc->valuelen  = nr_ReadLong ( descBuf + DESC_VALUELEN );
  645.         desc->parent    = nr_ReadLong ( descBuf + DESC_PARENT );
  646.  
  647.         if ( TYPE_IS_ENTRY(desc->type) ) {
  648.             desc->down = 0;
  649.             desc->valuebuf  = nr_ReadShort( descBuf + DESC_VALUEBUF );
  650.         }
  651.         else {  /* TYPE is KEY */
  652.             desc->down      = nr_ReadLong( descBuf + DESC_DOWN );
  653.             desc->valuebuf  = 0;
  654.         }
  655.  
  656.         if (desc->location != offset)
  657.             err = REGERR_BADLOCN;
  658.         else if ( desc->type & REGTYPE_DELETED )
  659.             err = REGERR_DELETED;
  660.     }
  661.  
  662.     return err;
  663.  
  664. }    /* ReadDesc */
  665.  
  666.  
  667.  
  668. static REGERR nr_ReadName(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf)
  669. {
  670.  
  671.     REGERR err;
  672.  
  673.     XP_ASSERT(reg);
  674.     XP_ASSERT(desc->name > 0);
  675.     XP_ASSERT(desc->name < reg->hdr.avail);
  676.     XP_ASSERT(buflen > 0);
  677.     XP_ASSERT(buf);
  678.  
  679.     if ( desc->namelen > buflen )
  680.         return REGERR_BUFTOOSMALL;
  681.  
  682.     err = nr_ReadFile(reg->fh, desc->name, desc->namelen, buf);
  683.  
  684.     buf[buflen-1] = '\0';    /* avoid runaways */
  685.  
  686.     return err;
  687.  
  688. }    /* ReadName */
  689.  
  690.  
  691.  
  692. static REGERR nr_ReadData(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf)
  693. {
  694.  
  695.     REGERR err;
  696.  
  697.     XP_ASSERT(reg);
  698.     XP_ASSERT(desc->value > 0);
  699.     XP_ASSERT(desc->value < reg->hdr.avail);
  700.     XP_ASSERT(buflen > 0);
  701.     XP_ASSERT(buf);
  702.  
  703.     if ( desc->valuelen > buflen )
  704.         return REGERR_BUFTOOSMALL;
  705.  
  706.       err = nr_ReadFile(reg->fh, desc->value, desc->valuelen, buf);
  707.  
  708.     return err;
  709.  
  710. }    /* nr_ReadData */
  711.  
  712.  
  713.  
  714. static REGERR nr_WriteDesc(REGFILE *reg, REGDESC *desc)
  715. {
  716.     char descBuf[ DESC_SIZE ];
  717.  
  718.     XP_ASSERT(reg);
  719.     XP_ASSERT(desc);
  720.     XP_ASSERT( desc->location >= HDRRESERVE );
  721.     XP_ASSERT( desc->location < reg->hdr.avail );
  722.  
  723.     if (reg->readOnly)
  724.         return REGERR_READONLY;
  725.  
  726.     /* convert to XP int format */
  727.     nr_WriteLong ( desc->location,  descBuf + DESC_LOCATION );
  728.     nr_WriteLong ( desc->name,      descBuf + DESC_NAME );
  729.     nr_WriteShort( desc->namelen,   descBuf + DESC_NAMELEN );
  730.     nr_WriteShort( desc->type,      descBuf + DESC_TYPE );
  731.     nr_WriteLong ( desc->left,      descBuf + DESC_LEFT );
  732.     nr_WriteLong ( desc->value,     descBuf + DESC_VALUE );
  733.     nr_WriteLong ( desc->valuelen,  descBuf + DESC_VALUELEN );
  734.     nr_WriteLong ( desc->parent,    descBuf + DESC_PARENT );
  735.  
  736.     if ( TYPE_IS_ENTRY(desc->type) ) {
  737.         XP_ASSERT( 0 == desc->down );
  738.         nr_WriteLong( desc->valuebuf,  descBuf + DESC_VALUEBUF );
  739.     }
  740.     else {  /* TYPE is KEY */
  741.         XP_ASSERT( 0 == desc->valuebuf );
  742.         nr_WriteLong( desc->down,      descBuf + DESC_DOWN );
  743.     }
  744.  
  745.     return nr_WriteFile(reg->fh, desc->location, DESC_SIZE, descBuf);
  746. }   /* nr_WriteDesc */
  747.  
  748.  
  749.  
  750. static REGERR nr_AppendDesc(REGFILE *reg, REGDESC *desc, REGOFF *result)
  751. {
  752.  
  753.     REGERR err;
  754.     char descBuf[ DESC_SIZE ];
  755.  
  756.     XP_ASSERT(reg);
  757.     XP_ASSERT(desc);
  758.     XP_ASSERT(result);
  759.  
  760.     *result = 0;
  761.  
  762.     if (reg->readOnly)
  763.         return REGERR_READONLY;
  764.  
  765.     desc->location = reg->hdr.avail;
  766.  
  767.     /* convert to XP int format */
  768.     nr_WriteLong ( desc->location,  descBuf + DESC_LOCATION );
  769.     nr_WriteLong ( desc->name,      descBuf + DESC_NAME );
  770.     nr_WriteShort( desc->namelen,   descBuf + DESC_NAMELEN );
  771.     nr_WriteShort( desc->type,      descBuf + DESC_TYPE );
  772.     nr_WriteLong ( desc->left,      descBuf + DESC_LEFT );
  773.     nr_WriteLong ( desc->value,     descBuf + DESC_VALUE );
  774.     nr_WriteLong ( desc->valuelen,  descBuf + DESC_VALUELEN );
  775.     nr_WriteLong ( desc->parent,    descBuf + DESC_PARENT );
  776.  
  777.     if ( TYPE_IS_ENTRY(desc->type) ) {
  778.         XP_ASSERT( 0 == desc->down );
  779.         nr_WriteLong( desc->valuebuf,  descBuf + DESC_VALUEBUF );
  780.     }
  781.     else {  /* TYPE is KEY */
  782.         XP_ASSERT( 0 == desc->valuebuf );
  783.         nr_WriteLong( desc->down,      descBuf + DESC_DOWN );
  784.     }
  785.  
  786.     err = nr_WriteFile(reg->fh, reg->hdr.avail, DESC_SIZE, descBuf);
  787.  
  788.     if (err == REGERR_OK)
  789.     {
  790.         *result = reg->hdr.avail;
  791.         reg->hdr.avail += DESC_SIZE;
  792.         reg->hdrDirty = 1;
  793. #if NOCACHE_HDR
  794.         err = nr_WriteHdr(reg);
  795. #endif
  796.     }
  797.  
  798.     return err;
  799.  
  800. }    /* AppendDesc */
  801.  
  802.  
  803.  
  804. static REGERR nr_AppendName(REGFILE *reg, char *name, REGDESC *desc)
  805. {
  806.     REGERR err;
  807.     int len;
  808.     char *p;
  809.  
  810.     XP_ASSERT(reg);
  811.     XP_ASSERT(name);
  812.     XP_ASSERT(desc);
  813.  
  814.     if (reg->readOnly)
  815.         return REGERR_READONLY;
  816.  
  817.     len = XP_STRLEN(name) + 1;
  818.  
  819.     /* check for valid name parameter */
  820.     if ( len == 1 )
  821.         return REGERR_PARAM;
  822.  
  823.     if ( len > MAXREGNAMELEN )
  824.         return REGERR_NAMETOOLONG;
  825.  
  826.     for ( p = name; (*p != 0); p++ ) {
  827.         if ( INVALID_NAME_CHAR(*p) )
  828.             return REGERR_BADNAME;
  829.     }
  830.  
  831.     /* save the name */
  832.     err = nr_WriteFile(reg->fh, reg->hdr.avail, len, name);
  833.  
  834.     /* if write successful update the desc and hdr */
  835.     if (err == REGERR_OK)
  836.     {
  837.         desc->namelen = (uint16)len;
  838.         desc->name = reg->hdr.avail;
  839.         reg->hdr.avail += len;
  840.         reg->hdrDirty = 1;
  841. #if NOCACHE_HDR
  842.         err = nr_WriteHdr(reg);
  843. #endif
  844.     }
  845.  
  846.     return err;
  847.  
  848. }    /* nr_AppendName */
  849.  
  850.  
  851.  
  852. static REGERR nr_WriteString(REGFILE *reg, char *string, REGDESC *desc)
  853. {
  854.     uint32 len;
  855.  
  856.     XP_ASSERT(string);
  857.     if (reg->readOnly)
  858.         return REGERR_READONLY;
  859.     len = XP_STRLEN(string) + 1;
  860.  
  861.     return nr_WriteData( reg, string, len, desc );
  862.  
  863. }    /* nr_WriteString */
  864.  
  865.  
  866.  
  867. static REGERR nr_WriteData(REGFILE *reg, char *string, uint32 len, REGDESC *desc)
  868. {
  869.     REGERR err;
  870.  
  871.     XP_ASSERT(reg);
  872.     XP_ASSERT(string);
  873.     XP_ASSERT(desc);
  874.  
  875.     if (reg->readOnly)
  876.         return REGERR_READONLY;
  877.  
  878.     if ( len == 0 )
  879.         return REGERR_PARAM;
  880.  
  881.     if ( len > MAXREGVALUELEN )
  882.         return REGERR_NAMETOOLONG;
  883.  
  884.     /* save the data in the same place if it fits */
  885.     if ( len <= desc->valuebuf ) {
  886.         err = nr_WriteFile( reg->fh, desc->value, len, string );
  887.         if ( err == REGERR_OK ) {
  888.             desc->valuelen = len;
  889.         }
  890.     }
  891.     else {
  892.         /* otherwise append new data */
  893.         err = nr_AppendData( reg, string, len, desc );
  894.     }
  895.  
  896.     return err;
  897.  
  898. }    /* nr_WriteData */
  899.  
  900.  
  901.  
  902. static REGERR nr_AppendString(REGFILE *reg, char *string, REGDESC *desc)
  903. {
  904.     uint32 len;
  905.  
  906.     XP_ASSERT(string);
  907.     if (reg->readOnly)
  908.         return REGERR_READONLY;
  909.     len = XP_STRLEN(string) + 1;
  910.  
  911.     return nr_AppendData( reg, string, len, desc );
  912.  
  913. }    /* nr_AppendString */
  914.  
  915.  
  916.  
  917. static REGERR nr_AppendData(REGFILE *reg, char *string, uint32 len, REGDESC *desc)
  918. {
  919.     REGERR err;
  920.  
  921.     XP_ASSERT(reg);
  922.     XP_ASSERT(string);
  923.     XP_ASSERT(desc);
  924.  
  925.     if (reg->readOnly)
  926.         return REGERR_READONLY;
  927.  
  928.     if ( len == 0 )
  929.         return REGERR_PARAM;
  930.  
  931.     if ( len > MAXREGVALUELEN )
  932.         return REGERR_NAMETOOLONG;
  933.  
  934.     /* save the string */
  935.     err = nr_WriteFile(reg->fh, reg->hdr.avail, len, string);
  936.     if (err == REGERR_OK)
  937.     {
  938.         desc->value     = reg->hdr.avail;
  939.         desc->valuelen  = len;
  940.         desc->valuebuf  = len;
  941.  
  942.         reg->hdr.avail += len;
  943.         reg->hdrDirty   = 1;
  944. #if NOCACHE_HDR
  945.         err = nr_WriteHdr(reg);
  946. #endif
  947.     }
  948.  
  949.     return err;
  950.  
  951. }    /* nr_AppendData */
  952.  
  953.  
  954.  
  955. /* --------------------------------------------------------------------
  956.  * Path Parsing
  957.  * --------------------------------------------------------------------
  958.  */
  959. static REGERR nr_NextName(char *pPath, char *buf, uint32 bufsize, char **newPath);
  960. static REGERR nr_RemoveName(char *path);
  961. static REGERR nr_CatName(REGFILE *reg, REGOFF node, char *path, uint32 bufsize,
  962.                     REGDESC *desc);
  963. static REGERR nr_ReplaceName(REGFILE *reg, REGOFF node, char *path,
  964.                     uint32 bufsize, REGDESC *desc);
  965.  
  966. #if 0 /* old interface */
  967. static char * nr_LastDelim(char *pPath);
  968. static Bool   nr_IsQualifiedEntry(char *pPath);
  969. static REGERR nr_SplitEntry(char *pPath, char **name, char **value);
  970. static REGERR nr_CatNameValue(char *path, int bufsize, REGDESC *desc);
  971. static REGERR nr_ReplaceNameValue(char *path, int bufsize, REGDESC *desc);
  972. #endif
  973. /* -------------------------------------------------------------------- */
  974.  
  975.  
  976. /* Scans path at 'pPath' and copies next name segment into 'buf'.
  977.  * Also sets 'newPath' to point at the next segment of pPath.
  978.  */
  979. static REGERR nr_NextName(char *pPath, char *buf, uint32 bufsize, char **newPath)
  980. {
  981.     uint32 len = 0;
  982.     REGERR err = REGERR_OK;
  983.  
  984.     /* initialization and validation */
  985.     XP_ASSERT(buf);
  986.  
  987.     *newPath = NULL;
  988.     *buf = '\0';
  989.  
  990.     if ( pPath==NULL || *pPath=='\0' )
  991.         return REGERR_NOMORE;
  992.  
  993.     /* ... skip an initial path delimiter */
  994.     if ( *pPath == PATHDEL ) {
  995.         pPath++;
  996.  
  997.         if ( *pPath == '\0' )
  998.             return REGERR_NOMORE;
  999.     }
  1000.  
  1001.     /* ... missing name segment or initial blank are errors*/
  1002.     if ( *pPath == PATHDEL || *pPath == ' ' )
  1003.         return REGERR_BADNAME;
  1004.  
  1005.     /* copy first path segment into return buf */
  1006.     while ( *pPath != '\0' && *pPath != PATHDEL )
  1007.     {
  1008.         if ( len == bufsize ) {
  1009.             err = REGERR_NAMETOOLONG;
  1010.             break;
  1011.         }
  1012.         if ( *pPath < ' ' && *pPath > 0 )
  1013.             return REGERR_BADNAME;
  1014.  
  1015.         *buf++ = *pPath++;
  1016.         len++;
  1017.     }
  1018.     *buf = '\0';
  1019.  
  1020.     /* ... name segment can't end with blanks, either */
  1021.     if ( ' ' == *(buf-1) )
  1022.         return REGERR_BADNAME;
  1023.  
  1024.     /* return a pointer to the start of the next segment */
  1025.     *newPath = pPath;
  1026.  
  1027.     return err;
  1028.  
  1029. }    /* nr_NextName */
  1030.  
  1031.  
  1032.  
  1033. static REGERR nr_CatName(REGFILE *reg, REGOFF node, char *path, uint32 bufsize, REGDESC *desc)
  1034. {
  1035.     REGERR err = REGERR_OK;
  1036.  
  1037.     char   *p;
  1038.     uint32 len = XP_STRLEN(path);
  1039.  
  1040.     if (len > 0)
  1041.     {
  1042.         p = &path[len-1];
  1043.         if (*p != PATHDEL)
  1044.         {
  1045.             if ( len < bufsize ) {
  1046.                 p++;
  1047.                 *p = PATHDEL;
  1048.                 len++;
  1049.             }
  1050.             else
  1051.                 err = REGERR_BUFTOOSMALL;
  1052.         }
  1053.         p++;    /* point one past PATHDEL */
  1054.     }
  1055.     else
  1056.         p = path;
  1057.  
  1058.     if ( err == REGERR_OK ) {
  1059.         err = nr_ReadDesc( reg, node, desc );
  1060.         if ( err == REGERR_OK ) {
  1061.             err = nr_ReadName( reg, desc, bufsize-len, p );
  1062.         }
  1063.     }
  1064.  
  1065.     return err;
  1066.  
  1067. }    /* CatName */
  1068.  
  1069.  
  1070.  
  1071. static REGERR nr_ReplaceName(REGFILE *reg, REGOFF node, char *path, uint32 bufsize, REGDESC *desc)
  1072. {
  1073.     /* NOTE! It is EXREMELY important that names be in UTF-8; otherwise
  1074.      * the backwards path search will fail for multi-byte/Unicode names
  1075.      */
  1076.  
  1077.     char   *p;
  1078.     uint32 len;
  1079.     REGERR err;
  1080.  
  1081.     XP_ASSERT(path);
  1082.  
  1083.     len = XP_STRLEN(path);
  1084.     if ( len > bufsize )
  1085.         return REGERR_PARAM;
  1086.  
  1087.     if ( len > 0 ) {
  1088.         p = &path[len-1];
  1089.  
  1090.         while ((p > path) && (*p != PATHDEL)) {
  1091.             --p;
  1092.             --len;
  1093.         }
  1094.         if ( *p == PATHDEL ) {
  1095.             p++;
  1096.             len++;
  1097.         }
  1098.     }
  1099.     else
  1100.         p = path;
  1101.  
  1102.  
  1103.     err = nr_ReadDesc( reg, node, desc );
  1104.     if ( err == REGERR_OK ) {
  1105.         err = nr_ReadName( reg, desc, bufsize-len, p );
  1106.     }
  1107.  
  1108.     return err;
  1109.  
  1110. }    /* ReplaceName */
  1111.  
  1112.  
  1113. static REGERR nr_RemoveName(char *path)
  1114. {
  1115.     /* Typical inputs:
  1116.      * path = "/Machine/4.0/"    output = "/Machine"
  1117.      * path = "/Machine"        output = ""
  1118.      * path = ""                output = REGERR_NOMORE
  1119.      *
  1120.      * NOTE! It is EXREMELY important that names be in UTF-8; otherwise
  1121.      * the backwards path search will fail for multi-byte/Unicode names
  1122.      */
  1123.  
  1124.     int len = XP_STRLEN(path);
  1125.     char *p;
  1126.     if (len < 1)
  1127.         return REGERR_NOMORE;
  1128.  
  1129.     p = &path[len-1];
  1130.     /* if last char is '/', ignore it */
  1131.     if (*p == PATHDEL)
  1132.         p--;
  1133.  
  1134.     while ((p > path) && (*p != PATHDEL))
  1135.         p--;
  1136.  
  1137. /*    if (*p != PATHDEL)
  1138.         return REGERR_NOMORE;
  1139. */
  1140.  
  1141.     *p = '\0';
  1142.     return REGERR_OK;
  1143.  
  1144. }    /* RemoveName */
  1145.  
  1146.  
  1147.  
  1148. /* --------------------------------------------------------------------
  1149.  * Key/Entry Management
  1150.  * --------------------------------------------------------------------
  1151.  */
  1152. static REGERR nr_Find(REGFILE *reg, REGOFF offParent, char *pPath,
  1153.     REGDESC *pDesc,    REGOFF *pPrev, REGOFF *pParent);
  1154.  
  1155. static REGERR nr_FindAtLevel(REGFILE *reg, REGOFF offFirst, char *pName,
  1156.     REGDESC *pDesc, REGOFF *pOffPrev);
  1157.  
  1158. static REGERR nr_CreateSubKey(REGFILE *reg, REGDESC *pParent, char *name);
  1159. static REGERR nr_CreateEntryString(REGFILE *reg, REGDESC *pParent,
  1160.     char *name, char *value);
  1161. static REGERR nr_CreateEntry(REGFILE *reg, REGDESC *pParent, char *name,
  1162.     uint16 type, char *buffer, uint32 length);
  1163. /* -------------------------------------------------------------------- */
  1164.  
  1165.  
  1166.  
  1167. static REGERR nr_Find(REGFILE *reg,
  1168.             REGOFF offParent,
  1169.             char *pPath,
  1170.             REGDESC *pDesc,
  1171.             REGOFF *pPrev,
  1172.             REGOFF *pParent)
  1173. {
  1174.  
  1175.     REGERR  err;
  1176.     REGDESC desc;
  1177.     REGOFF  offPrev = 0;
  1178.     char    namebuf[MAXREGNAMELEN];
  1179.     char    *p;
  1180.  
  1181.     XP_ASSERT( pPath != NULL );
  1182.     XP_ASSERT( offParent >= HDRRESERVE );
  1183.     XP_ASSERT( VALID_FILEHANDLE( reg->fh ) );
  1184.  
  1185.     if (pPrev)
  1186.         *pPrev = 0;
  1187.     if (pParent)
  1188.         *pParent = 0;
  1189.  
  1190.     /* read starting desc */
  1191.     err = nr_ReadDesc( reg, offParent, &desc);
  1192.  
  1193.        /* Walk 'path', reading keys into 'desc' */
  1194.     p = pPath;
  1195.     while ( err == REGERR_OK ) {
  1196.  
  1197.         err = nr_NextName(p, namebuf, sizeof(namebuf), &p);
  1198.  
  1199.         if ( err == REGERR_OK ) {
  1200.             /* save current location as parent of next segment */
  1201.             offParent = desc.location;
  1202.             /* look for name at next level down */
  1203.             err = nr_FindAtLevel(reg, desc.down, namebuf, &desc, &offPrev);
  1204.         }
  1205.       }
  1206.  
  1207.     if ( err == REGERR_NOMORE ) {
  1208.         /* we found all the segments of the path--success! */
  1209.         err = REGERR_OK;
  1210.  
  1211.         if (pDesc) {
  1212.             COPYDESC(pDesc, &desc);
  1213.         }
  1214.         if (pPrev) {
  1215.             *pPrev = offPrev;
  1216.         }
  1217.         if (pParent) {
  1218.             *pParent = offParent;
  1219.         }
  1220.     }
  1221.     
  1222.     return err;
  1223.  
  1224. }    /* nr_Find */
  1225.  
  1226.  
  1227. /* nr_FindAtLevel -- looks for a node matching "pName" on the level starting
  1228.  *                   with "offset".  Returns REGERR_OK if found, REGERR_NOFIND
  1229.  *                   if not (plus other error conditions).
  1230.  *
  1231.  *                   If pDesc and pOffPrev are valid pointers *AND* the name is
  1232.  *                   found then pDesc will point at the REGDESC of the node and
  1233.  *                   pOffPrev will be the offset of the desc for the previous
  1234.  *                   node at the same level.  These values aren't touched
  1235.  *                   if the node is not found.
  1236.  */
  1237. static REGERR nr_FindAtLevel(REGFILE *reg,
  1238.                              REGOFF offset,
  1239.                              char *pName,
  1240.                              REGDESC *pDesc,
  1241.                              REGOFF *pOffPrev)
  1242. {
  1243.     char    namebuf[MAXREGNAMELEN];
  1244.     REGDESC desc;
  1245.     REGERR  err;
  1246.     REGOFF  prev = 0;
  1247.  
  1248.     /* Note: offset=0 when there's no 'down' or 'left' */
  1249.     XP_ASSERT(reg);
  1250.     XP_ASSERT(offset < reg->hdr.avail);
  1251.     XP_ASSERT(pName);
  1252.     XP_ASSERT(*pName);
  1253.  
  1254.     while ( offset != 0 )
  1255.     {
  1256.         /* get name of next node */
  1257.         err = nr_ReadDesc(reg, offset, &desc);
  1258.         if (err != REGERR_OK)
  1259.             return err;
  1260.  
  1261.         err = nr_ReadName(reg, &desc, sizeof(namebuf), namebuf);
  1262.         if (err != REGERR_OK)
  1263.             return err;
  1264.  
  1265.         /* check to see if it's the one we want */
  1266.         if (XP_STRCASECMP(namebuf, pName) == 0) {
  1267.             /* Found it! */
  1268.             if ( pDesc != NULL ) {
  1269.                 COPYDESC( pDesc, &desc );
  1270.             }
  1271.             if ( pOffPrev != NULL ) {
  1272.                 *pOffPrev = prev;
  1273.             }
  1274.             return REGERR_OK;
  1275.         }
  1276.  
  1277.         /* advance to the next node */
  1278.         prev = offset;
  1279.         offset = desc.left;
  1280.     }
  1281.  
  1282.     return REGERR_NOFIND;
  1283. }    /* FindAtLevel */
  1284.  
  1285.  
  1286.  
  1287. static REGERR nr_CreateSubKey(REGFILE *reg, REGDESC *pParent, char *name)
  1288. {
  1289.     /* nr_CreateSubKey does NO error checking--callers *MUST*
  1290.      * ensure that there are no duplicates
  1291.      */
  1292.     REGDESC desc;
  1293.     REGERR err;
  1294.  
  1295.     XP_ASSERT(reg);
  1296.     XP_ASSERT(pParent);
  1297.     XP_ASSERT(name);
  1298.  
  1299.     err = nr_AppendName(reg, name, &desc);
  1300.     if (err != REGERR_OK)
  1301.         return err;
  1302.  
  1303.     desc.type = REGTYPE_KEY;
  1304.     desc.left = pParent->down;
  1305.     desc.down = 0;
  1306.     desc.value = 0;
  1307.     desc.valuelen = 0;
  1308.     desc.valuebuf = 0;
  1309.     desc.parent   = pParent->location;
  1310.  
  1311.     err = nr_AppendDesc(reg, &desc, &pParent->down);
  1312.     if (err != REGERR_OK)
  1313.         return err;
  1314.  
  1315.     /* printf("nr_CreateSubKey: %s @0x%lx\n", name, pParent->down); */
  1316.  
  1317.     err = nr_WriteDesc(reg, pParent);
  1318.     COPYDESC(pParent, &desc);
  1319.  
  1320.     return err;
  1321.  
  1322. }    /* nr_CreateSubKey */
  1323.  
  1324.  
  1325.  
  1326. static REGERR nr_CreateEntryString(REGFILE *reg, REGDESC *pParent, char *name, char *value)
  1327. {
  1328.     REGDESC desc;
  1329.     REGERR  err;
  1330.  
  1331.     XP_ASSERT(reg);
  1332.     XP_ASSERT(pParent);
  1333.     XP_ASSERT(name);
  1334.     XP_ASSERT(value);
  1335.  
  1336.     memset( &desc, 0, sizeof(REGDESC) );
  1337.  
  1338.     err = nr_AppendName(reg, name, &desc);
  1339.     if (err != REGERR_OK)
  1340.         return err;
  1341.  
  1342.     err = nr_AppendString(reg, value, &desc);
  1343.     if (err != REGERR_OK)
  1344.         return err;
  1345.  
  1346.     desc.type = REGTYPE_ENTRY_STRING_UTF;
  1347.     desc.left = pParent->value;
  1348.     desc.down = 0;
  1349.     desc.parent = pParent->location;
  1350.  
  1351.     err = nr_AppendDesc(reg, &desc, &pParent->value);
  1352.     if (err != REGERR_OK)
  1353.         return err;
  1354.  
  1355.     /* printf("nr_AddEntry: %s=%s @0x%lx\n", name, value, pParent->value); */
  1356.  
  1357.     return nr_WriteDesc(reg, pParent);
  1358.  
  1359. }    /* nr_CreateEntryString */
  1360.  
  1361.  
  1362.  
  1363. static REGERR nr_CreateEntry(REGFILE *reg, REGDESC *pParent, char *name,
  1364.     uint16 type, char *value, uint32 length)
  1365. {
  1366.     REGDESC desc;
  1367.     REGERR  err;
  1368.  
  1369.     XP_ASSERT(reg);
  1370.     XP_ASSERT(pParent);
  1371.     XP_ASSERT(name);
  1372.     XP_ASSERT(value);
  1373.  
  1374.     memset( &desc, 0, sizeof(REGDESC) );
  1375.  
  1376.     err = nr_AppendName(reg, name, &desc);
  1377.     if (err != REGERR_OK)
  1378.         return err;
  1379.  
  1380.     err = nr_AppendData(reg, value, length, &desc);
  1381.     if (err != REGERR_OK)
  1382.         return err;
  1383.  
  1384.     desc.type = type;
  1385.     desc.left = pParent->value;
  1386.     desc.down = 0;
  1387.     desc.parent = pParent->location;
  1388.  
  1389.     err = nr_AppendDesc(reg, &desc, &pParent->value);
  1390.     if (err != REGERR_OK)
  1391.         return err;
  1392.  
  1393.     /* printf("nr_AddEntry: %s=%s @0x%lx\n", name, value, pParent->value); */
  1394.  
  1395.     return nr_WriteDesc(reg, pParent);
  1396.  
  1397. }   /* nr_CreateEntry */
  1398.  
  1399.  
  1400.  
  1401. #if 0 /* old interface */
  1402. VR_INTERFACE(REGERR) NR_RegRename(RKEY key, char *path, char *newname)
  1403. {
  1404.  
  1405.     REGDESC desc;
  1406.     REGERR err;
  1407.  
  1408.     if ( !VALID_FILEHANDLE(gReg.fh) )
  1409.         return REGERR_FAIL;
  1410.  
  1411.     /* Okay to Update to "", but names must not be null */
  1412.     if (!newname || !*newname)
  1413.         return REGERR_PARAM;
  1414.  
  1415.     err = nr_Lock(&gReg);
  1416.     if (err != REGERR_OK)
  1417.         return err;
  1418.  
  1419.     /* Find the key or entry to rename (and validate the
  1420.         incoming parameters) */
  1421.     err = nr_Find((REGOFF)key, path, &desc, 0, 0);
  1422.     if (err != REGERR_OK)
  1423.         goto cleanup;
  1424.  
  1425.     /* Write the new name string to the file and update
  1426.         the key or entry's pointer */
  1427.     err = nr_AppendName(&gReg, newname, &desc);
  1428.     if (err != REGERR_OK)
  1429.         goto cleanup;
  1430.  
  1431.     /* Write the key or entry back to the disk and return */
  1432.     err = nr_WriteDesc(&gReg, &desc);
  1433.  
  1434. cleanup:
  1435.     nr_Unlock(&gReg);
  1436.     return err;
  1437.  
  1438. }    /* RegRename */
  1439.  
  1440.  
  1441.  
  1442. VR_INTERFACE(REGERR) NR_RegPack(char *newfilename)
  1443. {
  1444.  
  1445.     REGFILE dstReg;
  1446.     char *path = NULL;
  1447.     int err = REGERR_OK;
  1448.  
  1449.     dstReg.fh = NULL;
  1450.  
  1451.     if (!newfilename || !*newfilename)
  1452.         return REGERR_PARAM;
  1453.     if ( !VALID_FILEHANDLE(gReg.fh) )
  1454.         return REGERR_FAIL;
  1455.  
  1456.     /* open it & create a header by trying to read */
  1457.     err = nr_OpenFile(newfilename, &dstReg.fh);
  1458.     if (err != REGERR_OK)
  1459.         goto cleanup;
  1460.     err = nr_ReadHdr(&dstReg);
  1461.     if (err != REGERR_OK)
  1462.         goto cleanup;
  1463.  
  1464.     /* read records from the current registry file and
  1465.         add them to 'dstReg' */
  1466.     path = XP_ALLOC(PACKBUFFERSIZE);
  1467.     if (path == NULL)
  1468.     {
  1469.         err = REGERR_FAIL;
  1470.         goto cleanup;
  1471.     }
  1472.  
  1473.     XP_STRCPY(path, "/");
  1474.  
  1475.     err = nr_Lock(&gReg);
  1476.     if (err != REGERR_OK)
  1477.         goto cleanup;
  1478.  
  1479.     while (NR_RegNext( 0, PACKBUFFERSIZE, path ) == REGERR_OK)
  1480.     {
  1481.         err = nr_RegGenericAdd(&dstReg, 0, path);
  1482.         if (err != REGERR_OK)
  1483.             break;
  1484.     }
  1485.  
  1486.     nr_Unlock(&gReg);
  1487.  
  1488. cleanup:
  1489.     XP_FREEIF(path);
  1490.     if ( VALID_FILEHANDLE(dstReg.fh) )
  1491.     {
  1492.         /* even if not caching headers it could be dirty due to an error */
  1493.         if (dstReg.hdrDirty) {
  1494.             nr_WriteHdr(&dstReg);
  1495.         }
  1496.         nr_CloseFile(&dstReg.fh);
  1497.     }
  1498.  
  1499.     return err;
  1500.  
  1501. }    /* RegPack */
  1502.  
  1503. #endif /* old interface */
  1504.  
  1505.  
  1506.  
  1507. /* ---------------------------------------------------------------------
  1508.  * Intermediate API
  1509.  * ---------------------------------------------------------------------
  1510.  */
  1511. static REGOFF nr_TranslateKey( REGFILE *reg, RKEY key );
  1512. static void   nr_InitStdRkeys( REGFILE *reg );
  1513. static Bool   nr_ProtectedNode( REGFILE *reg, REGOFF key );
  1514. static REGERR nr_RegAddKey( REGFILE *reg, RKEY key, char *path, RKEY *newKey );
  1515. static void   nr_Upgrade_1_1( REGFILE *reg );
  1516. /* --------------------------------------------------------------------- */
  1517.  
  1518.  
  1519. static REGOFF nr_TranslateKey( REGFILE *reg, RKEY key )
  1520. {
  1521.     REGOFF retKey = 0;
  1522.  
  1523.     /* if it's a special key  */
  1524.     if ( key < HDRRESERVE )  {
  1525.         /* ...translate it */
  1526.         switch (key)
  1527.         {
  1528.             case ROOTKEY:
  1529.                 retKey = reg->hdr.root;
  1530.                 break;
  1531.  
  1532.             case ROOTKEY_VERSIONS:
  1533.                 retKey = reg->rkeys.versions;
  1534.                 break;
  1535.  
  1536.             case ROOTKEY_USERS:
  1537.                 retKey = reg->rkeys.users;
  1538.                 break;
  1539.  
  1540.             case ROOTKEY_COMMON:
  1541.                 retKey = reg->rkeys.common;
  1542.                 break;
  1543.  
  1544. #ifndef STANDALONE_REGISTRY
  1545.             case ROOTKEY_CURRENT_USER:
  1546.                 if ( reg->rkeys.current_user == 0 ) {
  1547.                     /* not initialized--find the current user key */
  1548.                     RKEY    userkey = 0;
  1549.                     REGERR  err;
  1550.                     char*   profName;
  1551.  
  1552.                     err = PREF_CopyDefaultCharPref( "profile.name", &profName );
  1553.  
  1554.                     if (err == PREF_NOERROR ) {
  1555.                         /* Don't assign a slot for missing or magic profile */
  1556.                         if ( '\0' == *profName ||
  1557.                             0 == XP_STRCMP(ASW_MAGIC_PROFILE_NAME, profName)) 
  1558.                         {
  1559.                             err = REGERR_FAIL;
  1560.                         } else {
  1561.                             err = nr_RegAddKey( reg, reg->rkeys.users, profName, &userkey );
  1562.                         }
  1563.                         XP_FREE(profName);
  1564.                     }
  1565.                     else {
  1566.                         err = nr_RegAddKey( reg, reg->rkeys.users, "default", &userkey );
  1567.                     }
  1568.  
  1569.                     if ( err == REGERR_OK ) {
  1570.                         reg->rkeys.current_user = userkey;
  1571.                     }
  1572.                 }
  1573.                 retKey = reg->rkeys.current_user;
  1574.                 break;
  1575. #endif /* !STANDALONE_REGISTRY */
  1576.  
  1577.             case ROOTKEY_PRIVATE:
  1578.                 retKey = reg->rkeys.privarea;
  1579.                 break;
  1580.  
  1581.             default:
  1582.                 /* not a valid key */
  1583.                 retKey = 0;
  1584.                 break;
  1585.         }
  1586.     }
  1587.     else {
  1588.         /* ...otherwise it's fine as-is */
  1589.         retKey = (REGOFF)key;
  1590.     }
  1591.     return ( retKey );
  1592. }  /* nr_TranslateKey */
  1593.  
  1594.  
  1595.  
  1596. static void   nr_InitStdRkeys( REGFILE *reg )
  1597. {
  1598.     REGERR      err;
  1599.     RKEY        key;
  1600.     char        *profName;
  1601.  
  1602.     XP_ASSERT( reg != NULL );
  1603.  
  1604.     /* initialize to invalid key values */
  1605.     memset( ®->rkeys, 0, sizeof(STDNODES) );
  1606.  
  1607.     /* Add each key before looking it up.  Adding an already
  1608.      * existing key is harmless, and these MUST exist.
  1609.      */
  1610.  
  1611.     /* ROOTKEY_USERS */
  1612.     err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_USERS_STR, &key );
  1613.     if ( err == REGERR_OK ) {
  1614.         reg->rkeys.users = key;
  1615.     }
  1616.  
  1617.     /* ROOTKEY_COMMON */
  1618.     err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_COMMON_STR, &key );
  1619.     if ( err == REGERR_OK ) {
  1620.         reg->rkeys.common = key;
  1621.     }
  1622.  
  1623.     /* ROOTKEY_VERSIONS */
  1624.     err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_VERSIONS_STR, &key );
  1625.     if ( err == REGERR_OK ) {
  1626.         reg->rkeys.versions = key;
  1627.     }
  1628.  
  1629.     /* ROOTKEY_CURRENT_USER */
  1630. #if 0 /* delay until first use */ /*#ifndef STANDALONE_REGISTRY */
  1631.     err = PREF_CopyDefaultCharPref( "profile.name", &profName );
  1632.  
  1633.     if (err == PREF_NOERROR ) {
  1634.         err = nr_RegAddKey( reg, reg->rkeys.users, profName, &key );
  1635.         XP_FREE(profName);
  1636.     }
  1637.     else {
  1638.         err = nr_RegAddKey( reg, reg->rkeys.users, "default", &key );
  1639.     }
  1640.     if ( err == REGERR_OK ) {
  1641.         reg->rkeys.current_user = key;
  1642.     }
  1643. #endif
  1644.  
  1645.     /* ROOTKEY_PRIVATE */
  1646.     err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_PRIVATE_STR, &key );
  1647.     if ( err == REGERR_OK ) {
  1648.         reg->rkeys.privarea = key;
  1649.     }
  1650. }   /* nr_InitStdRkeys */
  1651.  
  1652.  
  1653.  
  1654. static Bool nr_ProtectedNode( REGFILE *reg, REGOFF key )
  1655. {
  1656.     if ( (key == reg->hdr.root) ||
  1657.          (key == reg->rkeys.users) ||
  1658.          (key == reg->rkeys.versions) ||
  1659.          (key == reg->rkeys.common) ||
  1660.          (key == reg->rkeys.current_user) )
  1661.     {
  1662.         return TRUE;
  1663.     }
  1664.     else
  1665.         return FALSE;
  1666. }
  1667.  
  1668.  
  1669.  
  1670. static REGERR nr_RegAddKey( REGFILE *reg, RKEY key, char *path, RKEY *newKey )
  1671. {
  1672.     REGERR      err;
  1673.     REGDESC     desc;
  1674.     char        namebuf[MAXREGNAMELEN];
  1675.     char        *p;
  1676.  
  1677.     XP_ASSERT( path != NULL );
  1678.     XP_ASSERT( *path != '\0' );
  1679.     XP_ASSERT( key >= HDRRESERVE );
  1680.     XP_ASSERT( VALID_FILEHANDLE( reg->fh ) );
  1681.  
  1682.     /* lock registry */
  1683.     err = nr_Lock( reg );
  1684.     if ( err != REGERR_OK ) {
  1685.         return err;
  1686.     }
  1687.  
  1688.     /* Get starting desc */
  1689.     err = nr_ReadDesc( reg, key, &desc );
  1690.  
  1691.     /* Walk 'path', reading keys into 'desc' */
  1692.     p = path;
  1693.     while ( err == REGERR_OK ) {
  1694.  
  1695.         /* get next name on the path */
  1696.         err = nr_NextName(p, namebuf, sizeof(namebuf), &p);
  1697.         if ( err == REGERR_OK ) {
  1698.  
  1699.             /* look for name at next level down */
  1700.             err = nr_FindAtLevel(reg, desc.down, namebuf, &desc, 0);
  1701.  
  1702.             /* if key is not found */
  1703.             if ( err == REGERR_NOFIND ) {
  1704.                 /* add it as a sub-key to the last found key */
  1705.                 err = nr_CreateSubKey(reg, &desc, namebuf);
  1706.             }
  1707.         }
  1708.     }
  1709.  
  1710.     XP_ASSERT( err != REGERR_OK );
  1711.     /* it's good to have processed the whole path */
  1712.     if ( err == REGERR_NOMORE ) {
  1713.         err = REGERR_OK;
  1714.  
  1715.         /* return new key if the caller wants it */
  1716.         if ( newKey != NULL ) {
  1717.             *newKey = desc.location;
  1718.         }
  1719.     }
  1720.  
  1721.     /* unlock registry */
  1722.     nr_Unlock(reg);
  1723.  
  1724.     return err;
  1725.  
  1726. }   /* nr_RegAddKey */
  1727.  
  1728.  
  1729.  
  1730. static void nr_Upgrade_1_1(REGFILE *reg)
  1731. {
  1732.     REGDESC desc;
  1733.     REGOFF  level;
  1734.     REGERR err;
  1735.  
  1736.     XP_ASSERT(reg);
  1737.  
  1738.     /* find offset for start of the top-level nodes */
  1739.     err = nr_ReadDesc( reg, reg->hdr.root, &desc);
  1740.     if ( err != REGERR_OK ) {
  1741.         return;
  1742.     }
  1743.     level = desc.down;
  1744.  
  1745.     /* ROOTKEY_VERSIONS */
  1746.     err = nr_FindAtLevel( reg, level, OLD_VERSIONS_STR, &desc, NULL );
  1747.     if ( err == REGERR_NOFIND ) {
  1748.         err = REGERR_OK;
  1749.     }
  1750.     else if ( REGERR_OK == err ) {
  1751.         err = nr_AppendName( reg, ROOTKEY_VERSIONS_STR, &desc );
  1752.         if ( REGERR_OK == err ) {
  1753.             err = nr_WriteDesc( reg, &desc );
  1754.         }
  1755.     }
  1756.     if ( err != REGERR_OK )
  1757.         return;
  1758.  
  1759.     /* ROOTKEY_USERS */
  1760.     err = nr_FindAtLevel( reg, level, OLD_USERS_STR, &desc, NULL );
  1761.     if ( err == REGERR_NOFIND ) {
  1762.         err = REGERR_OK;
  1763.     }
  1764.     else if ( REGERR_OK == err ) {
  1765.         err = nr_AppendName( reg, ROOTKEY_USERS_STR, &desc );
  1766.         if ( REGERR_OK == err ) {
  1767.             err = nr_WriteDesc( reg, &desc );
  1768.         }
  1769.     }
  1770.     if ( err != REGERR_OK )
  1771.         return;
  1772.  
  1773.     /* ROOTKEY_COMMON */
  1774.     err = nr_FindAtLevel( reg, level, OLD_COMMON_STR, &desc, NULL );
  1775.     if ( err == REGERR_NOFIND ) {
  1776.         err = REGERR_OK;
  1777.     }
  1778.     else if ( REGERR_OK == err ) {
  1779.         err = nr_AppendName( reg, ROOTKEY_COMMON_STR, &desc );
  1780.         if ( REGERR_OK == err ) {
  1781.             err = nr_WriteDesc( reg, &desc );
  1782.         }
  1783.     }
  1784.     if ( err != REGERR_OK )
  1785.         return;
  1786.  
  1787.     /* Mark registry with new version */
  1788.     reg->hdr.verMinor = MINOR_VERSION;
  1789.     nr_WriteHdr(reg);
  1790.  
  1791. }
  1792.  
  1793.  
  1794.  
  1795. /* ---------------------------------------------------------------------
  1796.  * Public API
  1797.  * --------------------------------------------------------------------- */
  1798.  
  1799. /* ---------------------------------------------------------------------
  1800.  * NReg_Open - Open a netscape XP registry
  1801.  *
  1802.  * Parameters:
  1803.  *    filename   - registry file to open. NULL or ""  opens standard
  1804.  *                 local registry. (This parameter currently ignored)
  1805.  *    hReg       - OUT: handle to opened registry
  1806.  *
  1807.  * Output:
  1808.  * ---------------------------------------------------------------------
  1809.  */
  1810. VR_INTERFACE(REGERR) NR_RegOpen( char *filename, HREG *hReg )
  1811. {
  1812.     REGERR    status = REGERR_OK;
  1813.     REGFILE   *pReg;
  1814.     REGHANDLE *pHandle;
  1815.  
  1816.     XP_ASSERT(bRegStarted);
  1817.  
  1818.     /* initialize output handle in case of error */
  1819.     *hReg = NULL;
  1820.  
  1821. #if !defined(STANDALONE_REGISTRY)
  1822.     PR_EnterMonitor(reglist_monitor);
  1823. #endif
  1824.  
  1825.     /* Look for named file in list of open registries */
  1826.     if (filename == NULL) {
  1827.         filename = "";
  1828.     }
  1829.     pReg = vr_findRegFile( filename );
  1830.  
  1831.     /* if registry not already open */
  1832.     if (pReg == NULL) {
  1833.  
  1834.         /* ...then open it */
  1835.         pReg = (REGFILE*)XP_ALLOC( sizeof(REGFILE) );
  1836.         if ( pReg == NULL ) {
  1837.             status = REGERR_MEMORY;
  1838.             goto bail;
  1839.         }
  1840.         XP_MEMSET(pReg, 0, sizeof(REGFILE));
  1841.  
  1842.         pReg->inInit = TRUE;
  1843.         pReg->filename = XP_STRDUP(filename);
  1844.         if (pReg->filename == NULL) {
  1845.             XP_FREE( pReg );
  1846.             status = REGERR_MEMORY;
  1847.             goto bail;
  1848.         }
  1849.  
  1850.         status = nr_OpenFile( filename, &(pReg->fh) );
  1851.         if (status == REGERR_READONLY) {
  1852.             /* Open, but read only */
  1853.             pReg->readOnly = TRUE;
  1854.             status = REGERR_OK;
  1855.         }
  1856.         if ( status != REGERR_OK ) {
  1857.             XP_FREE( pReg );
  1858.             goto bail;
  1859.         }
  1860.  
  1861.         /* ...read and validate the header */
  1862.         status = nr_ReadHdr( pReg );
  1863.         if ( status != REGERR_OK ) {
  1864.             nr_CloseFile( &(pReg->fh) );
  1865.             XP_FREE( pReg );
  1866.             goto bail;
  1867.         }
  1868.  
  1869.         /* ...other misc initialization */
  1870.         pReg->refCount = 0;
  1871.  
  1872. #ifndef STANDALONE_REGISTRY
  1873.         pReg->monitor = PR_NewMonitor();
  1874. #endif
  1875.         /* now done with everything that needs to protect the header */
  1876.         pReg->inInit = FALSE;
  1877.  
  1878.         if ( pReg->hdr.verMajor == 1 && pReg->hdr.verMinor <= 1 )
  1879.             nr_Upgrade_1_1(pReg);
  1880.  
  1881.         nr_InitStdRkeys( pReg );
  1882.  
  1883.         /* ...and add it to the list */
  1884.         nr_AddNode( pReg );
  1885.     }
  1886.  
  1887.     /* create a new handle to the regfile */
  1888.     pHandle = (REGHANDLE*)XP_ALLOC( sizeof(REGHANDLE) );
  1889.     if ( pHandle == NULL ) {
  1890.         /* we can't create the handle */
  1891.         if ( pReg->refCount == 0 ) {
  1892.             /* we've just opened it so close it and remove node */
  1893.             nr_CloseFile( &(pReg->fh) );
  1894.             nr_DeleteNode( pReg );
  1895.         }
  1896.  
  1897.         status = REGERR_MEMORY;
  1898.         goto bail;
  1899.     }
  1900.  
  1901.     pHandle->magic   = MAGIC_NUMBER;
  1902.     pHandle->pReg    = pReg;
  1903.  
  1904.     /* success: bump the reference count and return the handle */
  1905.     pReg->refCount++;
  1906.     *hReg = (void*)pHandle;
  1907.  
  1908. bail:
  1909. #if !defined(STANDALONE_REGISTRY)
  1910.     PR_ExitMonitor(reglist_monitor);
  1911. #endif
  1912.  
  1913.     return status;
  1914.  
  1915. }   /* NR_RegOpen */
  1916.  
  1917.  
  1918.  
  1919. /* ---------------------------------------------------------------------
  1920.  * NR_RegClose - Close a netscape XP registry
  1921.  *
  1922.  * Parameters:
  1923.  *    hReg     - handle of open registry to be closed.
  1924.  *
  1925.  * After calling this routine the handle is no longer valid
  1926.  * ---------------------------------------------------------------------
  1927.  */
  1928. VR_INTERFACE(REGERR) NR_RegClose( HREG hReg )
  1929. {
  1930.     REGERR      err;
  1931.     REGHANDLE*  reghnd = (REGHANDLE*)hReg;
  1932.  
  1933.     XP_ASSERT(bRegStarted);
  1934.  
  1935.     /* verify handle */
  1936.     err = VERIFY_HREG( hReg );
  1937.     if ( err != REGERR_OK )
  1938.         return err;
  1939.  
  1940.     XP_ASSERT( VALID_FILEHANDLE(reghnd->pReg->fh) );
  1941.  
  1942. #if !defined(STANDALONE_REGISTRY)
  1943.     PR_EnterMonitor(reglist_monitor);
  1944. #endif
  1945.     /* lower REGFILE user count */
  1946.     reghnd->pReg->refCount--;
  1947.  
  1948.     /* if registry is no longer in use */
  1949.     if ( reghnd->pReg->refCount < 1 ) {
  1950.         /* ...then close the file */
  1951.         if ( reghnd->pReg->hdrDirty ) {
  1952.             nr_WriteHdr( reghnd->pReg );
  1953.         }
  1954.         nr_CloseFile( &(reghnd->pReg->fh) );
  1955.         /* ...and delete REGFILE node from list */
  1956.         nr_DeleteNode( reghnd->pReg );
  1957.     }
  1958. #if !defined(STANDALONE_REGISTRY)
  1959.     PR_ExitMonitor(reglist_monitor);
  1960. #endif
  1961.  
  1962.     reghnd->magic = 0;    /* prevent accidental re-use */
  1963.     XP_FREE( reghnd );
  1964.  
  1965.     return REGERR_OK;
  1966.  
  1967. }   /* NR_RegClose */
  1968.  
  1969.  
  1970.  
  1971. /* ---------------------------------------------------------------------
  1972.  * NR_RegPack    - Pack an open registry.  
  1973.  *                Registry is locked the entire time.
  1974.  *
  1975.  * Parameters:
  1976.  *    hReg     - handle of open registry to pack
  1977.  * ---------------------------------------------------------------------
  1978.  */
  1979. VR_INTERFACE(REGERR) NR_RegPack( HREG hReg )
  1980. {
  1981.     XP_ASSERT(bRegStarted);
  1982.  
  1983.     return REGERR_FAIL;
  1984. }
  1985.  
  1986.  
  1987.  
  1988. /* ---------------------------------------------------------------------
  1989.  * NR_RegAddKey - Add a key node to the registry
  1990.  *
  1991.  *      This routine is simply a wrapper to perform user input
  1992.  *      validation and translation from HREG and standard key
  1993.  *      values into the internal format
  1994.  *
  1995.  * Parameters:
  1996.  *    hReg     - handle of open registry
  1997.  *    key      - registry key obtained from NR_RegGetKey(),
  1998.  *               or one of the standard top-level keys
  1999.  *    path     - relative path of key to be added.  Intermediate
  2000.  *               nodes will also be added if necessary.
  2001.  * ---------------------------------------------------------------------
  2002.  */
  2003. VR_INTERFACE(REGERR) NR_RegAddKey( HREG hReg, RKEY key, char *path, RKEY *newKey )
  2004. {
  2005.     REGERR      err;
  2006.     REGOFF      start;
  2007.     REGFILE*    reg;
  2008.  
  2009.     XP_ASSERT(bRegStarted);
  2010.  
  2011.     /* verify parameters */
  2012.     err = VERIFY_HREG( hReg );
  2013.     if ( err != REGERR_OK )
  2014.         return err;
  2015.  
  2016.     reg = ((REGHANDLE*)hReg)->pReg;
  2017.  
  2018.     start = nr_TranslateKey( reg, key );
  2019.  
  2020.     /* ... don't allow additional children of ROOTKEY */
  2021.     if ( start == reg->hdr.root )
  2022.         return REGERR_PARAM;
  2023.  
  2024.     if ( path == NULL || *path == '\0' || start == 0 || reg == NULL )
  2025.         return REGERR_PARAM;
  2026.  
  2027.     /* call the real routine */
  2028.     return nr_RegAddKey( reg, start, path, newKey );
  2029. }   /* NR_RegAddKey */
  2030.  
  2031.  
  2032.  
  2033. /* ---------------------------------------------------------------------
  2034.  * NR_RegDeleteKey - Delete the specified key and all of its children
  2035.  *
  2036.  * Note that delete simply orphans blocks and makes no attempt
  2037.  * to reclaim space in the file. Use NR_RegPack()
  2038.  *
  2039.  * Parameters:
  2040.  *    hReg     - handle of open registry
  2041.  *    key      - starting node RKEY, typically one of the standard ones.
  2042.  *    path     - relative path of key to delete
  2043.  * ---------------------------------------------------------------------
  2044.  */
  2045. VR_INTERFACE(REGERR) NR_RegDeleteKey( HREG hReg, RKEY key, char *path )
  2046. {
  2047.     REGERR      err;
  2048.     REGOFF      start;
  2049.     REGFILE*    reg;
  2050.     REGDESC     desc;
  2051.     REGDESC     predecessor;
  2052.     REGOFF      offPrev;
  2053.     REGOFF      offParent;
  2054.     REGOFF*     link;
  2055.  
  2056.     XP_ASSERT(bRegStarted);
  2057.  
  2058.     /* verify parameters */
  2059.     err = VERIFY_HREG( hReg );
  2060.     if ( err != REGERR_OK )
  2061.         return err;
  2062.  
  2063.     reg = ((REGHANDLE*)hReg)->pReg;
  2064.  
  2065.     start = nr_TranslateKey( reg, key );
  2066.     if ( path == NULL || *path == '\0' || start == 0 )
  2067.         return REGERR_PARAM;
  2068.  
  2069.     /* lock registry */
  2070.     err = nr_Lock( reg );
  2071.     if ( err != REGERR_OK )
  2072.         return err;
  2073.  
  2074.     /* find the specified key */
  2075.     err = nr_Find( reg, start, path, &desc, &offPrev, &offParent );
  2076.     if ( err == REGERR_OK ) {
  2077.  
  2078.         XP_ASSERT( !TYPE_IS_ENTRY( desc.type ) );
  2079.  
  2080.         /* make sure it's childless and not a top-level key */
  2081.         if ( (desc.down == 0) && !nr_ProtectedNode( reg, desc.location ) ) {
  2082.  
  2083.             /* Are we the first on our level? */
  2084.             if ( offPrev == 0 ) {
  2085.                 /* Yes: link to parent's "down" pointer */
  2086.                 err = nr_ReadDesc( reg, offParent, &predecessor );
  2087.                 link = &(predecessor.down);
  2088.             }
  2089.             else {
  2090.                 /* No: link using predecessor's "left" pointer */
  2091.                 err = nr_ReadDesc( reg, offPrev, &predecessor );
  2092.                 link = &(predecessor.left);
  2093.             }
  2094.  
  2095.             /* If we read the predecessor desc OK */
  2096.             if (err == REGERR_OK) {
  2097.                 XP_ASSERT( *link == desc.location );
  2098.  
  2099.                 /* link predecessor to next, removing current node from chain */
  2100.                 *link = desc.left;
  2101.  
  2102.                 /* Write the updated predecessor */
  2103.                 err = nr_WriteDesc( reg, &predecessor );
  2104.                 if ( err == REGERR_OK ) {
  2105.                     /* Mark key deleted to prevent bogus use by anyone
  2106.                      * who is holding an RKEY for that node
  2107.                      */
  2108.                     desc.type |= REGTYPE_DELETED;
  2109.                     err = nr_WriteDesc( reg, &desc );
  2110.                 }
  2111.             }
  2112.         }
  2113.         else {
  2114.             /* specified node is protected from deletion */
  2115.             err = REGERR_FAIL;
  2116.         }
  2117.     }
  2118.  
  2119.     /* unlock registry */
  2120.     nr_Unlock(reg);
  2121.  
  2122.     return err;
  2123.  
  2124. }   /* NR_RegDeleteKey */
  2125.  
  2126.  
  2127.  
  2128. /* ---------------------------------------------------------------------
  2129.  * NR_RegGetKey - Get the RKEY value of a node from its path
  2130.  *
  2131.  * Parameters:
  2132.  *    hReg     - handle of open registry
  2133.  *    key      - starting node RKEY, typically one of the standard ones.
  2134.  *    path     - relative path of key to find.  (a blank path just gives you
  2135.  *               the starting key--useful for verification, VersionRegistry)
  2136.  * ---------------------------------------------------------------------
  2137.  */
  2138. VR_INTERFACE(REGERR) NR_RegGetKey( HREG hReg, RKEY key, char *path, RKEY *result )
  2139. {
  2140.     REGERR      err;
  2141.     REGOFF      start;
  2142.     REGFILE*    reg;
  2143.     REGDESC     desc;
  2144.  
  2145.     XP_ASSERT(bRegStarted);
  2146.  
  2147.     /* verify parameters */
  2148.     err = VERIFY_HREG( hReg );
  2149.     if ( err != REGERR_OK )
  2150.         return err;
  2151.  
  2152.     reg = ((REGHANDLE*)hReg)->pReg;
  2153.     start = nr_TranslateKey( reg, key );
  2154.  
  2155.     if ( path == NULL || start == 0 || result == NULL )
  2156.         return REGERR_PARAM;
  2157.  
  2158.     /* find the specified key */
  2159.     err = nr_Find( reg, start, path, &desc, 0, 0 );
  2160.     if ( err == REGERR_OK ) {
  2161.         *result = (RKEY)desc.location;
  2162.     }
  2163.  
  2164.     return err;
  2165.  
  2166. }   /* NR_RegGetKey */
  2167.  
  2168.  
  2169.  
  2170. /* ---------------------------------------------------------------------
  2171.  * NR_RegGetEntryInfo - Get some basic info about the entry data
  2172.  *
  2173.  * Parameters:
  2174.  *    hReg     - handle of open registry
  2175.  *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
  2176.  *    name     - name of entry
  2177.  *    info     - return: Entry info object
  2178.  * ---------------------------------------------------------------------
  2179.  */
  2180. VR_INTERFACE(REGERR) NR_RegGetEntryInfo( HREG hReg, RKEY key, char *name, 
  2181.                             REGINFO *info )
  2182. {
  2183.     REGERR      err;
  2184.     REGFILE*    reg;
  2185.     REGDESC     desc;
  2186.     
  2187.     XP_ASSERT(bRegStarted);
  2188.  
  2189.     /* verify parameters */
  2190.     err = VERIFY_HREG( hReg );
  2191.     if ( err != REGERR_OK )
  2192.         return err;
  2193.  
  2194.     if ( name == NULL || *name == '\0' || info == NULL )
  2195.         return REGERR_PARAM;
  2196.  
  2197.     reg = ((REGHANDLE*)hReg)->pReg;
  2198.  
  2199.     /* read starting desc */
  2200.     err = nr_ReadDesc( reg, key, &desc);
  2201.     if ( err == REGERR_OK ) {
  2202.  
  2203.         /* if the named entry exists */
  2204.         err = nr_FindAtLevel( reg, desc.value, name, &desc, NULL );
  2205.         if ( err == REGERR_OK ) {
  2206.  
  2207.             /* ... return the values */
  2208.             if ( info->size != sizeof(REGINFO) )
  2209.                 err = REGERR_PARAM;
  2210.             else {
  2211.                 info->entryType   = desc.type;
  2212.                 info->entryLength = desc.valuelen;
  2213.             }
  2214.         }
  2215.     }
  2216.  
  2217.     return err;
  2218.  
  2219. }   /* NR_RegGetEntryInfo */
  2220.  
  2221.  
  2222.        
  2223. /* ---------------------------------------------------------------------
  2224.  * NR_RegGetEntryString - Get the UTF string value associated with the
  2225.  *                       named entry of the specified key.
  2226.  *
  2227.  * Parameters:
  2228.  *    hReg     - handle of open registry
  2229.  *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
  2230.  *    name     - name of entry
  2231.  *    buffer   - destination for string
  2232.  *    bufsize  - size of buffer
  2233.  * ---------------------------------------------------------------------
  2234.  */
  2235. VR_INTERFACE(REGERR) NR_RegGetEntryString( HREG  hReg, RKEY  key, char  *name,
  2236.                             char  *buffer, uint32 bufsize)
  2237. {
  2238.     REGERR      err;
  2239.     REGFILE*    reg;
  2240.     REGDESC     desc;
  2241.  
  2242.     XP_ASSERT(bRegStarted);
  2243.  
  2244.     /* verify parameters */
  2245.     err = VERIFY_HREG( hReg );
  2246.     if ( err != REGERR_OK )
  2247.         return err;
  2248.  
  2249.     if ( name == NULL || *name == '\0' || buffer == NULL || bufsize == 0 )
  2250.         return REGERR_PARAM;
  2251.  
  2252.     reg = ((REGHANDLE*)hReg)->pReg;
  2253.  
  2254.     /* read starting desc */
  2255.     err = nr_ReadDesc( reg, key, &desc);
  2256.     if ( err == REGERR_OK ) 
  2257.     {
  2258.         /* if the named entry exists */
  2259.         err = nr_FindAtLevel( reg, desc.value, name, &desc, NULL );
  2260.         if ( err == REGERR_OK ) 
  2261.         {
  2262.             /* read the string */
  2263.             if ( desc.type == REGTYPE_ENTRY_STRING_UTF ) 
  2264.             {
  2265.                 err = nr_ReadData( reg, &desc, bufsize, buffer );
  2266.                 /* prevent run-away strings */
  2267.                 buffer[bufsize-1] = '\0';
  2268.             }
  2269.             else {
  2270.                 err = REGERR_BADTYPE;
  2271.             }
  2272.         }
  2273.     }
  2274.  
  2275.     return err;
  2276.  
  2277. }   /* NR_RegGetEntryString */
  2278.  
  2279.  
  2280.  
  2281.  
  2282. /* ---------------------------------------------------------------------
  2283.  * NR_RegGetEntry - Get the value data associated with the
  2284.  *                  named entry of the specified key.
  2285.  *
  2286.  * Parameters:
  2287.  *    hReg     - handle of open registry
  2288.  *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
  2289.  *    name     - name of entry
  2290.  *    buffer   - destination for data
  2291.  *    size     - in:  size of buffer
  2292.  *               out: size of actual data (incl. \0 term. for strings)
  2293.  * ---------------------------------------------------------------------
  2294.  */
  2295. VR_INTERFACE(REGERR) NR_RegGetEntry( HREG hReg, RKEY key, char *name,
  2296.     void *buffer, uint32 *size )
  2297. {
  2298.     REGERR      err;
  2299.     REGFILE*    reg;
  2300.     REGDESC     desc;
  2301.     char        *tmpbuf = NULL;  /* malloc a tmp buffer to convert XP int arrays */
  2302.     uint32      nInt;
  2303.     uint32      *pISrc;
  2304.     uint32      *pIDest;
  2305.     XP_Bool     needFree = FALSE;
  2306.  
  2307.     XP_ASSERT(bRegStarted);
  2308.  
  2309.     /* verify parameters */
  2310.     err = VERIFY_HREG( hReg );
  2311.     if ( err != REGERR_OK )
  2312.         return err;
  2313.  
  2314.     if ( name == NULL || *name == '\0' || buffer == NULL || size == NULL )
  2315.         return REGERR_PARAM;
  2316.  
  2317.     reg = ((REGHANDLE*)hReg)->pReg;
  2318.  
  2319.     /* read starting desc */
  2320.     err = nr_ReadDesc( reg, key, &desc);
  2321.     if ( err == REGERR_OK )
  2322.     {
  2323.         /* if the named entry exists */
  2324.         err = nr_FindAtLevel( reg, desc.value, name, &desc, NULL );
  2325.         if ( err == REGERR_OK )
  2326.         {
  2327.             if ( desc.valuelen > *size ) {
  2328.                 err = REGERR_BUFTOOSMALL;
  2329.             }
  2330.             else if ( desc.valuelen == 0 ) {
  2331.                 err = REGERR_FAIL;
  2332.             }
  2333.             else switch (desc.type)
  2334.             {
  2335.                 /* platform independent array of 32-bit integers */
  2336.                 case REGTYPE_ENTRY_INT32_ARRAY:
  2337.                     tmpbuf = (char*)XP_ALLOC( desc.valuelen );
  2338.                     if ( tmpbuf != NULL ) 
  2339.                     {
  2340.                         needFree = TRUE;
  2341.                         err = nr_ReadData( reg, &desc, desc.valuelen, tmpbuf );
  2342.                         if ( REGERR_OK == err )
  2343.                         {
  2344.                             /* convert int array */
  2345.                             nInt = (desc.valuelen / INTSIZE);
  2346.                             pISrc = (uint32*)tmpbuf;
  2347.                             pIDest = (uint32*)buffer;
  2348.                             for(; nInt > 0; nInt--, pISrc++, pIDest++) {
  2349.                                 *pIDest = nr_ReadLong((char*)pISrc);
  2350.                             }
  2351.                         }
  2352.                     }
  2353.                     else
  2354.                         err = REGERR_MEMORY;
  2355.  
  2356.                     break;
  2357.  
  2358.  
  2359.                 case REGTYPE_ENTRY_STRING_UTF:
  2360.                     tmpbuf = (char*)buffer;
  2361.                     err = nr_ReadData( reg, &desc, *size, tmpbuf );
  2362.                     /* prevent run-away strings */
  2363.                     tmpbuf[(*size)-1] = '\0';
  2364.                     break;
  2365.  
  2366.  
  2367.                 case REGTYPE_ENTRY_BYTES:
  2368.                 default:              /* return raw data for unknown types */
  2369.                     err = nr_ReadData( reg, &desc, *size, (char*)buffer );
  2370.                     break;
  2371.             }
  2372.             
  2373.             /* return the actual data size */
  2374.             *size = desc.valuelen;
  2375.         }
  2376.     }
  2377.  
  2378.     if (needFree)
  2379.         XP_FREE(tmpbuf);
  2380.  
  2381.     return err;
  2382.  
  2383. }   /* NR_RegGetEntry */
  2384.  
  2385.  
  2386.  
  2387. /* ---------------------------------------------------------------------
  2388.  * NR_RegSetEntryString - Store a UTF string value associated with the
  2389.  *                       named entry of the specified key.  Used for
  2390.  *                       both creation and update.
  2391.  *
  2392.  * Parameters:
  2393.  *    hReg     - handle of open registry
  2394.  *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
  2395.  *    name     - name of entry
  2396.  *    buffer   - UTF String to store
  2397.  * ---------------------------------------------------------------------
  2398.  */
  2399. VR_INTERFACE(REGERR) NR_RegSetEntryString( HREG hReg, RKEY key, char *name,
  2400.                                      char *buffer )
  2401. {
  2402.     REGERR      err;
  2403.     REGFILE*    reg;
  2404.     REGDESC     desc;
  2405.     REGDESC     parent;
  2406.  
  2407.     XP_ASSERT(bRegStarted);
  2408.  
  2409.     /* verify parameters */
  2410.     err = VERIFY_HREG( hReg );
  2411.     if ( err != REGERR_OK )
  2412.         return err;
  2413.  
  2414.     if ( name == NULL || *name == '\0' || buffer == NULL )
  2415.         return REGERR_PARAM;
  2416.  
  2417.     reg = ((REGHANDLE*)hReg)->pReg;
  2418.  
  2419.     /* lock registry */
  2420.     err = nr_Lock( reg );
  2421.     if ( err != REGERR_OK )
  2422.         return err;
  2423.  
  2424.     /* read starting desc */
  2425.     err = nr_ReadDesc( reg, key, &parent);
  2426.     if ( err == REGERR_OK ) {
  2427.  
  2428.         /* if the named entry already exists */
  2429.         err = nr_FindAtLevel( reg, parent.value, name, &desc, NULL );
  2430.         if ( err == REGERR_OK ) {
  2431.             /* then update the existing one */
  2432.             err = nr_WriteString( reg, buffer, &desc );
  2433.             if ( err == REGERR_OK ) {
  2434.                 desc.type = REGTYPE_ENTRY_STRING_UTF;
  2435.                 err = nr_WriteDesc( reg, &desc );
  2436.             }
  2437.         }
  2438.         else if ( err == REGERR_NOFIND ) {
  2439.             /* otherwise create a new entry */
  2440.             err = nr_CreateEntryString( reg, &parent, name, buffer );
  2441.         }
  2442.         /* other errors fall through */
  2443.     }
  2444.  
  2445.     /* unlock registry */
  2446.     nr_Unlock( reg );
  2447.  
  2448.     return err;
  2449.  
  2450. }   /* NR_RegSetEntryString */
  2451.  
  2452.  
  2453.  
  2454. /* ---------------------------------------------------------------------
  2455.  * NR_RegSetEntry - Store value data associated with the named entry
  2456.  *                  of the specified key.  Used for both creation and update.
  2457.  *
  2458.  * Parameters:
  2459.  *    hReg     - handle of open registry
  2460.  *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
  2461.  *    name     - name of entry
  2462.  *    type     - type of data to be stored
  2463.  *    buffer   - data to store
  2464.  *    size     - length of data to store in bytes
  2465.  * ---------------------------------------------------------------------
  2466.  */
  2467. VR_INTERFACE(REGERR) NR_RegSetEntry( HREG hReg, RKEY key, char *name, uint16 type,
  2468.     void *buffer, uint32 size )
  2469. {
  2470.     REGERR      err;
  2471.     REGFILE*    reg;
  2472.     REGDESC     desc;
  2473.     REGDESC     parent;
  2474.     char        *data;
  2475.     uint32      nInt;
  2476.     uint32      *pIDest;
  2477.     uint32      *pISrc;
  2478.     XP_Bool     needFree = FALSE;
  2479.  
  2480.     XP_ASSERT(bRegStarted);
  2481.  
  2482.     /* verify parameters */
  2483.     err = VERIFY_HREG( hReg );
  2484.     if ( err != REGERR_OK )
  2485.         return err;
  2486.  
  2487.     if ( name == NULL || *name == '\0' || buffer == NULL || size == 0 )
  2488.         return REGERR_PARAM;
  2489.  
  2490.     reg = ((REGHANDLE*)hReg)->pReg;
  2491.  
  2492.     /* validate type and convert numerics to XP format */
  2493.     switch (type)
  2494.     {
  2495.         case REGTYPE_ENTRY_BYTES:
  2496.             data = (char*)buffer;
  2497.             break;
  2498.  
  2499.  
  2500.         case REGTYPE_ENTRY_STRING_UTF:
  2501.             data = (char*)buffer;
  2502.             /* string must be null terminated */
  2503.             if ( data[size-1] != '\0' )
  2504.                 return REGERR_PARAM;
  2505.             break;
  2506.  
  2507.  
  2508.         case REGTYPE_ENTRY_INT32_ARRAY:
  2509.             /* verify no partial integers */
  2510.             if ( (size % INTSIZE) != 0 )
  2511.                 return REGERR_PARAM;
  2512.  
  2513.             /* get a conversion buffer */
  2514.             data = (char*)XP_ALLOC(size);
  2515.             if ( data == NULL )
  2516.                 return REGERR_MEMORY;
  2517.             else
  2518.                 needFree = TRUE;
  2519.  
  2520.             /* convert array to XP format */
  2521.             nInt = ( size / INTSIZE );
  2522.             pIDest = (uint32*)data;
  2523.             pISrc  = (uint32*)buffer;
  2524.  
  2525.             for( ; nInt > 0; nInt--, pIDest++, pISrc++) {
  2526.                 nr_WriteLong( *pISrc, (char*)pIDest );
  2527.             }
  2528.             break;
  2529.  
  2530.  
  2531.         default:
  2532.             return REGERR_BADTYPE;
  2533.     }
  2534.  
  2535.     /* lock registry */
  2536.     err = nr_Lock( reg );
  2537.     if ( REGERR_OK == err )
  2538.     {
  2539.         /* read starting desc */
  2540.         err = nr_ReadDesc( reg, key, &parent);
  2541.         if ( err == REGERR_OK ) 
  2542.         {
  2543.             /* if the named entry already exists */
  2544.             err = nr_FindAtLevel( reg, parent.value, name, &desc, NULL );
  2545.             if ( err == REGERR_OK ) 
  2546.             {
  2547.                 /* then update the existing one */
  2548.                 err = nr_WriteData( reg, data, size, &desc );
  2549.                 if ( err == REGERR_OK ) 
  2550.                 {
  2551.                     desc.type = type;
  2552.                     err = nr_WriteDesc( reg, &desc );
  2553.                 }
  2554.             }
  2555.             else if ( err == REGERR_NOFIND ) 
  2556.             {
  2557.                 /* otherwise create a new entry */
  2558.                 err = nr_CreateEntry( reg, &parent, name, type, data, size );
  2559.             }
  2560.             else {
  2561.                 /* other errors fall through */
  2562.             }
  2563.         }
  2564.     }
  2565.  
  2566.     /* unlock registry */
  2567.     nr_Unlock( reg );
  2568.  
  2569.     if (needFree)
  2570.         XP_FREE(data);
  2571.  
  2572.     return err;
  2573.  
  2574. }   /* NR_RegSetEntry */
  2575.  
  2576.  
  2577.  
  2578. /* ---------------------------------------------------------------------
  2579.  * NR_RegDeleteEntry - Delete the named entry
  2580.  *
  2581.  * Parameters:
  2582.  *    hReg     - handle of open registry
  2583.  *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
  2584.  *    name     - name of entry
  2585.  * ---------------------------------------------------------------------
  2586.  */
  2587. VR_INTERFACE(REGERR) NR_RegDeleteEntry( HREG hReg, RKEY key, char *name )
  2588. {
  2589.     REGERR      err;
  2590.     REGFILE*    reg;
  2591.     REGDESC     desc;
  2592.     REGDESC     parent;
  2593.     REGOFF      offPrev;
  2594.  
  2595.     XP_ASSERT(bRegStarted);
  2596.  
  2597.     /* verify parameters */
  2598.     err = VERIFY_HREG( hReg );
  2599.     if ( err != REGERR_OK )
  2600.         return err;
  2601.  
  2602.     if ( name == NULL || *name == '\0' )
  2603.         return REGERR_PARAM;
  2604.  
  2605.     reg = ((REGHANDLE*)hReg)->pReg;
  2606.  
  2607.     /* lock registry */
  2608.     err = nr_Lock( reg );
  2609.     if ( err != REGERR_OK )
  2610.         return err;
  2611.  
  2612.     /* read starting desc */
  2613.     err = nr_ReadDesc( reg, key, &parent);
  2614.     if ( err == REGERR_OK ) {
  2615.  
  2616.         /* look up the named entry */
  2617.         err = nr_FindAtLevel( reg, parent.value, name, &desc, &offPrev );
  2618.         if ( err == REGERR_OK ) {
  2619.  
  2620.             XP_ASSERT( TYPE_IS_ENTRY( desc.type ) );
  2621.  
  2622.             /* if entry is the head of a chain */
  2623.             if ( offPrev == 0 ) {
  2624.                 /* hook parent key to next entry */
  2625.                 XP_ASSERT( parent.value == desc.location );
  2626.                 parent.value = desc.left;
  2627.             }
  2628.             else {
  2629.                 /* otherwise hook previous entry to next */
  2630.                 err = nr_ReadDesc( reg, offPrev, &parent );
  2631.                 parent.left = desc.left;
  2632.             }
  2633.             /* write out changed desc for previous node */
  2634.             if ( err == REGERR_OK ) {
  2635.                 err = nr_WriteDesc( reg, &parent );
  2636.                 /* zap the deleted desc because an enum state may contain a
  2637.                  * reference to a specific entry node
  2638.                  */
  2639.                 if ( err == REGERR_OK ) {
  2640.                     desc.type |= REGTYPE_DELETED;
  2641.                     err = nr_WriteDesc( reg, &desc );
  2642.                 }
  2643.             }
  2644.         }
  2645.     }
  2646.  
  2647.     /* unlock registry */
  2648.     nr_Unlock( reg );
  2649.  
  2650.     return err;
  2651.  
  2652. }   /* NR_RegDeleteEntry */
  2653.  
  2654.  
  2655.  
  2656. /* ---------------------------------------------------------------------
  2657.  * NR_RegEnumSubkeys - Enumerate the subkey names for the specified key
  2658.  *
  2659.  * Parameters:
  2660.  *    hReg     - handle of open registry
  2661.  *    key      - RKEY of key to enumerate--obtain with NR_RegGetKey()
  2662.  *    eState   - enumerations state, must contain NULL to start
  2663.  *    buffer   - location to store subkey names.  Once an enumeration
  2664.  *               is started user must not modify contents since values
  2665.  *               are built using the previous contents.
  2666.  *    bufsize  - size of buffer for names
  2667.  *    style    - 0 returns direct child keys only, REGENUM_DESCEND
  2668.  *               returns entire sub-tree
  2669.  * ---------------------------------------------------------------------
  2670.  */
  2671. VR_INTERFACE(REGERR) NR_RegEnumSubkeys( HREG hReg, RKEY key, REGENUM *state,
  2672.                                     char *buffer, uint32 bufsize, uint32 style)
  2673. {
  2674.     REGERR      err;
  2675.     REGFILE*    reg;
  2676.     REGDESC     desc;
  2677.  
  2678.     XP_ASSERT(bRegStarted);
  2679.  
  2680.     /* verify parameters */
  2681.     err = VERIFY_HREG( hReg );
  2682.     if ( err != REGERR_OK )
  2683.         return err;
  2684.  
  2685.     reg = ((REGHANDLE*)hReg)->pReg;
  2686.  
  2687.     key = nr_TranslateKey( reg, key );
  2688.  
  2689.     /* verify starting key */
  2690.     err = nr_ReadDesc( reg, key, &desc);
  2691.     if ( err != REGERR_OK )
  2692.         return err;
  2693.  
  2694.     /* if in initial state */
  2695.     if ( *state == 0 ) {
  2696.  
  2697.         /* get first subkey */
  2698.         if ( desc.down != 0 ) {
  2699.             *buffer = '\0';
  2700.             err = nr_ReplaceName( reg, desc.down, buffer, bufsize, &desc );
  2701.         }
  2702.         else  {
  2703.             /* there *are* no child keys */
  2704.             err = REGERR_NOMORE;
  2705.         }
  2706.     }
  2707.     /* else already enumerating so get current 'state' node */
  2708.     else if ( REGERR_OK == (err = nr_ReadDesc( reg, *state, &desc )) ) {
  2709.  
  2710.         /* if traversing the tree */
  2711.         if ( style & REGENUM_DESCEND ) {
  2712.             if ( desc.down != 0 ) {
  2713.                 /* append name of first child key */
  2714.                 err = nr_CatName( reg, desc.down, buffer, bufsize, &desc );
  2715.             }
  2716.             else if ( desc.left != 0 ) {
  2717.                 /* replace last segment with next sibling */
  2718.                 err = nr_ReplaceName( reg, desc.left, buffer, bufsize, &desc );
  2719.             }
  2720.             else {
  2721.                 /* done with level, pop up as many times as necessary */
  2722.                 while ( err == REGERR_OK ) {
  2723.                     if ( desc.parent != key && desc.parent != 0 ) {
  2724.                         err = nr_RemoveName( buffer );
  2725.                         if ( err == REGERR_OK ) {
  2726.                             err = nr_ReadDesc( reg, desc.parent, &desc );
  2727.                             if ( err == REGERR_OK && desc.left != 0 ) {
  2728.                                 err = nr_ReplaceName( reg, desc.left, buffer,
  2729.                                                       bufsize, &desc );
  2730.                                 break;  /* found a node */
  2731.                             }
  2732.                         }
  2733.                     }
  2734.                     else {
  2735.                         err = REGERR_NOMORE;
  2736.                     }
  2737.                 }
  2738.             }
  2739.         }
  2740.         /* else immediate child keys only */
  2741.         else {
  2742.             /* get next key in chain */
  2743.             if ( desc.left != 0 ) {
  2744.                 *buffer = '\0';
  2745.                 err =  nr_ReplaceName( reg, desc.left, buffer, bufsize, &desc );
  2746.             }
  2747.             else {
  2748.                 err = REGERR_NOMORE;
  2749.             }
  2750.         }
  2751.     }
  2752.  
  2753.     /* set enum state to current key */
  2754.     if ( err == REGERR_OK ) {
  2755.         *state = desc.location;
  2756.     }
  2757.  
  2758.     return err;
  2759.  
  2760. }   /* NR_RegEnumSubkeys */
  2761.  
  2762.  
  2763.  
  2764. /* ---------------------------------------------------------------------
  2765.  * NR_RegEnumEntries - Enumerate the entry names for the specified key
  2766.  *
  2767.  * Parameters:
  2768.  *    hReg     - handle of open registry
  2769.  *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
  2770.  *    eState   - enumerations state, must contain NULL to start
  2771.  *    buffer   - location to store entry names
  2772.  *    bufsize  - size of buffer for names
  2773.  * ---------------------------------------------------------------------
  2774.  */
  2775. VR_INTERFACE(REGERR) NR_RegEnumEntries( HREG hReg, RKEY key, REGENUM *state,
  2776.                             char *buffer, uint32 bufsize, REGINFO *info )
  2777. {
  2778.     REGERR      err;
  2779.     REGFILE*    reg;
  2780.     REGDESC     desc;
  2781.  
  2782.     XP_ASSERT(bRegStarted);
  2783.  
  2784.     /* verify parameters */
  2785.     err = VERIFY_HREG( hReg );
  2786.     if ( err != REGERR_OK )
  2787.         return err;
  2788.  
  2789.     reg = ((REGHANDLE*)hReg)->pReg;
  2790.  
  2791.     /* verify starting key */
  2792.     err = nr_ReadDesc( reg, key, &desc);
  2793.     if ( err != REGERR_OK )
  2794.         return err;
  2795.  
  2796.     if ( *state == 0 ) {
  2797.         /* initial state--get first entry */
  2798.  
  2799.         if ( desc.value != 0 ) {
  2800.             *buffer = '\0';
  2801.             err =  nr_ReplaceName( reg, desc.value, buffer, bufsize, &desc );
  2802.         }
  2803.         else  { 
  2804.             /* there *are* no entries */
  2805.             err = REGERR_NOMORE;
  2806.         }
  2807.     }
  2808.     else {
  2809.         /* 'state' stores previous entry */
  2810.         err = nr_ReadDesc( reg, *state, &desc );
  2811.         if ( err == REGERR_OK ) {
  2812.  
  2813.             /* get next entry in chain */
  2814.             if ( desc.left != 0 ) {
  2815.                 *buffer = '\0';
  2816.                 err =  nr_ReplaceName( reg, desc.left, buffer, bufsize, &desc );
  2817.             }
  2818.             else {
  2819.                 /* at end of chain */
  2820.                 err = REGERR_NOMORE;
  2821.             }
  2822.         }
  2823.     }
  2824.  
  2825.     /* if we found an entry */
  2826.     if ( err == REGERR_OK ) {
  2827.  
  2828.         /* set enum state to current entry */
  2829.         *state = desc.location;
  2830.  
  2831.         /* return REGINFO if requested */
  2832.         if ( info != NULL && info->size >= sizeof(REGINFO) ) {
  2833.             info->entryType   = desc.type;
  2834.             info->entryLength = desc.valuelen;
  2835.         }
  2836.     }
  2837.  
  2838.     return err;
  2839.  
  2840. }   /* NR_RegEnumEntries */
  2841.  
  2842.  
  2843.  
  2844.  
  2845. #ifndef STANDALONE_REGISTRY
  2846. #include "VerReg.h"
  2847.  
  2848. #ifdef WIN32
  2849. extern BOOL WFE_IsMoveFileExBroken();
  2850. #endif
  2851. /* ---------------------------------------------------------------------
  2852.  * ---------------------------------------------------------------------
  2853.  * Registry initialization and shut-down
  2854.  * ---------------------------------------------------------------------
  2855.  * ---------------------------------------------------------------------
  2856.  */
  2857. #ifdef BROKEN
  2858. extern void SU_InitMonitor(void);
  2859. extern void SU_DestroyMonitor(void);
  2860. #endif
  2861. extern PRMonitor *vr_monitor;
  2862. #ifdef XP_UNIX
  2863. extern XP_Bool bGlobalRegistry;
  2864. #endif
  2865.  
  2866. #ifdef XP_MAC
  2867. #pragma export on
  2868. #endif
  2869.  
  2870. void NR_StartupRegistry(void)
  2871. {
  2872.     HREG reg;
  2873.     RKEY key;
  2874.     REGERR  err;
  2875.     REGENUM state;
  2876.     XP_Bool removeFromList;
  2877.     XP_StatStruct stat;
  2878.  
  2879.     vr_monitor = PR_NewMonitor();
  2880.     XP_ASSERT( vr_monitor != NULL );
  2881.     reglist_monitor = PR_NewMonitor();
  2882.     XP_ASSERT( reglist_monitor != NULL );
  2883. #ifdef XP_UNIX
  2884.     bGlobalRegistry = ( getenv(UNIX_GLOBAL_FLAG) != NULL );
  2885. #endif
  2886.  
  2887. #ifdef BROKEN
  2888.     SU_InitMonitor();
  2889. #endif
  2890.     bRegStarted = TRUE;
  2891.     /* need to register a PREF callback for "profile.name" */
  2892.  
  2893.     /* check to see that we have a valid registry */
  2894.     if (REGERR_OK == NR_RegOpen("", ®))
  2895.     {
  2896. #ifdef XP_PC
  2897.         /* perform scheduled file deletions and replacements (PC only) */
  2898.         if (REGERR_OK ==  NR_RegGetKey(reg, ROOTKEY_PRIVATE,
  2899.             REG_DELETE_LIST_KEY,&key))
  2900.         {
  2901.             char *urlFile;
  2902.             char *pFile;
  2903.             char buf[MAXREGNAMELEN];
  2904.  
  2905.             state = 0;
  2906.             while (REGERR_OK == NR_RegEnumEntries(reg, key, &state,
  2907.                 buf, sizeof(buf), NULL ))
  2908.             {
  2909.                 urlFile = XP_PlatformFileToURL(buf);
  2910.                 if ( urlFile == NULL)
  2911.                     continue;
  2912.                 pFile = urlFile+7;
  2913.  
  2914.                 removeFromList = FALSE;
  2915.                 if (0 == XP_FileRemove(pFile, xpURL)) {
  2916.                     /* file was successfully deleted */
  2917.                     removeFromList = TRUE;
  2918.                 }
  2919.                 else if (XP_Stat(pFile, &stat, xpURL) != 0) {
  2920.                     /* file doesn't appear to exist */
  2921.                     removeFromList = TRUE;
  2922.                 }
  2923.  
  2924.                 if (removeFromList) {
  2925.                     err = NR_RegDeleteEntry( reg, key, buf );
  2926.                     /* must reset state or enum will stop on deleted entry */
  2927.                     if ( err == REGERR_OK )
  2928.                         state = 0;
  2929.                 }
  2930.  
  2931.                 XP_FREEIF(urlFile);
  2932.             }
  2933.             /* delete list node if empty */
  2934.             state = 0;
  2935.             if (REGERR_NOMORE == NR_RegEnumEntries( reg, key, &state, buf, 
  2936.                 sizeof(buf), NULL ))
  2937.             {
  2938.                 NR_RegDeleteKey(reg, ROOTKEY_PRIVATE, REG_DELETE_LIST_KEY);
  2939.             }
  2940.         }
  2941.  
  2942.         /* replace files if any listed */
  2943.         if (REGERR_OK ==  NR_RegGetKey(reg, ROOTKEY_PRIVATE,
  2944.             REG_REPLACE_LIST_KEY, &key))
  2945.         {
  2946.             char tmpfile[MAXREGNAMELEN];
  2947.             char target[MAXREGNAMELEN];
  2948.  
  2949.             state = 0;
  2950.             while (REGERR_OK == NR_RegEnumEntries(reg, key, &state,
  2951.                 tmpfile, sizeof(tmpfile), NULL ))
  2952.             {
  2953.                 removeFromList = FALSE;
  2954.                 if (XP_Stat(tmpfile, &stat, xpURL) != 0)
  2955.                 {
  2956.                     /* new file is gone! */
  2957.                     removeFromList = TRUE;
  2958.                 }
  2959.                 else if ( REGERR_OK != NR_RegGetEntryString( reg, key, 
  2960.                     tmpfile, target, sizeof(target) ) )
  2961.                 {
  2962.                     /* can't read target filename, corruption? */
  2963.                     removeFromList = TRUE;
  2964.                 }
  2965.                 else {
  2966.                     if (XP_Stat(target, &stat, xpURL) == 0) {
  2967.                         /* need to delete old file first */
  2968.                         XP_FileRemove( target, xpURL );
  2969.                     }
  2970.                     if (0 == XP_FileRename(tmpfile, xpURL, target, xpURL)) {
  2971.                         removeFromList = TRUE;
  2972.                     }
  2973.                 }
  2974.  
  2975.                 if (removeFromList) {
  2976.                     err = NR_RegDeleteEntry( reg, key, tmpfile );
  2977.                     /* must reset state or enum will stop on deleted entry */
  2978.                     if ( err == REGERR_OK )
  2979.                         state = 0;
  2980.                 }
  2981.             }
  2982.             /* delete list node if empty */
  2983.             state = 0;
  2984.             if (REGERR_NOMORE == NR_RegEnumEntries(reg, key, &state, tmpfile, 
  2985.                 sizeof(tmpfile), NULL )) 
  2986.             {
  2987.                 NR_RegDeleteKey(reg, ROOTKEY_PRIVATE, REG_REPLACE_LIST_KEY);
  2988.             }
  2989.         }
  2990. #endif /* XP_PC */
  2991.         NR_RegClose(reg);
  2992.     }
  2993.     else {
  2994.         /* Couldn't open -- make a VersionRegistry call to force creation */
  2995.         /* Didn't worry about this before because Netcaster was doing it */
  2996.         VR_InRegistry("/Netscape");
  2997.         VR_Close();
  2998.     }
  2999. }
  3000.  
  3001. void NR_ShutdownRegistry(void)
  3002. {
  3003.     REGFILE* pReg;
  3004.  
  3005.     if ( vr_monitor != NULL ) {
  3006.         VR_Close();
  3007.         PR_DestroyMonitor(vr_monitor);
  3008.         vr_monitor = NULL;
  3009.     }
  3010.  
  3011.     /* close any forgotten open registries */
  3012.     while ( RegList != NULL ) {
  3013.         pReg = RegList;
  3014.         if ( pReg->hdrDirty ) {
  3015.             nr_WriteHdr( pReg );
  3016.         }
  3017.         nr_CloseFile( &(pReg->fh) );
  3018.         nr_DeleteNode( pReg );
  3019.     }
  3020.     
  3021.     if ( reglist_monitor != NULL ) {
  3022.         PR_DestroyMonitor( reglist_monitor );
  3023.         reglist_monitor = NULL;
  3024.     }
  3025. #ifdef BROKEN
  3026.     SU_DestroyMonitor();
  3027. #endif
  3028. }
  3029.  
  3030. #ifdef XP_MAC
  3031. #pragma export reset
  3032. #endif
  3033.  
  3034. #endif /* STANDALONE_REGISTRY */
  3035.  
  3036. /* EOF: reg.c */
  3037.