~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/modules/libreg/src/reg.c

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
4
 * License Version 1.1 (the "License"); you may not use this file
 
5
 * except in compliance with the License. You may obtain a copy of
 
6
 * the License at http://www.mozilla.org/NPL/
 
7
 *
 
8
 * Software distributed under the License is distributed on an "AS
 
9
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 
10
 * implied. See the License for the specific language governing
 
11
 * rights and limitations under the License.
 
12
 *
 
13
 * The Original Code is Mozilla Communicator client code, 
 
14
 * released March 31, 1998. 
 
15
 *
 
16
 * The Initial Developer of the Original Code is Netscape Communications 
 
17
 * Corporation.  Portions created by Netscape are
 
18
 * Copyright (C) 1998 Netscape Communications Corporation. All
 
19
 * Rights Reserved.
 
20
 *
 
21
 * Contributor(s): 
 
22
 *     Daniel Veditz <dveditz@netscape.com>
 
23
 */
 
24
/* ====================================================================
 
25
 * reg.c
 
26
 * XP Registry functions
 
27
 * ====================================================================
 
28
 */
 
29
 
 
30
/* TODO:
 
31
 *  - Replace 'malloc' in NR_RegPack with the Netscape XP equivalent
 
32
 *  - Solve DOS 'errno' problem mentioned below
 
33
 *  - Solve rename across volume problem described in VR_PackRegistry
 
34
 */
 
35
 
 
36
/* Preprocessor Defines
 
37
 *  STANDALONE_REGISTRY - define if not linking with Navigator
 
38
 *  NOCACHE_HDR         - define if multi-process access to registry
 
39
 *  SELF_REPAIR         - undefine to skip header update on open
 
40
 *  VERIFY_READ         - define TRUE to double-check short reads
 
41
 *
 
42
#define NOCACHE_HDR     1
 
43
 */
 
44
#define SELF_REPAIR     1
 
45
#ifdef DEBUG
 
46
#define VERIFY_READ     1
 
47
#endif
 
48
 
 
49
#include <stdio.h>
 
50
#include <string.h>
 
51
 
 
52
#ifdef STANDALONE_REGISTRY
 
53
#include <stdlib.h>
 
54
#include <assert.h>
 
55
#include <errno.h>
 
56
 
 
57
#if defined(XP_MAC) || defined(XP_MACOSX)
 
58
  #include <Errors.h>
 
59
#endif
 
60
 
 
61
#else
 
62
 
 
63
#include "prtypes.h"
 
64
#include "prlog.h"
 
65
#include "prerror.h"
 
66
#include "prprf.h"
 
67
 
 
68
#endif /*STANDALONE_REGISTRY*/
 
69
 
 
70
#if defined(SUNOS4)
 
71
#include <unistd.h>  /* for SEEK_SET */
 
72
#endif /* SUNOS4 */
 
73
 
 
74
#include "reg.h"
 
75
#include "NSReg.h"
 
76
 
 
77
#if defined(XP_MAC)
 
78
#define MAX_PATH 512
 
79
#elif defined(XP_MACOSX)
 
80
#define MAX_PATH PATH_MAX
 
81
#elif defined(XP_UNIX)
 
82
#ifndef MAX_PATH
 
83
#define MAX_PATH 1024
 
84
#endif
 
85
#elif defined(XP_OS2)
 
86
#ifndef MAX_PATH
 
87
#define MAX_PATH 260
 
88
#endif
 
89
#elif defined(WIN32)
 
90
#define MAX_PATH _MAX_PATH
 
91
#elif defined(XP_BEOS)
 
92
#include <limits.h>
 
93
#define MAX_PATH PATH_MAX
 
94
#endif
 
95
 
 
96
 
 
97
 /* NOTE! It is EXREMELY important that node names be in UTF-8; otherwise
 
98
 * backwards path search for delim char will fail for multi-byte/Unicode names
 
99
 */
 
100
 
 
101
/* ====================================================================
 
102
 * Overview
 
103
 * --------------------------------------------------------------------
 
104
 *
 
105
 *  Layers:
 
106
 *      Interface
 
107
 *          Path Parsing
 
108
 *              Key/Entry Management
 
109
 *                  Block I/O
 
110
 *                      Virtual I/O
 
111
 *
 
112
 * The functions in this file search and add to a binary Registry file
 
113
 * quite efficiently.  So efficiently, squeezing out space left by
 
114
 * deleted and updated objects requires a separate "pack" operation.
 
115
 *
 
116
 * Terms:
 
117
 * As used here, a 'key' is a node in the tree. The root of the tree
 
118
 * exists in an otherwise empty Registry as is itself a key.  Every key
 
119
 * has 0 or more sub-keys. Every key also has 0 or more 'entry's. Both
 
120
 * entries and keys have names. Entries also have values associated.
 
121
 * Names and values are simply strings of characters. These strings
 
122
 * may be quoted so that they can include path delimiter and equals
 
123
 * sign characters which are otherwise reserved.
 
124
 * ====================================================================
 
125
 */
 
126
 
 
127
/* --------------------------------------------------------------------
 
128
 * Module Global Data
 
129
 *
 
130
 * use of this data must be protected by the reglist lock
 
131
 * --------------------------------------------------------------------
 
132
 */
 
133
 
 
134
#if !defined(STANDALONE_REGISTRY)
 
135
static PRLock   *reglist_lock = NULL;
 
136
#endif
 
137
 
 
138
static REGFILE  *RegList = NULL;
 
139
static int32    regStartCount = 0;
 
140
char            *globalRegName = NULL;
 
141
static char     *user_name = NULL;
 
142
 
 
143
 
 
144
 
 
145
 
 
146
#if defined(XP_MAC) || defined(XP_MACOSX)
 
147
 
 
148
void nr_MacAliasFromPath(const char * fileName, void ** alias, int32 * length);
 
149
char * nr_PathFromMacAlias(const void * alias, uint32 aliasLength);
 
150
 
 
151
#include <Aliases.h>
 
152
#include <TextUtils.h>
 
153
#include <Memory.h>
 
154
#include <Folders.h>
 
155
 
 
156
#ifdef XP_MACOSX
 
157
  #include "MoreFilesX.h"
 
158
#else
 
159
  #include "FullPath.h"
 
160
#endif
 
161
 
 
162
static void copyCStringToPascal(Str255 dest, const char *src)
 
163
{
 
164
    size_t copyLen = strlen(src);
 
165
    if (copyLen > 255)
 
166
        copyLen = 255;
 
167
    BlockMoveData(src, &dest[1], copyLen);
 
168
    dest[0] = copyLen;
 
169
}
 
170
 
 
171
#ifdef XP_MACOSX
 
172
static OSErr isFileInTrash(FSRef *fsRef, PRBool *inTrash)
 
173
{
 
174
    OSErr err;
 
175
    FSCatalogInfo catalogInfo;
 
176
 
 
177
    if (fsRef == NULL || inTrash == NULL)
 
178
        return paramErr;
 
179
    *inTrash = PR_FALSE;
 
180
 
 
181
    err = FSGetCatalogInfo(fsRef, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL);
 
182
    if (err == noErr)
 
183
    {
 
184
        FSRef trashFSRef, currFSRef, parentFSRef;
 
185
        err = FSFindFolder(catalogInfo.volume, kTrashFolderType, false, &trashFSRef);
 
186
        if (err == noErr)
 
187
        {
 
188
            /* FSRefGetParentRef returns noErr and a zeroed FSRef when it reaches the top */
 
189
            for (currFSRef = *fsRef;
 
190
                 (FSGetParentRef(&currFSRef, &parentFSRef) == noErr && FSRefValid(&parentFSRef));
 
191
                 currFSRef = parentFSRef)
 
192
            {
 
193
                if (FSCompareFSRefs(&parentFSRef, &trashFSRef) == noErr)
 
194
                {
 
195
                    *inTrash = PR_TRUE;
 
196
                    break;
 
197
                }
 
198
            }
 
199
        }
 
200
    }
 
201
    return err;
 
202
}
 
203
#else
 
204
static OSErr isFileInTrash(FSSpec *fileSpec, PRBool *inTrash)
 
205
{
 
206
    OSErr err;
 
207
    short vRefNum;
 
208
    long dirID;
 
209
    
 
210
    if (fileSpec == NULL || inTrash == NULL)
 
211
        return paramErr;
 
212
    *inTrash = PR_FALSE;
 
213
    
 
214
    /* XXX - Only works if the file is in the top level of the trash dir */
 
215
    err = FindFolder(fileSpec->vRefNum, kTrashFolderType, false, &vRefNum, &dirID);
 
216
    if (err == noErr)
 
217
        if (dirID == fileSpec->parID)  /* File is inside the trash */
 
218
            *inTrash = PR_TRUE;
 
219
    
 
220
    return err;
 
221
}
 
222
#endif
 
223
 
 
224
/* returns an alias as a malloc'd pointer.
 
225
 * On failure, *alias is NULL
 
226
 */
 
227
void nr_MacAliasFromPath(const char * fileName, void ** alias, int32 * length)
 
228
{
 
229
    OSErr err;
 
230
    Str255 pascalName;
 
231
    FSRef fsRef;
 
232
    FSSpec fs;
 
233
    AliasHandle macAlias;
 
234
    *alias = NULL;
 
235
    *length = 0;
 
236
    
 
237
#ifdef XP_MACOSX
 
238
    err = FSPathMakeRef((const UInt8*)fileName, &fsRef, NULL);
 
239
    if ( err != noErr )
 
240
        return;
 
241
    err = FSNewAlias(NULL, &fsRef, &macAlias);
 
242
#else
 
243
    copyCStringToPascal(pascalName, fileName);
 
244
    err = FSMakeFSSpec(0, 0, pascalName, &fs);
 
245
    if ( err != noErr )
 
246
        return;
 
247
    err = NewAlias(NULL, &fs, &macAlias);
 
248
#endif
 
249
    
 
250
    if ( (err != noErr) || ( macAlias == NULL ))
 
251
        return;
 
252
    *length = GetHandleSize( (Handle) macAlias );
 
253
    *alias = XP_ALLOC( *length );
 
254
    if ( *alias == NULL )
 
255
    {
 
256
        DisposeHandle((Handle)macAlias);
 
257
        return;
 
258
    }
 
259
    HLock( (Handle) macAlias );
 
260
    XP_MEMCPY(*alias, *macAlias , *length);
 
261
    HUnlock( (Handle) macAlias );
 
262
    DisposeHandle( (Handle) macAlias);
 
263
    return;
 
264
}
 
265
 
 
266
/* resolves an alias, and returns a full path to the Mac file
 
267
 * If the alias changed, it would be nice to update our alias pointers
 
268
 */
 
269
char * nr_PathFromMacAlias(const void * alias, uint32 aliasLength)
 
270
{
 
271
    OSErr           err;
 
272
    AliasHandle     h           = NULL;
 
273
    Handle          fullPath    = NULL;
 
274
    short           fullPathLength;
 
275
    char *          cpath       = NULL;
 
276
    PRBool          inTrash;
 
277
    FSRef           fsRef;
 
278
    FSCatalogInfo   catalogInfo;
 
279
    UInt8           pathBuf[MAX_PATH];
 
280
    FSSpec          fs;
 
281
    Boolean         wasChanged; /* Change flag, it would be nice to change the alias on disk 
 
282
                        if the file location changed */
 
283
    
 
284
    
 
285
    XP_MEMSET( &fs, '\0', sizeof(FSSpec) );
 
286
    
 
287
    
 
288
    /* Copy the alias to a handle and resolve it */
 
289
    h = (AliasHandle) NewHandle(aliasLength);
 
290
    if ( h == NULL)
 
291
        goto fail;
 
292
        
 
293
        
 
294
    HLock( (Handle) h);
 
295
    XP_MEMCPY( *h, alias, aliasLength );
 
296
    HUnlock( (Handle) h);
 
297
    
 
298
#ifdef XP_MACOSX
 
299
    err = FSResolveAlias(NULL, h, &fsRef, &wasChanged);
 
300
    if (err != noErr)
 
301
        goto fail;
 
302
 
 
303
    /* if the alias has changed and the file is now in the trash,
 
304
       assume that user has deleted it and that we do not want to look at it */
 
305
    if (wasChanged && (isFileInTrash(&fsRef, &inTrash) == noErr) && inTrash)
 
306
        goto fail;
 
307
    err = FSRefMakePath(&fsRef, pathBuf, sizeof(pathBuf));
 
308
    if (err != noErr)
 
309
        goto fail;
 
310
    fullPathLength = XP_STRLEN(pathBuf);
 
311
    cpath = (char*) XP_ALLOC(fullPathLength + 1);
 
312
    if ( cpath == NULL)
 
313
        goto fail;
 
314
    XP_MEMCPY(cpath, pathBuf, fullPathLength + 1);
 
315
#else    
 
316
    err = ResolveAlias(NULL, h, &fs, &wasChanged);
 
317
    if (err != noErr)
 
318
        goto fail;
 
319
    
 
320
    /* if the alias has changed and the file is now in the trash,
 
321
       assume that user has deleted it and that we do not want to look at it */
 
322
    if (wasChanged && (isFileInTrash(&fs, &inTrash) == noErr) && inTrash)
 
323
        goto fail;
 
324
    
 
325
    /* Get the full path and create a char * out of it */
 
326
 
 
327
    err = GetFullPath(fs.vRefNum, fs.parID,fs.name, &fullPathLength, &fullPath);
 
328
    if ( (err != noErr) || (fullPath == NULL) )
 
329
        goto fail;
 
330
    
 
331
    cpath = (char*) XP_ALLOC(fullPathLength + 1);
 
332
    if ( cpath == NULL)
 
333
        goto fail;
 
334
    
 
335
    HLock( fullPath );
 
336
    XP_MEMCPY(cpath, *fullPath, fullPathLength);
 
337
    cpath[fullPathLength] = 0;
 
338
    HUnlock( fullPath );
 
339
#endif    
 
340
    /* Drop through */
 
341
fail:
 
342
    if (h != NULL)
 
343
        DisposeHandle( (Handle) h);
 
344
    if (fullPath != NULL)
 
345
        DisposeHandle( fullPath);
 
346
    return cpath;
 
347
}
 
348
 
 
349
#endif
 
350
 
 
351
 
 
352
/* --------------------------------------------------------------------
 
353
 * Registry List management
 
354
 * --------------------------------------------------------------------
 
355
 */
 
356
static void nr_AddNode(REGFILE* pReg);
 
357
static void nr_DeleteNode(REGFILE *pReg);
 
358
static REGFILE* vr_findRegFile(const char *filename);
 
359
 
 
360
/* -------------------------------------------------------------------- */
 
361
 
 
362
static void nr_AddNode(REGFILE* pReg)
 
363
{
 
364
    /* add node to head of list */
 
365
    pReg->next = RegList;
 
366
    pReg->prev = NULL;
 
367
 
 
368
    RegList = pReg;
 
369
 
 
370
    if ( pReg->next != NULL ) {
 
371
        pReg->next->prev = pReg;
 
372
    }
 
373
}
 
374
 
 
375
static void nr_DeleteNode(REGFILE* pReg)
 
376
{
 
377
    /* if at head of list... */
 
378
    if ( pReg->prev == NULL ) {
 
379
        RegList = pReg->next;
 
380
    }
 
381
    else {
 
382
        pReg->prev->next = pReg->next;
 
383
    }
 
384
 
 
385
    if ( pReg->next != NULL ) {
 
386
        pReg->next->prev = pReg->prev;
 
387
    }
 
388
 
 
389
    /* free memory */
 
390
#ifndef STANDALONE_REGISTRY
 
391
    if ( pReg->lock != NULL )
 
392
        PR_DestroyLock( pReg->lock );
 
393
#endif
 
394
    XP_FREEIF( pReg->filename );
 
395
    XP_FREE( pReg );
 
396
}
 
397
 
 
398
static REGFILE* vr_findRegFile(const char *filename)
 
399
{
 
400
    REGFILE *pReg;
 
401
 
 
402
    pReg = RegList;
 
403
    while( pReg != NULL ) {
 
404
#if defined(XP_UNIX) && !defined(XP_MACOSX) || defined XP_BEOS
 
405
        if ( 0 == XP_STRCMP( filename, pReg->filename ) ) {
 
406
#else
 
407
        if ( 0 == XP_STRCASECMP( filename, pReg->filename ) ) {
 
408
#endif
 
409
            break;
 
410
        }
 
411
        pReg = pReg->next;
 
412
    }
 
413
 
 
414
    return pReg;
 
415
}
 
416
 
 
417
 
 
418
/* --------------------------------------------------------------------
 
419
 * Virtual I/O
 
420
 *  Platform-specifics go in this section
 
421
 * --------------------------------------------------------------------
 
422
 */
 
423
static REGERR nr_OpenFile(const char *path, FILEHANDLE *fh);
 
424
static REGERR nr_CloseFile(FILEHANDLE *fh); /* Note: fh is a pointer */
 
425
static REGERR nr_ReadFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer);
 
426
static REGERR nr_WriteFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer);
 
427
static REGERR nr_LockRange(FILEHANDLE fh, REGOFF offset, int32 len);
 
428
static REGERR nr_UnlockRange(FILEHANDLE fh, REGOFF offset, int32 len);
 
429
static int32  nr_GetFileLength(FILEHANDLE fh);
 
430
/* -------------------------------------------------------------------- */
 
431
 
 
432
#ifdef STANDALONE_REGISTRY
 
433
static REGERR nr_OpenFile(const char *path, FILEHANDLE *fh)
 
434
{
 
435
    XP_ASSERT( path != NULL );
 
436
    XP_ASSERT( fh != NULL );
 
437
 
 
438
    /* Open the file for exclusive random read/write */
 
439
    (*fh) = vr_fileOpen(path, XP_FILE_UPDATE_BIN);
 
440
    if ( !VALID_FILEHANDLE(*fh) )
 
441
    {
 
442
        switch (errno)
 
443
        {
 
444
#if defined(XP_MAC) || defined(XP_MACOSX)
 
445
        case fnfErr:
 
446
#else
 
447
        case ENOENT:    /* file not found */
 
448
#endif
 
449
            return REGERR_NOFILE;
 
450
 
 
451
#if defined(XP_MAC) || defined(XP_MACOSX)
 
452
        case opWrErr:
 
453
#else
 
454
        case EROFS:     /* read-only file system */
 
455
        case EACCES:    /* file in use or read-only file*/
 
456
#endif
 
457
            /* try read only */
 
458
            (*fh) = vr_fileOpen(path, XP_FILE_READ_BIN);
 
459
            if ( VALID_FILEHANDLE(*fh) )
 
460
                return REGERR_READONLY;
 
461
            else
 
462
                return REGERR_FAIL;
 
463
 
 
464
        default:
 
465
            return REGERR_FAIL;
 
466
        }
 
467
    }
 
468
 
 
469
    return REGERR_OK;
 
470
 
 
471
}   /* OpenFile */
 
472
#else
 
473
static REGERR nr_OpenFile(const char *path, FILEHANDLE *fh)
 
474
{
 
475
    PR_ASSERT( path != NULL );
 
476
    PR_ASSERT( fh != NULL );
 
477
 
 
478
    /* Open the file for exclusive random read/write */
 
479
    *fh = XP_FileOpen(path, XP_FILE_UPDATE_BIN);
 
480
    if ( !VALID_FILEHANDLE(*fh) )
 
481
    {
 
482
        XP_StatStruct st;
 
483
        if ( XP_Stat( path, &st ) != 0 )
 
484
        {
 
485
            /* file doesn't exist, so create */
 
486
            *fh = XP_FileOpen(path, XP_FILE_TRUNCATE_BIN);
 
487
        }
 
488
    }
 
489
 
 
490
    if ( !VALID_FILEHANDLE(*fh) )
 
491
    {
 
492
      /* For whatever reason we failed every attempt of getting */
 
493
      /* a read/write registry. Let's try a read-only registry. */
 
494
      (*fh) = XP_FileOpen(path, XP_FILE_READ_BIN);
 
495
      if ( VALID_FILEHANDLE(*fh) )
 
496
        return REGERR_READONLY;
 
497
      else
 
498
        /* we are in big trouble now */
 
499
        return REGERR_FAIL;
 
500
    }
 
501
 
 
502
    /* succeded in getting a read/write registry */
 
503
    return REGERR_OK;
 
504
 
 
505
}   /* OpenFile */
 
506
#endif
 
507
 
 
508
 
 
509
static REGERR nr_CloseFile(FILEHANDLE *fh)
 
510
{
 
511
    /* NOTE: 'fh' is a pointer, unlike other Close functions
 
512
     *       This is necessary so that nr_CloseFile can set it to NULL
 
513
     */
 
514
 
 
515
    XP_ASSERT( fh != NULL );
 
516
    if ( VALID_FILEHANDLE(*fh) )
 
517
        XP_FileClose(*fh);
 
518
    (*fh) = NULL;
 
519
    return REGERR_OK;
 
520
 
 
521
}   /* CloseFile */
 
522
 
 
523
 
 
524
 
 
525
static REGERR nr_ReadFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer)
 
526
{
 
527
#if VERIFY_READ
 
528
    #define        FILLCHAR  0xCC
 
529
    unsigned char* p;
 
530
    unsigned char* dbgend = (unsigned char*)buffer+len;
 
531
#endif
 
532
 
 
533
    int32 readlen;
 
534
    REGERR err = REGERR_OK;
 
535
 
 
536
    XP_ASSERT(len > 0);
 
537
    XP_ASSERT(buffer != NULL);
 
538
    XP_ASSERT(fh != NULL);
 
539
 
 
540
#if VERIFY_READ
 
541
    XP_MEMSET(buffer, FILLCHAR, len);
 
542
#endif
 
543
 
 
544
    if (XP_FileSeek(fh, offset, SEEK_SET) != 0 ) {
 
545
        err = REGERR_FAIL;
 
546
    }
 
547
    else {
 
548
        readlen = XP_FileRead(buffer, len, fh );
 
549
        /* PR_READ() returns an unreliable length, check EOF separately */
 
550
        if (readlen < 0) {
 
551
#if !defined(STANDALONE_REGISTRY) || (!defined(XP_MAC) && !defined(XP_MACOSX))
 
552
    #if defined(STANDALONE_REGISTRY)
 
553
            if (errno == EBADF) /* bad file handle, not open for read, etc. */
 
554
    #else
 
555
            if (PR_GetError() == PR_BAD_DESCRIPTOR_ERROR)
 
556
    #endif
 
557
                err = REGERR_FAIL;
 
558
            else
 
559
#endif
 
560
                err = REGERR_BADREAD;
 
561
        }
 
562
        else if (readlen < len) {
 
563
#if VERIFY_READ
 
564
            /* PR_READ() says we hit EOF but return length is unreliable. */
 
565
            /* If buffer has new data beyond what PR_READ() says it got */
 
566
            /* we'll assume the read was OK--this is a gamble but */
 
567
            /* missing errors will cause fewer problems than too many. */
 
568
            p = (unsigned char*)buffer + readlen;
 
569
            while ( (p < dbgend) && (*p == (unsigned char)FILLCHAR) ) {
 
570
                p++;
 
571
            }
 
572
 
 
573
            /* really was EOF if it's all FILLCHAR's */
 
574
            if ( p == dbgend ) {
 
575
                err = REGERR_BADREAD;
 
576
            }
 
577
#else
 
578
            err = REGERR_BADREAD;
 
579
#endif
 
580
        }
 
581
    }
 
582
 
 
583
    return err;
 
584
 
 
585
}   /* ReadFile */
 
586
 
 
587
 
 
588
 
 
589
static REGERR nr_WriteFile(FILEHANDLE fh, REGOFF offset, int32 len, void *buffer)
 
590
{
 
591
 
 
592
    /* Note: 'offset' will commonly be the end of the file, in which
 
593
     * case this function extends the file to 'offset'+'len'. This may
 
594
     * be a two-step operation on some platforms.
 
595
     */
 
596
    XP_ASSERT(len > 0);
 
597
    XP_ASSERT(buffer);
 
598
    XP_ASSERT(fh != NULL);
 
599
 
 
600
    if (XP_FileSeek(fh, offset, SEEK_SET) != 0)
 
601
        return REGERR_FAIL;
 
602
 
 
603
    if ((int32)XP_FileWrite(buffer, len, fh) != len)
 
604
    {
 
605
        /* disk full or some other catastrophic error */
 
606
        return REGERR_FAIL;
 
607
    }
 
608
 
 
609
    return REGERR_OK;
 
610
 
 
611
}   /* WriteFile */
 
612
 
 
613
 
 
614
 
 
615
static REGERR nr_LockRange(FILEHANDLE fh, REGOFF offset, int32 len)
 
616
{
 
617
    /* TODO: Implement XP lock function with built-in retry. */
 
618
 
 
619
    return REGERR_OK;
 
620
 
 
621
}   /* LockRange */
 
622
 
 
623
 
 
624
 
 
625
static REGERR nr_UnlockRange(FILEHANDLE fh, REGOFF offset, int32 len)
 
626
{
 
627
    /* TODO: Implement XP unlock function with built-in retry. */
 
628
 
 
629
    return REGERR_OK;
 
630
 
 
631
}   /* UnlockRange */
 
632
 
 
633
 
 
634
 
 
635
#if SELF_REPAIR
 
636
static int32 nr_GetFileLength(FILEHANDLE fh)
 
637
{
 
638
    int32 length;
 
639
    int32 curpos;
 
640
 
 
641
    curpos = XP_FileTell(fh);
 
642
    XP_FileSeek(fh, 0, SEEK_END);
 
643
    length = XP_FileTell(fh);
 
644
    XP_FileSeek(fh, curpos, SEEK_SET);
 
645
    return length;
 
646
 
 
647
}   /* GetFileLength */
 
648
#endif
 
649
 
 
650
 
 
651
 
 
652
/* --------------------------------------------------------------------
 
653
 * Numeric converters
 
654
 * --------------------------------------------------------------------
 
655
 * The converters read and write integers in a common format so we
 
656
 * can transport registries without worrying about endian problems.
 
657
 *
 
658
 * The buffers *MUST* be the appropriate size!
 
659
 * --------------------------------------------------------------------
 
660
 */
 
661
static uint32 nr_ReadLong(char *buffer);
 
662
static uint16 nr_ReadShort(char *buffer);
 
663
static void   nr_WriteLong(uint32 num, char *buffer);
 
664
static void   nr_WriteShort(uint16 num, char *buffer);
 
665
/* -------------------------------------------------------------------- */
 
666
 
 
667
 
 
668
 
 
669
static uint16 nr_ReadShort(char *buffer)
 
670
{
 
671
    uint16 val;
 
672
    uint8 *p = (uint8*)buffer;
 
673
 
 
674
    val = (uint16)(*p + (uint16)( *(p+1) * 0x100 ));
 
675
 
 
676
    return val;
 
677
}
 
678
 
 
679
 
 
680
 
 
681
static uint32 nr_ReadLong(char *buffer)
 
682
{
 
683
    uint32 val;
 
684
    uint8 *p = (uint8*)buffer;
 
685
 
 
686
    val = *p
 
687
        + (uint32)(*(p+1) * 0x100L)
 
688
        + (uint32)(*(p+2) * 0x10000L )
 
689
        + (uint32)(*(p+3) * 0x1000000L );
 
690
 
 
691
    return val;
 
692
}
 
693
 
 
694
 
 
695
 
 
696
static void  nr_WriteLong(uint32 num, char *buffer)
 
697
{
 
698
    uint8 *p = (uint8*)buffer;
 
699
    *p++ = (uint8)(num & 0x000000FF);
 
700
    num /= 0x100;
 
701
    *p++ = (uint8)(num & 0x000000FF);
 
702
    num /= 0x100;
 
703
    *p++ = (uint8)(num & 0x000000FF);
 
704
    num /= 0x100;
 
705
    *p   = (uint8)(num & 0x000000FF);
 
706
}
 
707
 
 
708
 
 
709
 
 
710
static void  nr_WriteShort(uint16 num, char *buffer)
 
711
{
 
712
    uint8 *p = (uint8*)buffer;
 
713
 
 
714
    *p = (uint8)(num & 0x00FF);
 
715
    *(p+1) = (uint8)(num / 0x100);
 
716
}
 
717
 
 
718
 
 
719
 
 
720
/* --------------------------------------------------------------------
 
721
 * Block I/O
 
722
 * --------------------------------------------------------------------
 
723
 */
 
724
static REGERR nr_ReadHdr(REGFILE *reg); /* Reads the file header, creates file if empty */
 
725
static REGERR nr_WriteHdr(REGFILE *reg);    /* Writes the file header */
 
726
static REGERR nr_CreateRoot(REGFILE *reg);
 
727
 
 
728
static REGERR nr_Lock(REGFILE *reg);
 
729
static REGERR nr_Unlock(REGFILE *reg);
 
730
 
 
731
static REGERR nr_ReadDesc(REGFILE *reg, REGOFF offset, REGDESC *desc);      /* reads a desc */
 
732
static REGERR nr_ReadName(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf);
 
733
static REGERR nr_ReadData(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf);
 
734
 
 
735
static REGERR nr_WriteDesc(REGFILE *reg, REGDESC *desc);                    /* writes a desc */
 
736
static REGERR nr_WriteString(REGFILE *reg, char *string, REGDESC *desc);    /* writes a string */
 
737
static REGERR nr_WriteData(REGFILE *reg, char *string, uint32 len, REGDESC *desc);  /* writes a string */
 
738
 
 
739
static REGERR nr_AppendDesc(REGFILE *reg, REGDESC *desc, REGOFF *result);   /* adds a desc */
 
740
static REGERR nr_AppendName(REGFILE *reg, char *name, REGDESC *desc);       /* adds a name */
 
741
static REGERR nr_AppendString(REGFILE *reg, char *string, REGDESC *desc);   /* adds a string */
 
742
static REGERR nr_AppendData(REGFILE *reg, char *string, uint32 len, REGDESC *desc); /* adds a string */
 
743
 
 
744
static XP_Bool nr_IsValidUTF8(char *string);    /* checks if a string is UTF-8 encoded */
 
745
/* -------------------------------------------------------------------- */
 
746
 
 
747
 
 
748
 
 
749
static REGERR nr_ReadHdr(REGFILE *reg)
 
750
{
 
751
 
 
752
    int err;
 
753
    long filelength;
 
754
    char hdrBuf[sizeof(REGHDR)];
 
755
 
 
756
    XP_ASSERT(reg);
 
757
    reg->hdrDirty = 0;
 
758
 
 
759
    err = nr_ReadFile(reg->fh, 0, sizeof(REGHDR), &hdrBuf);
 
760
 
 
761
    switch (err)
 
762
    {
 
763
    case REGERR_BADREAD:
 
764
        /* header doesn't exist, so create one */
 
765
        err = nr_CreateRoot(reg);
 
766
        break;
 
767
 
 
768
    case REGERR_OK:
 
769
        /* header read successfully -- convert */
 
770
        reg->hdr.magic    = nr_ReadLong ( hdrBuf + HDR_MAGIC );
 
771
        reg->hdr.verMajor = nr_ReadShort( hdrBuf + HDR_VERMAJOR );
 
772
        reg->hdr.verMinor = nr_ReadShort( hdrBuf + HDR_VERMINOR );
 
773
        reg->hdr.avail    = nr_ReadLong ( hdrBuf + HDR_AVAIL );
 
774
        reg->hdr.root     = nr_ReadLong ( hdrBuf + HDR_ROOT );
 
775
 
 
776
        /* check to see if it's the right file type */
 
777
        if (reg->hdr.magic != MAGIC_NUMBER) {
 
778
            err = REGERR_BADMAGIC;
 
779
            break;
 
780
        }
 
781
 
 
782
        /* Check registry version
 
783
         * If the major version is bumped we're incompatible
 
784
         * (minor version just means some new features were added)
 
785
         *
 
786
         * Upgrade code will go here in the future...
 
787
         */
 
788
        if ( reg->hdr.verMajor > MAJOR_VERSION ) {
 
789
            err = REGERR_REGVERSION;
 
790
            break;
 
791
        }
 
792
 
 
793
#if SELF_REPAIR
 
794
        if ( reg->inInit && !(reg->readOnly) ) {
 
795
            filelength = nr_GetFileLength(reg->fh);
 
796
            if (reg->hdr.avail != filelength)
 
797
            {
 
798
                reg->hdr.avail = filelength;
 
799
                reg->hdrDirty = 1;
 
800
#if NOCACHE_HDR
 
801
                err = nr_WriteHdr(reg);
 
802
#endif
 
803
            }
 
804
        }
 
805
#endif  /* SELF_REPAIR */
 
806
        break;
 
807
 
 
808
    default:
 
809
        /* unexpected error from nr_ReadFile()*/
 
810
        XP_ASSERT(FALSE);
 
811
        err = REGERR_FAIL;
 
812
        break;
 
813
    }   /* switch */
 
814
 
 
815
    return err;
 
816
 
 
817
}   /* ReadHdr */
 
818
 
 
819
 
 
820
 
 
821
static REGERR nr_WriteHdr(REGFILE *reg)
 
822
{
 
823
    REGERR err;
 
824
    char hdrBuf[sizeof(REGHDR)];
 
825
 
 
826
    XP_ASSERT(reg);
 
827
 
 
828
    if (reg->readOnly)
 
829
        return REGERR_READONLY;
 
830
 
 
831
    /* convert to XP int format */
 
832
    nr_WriteLong ( reg->hdr.magic,    hdrBuf + HDR_MAGIC );
 
833
    nr_WriteShort( reg->hdr.verMajor, hdrBuf + HDR_VERMAJOR );
 
834
    nr_WriteShort( reg->hdr.verMinor, hdrBuf + HDR_VERMINOR );
 
835
    nr_WriteLong ( reg->hdr.avail,    hdrBuf + HDR_AVAIL );
 
836
    nr_WriteLong ( reg->hdr.root,     hdrBuf + HDR_ROOT );
 
837
 
 
838
    /* err = nr_WriteFile(reg->fh, 0, sizeof(REGHDR), &reg->hdr); */
 
839
    err = nr_WriteFile(reg->fh, 0, sizeof(hdrBuf), &hdrBuf);
 
840
 
 
841
    if (err == REGERR_OK)
 
842
        reg->hdrDirty = 0;
 
843
 
 
844
    return err;
 
845
 
 
846
}   /* WriteHdr */
 
847
 
 
848
 
 
849
 
 
850
static REGERR nr_CreateRoot(REGFILE *reg)
 
851
{
 
852
    /* Called when an empty file is detected by ReadHdr */
 
853
    REGERR err;
 
854
    REGDESC root;
 
855
 
 
856
    XP_ASSERT(reg);
 
857
 
 
858
    /* Create 'hdr' */
 
859
    reg->hdr.magic      = MAGIC_NUMBER;
 
860
    reg->hdr.verMajor   = MAJOR_VERSION;
 
861
    reg->hdr.verMinor   = MINOR_VERSION;
 
862
    reg->hdr.root       = 0;
 
863
    reg->hdr.avail      = HDRRESERVE;
 
864
 
 
865
    /* Create root descriptor */
 
866
    root.location   = 0;
 
867
    root.left       = 0;
 
868
    root.value      = 0;
 
869
    root.down       = 0;
 
870
    root.type       = REGTYPE_KEY;
 
871
    root.valuelen   = 0;
 
872
    root.valuebuf   = 0;
 
873
    root.parent     = 0;
 
874
 
 
875
    err = nr_AppendName(reg, ROOTKEY_STR, &root);
 
876
    if (err != REGERR_OK)
 
877
        return err;
 
878
 
 
879
    err = nr_AppendDesc(reg, &root, &reg->hdr.root);
 
880
    if (err != REGERR_OK)
 
881
        return err;
 
882
 
 
883
    return nr_WriteHdr(reg);    /* actually commit to disk */
 
884
 
 
885
    /* Create standard top-level nodes */
 
886
 
 
887
}   /* CreateRoot */
 
888
 
 
889
 
 
890
 
 
891
static REGERR nr_Lock(REGFILE *reg)
 
892
{
 
893
    REGERR status;
 
894
 
 
895
    /* lock file */
 
896
    status = nr_LockRange(reg->fh, 0, sizeof(REGHDR));
 
897
 
 
898
    if (status == REGERR_OK)
 
899
    {
 
900
        /* lock the object */
 
901
        PR_Lock( reg->lock );
 
902
 
 
903
#if NOCACHE_HDR
 
904
        /* try to refresh header info */
 
905
        status = nr_ReadHdr(reg);
 
906
        if ( status != REGERR_OK ) {
 
907
            PR_Unlock( reg->lock );
 
908
        }
 
909
#endif
 
910
    }
 
911
 
 
912
    return status;
 
913
}   /* Lock */
 
914
 
 
915
 
 
916
 
 
917
static REGERR nr_Unlock(REGFILE *reg)
 
918
{
 
919
    PR_Unlock( reg->lock );
 
920
 
 
921
    return nr_UnlockRange(reg->fh, 0, sizeof(REGHDR));
 
922
}   /* Unlock */
 
923
 
 
924
 
 
925
 
 
926
static REGERR nr_ReadDesc(REGFILE *reg, REGOFF offset, REGDESC *desc)
 
927
{
 
928
 
 
929
    REGERR err;
 
930
    char descBuf[ DESC_SIZE ];
 
931
 
 
932
    XP_ASSERT(reg);
 
933
    XP_ASSERT(offset >= HDRRESERVE);
 
934
    XP_ASSERT(offset < reg->hdr.avail);
 
935
    XP_ASSERT(desc);
 
936
 
 
937
    err = nr_ReadFile(reg->fh, offset, DESC_SIZE, &descBuf);
 
938
    if (err == REGERR_OK)
 
939
    {
 
940
        desc->location  = nr_ReadLong ( descBuf + DESC_LOCATION );
 
941
        desc->name      = nr_ReadLong ( descBuf + DESC_NAME );
 
942
        desc->namelen   = nr_ReadShort( descBuf + DESC_NAMELEN );
 
943
        desc->type      = nr_ReadShort( descBuf + DESC_TYPE );
 
944
        desc->left      = nr_ReadLong ( descBuf + DESC_LEFT );
 
945
        desc->value     = nr_ReadLong ( descBuf + DESC_VALUE );
 
946
        desc->valuelen  = nr_ReadLong ( descBuf + DESC_VALUELEN );
 
947
        desc->parent    = nr_ReadLong ( descBuf + DESC_PARENT );
 
948
 
 
949
        if ( TYPE_IS_ENTRY(desc->type) ) {
 
950
            desc->down = 0;
 
951
            desc->valuebuf  = nr_ReadLong( descBuf + DESC_VALUEBUF );
 
952
        }
 
953
        else {  /* TYPE is KEY */
 
954
            desc->down      = nr_ReadLong( descBuf + DESC_DOWN );
 
955
            desc->valuebuf  = 0;
 
956
        }
 
957
 
 
958
        if (desc->location != offset)
 
959
            err = REGERR_BADLOCN;
 
960
        else if ( desc->type & REGTYPE_DELETED )
 
961
            err = REGERR_DELETED;
 
962
    }
 
963
 
 
964
    return err;
 
965
 
 
966
}   /* ReadDesc */
 
967
 
 
968
 
 
969
 
 
970
static REGERR nr_ReadName(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf)
 
971
{
 
972
 
 
973
    REGERR err;
 
974
 
 
975
    XP_ASSERT(reg);
 
976
    XP_ASSERT(desc->name > 0);
 
977
    XP_ASSERT(desc->name < reg->hdr.avail);
 
978
    XP_ASSERT(buflen > 0);
 
979
    XP_ASSERT(buf);
 
980
 
 
981
    if ( desc->namelen > buflen )
 
982
        return REGERR_BUFTOOSMALL;
 
983
 
 
984
    err = nr_ReadFile(reg->fh, desc->name, desc->namelen, buf);
 
985
 
 
986
    buf[buflen-1] = '\0';   /* avoid runaways */
 
987
 
 
988
    return err;
 
989
 
 
990
}   /* ReadName */
 
991
 
 
992
 
 
993
 
 
994
static REGERR nr_ReadData(REGFILE *reg, REGDESC *desc, uint32 buflen, char *buf)
 
995
{
 
996
 
 
997
    REGERR err;
 
998
 
 
999
    XP_ASSERT(reg);
 
1000
    XP_ASSERT(desc->value > 0);
 
1001
    XP_ASSERT(desc->value < reg->hdr.avail);
 
1002
    XP_ASSERT(buflen > 0);
 
1003
    XP_ASSERT(buf);
 
1004
 
 
1005
    if ( desc->valuelen > buflen )
 
1006
        return REGERR_BUFTOOSMALL;
 
1007
 
 
1008
    err = nr_ReadFile(reg->fh, desc->value, desc->valuelen, buf);
 
1009
 
 
1010
    return err;
 
1011
 
 
1012
}   /* nr_ReadData */
 
1013
 
 
1014
 
 
1015
 
 
1016
static REGERR nr_WriteDesc(REGFILE *reg, REGDESC *desc)
 
1017
{
 
1018
    char descBuf[ DESC_SIZE ];
 
1019
 
 
1020
    XP_ASSERT(reg);
 
1021
    XP_ASSERT(desc);
 
1022
    XP_ASSERT( desc->location >= HDRRESERVE );
 
1023
    XP_ASSERT( desc->location < reg->hdr.avail );
 
1024
 
 
1025
    if (reg->readOnly)
 
1026
        return REGERR_READONLY;
 
1027
 
 
1028
    /* convert to XP int format */
 
1029
    nr_WriteLong ( desc->location,  descBuf + DESC_LOCATION );
 
1030
    nr_WriteLong ( desc->name,      descBuf + DESC_NAME );
 
1031
    nr_WriteShort( desc->namelen,   descBuf + DESC_NAMELEN );
 
1032
    nr_WriteShort( desc->type,      descBuf + DESC_TYPE );
 
1033
    nr_WriteLong ( desc->left,      descBuf + DESC_LEFT );
 
1034
    nr_WriteLong ( desc->value,     descBuf + DESC_VALUE );
 
1035
    nr_WriteLong ( desc->valuelen,  descBuf + DESC_VALUELEN );
 
1036
    nr_WriteLong ( desc->parent,    descBuf + DESC_PARENT );
 
1037
 
 
1038
    if ( TYPE_IS_ENTRY(desc->type) ) {
 
1039
        XP_ASSERT( 0 == desc->down );
 
1040
        nr_WriteLong( desc->valuebuf,  descBuf + DESC_VALUEBUF );
 
1041
    }
 
1042
    else {  /* TYPE is KEY */
 
1043
        XP_ASSERT( 0 == desc->valuebuf );
 
1044
        nr_WriteLong( desc->down,      descBuf + DESC_DOWN );
 
1045
    }
 
1046
 
 
1047
    return nr_WriteFile(reg->fh, desc->location, DESC_SIZE, descBuf);
 
1048
}   /* nr_WriteDesc */
 
1049
 
 
1050
 
 
1051
 
 
1052
static REGERR nr_AppendDesc(REGFILE *reg, REGDESC *desc, REGOFF *result)
 
1053
{
 
1054
 
 
1055
    REGERR err;
 
1056
    char descBuf[ DESC_SIZE ];
 
1057
 
 
1058
    XP_ASSERT(reg);
 
1059
    XP_ASSERT(desc);
 
1060
    XP_ASSERT(result);
 
1061
 
 
1062
    *result = 0;
 
1063
 
 
1064
    if (reg->readOnly)
 
1065
        return REGERR_READONLY;
 
1066
 
 
1067
    desc->location = reg->hdr.avail;
 
1068
 
 
1069
    /* convert to XP int format */
 
1070
    nr_WriteLong ( desc->location,  descBuf + DESC_LOCATION );
 
1071
    nr_WriteLong ( desc->name,      descBuf + DESC_NAME );
 
1072
    nr_WriteShort( desc->namelen,   descBuf + DESC_NAMELEN );
 
1073
    nr_WriteShort( desc->type,      descBuf + DESC_TYPE );
 
1074
    nr_WriteLong ( desc->left,      descBuf + DESC_LEFT );
 
1075
    nr_WriteLong ( desc->value,     descBuf + DESC_VALUE );
 
1076
    nr_WriteLong ( desc->valuelen,  descBuf + DESC_VALUELEN );
 
1077
    nr_WriteLong ( desc->parent,    descBuf + DESC_PARENT );
 
1078
 
 
1079
    if ( TYPE_IS_ENTRY(desc->type) ) {
 
1080
        XP_ASSERT( 0 == desc->down );
 
1081
        nr_WriteLong( desc->valuebuf,  descBuf + DESC_VALUEBUF );
 
1082
    }
 
1083
    else {  /* TYPE is KEY */
 
1084
        XP_ASSERT( 0 == desc->valuebuf );
 
1085
        nr_WriteLong( desc->down,      descBuf + DESC_DOWN );
 
1086
    }
 
1087
 
 
1088
    err = nr_WriteFile(reg->fh, reg->hdr.avail, DESC_SIZE, descBuf);
 
1089
 
 
1090
    if (err == REGERR_OK)
 
1091
    {
 
1092
        *result = reg->hdr.avail;
 
1093
        reg->hdr.avail += DESC_SIZE;
 
1094
        reg->hdrDirty = 1;
 
1095
#if NOCACHE_HDR
 
1096
        err = nr_WriteHdr(reg);
 
1097
#endif
 
1098
    }
 
1099
 
 
1100
    return err;
 
1101
 
 
1102
}   /* AppendDesc */
 
1103
 
 
1104
 
 
1105
 
 
1106
static REGERR nr_AppendName(REGFILE *reg, char *name, REGDESC *desc)
 
1107
{
 
1108
    REGERR err;
 
1109
    int len;
 
1110
    char *p;
 
1111
 
 
1112
    XP_ASSERT(reg);
 
1113
    XP_ASSERT(name);
 
1114
    XP_ASSERT(desc);
 
1115
 
 
1116
    if (!nr_IsValidUTF8(name))
 
1117
        return REGERR_BADUTF8;
 
1118
    if (reg->readOnly)
 
1119
        return REGERR_READONLY;
 
1120
 
 
1121
    len = XP_STRLEN(name) + 1;
 
1122
 
 
1123
    /* check for valid name parameter */
 
1124
    if ( len == 1 )
 
1125
        return REGERR_PARAM;
 
1126
 
 
1127
    if ( len > MAXREGNAMELEN )
 
1128
        return REGERR_NAMETOOLONG;
 
1129
 
 
1130
    for ( p = name; (*p != 0); p++ ) {
 
1131
        if ( INVALID_NAME_CHAR(*p) )
 
1132
            return REGERR_BADNAME;
 
1133
    }
 
1134
 
 
1135
    /* save the name */
 
1136
    err = nr_WriteFile(reg->fh, reg->hdr.avail, len, name);
 
1137
 
 
1138
    /* if write successful update the desc and hdr */
 
1139
    if (err == REGERR_OK)
 
1140
    {
 
1141
        desc->namelen = (uint16)len;
 
1142
        desc->name = reg->hdr.avail;
 
1143
        reg->hdr.avail += len;
 
1144
        reg->hdrDirty = 1;
 
1145
#if NOCACHE_HDR
 
1146
        err = nr_WriteHdr(reg);
 
1147
#endif
 
1148
    }
 
1149
 
 
1150
    return err;
 
1151
 
 
1152
}   /* nr_AppendName */
 
1153
 
 
1154
 
 
1155
 
 
1156
static REGERR nr_WriteString(REGFILE *reg, char *string, REGDESC *desc)
 
1157
{
 
1158
    uint32 len;
 
1159
 
 
1160
    XP_ASSERT(string);
 
1161
    if (!nr_IsValidUTF8(string))
 
1162
        return REGERR_BADUTF8;
 
1163
    if (reg->readOnly)
 
1164
        return REGERR_READONLY;
 
1165
    len = XP_STRLEN(string) + 1;
 
1166
 
 
1167
    return nr_WriteData( reg, string, len, desc );
 
1168
 
 
1169
}   /* nr_WriteString */
 
1170
 
 
1171
 
 
1172
 
 
1173
static REGERR nr_WriteData(REGFILE *reg, char *string, uint32 len, REGDESC *desc)
 
1174
{
 
1175
    REGERR err;
 
1176
 
 
1177
    XP_ASSERT(reg);
 
1178
    XP_ASSERT(string);
 
1179
    XP_ASSERT(desc);
 
1180
 
 
1181
    if (reg->readOnly)
 
1182
        return REGERR_READONLY;
 
1183
 
 
1184
    if ( len == 0 )
 
1185
        return REGERR_PARAM;
 
1186
 
 
1187
    if ( len > MAXREGVALUELEN )
 
1188
        return REGERR_NAMETOOLONG;
 
1189
 
 
1190
    /* save the data in the same place if it fits */
 
1191
    if ( len <= desc->valuebuf ) {
 
1192
        err = nr_WriteFile( reg->fh, desc->value, len, string );
 
1193
        if ( err == REGERR_OK ) {
 
1194
            desc->valuelen = len;
 
1195
        }
 
1196
    }
 
1197
    else {
 
1198
        /* otherwise append new data */
 
1199
        err = nr_AppendData( reg, string, len, desc );
 
1200
    }
 
1201
 
 
1202
    return err;
 
1203
 
 
1204
}   /* nr_WriteData */
 
1205
 
 
1206
 
 
1207
 
 
1208
static REGERR nr_AppendString(REGFILE *reg, char *string, REGDESC *desc)
 
1209
{
 
1210
    uint32 len;
 
1211
 
 
1212
    XP_ASSERT(string);
 
1213
    if (!nr_IsValidUTF8(string))
 
1214
        return REGERR_BADUTF8;
 
1215
    if (reg->readOnly)
 
1216
        return REGERR_READONLY;
 
1217
    len = XP_STRLEN(string) + 1;
 
1218
 
 
1219
    return nr_AppendData( reg, string, len, desc );
 
1220
 
 
1221
}   /* nr_AppendString */
 
1222
 
 
1223
 
 
1224
 
 
1225
static REGERR nr_AppendData(REGFILE *reg, char *string, uint32 len, REGDESC *desc)
 
1226
{
 
1227
    REGERR err;
 
1228
 
 
1229
    XP_ASSERT(reg);
 
1230
    XP_ASSERT(string);
 
1231
    XP_ASSERT(desc);
 
1232
 
 
1233
    if (reg->readOnly)
 
1234
        return REGERR_READONLY;
 
1235
 
 
1236
    if ( len == 0 )
 
1237
        return REGERR_PARAM;
 
1238
 
 
1239
    if ( len > MAXREGVALUELEN )
 
1240
        return REGERR_NAMETOOLONG;
 
1241
 
 
1242
    /* save the string */
 
1243
    err = nr_WriteFile(reg->fh, reg->hdr.avail, len, string);
 
1244
    if (err == REGERR_OK)
 
1245
    {
 
1246
        desc->value     = reg->hdr.avail;
 
1247
        desc->valuelen  = len;
 
1248
        desc->valuebuf  = len;
 
1249
 
 
1250
        reg->hdr.avail += len;
 
1251
        reg->hdrDirty   = 1;
 
1252
#if NOCACHE_HDR
 
1253
        err = nr_WriteHdr(reg);
 
1254
#endif
 
1255
    }
 
1256
 
 
1257
    return err;
 
1258
 
 
1259
}   /* nr_AppendData */
 
1260
 
 
1261
static XP_Bool nr_IsValidUTF8(char *string)
 
1262
{
 
1263
    int follow = 0;
 
1264
    char *c;
 
1265
    unsigned char ch;
 
1266
 
 
1267
    XP_ASSERT(string);
 
1268
    if ( !string )
 
1269
        return FALSE;
 
1270
 
 
1271
    for ( c = string; *c != '\0'; c++ )
 
1272
    {
 
1273
        ch = (unsigned char)*c;
 
1274
        if( follow == 0 )
 
1275
        {
 
1276
            /* expecting an initial byte */
 
1277
            if ( ch <= 0x7F )
 
1278
            {
 
1279
                /* standard byte -- do nothing */
 
1280
            }
 
1281
            else if ((0xC0 & ch) == 0x80)
 
1282
            {
 
1283
                /* follow byte illegal here */
 
1284
                return FALSE;
 
1285
            }
 
1286
            else if ((0xE0 & ch) == 0xC0)
 
1287
            {
 
1288
                follow = 1;
 
1289
            }
 
1290
            else if ((0xF0 & ch) == 0xE0)
 
1291
            {
 
1292
                follow = 2;
 
1293
            }
 
1294
            else
 
1295
            { 
 
1296
                /* unexpected (unsupported) initial byte */
 
1297
                return FALSE;
 
1298
            }
 
1299
        }
 
1300
        else 
 
1301
        {
 
1302
            XP_ASSERT( follow > 0 );
 
1303
            if ((0xC0 & ch) == 0x80)
 
1304
            {
 
1305
                /* expecting follow byte and found one */
 
1306
                follow--;
 
1307
            }
 
1308
            else 
 
1309
            {
 
1310
                /* invalid state */
 
1311
                return FALSE;
 
1312
            }
 
1313
        }
 
1314
    } /* for */
 
1315
 
 
1316
    if ( follow != 0 )
 
1317
    {
 
1318
        /* invalid state -- interrupted character */
 
1319
        return FALSE;
 
1320
    }
 
1321
    
 
1322
    return TRUE;
 
1323
}   /* checks if a string is UTF-8 encoded */
 
1324
 
 
1325
/* --------------------------------------------------------------------
 
1326
 * Path Parsing
 
1327
 * --------------------------------------------------------------------
 
1328
 */
 
1329
static REGERR nr_NextName(const char *pPath, char *buf, uint32 bufsize, const char **newPath);
 
1330
static REGERR nr_RemoveName(char *path);
 
1331
static REGERR nr_CatName(REGFILE *reg, REGOFF node, char *path, uint32 bufsize,
 
1332
                    REGDESC *desc);
 
1333
static REGERR nr_ReplaceName(REGFILE *reg, REGOFF node, char *path,
 
1334
                    uint32 bufsize, REGDESC *desc);
 
1335
/* -------------------------------------------------------------------- */
 
1336
 
 
1337
 
 
1338
/* Scans path at 'pPath' and copies next name segment into 'buf'.
 
1339
 * Also sets 'newPath' to point at the next segment of pPath.
 
1340
 */
 
1341
static REGERR nr_NextName(const char *pPath, char *buf, uint32 bufsize, const char **newPath)
 
1342
{
 
1343
    uint32 len = 0;
 
1344
    REGERR err = REGERR_OK;
 
1345
 
 
1346
    /* initialization and validation */
 
1347
    XP_ASSERT(buf);
 
1348
 
 
1349
    *newPath = NULL;
 
1350
    *buf = '\0';
 
1351
 
 
1352
    if ( pPath==NULL || *pPath=='\0' )
 
1353
        return REGERR_NOMORE;
 
1354
 
 
1355
    /* ... skip an initial path delimiter */
 
1356
    if ( *pPath == PATHDEL ) {
 
1357
        pPath++;
 
1358
 
 
1359
        if ( *pPath == '\0' )
 
1360
            return REGERR_NOMORE;
 
1361
    }
 
1362
 
 
1363
    /* ... missing name segment or initial blank are errors*/
 
1364
    if ( *pPath == PATHDEL || *pPath == ' ' )
 
1365
        return REGERR_BADNAME;
 
1366
 
 
1367
    /* copy first path segment into return buf */
 
1368
    while ( *pPath != '\0' && *pPath != PATHDEL )
 
1369
    {
 
1370
        if ( len == bufsize ) {
 
1371
            err = REGERR_NAMETOOLONG;
 
1372
            break;
 
1373
        }
 
1374
        if ( *pPath < ' ' && *pPath > 0 )
 
1375
            return REGERR_BADNAME;
 
1376
 
 
1377
        *buf++ = *pPath++;
 
1378
        len++;
 
1379
    }
 
1380
    *buf = '\0';
 
1381
 
 
1382
    /* ... name segment can't end with blanks, either */
 
1383
    if ( ' ' == *(buf-1) )
 
1384
        return REGERR_BADNAME;
 
1385
 
 
1386
    /* return a pointer to the start of the next segment */
 
1387
    *newPath = pPath;
 
1388
 
 
1389
    return err;
 
1390
 
 
1391
}   /* nr_NextName */
 
1392
 
 
1393
 
 
1394
 
 
1395
 
 
1396
static REGERR nr_CatName(REGFILE *reg, REGOFF node, char *path, uint32 bufsize, REGDESC *desc)
 
1397
{
 
1398
    REGERR err = REGERR_OK;
 
1399
 
 
1400
    char   *p;
 
1401
    uint32 len = XP_STRLEN(path);
 
1402
 
 
1403
    if (len > 0)
 
1404
    {
 
1405
        p = &path[len-1];
 
1406
        if (*p != PATHDEL)
 
1407
        {
 
1408
            if ( len < bufsize ) {
 
1409
                p++;
 
1410
                *p = PATHDEL;
 
1411
                len++;
 
1412
            }
 
1413
            else
 
1414
                err = REGERR_BUFTOOSMALL;
 
1415
        }
 
1416
        p++;    /* point one past PATHDEL */
 
1417
    }
 
1418
    else
 
1419
        p = path;
 
1420
 
 
1421
    if ( err == REGERR_OK ) {
 
1422
        err = nr_ReadDesc( reg, node, desc );
 
1423
        if ( err == REGERR_OK ) {
 
1424
            err = nr_ReadName( reg, desc, bufsize-len, p );
 
1425
        }
 
1426
    }
 
1427
 
 
1428
    return err;
 
1429
 
 
1430
}   /* CatName */
 
1431
 
 
1432
 
 
1433
 
 
1434
static REGERR nr_ReplaceName(REGFILE *reg, REGOFF node, char *path, uint32 bufsize, REGDESC *desc)
 
1435
{
 
1436
    /* NOTE! It is EXREMELY important that names be in UTF-8; otherwise
 
1437
     * the backwards path search will fail for multi-byte/Unicode names
 
1438
     */
 
1439
 
 
1440
    char   *p;
 
1441
    uint32 len;
 
1442
    REGERR err;
 
1443
 
 
1444
    XP_ASSERT(path);
 
1445
 
 
1446
    len = XP_STRLEN(path);
 
1447
    if ( len > bufsize )
 
1448
        return REGERR_PARAM;
 
1449
 
 
1450
    if ( len > 0 ) {
 
1451
        p = &path[len-1];
 
1452
 
 
1453
        while ((p > path) && (*p != PATHDEL)) {
 
1454
            --p;
 
1455
            --len;
 
1456
        }
 
1457
        if ( *p == PATHDEL ) {
 
1458
            p++; 
 
1459
            len++;
 
1460
        }
 
1461
    }
 
1462
    else
 
1463
        p = path;
 
1464
 
 
1465
 
 
1466
    err = nr_ReadDesc( reg, node, desc );
 
1467
    if ( err == REGERR_OK ) {
 
1468
        err = nr_ReadName( reg, desc, bufsize-len, p );
 
1469
    }
 
1470
 
 
1471
    return err;
 
1472
 
 
1473
}   /* ReplaceName */
 
1474
 
 
1475
 
 
1476
static REGERR nr_RemoveName(char *path)
 
1477
{
 
1478
    /* Typical inputs:
 
1479
     * path = "/Machine/4.0/"   output = "/Machine"
 
1480
     * path = "/Machine"        output = ""
 
1481
     * path = ""                output = REGERR_NOMORE
 
1482
     *
 
1483
     * NOTE! It is EXREMELY important that names be in UTF-8; otherwise
 
1484
     * the backwards path search will fail for multi-byte/Unicode names
 
1485
     */
 
1486
 
 
1487
    int len = XP_STRLEN(path);
 
1488
    char *p;
 
1489
    if (len < 1)
 
1490
        return REGERR_NOMORE;
 
1491
 
 
1492
    p = &path[len-1];
 
1493
    /* if last char is '/', ignore it */
 
1494
    if (*p == PATHDEL)
 
1495
        p--;
 
1496
 
 
1497
    while ((p > path) && (*p != PATHDEL))
 
1498
        p--;
 
1499
 
 
1500
/*  if (*p != PATHDEL)
 
1501
        return REGERR_NOMORE;
 
1502
*/
 
1503
 
 
1504
    *p = '\0';
 
1505
    return REGERR_OK;
 
1506
 
 
1507
}   /* RemoveName */
 
1508
 
 
1509
 
 
1510
 
 
1511
/* --------------------------------------------------------------------
 
1512
 * Key/Entry Management
 
1513
 * --------------------------------------------------------------------
 
1514
 */
 
1515
static REGERR nr_Find(REGFILE *reg, REGOFF offParent, const char *pPath,
 
1516
    REGDESC *pDesc, REGOFF *pPrev, REGOFF *pParent, XP_Bool raw);
 
1517
 
 
1518
static REGERR nr_FindAtLevel(REGFILE *reg, REGOFF offFirst, const char *pName,
 
1519
    REGDESC *pDesc, REGOFF *pOffPrev);
 
1520
 
 
1521
static REGERR nr_CreateSubKey(REGFILE *reg, REGOFF parent, REGDESC *pDesc,
 
1522
                              char *name);
 
1523
static REGERR nr_CreateEntryString(REGFILE *reg, REGDESC *pParent, 
 
1524
    char *name, char *value);
 
1525
static REGERR nr_CreateEntry(REGFILE *reg, REGDESC *pParent, char *name,
 
1526
    uint16 type, char *buffer, uint32 length);
 
1527
/* -------------------------------------------------------------------- */
 
1528
 
 
1529
 
 
1530
 
 
1531
static REGERR nr_Find(REGFILE *reg,
 
1532
            REGOFF offParent,
 
1533
            const char *pPath,
 
1534
            REGDESC *pDesc,
 
1535
            REGOFF *pPrev,
 
1536
            REGOFF *pParent,
 
1537
            XP_Bool raw)
 
1538
{
 
1539
 
 
1540
    REGERR  err;
 
1541
    REGDESC desc;
 
1542
    REGOFF  offPrev = 0;
 
1543
    char    namebuf[MAXREGNAMELEN];
 
1544
    const char    *p;
 
1545
 
 
1546
    XP_ASSERT( pPath != NULL );
 
1547
    XP_ASSERT( offParent >= HDRRESERVE );
 
1548
    XP_ASSERT( VALID_FILEHANDLE( reg->fh ) );
 
1549
 
 
1550
    if (pPrev)
 
1551
        *pPrev = 0;
 
1552
    if (pParent)
 
1553
        *pParent = 0;
 
1554
 
 
1555
    /* read starting desc */
 
1556
    err = nr_ReadDesc( reg, offParent, &desc);
 
1557
 
 
1558
    if (raw == TRUE) {
 
1559
        if ( err == REGERR_OK ) {
 
1560
            /* save current location as parent of next segment */
 
1561
            offParent = desc.location;
 
1562
            /* look for name at next level down */
 
1563
            err = nr_FindAtLevel(reg, desc.down, pPath, &desc, &offPrev);
 
1564
        }
 
1565
    }
 
1566
    else {
 
1567
        /* Walk 'path', reading keys into 'desc' */
 
1568
        p = pPath;
 
1569
        while ( err == REGERR_OK ) 
 
1570
        {
 
1571
            err = nr_NextName(p, namebuf, sizeof(namebuf), &p);
 
1572
 
 
1573
            if ( err == REGERR_OK ) {
 
1574
                /* save current location as parent of next segment */
 
1575
                offParent = desc.location;
 
1576
                /* look for name at next level down */
 
1577
                err = nr_FindAtLevel(reg, desc.down, namebuf, &desc, &offPrev);
 
1578
            }
 
1579
        }
 
1580
    }
 
1581
 
 
1582
    if ( (raw == FALSE && err == REGERR_NOMORE) ||
 
1583
            (raw == TRUE && err == REGERR_OK) ) {
 
1584
        /* we found all the segments of the path--success! */
 
1585
        err = REGERR_OK;
 
1586
 
 
1587
        if (pDesc) {
 
1588
            COPYDESC(pDesc, &desc);
 
1589
        }
 
1590
        if (pPrev) {
 
1591
            *pPrev = offPrev;
 
1592
        }
 
1593
        if (pParent) {
 
1594
            *pParent = offParent;
 
1595
        }
 
1596
    }
 
1597
    
 
1598
    return err;
 
1599
 
 
1600
}   /* nr_Find */
 
1601
 
 
1602
 
 
1603
 
 
1604
 
 
1605
/* nr_FindAtLevel -- looks for a node matching "pName" on the level starting
 
1606
 *                   with "offset".  Returns REGERR_OK if found, REGERR_NOFIND
 
1607
 *                   if not (plus other error conditions).
 
1608
 *
 
1609
 *                   If pDesc and pOffPrev are valid pointers *AND* the name is
 
1610
 *                   found then pDesc will point at the REGDESC of the node and
 
1611
 *                   pOffPrev will be the offset of the desc for the previous
 
1612
 *                   node at the same level.  
 
1613
 *
 
1614
 *                   If the node is *NOT* found (REGERR_NOFIND is returned)
 
1615
 *                   pDesc will point at the REGDESC of the last found node
 
1616
 *                   (as will pOffPrev). If some other error is returned then
 
1617
 *                   THese values must not be used.
 
1618
 */
 
1619
static REGERR nr_FindAtLevel(REGFILE *reg,
 
1620
                             REGOFF offset,
 
1621
                             const char *pName,
 
1622
                             REGDESC *pDesc,
 
1623
                             REGOFF *pOffPrev)
 
1624
{
 
1625
    char    namebuf[MAXREGNAMELEN];
 
1626
    REGDESC desc;
 
1627
    REGERR  err;
 
1628
    REGOFF  prev = 0;
 
1629
 
 
1630
    /* Note: offset=0 when there's no 'down' or 'left' */
 
1631
    XP_ASSERT(reg);
 
1632
    XP_ASSERT(offset < reg->hdr.avail);
 
1633
    XP_ASSERT(pName);
 
1634
    XP_ASSERT(*pName);
 
1635
 
 
1636
    while ( offset != 0 )
 
1637
    {
 
1638
        /* get name of next node */
 
1639
        err = nr_ReadDesc(reg, offset, &desc);
 
1640
        if (err != REGERR_OK)
 
1641
            return err;
 
1642
 
 
1643
        err = nr_ReadName(reg, &desc, sizeof(namebuf), namebuf);
 
1644
        if (err != REGERR_OK)
 
1645
            return err;
 
1646
 
 
1647
        /* check to see if it's the one we want */
 
1648
        if (XP_STRCMP(namebuf, pName) == 0) {
 
1649
            /* Found it! Signaled by non-zero offset */
 
1650
            break;
 
1651
        }
 
1652
 
 
1653
        /* advance to the next node */
 
1654
        prev = offset;
 
1655
        offset = desc.left;
 
1656
    }
 
1657
 
 
1658
    if ( pDesc != NULL && (prev || offset)) {
 
1659
        /* prev and offset BOTH null means we never loaded a desc */
 
1660
        COPYDESC( pDesc, &desc );
 
1661
    }
 
1662
    if ( pOffPrev != NULL ) {
 
1663
        *pOffPrev = prev;
 
1664
    }
 
1665
 
 
1666
    if ( offset != 0 ) /* if we found one */
 
1667
        return REGERR_OK;
 
1668
    else
 
1669
        return REGERR_NOFIND;
 
1670
}   /* FindAtLevel */
 
1671
 
 
1672
 
 
1673
 
 
1674
static REGERR nr_CreateSubKey(REGFILE *reg,
 
1675
                              REGOFF parent,
 
1676
                              REGDESC *pDesc,
 
1677
                              char *name)
 
1678
{
 
1679
    /* nr_CreateSubKey does NO error checking--callers *MUST*
 
1680
     * ensure that there are no duplicates
 
1681
     */
 
1682
    REGDESC desc;
 
1683
    REGERR err;
 
1684
 
 
1685
    XP_ASSERT(reg);
 
1686
    XP_ASSERT(pDesc);
 
1687
    XP_ASSERT(name);
 
1688
 
 
1689
    err = nr_AppendName(reg, name, &desc);
 
1690
    if (err != REGERR_OK)
 
1691
        return err;
 
1692
 
 
1693
    desc.type = REGTYPE_KEY;
 
1694
    desc.left = 0;
 
1695
    desc.down = 0;
 
1696
    desc.value = 0;
 
1697
    desc.valuelen = 0;
 
1698
    desc.valuebuf = 0;
 
1699
    desc.parent   = parent;
 
1700
 
 
1701
    if ( parent == pDesc->location ) {
 
1702
        /* It's a parent desc, so no siblings */
 
1703
        err = nr_AppendDesc(reg, &desc, &pDesc->down);
 
1704
    }
 
1705
    else {
 
1706
        /* It's a sibling desc */
 
1707
        XP_ASSERT( pDesc->left == 0 ); /* not the end of chain! */
 
1708
        err = nr_AppendDesc(reg, &desc, &pDesc->left);
 
1709
    }
 
1710
    if (err != REGERR_OK)
 
1711
        return err;
 
1712
 
 
1713
    /* write out the fixed up parent/sibling desc */
 
1714
    err = nr_WriteDesc(reg, pDesc);
 
1715
    COPYDESC(pDesc, &desc);
 
1716
 
 
1717
    return err;
 
1718
 
 
1719
}   /* nr_CreateSubKey */
 
1720
 
 
1721
 
 
1722
 
 
1723
static REGERR nr_CreateEntryString(REGFILE *reg, REGDESC *pParent, char *name, char *value)
 
1724
{
 
1725
    REGDESC desc;
 
1726
    REGERR  err;
 
1727
 
 
1728
    XP_ASSERT(reg);
 
1729
    XP_ASSERT(pParent);
 
1730
    XP_ASSERT(name);
 
1731
    XP_ASSERT(value);
 
1732
 
 
1733
    XP_MEMSET( &desc, 0, sizeof(REGDESC) );
 
1734
 
 
1735
    err = nr_AppendName(reg, name, &desc);
 
1736
    if (err != REGERR_OK)
 
1737
        return err;
 
1738
 
 
1739
    err = nr_AppendString(reg, value, &desc);
 
1740
    if (err != REGERR_OK)
 
1741
        return err;
 
1742
 
 
1743
    desc.type = REGTYPE_ENTRY_STRING_UTF;
 
1744
    desc.left = pParent->value;
 
1745
    desc.down = 0;
 
1746
    desc.parent = pParent->location;
 
1747
 
 
1748
    err = nr_AppendDesc(reg, &desc, &pParent->value);
 
1749
    if (err != REGERR_OK)
 
1750
        return err;
 
1751
 
 
1752
    /* printf("nr_AddEntry: %s=%s @0x%lx\n", name, value, pParent->value); */
 
1753
 
 
1754
    return nr_WriteDesc(reg, pParent);
 
1755
 
 
1756
}   /* nr_CreateEntryString */
 
1757
 
 
1758
 
 
1759
 
 
1760
static REGERR nr_CreateEntry(REGFILE *reg, REGDESC *pParent, char *name,
 
1761
    uint16 type, char *value, uint32 length)
 
1762
{
 
1763
    REGDESC desc;
 
1764
    REGERR  err;
 
1765
 
 
1766
    XP_ASSERT(reg);
 
1767
    XP_ASSERT(pParent);
 
1768
    XP_ASSERT(name);
 
1769
    XP_ASSERT(value);
 
1770
 
 
1771
    XP_MEMSET( &desc, 0, sizeof(REGDESC) );
 
1772
 
 
1773
    err = nr_AppendName(reg, name, &desc);
 
1774
    if (err != REGERR_OK)
 
1775
        return err;
 
1776
 
 
1777
    err = nr_AppendData(reg, value, length, &desc);
 
1778
    if (err != REGERR_OK)
 
1779
        return err;
 
1780
 
 
1781
    desc.type = type;
 
1782
    desc.left = pParent->value;
 
1783
    desc.down = 0;
 
1784
    desc.parent = pParent->location;
 
1785
 
 
1786
    err = nr_AppendDesc(reg, &desc, &pParent->value);
 
1787
    if (err != REGERR_OK)
 
1788
        return err;
 
1789
 
 
1790
    /* printf("nr_AddEntry: %s=%s @0x%lx\n", name, value, pParent->value); */
 
1791
 
 
1792
    return nr_WriteDesc(reg, pParent);
 
1793
 
 
1794
}   /* nr_CreateEntry */
 
1795
 
 
1796
 
 
1797
 
 
1798
 
 
1799
/* ---------------------------------------------------------------------
 
1800
 * Intermediate API
 
1801
 * ---------------------------------------------------------------------
 
1802
 */
 
1803
static REGOFF  nr_TranslateKey( REGFILE *reg, RKEY key );
 
1804
static REGERR  nr_InitStdRkeys( REGFILE *reg );
 
1805
static XP_Bool nr_ProtectedNode( REGFILE *reg, REGOFF key );
 
1806
static REGERR  nr_RegAddKey( REGFILE *reg, RKEY key, char *path, RKEY *newKey, XP_Bool raw );
 
1807
static REGERR  nr_RegDeleteKey( REGFILE *reg, RKEY key, char *path, XP_Bool raw );
 
1808
static REGERR  nr_RegOpen( const char *filename, HREG *hReg );
 
1809
static REGERR  nr_RegClose( HREG hReg );
 
1810
static char*   nr_GetUsername();
 
1811
static const char* nr_GetRegName (const char *name);
 
1812
static int     nr_RegSetBufferSize( HREG hReg, int bufsize );
 
1813
 
 
1814
/* --------------------------------------------------------------------- */
 
1815
 
 
1816
 
 
1817
static REGOFF nr_TranslateKey( REGFILE *reg, RKEY key )
 
1818
{
 
1819
    REGOFF retKey = 0;
 
1820
 
 
1821
    /* if it's a special key  */
 
1822
    if ( key < HDRRESERVE )  {
 
1823
        /* ...translate it */
 
1824
        switch (key)
 
1825
        {
 
1826
            case ROOTKEY:
 
1827
                retKey = reg->hdr.root;
 
1828
                break;
 
1829
 
 
1830
            case ROOTKEY_VERSIONS:
 
1831
                retKey = reg->rkeys.versions;
 
1832
                break;
 
1833
 
 
1834
            case ROOTKEY_USERS:
 
1835
                retKey = reg->rkeys.users;
 
1836
                break;
 
1837
 
 
1838
            case ROOTKEY_COMMON:
 
1839
                retKey = reg->rkeys.common;
 
1840
                break;
 
1841
 
 
1842
#ifndef STANDALONE_REGISTRY
 
1843
            case ROOTKEY_CURRENT_USER:
 
1844
                if ( reg->rkeys.current_user == 0 ) {
 
1845
                    /* not initialized--find the current user key */
 
1846
                    RKEY    userkey = 0;
 
1847
                    REGERR  err;
 
1848
                    char*   profName;
 
1849
 
 
1850
                    profName = nr_GetUsername();
 
1851
                    if ( NULL != profName ) {
 
1852
                        /* Don't assign a slot for missing or magic profile */
 
1853
                        if ( '\0' == *profName ||
 
1854
                            0 == XP_STRCMP(ASW_MAGIC_PROFILE_NAME, profName)) 
 
1855
                        {
 
1856
                            err = REGERR_FAIL;
 
1857
                        } else {
 
1858
                            err = nr_RegAddKey( reg, reg->rkeys.users, profName, &userkey, FALSE );
 
1859
                        }
 
1860
                        XP_FREE(profName);
 
1861
                    }
 
1862
                    else {
 
1863
                        err = nr_RegAddKey( reg, reg->rkeys.users, "default", &userkey, FALSE );
 
1864
                    }
 
1865
 
 
1866
                    if ( err == REGERR_OK ) {
 
1867
                        reg->rkeys.current_user = userkey;
 
1868
                    }
 
1869
                }
 
1870
                retKey = reg->rkeys.current_user;
 
1871
                break;
 
1872
#endif /* !STANDALONE_REGISTRY */
 
1873
 
 
1874
            case ROOTKEY_PRIVATE:
 
1875
                retKey = reg->rkeys.privarea;
 
1876
                break;
 
1877
 
 
1878
            default:
 
1879
                /* not a valid key */
 
1880
                retKey = 0;
 
1881
                break;
 
1882
        }
 
1883
    }
 
1884
    else {
 
1885
        /* ...otherwise it's fine as-is */
 
1886
        retKey = (REGOFF)key;
 
1887
    }
 
1888
    return ( retKey );
 
1889
}  /* nr_TranslateKey */
 
1890
 
 
1891
 
 
1892
 
 
1893
static REGERR nr_InitStdRkeys( REGFILE *reg )
 
1894
{
 
1895
    REGERR      err = REGERR_OK;
 
1896
    RKEY        key;
 
1897
 
 
1898
    XP_ASSERT( reg != NULL );
 
1899
 
 
1900
    /* initialize to invalid key values */
 
1901
    XP_MEMSET( &reg->rkeys, 0, sizeof(STDNODES) );
 
1902
 
 
1903
    /* Add each key before looking it up.  Adding an already
 
1904
     * existing key is harmless, and these MUST exist.
 
1905
     */
 
1906
 
 
1907
    /* ROOTKEY_USERS */
 
1908
    err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_USERS_STR, &key, FALSE );
 
1909
    if ( err != REGERR_OK )
 
1910
        return err;
 
1911
    reg->rkeys.users = key;
 
1912
 
 
1913
    /* ROOTKEY_COMMON */
 
1914
    err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_COMMON_STR, &key, FALSE );
 
1915
    if ( err != REGERR_OK ) 
 
1916
        return err;
 
1917
    reg->rkeys.common = key;
 
1918
 
 
1919
    /* ROOTKEY_VERSIONS */
 
1920
    err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_VERSIONS_STR, &key, FALSE );
 
1921
    if ( err != REGERR_OK )
 
1922
        return err;
 
1923
    reg->rkeys.versions = key;
 
1924
 
 
1925
    /* ROOTKEY_CURRENT_USER */
 
1926
    /* delay until first use -- see nr_TranslateKey */
 
1927
 
 
1928
    /* ROOTKEY_PRIVATE */
 
1929
    err = nr_RegAddKey( reg, reg->hdr.root, ROOTKEY_PRIVATE_STR, &key, FALSE );
 
1930
    if ( err != REGERR_OK ) 
 
1931
        return err;
 
1932
    reg->rkeys.privarea = key;
 
1933
 
 
1934
    return err;
 
1935
}   /* nr_InitStdRkeys */
 
1936
 
 
1937
 
 
1938
 
 
1939
static XP_Bool nr_ProtectedNode( REGFILE *reg, REGOFF key )
 
1940
{
 
1941
    if ( (key == reg->hdr.root) ||
 
1942
         (key == reg->rkeys.users) ||
 
1943
         (key == reg->rkeys.versions) ||
 
1944
         (key == reg->rkeys.common) ||
 
1945
         (key == reg->rkeys.current_user) )
 
1946
    {
 
1947
        return TRUE;
 
1948
    }
 
1949
    else
 
1950
        return FALSE;
 
1951
}
 
1952
 
 
1953
 
 
1954
 
 
1955
static REGERR nr_RegAddKey( REGFILE *reg, RKEY key, char *path, RKEY *newKey, XP_Bool raw )
 
1956
{
 
1957
    REGERR      err;
 
1958
    REGDESC     desc;
 
1959
    REGOFF      start;
 
1960
    REGOFF      parent;
 
1961
    char        namebuf[MAXREGNAMELEN];
 
1962
    char        *p;
 
1963
 
 
1964
    XP_ASSERT( regStartCount > 0 );
 
1965
    XP_ASSERT( reg != NULL );
 
1966
    XP_ASSERT( path != NULL );
 
1967
    XP_ASSERT( *path != '\0' );
 
1968
    XP_ASSERT( VALID_FILEHANDLE( reg->fh ) );
 
1969
 
 
1970
    /* have to translate again in case this is an internal call */
 
1971
    start = nr_TranslateKey( reg, key );
 
1972
    if ( start == 0 )
 
1973
        return REGERR_PARAM;
 
1974
 
 
1975
    /* Get starting desc */
 
1976
    err = nr_ReadDesc( reg, start, &desc );
 
1977
 
 
1978
    if (raw == TRUE) {
 
1979
        if ( err == REGERR_OK) {
 
1980
            /* look for name at next level down */
 
1981
            parent = desc.location;
 
1982
            err = nr_FindAtLevel(reg, desc.down, path, &desc, 0);
 
1983
 
 
1984
            /* if key is not found */
 
1985
            if ( err == REGERR_NOFIND ) {
 
1986
                /* add it as a sub-key to the last found key */
 
1987
                err = nr_CreateSubKey(reg, parent, &desc, path);
 
1988
            }
 
1989
        }
 
1990
    }
 
1991
    else {
 
1992
        /* Walk 'path', reading keys into 'desc' */
 
1993
        p = path;
 
1994
        while ( err == REGERR_OK ) {
 
1995
 
 
1996
            /* get next name on the path */
 
1997
            err = nr_NextName(p, namebuf, sizeof(namebuf), &p);
 
1998
            if ( err == REGERR_OK ) {
 
1999
                /* look for name at next level down */
 
2000
                parent = desc.location;
 
2001
                err = nr_FindAtLevel(reg, desc.down, namebuf, &desc, 0);
 
2002
 
 
2003
                /* if key is not found */
 
2004
                if ( err == REGERR_NOFIND ) {
 
2005
                    /* add it as a sub-key to the last found key */
 
2006
                    err = nr_CreateSubKey(reg, parent, &desc, namebuf);
 
2007
                }
 
2008
            }
 
2009
        }
 
2010
    }
 
2011
 
 
2012
    /* it's good to have processed the whole path */
 
2013
    if ( (raw == FALSE && err == REGERR_NOMORE) ||
 
2014
         (raw == TRUE && err == REGERR_OK) ) 
 
2015
    {
 
2016
        err = REGERR_OK;
 
2017
 
 
2018
        /* return new key if the caller wants it */
 
2019
        if ( newKey != NULL ) {
 
2020
            *newKey = desc.location;
 
2021
        }
 
2022
    }
 
2023
 
 
2024
    return err;
 
2025
 
 
2026
}   /* nr_RegAddKey */
 
2027
 
 
2028
 
 
2029
 
 
2030
 
 
2031
static REGERR nr_RegDeleteKey( REGFILE *reg, RKEY key, char *path, XP_Bool raw )
 
2032
{
 
2033
    REGERR      err;
 
2034
    REGOFF      start;
 
2035
    REGDESC     desc;
 
2036
    REGDESC     predecessor;
 
2037
    REGOFF      offPrev;
 
2038
    REGOFF      offParent;
 
2039
    REGOFF*     link;
 
2040
 
 
2041
    XP_ASSERT( regStartCount > 0 );
 
2042
    XP_ASSERT( reg != NULL );
 
2043
    XP_ASSERT( VALID_FILEHANDLE( reg->fh ) );
 
2044
 
 
2045
    start = nr_TranslateKey( reg, key );
 
2046
    if ( path == NULL || *path == '\0' || start == 0 )
 
2047
        return REGERR_PARAM;
 
2048
 
 
2049
    /* find the specified key */
 
2050
    err = nr_Find( reg, start, path, &desc, &offPrev, &offParent, raw );
 
2051
    if ( err == REGERR_OK ) {
 
2052
 
 
2053
        XP_ASSERT( !TYPE_IS_ENTRY( desc.type ) );
 
2054
 
 
2055
        /* make sure it's childless and not a top-level key */
 
2056
        if ( (desc.down == 0) && !nr_ProtectedNode( reg, desc.location ) ) {
 
2057
 
 
2058
            /* Are we the first on our level? */
 
2059
            if ( offPrev == 0 ) {
 
2060
                /* Yes: link to parent's "down" pointer */
 
2061
                err = nr_ReadDesc( reg, offParent, &predecessor );
 
2062
                link = &(predecessor.down);
 
2063
            }
 
2064
            else {
 
2065
                /* No: link using predecessor's "left" pointer */
 
2066
                err = nr_ReadDesc( reg, offPrev, &predecessor );
 
2067
                link = &(predecessor.left);
 
2068
            }
 
2069
 
 
2070
            /* If we read the predecessor desc OK */
 
2071
            if (err == REGERR_OK) {
 
2072
                XP_ASSERT( *link == desc.location );
 
2073
 
 
2074
                /* link predecessor to next, removing current node from chain */
 
2075
                *link = desc.left;
 
2076
 
 
2077
                /* Write the updated predecessor */
 
2078
                err = nr_WriteDesc( reg, &predecessor );
 
2079
                if ( err == REGERR_OK ) {
 
2080
                    /* Mark key deleted to prevent bogus use by anyone
 
2081
                     * who is holding an RKEY for that node
 
2082
                     */
 
2083
                    desc.type |= REGTYPE_DELETED;
 
2084
                    err = nr_WriteDesc( reg, &desc );
 
2085
                }
 
2086
            }
 
2087
        }
 
2088
        else {
 
2089
            /* specified node is protected from deletion */
 
2090
            err = REGERR_FAIL;
 
2091
        }
 
2092
    }
 
2093
 
 
2094
    return err;
 
2095
 
 
2096
}   /* nr_RegDeleteKey */
 
2097
 
 
2098
 
 
2099
 
 
2100
static int nr_RegSetBufferSize( HREG hReg, int bufsize )
 
2101
{
 
2102
    REGERR      err = REGERR_OK;
 
2103
    REGHANDLE*  reghnd = (REGHANDLE*)hReg;
 
2104
    REGFILE*    reg;
 
2105
    XP_Bool     needDelete = FALSE;
 
2106
    int         newSize;
 
2107
 
 
2108
    /* verify handle */
 
2109
    err = VERIFY_HREG( hReg );
 
2110
    if ( err != REGERR_OK )
 
2111
        return -1;
 
2112
 
 
2113
    reg = reghnd->pReg;
 
2114
 
 
2115
    PR_Lock( reg->lock );
 
2116
 
 
2117
    newSize = XP_FileSetBufferSize( reg->fh, bufsize );
 
2118
 
 
2119
    PR_Unlock( reg->lock );
 
2120
 
 
2121
    return newSize;
 
2122
}
 
2123
 
 
2124
 
 
2125
 
 
2126
static REGERR nr_RegOpen( const char *filename, HREG *hReg )
 
2127
{
 
2128
    REGERR    status = REGERR_OK;
 
2129
    REGFILE   *pReg;
 
2130
    REGHANDLE *pHandle;
 
2131
 
 
2132
    XP_ASSERT( regStartCount > 0 );
 
2133
 
 
2134
    /* initialize output handle in case of error */
 
2135
    if ( hReg == NULL ) {
 
2136
        return REGERR_PARAM;
 
2137
    }
 
2138
    *hReg = NULL;
 
2139
    
 
2140
    /* Look for named file in list of open registries */
 
2141
    filename = nr_GetRegName( filename );
 
2142
    if (filename == NULL) {
 
2143
        filename = "";
 
2144
    }
 
2145
    pReg = vr_findRegFile( filename );
 
2146
 
 
2147
    /* if registry not already open */
 
2148
    if (pReg == NULL) {
 
2149
 
 
2150
        /* ...then open it */
 
2151
        pReg = (REGFILE*)XP_ALLOC( sizeof(REGFILE) );
 
2152
        if ( pReg == NULL ) {
 
2153
            status = REGERR_MEMORY;
 
2154
            goto bail;
 
2155
        }
 
2156
        XP_MEMSET(pReg, 0, sizeof(REGFILE));
 
2157
 
 
2158
        pReg->inInit = TRUE;
 
2159
        pReg->filename = XP_STRDUP(filename);
 
2160
        if (pReg->filename == NULL) {
 
2161
            XP_FREE( pReg );
 
2162
            status = REGERR_MEMORY;
 
2163
            goto bail;
 
2164
        }
 
2165
 
 
2166
        status = nr_OpenFile( filename, &(pReg->fh) );
 
2167
        if (status == REGERR_READONLY) {
 
2168
            /* Open, but read only */
 
2169
            pReg->readOnly = TRUE;
 
2170
            status = REGERR_OK;
 
2171
        }
 
2172
        if ( status != REGERR_OK ) {
 
2173
            XP_FREE( pReg->filename );
 
2174
            XP_FREE( pReg );
 
2175
 
 
2176
            goto bail;
 
2177
        }
 
2178
 
 
2179
        /* ...read and validate the header */
 
2180
        status = nr_ReadHdr( pReg );
 
2181
        if ( status != REGERR_OK ) {
 
2182
            nr_CloseFile( &(pReg->fh) );
 
2183
            XP_FREE( pReg->filename );
 
2184
            XP_FREE( pReg );
 
2185
            goto bail;
 
2186
        }
 
2187
 
 
2188
        /* ...other misc initialization */
 
2189
        pReg->refCount = 0;
 
2190
 
 
2191
#ifndef STANDALONE_REGISTRY
 
2192
        pReg->uniqkey = PR_Now();
 
2193
#endif
 
2194
 
 
2195
        status = nr_InitStdRkeys( pReg );
 
2196
        if ( status == REGERR_OK ) {
 
2197
            /* ...and add it to the list */
 
2198
            nr_AddNode( pReg );
 
2199
        }
 
2200
        else {
 
2201
            nr_CloseFile( &(pReg->fh) );
 
2202
            XP_FREE( pReg->filename );
 
2203
            XP_FREE( pReg );
 
2204
            goto bail;
 
2205
        }
 
2206
 
 
2207
#ifndef STANDALONE_REGISTRY
 
2208
        pReg->lock = PR_NewLock();
 
2209
#endif
 
2210
 
 
2211
        /* now done with everything that needs to protect the header */
 
2212
        pReg->inInit = FALSE;
 
2213
    }
 
2214
 
 
2215
    /* create a new handle to the regfile */
 
2216
    pHandle = (REGHANDLE*)XP_ALLOC( sizeof(REGHANDLE) );
 
2217
    if ( pHandle == NULL ) {
 
2218
        /* we can't create the handle */
 
2219
        if ( pReg->refCount == 0 ) {
 
2220
            /* we've just opened it so close it and remove node */
 
2221
            nr_CloseFile( &(pReg->fh) );
 
2222
            nr_DeleteNode( pReg );
 
2223
        }
 
2224
 
 
2225
        status = REGERR_MEMORY;
 
2226
        goto bail;
 
2227
    }
 
2228
 
 
2229
    pHandle->magic   = MAGIC_NUMBER;
 
2230
    pHandle->pReg    = pReg;
 
2231
 
 
2232
    /* success: bump the reference count and return the handle */
 
2233
    pReg->refCount++;
 
2234
    *hReg = (void*)pHandle;
 
2235
 
 
2236
bail:
 
2237
    return status;
 
2238
 
 
2239
}   /* nr_RegOpen */
 
2240
 
 
2241
 
 
2242
 
 
2243
static REGERR nr_RegClose( HREG hReg )
 
2244
{
 
2245
    REGERR      err = REGERR_OK;
 
2246
    REGHANDLE*  reghnd = (REGHANDLE*)hReg;
 
2247
    REGFILE*    reg;
 
2248
    XP_Bool     needDelete = FALSE;
 
2249
 
 
2250
    XP_ASSERT( regStartCount > 0 );
 
2251
 
 
2252
    /* verify handle */
 
2253
    err = VERIFY_HREG( hReg );
 
2254
    if ( err != REGERR_OK )
 
2255
        return err;
 
2256
 
 
2257
    reg = reghnd->pReg;
 
2258
 
 
2259
    PR_Lock( reg->lock );
 
2260
    if ( err == REGERR_OK )
 
2261
    {
 
2262
        XP_ASSERT( VALID_FILEHANDLE(reg->fh) );
 
2263
 
 
2264
        /* save changed header info */
 
2265
        if ( reg->hdrDirty ) {
 
2266
            nr_WriteHdr( reg );
 
2267
        }
 
2268
 
 
2269
        /* lower REGFILE user count */
 
2270
        reg->refCount--;
 
2271
 
 
2272
        /* if registry is no longer in use */
 
2273
        if ( reg->refCount < 1 ) 
 
2274
        {
 
2275
            /* ...then close the file */
 
2276
            nr_CloseFile( &(reg->fh) );
 
2277
 
 
2278
            /* ...and mark REGFILE node for deletion from list */
 
2279
            needDelete = TRUE;
 
2280
        }
 
2281
        else
 
2282
        {
 
2283
            /* ...otherwise make sure any writes are flushed */
 
2284
            XP_FileFlush( reg->fh );
 
2285
        }
 
2286
 
 
2287
        reghnd->magic = 0;    /* prevent accidental re-use */  
 
2288
        PR_Unlock( reg->lock );
 
2289
 
 
2290
        if ( needDelete )
 
2291
            nr_DeleteNode( reg );
 
2292
 
 
2293
        XP_FREE( reghnd );
 
2294
    }
 
2295
 
 
2296
    return err;
 
2297
 
 
2298
}   /* nr_RegClose */
 
2299
 
 
2300
 
 
2301
 
 
2302
static char *nr_GetUsername()
 
2303
{
 
2304
  if (NULL == user_name) {
 
2305
    return "default";
 
2306
  } else {
 
2307
    return user_name;
 
2308
  }
 
2309
}
 
2310
 
 
2311
static const char* nr_GetRegName (const char *name)
 
2312
{
 
2313
    if (name == NULL || *name == '\0') {
 
2314
        XP_ASSERT( globalRegName != NULL );
 
2315
        return globalRegName;
 
2316
    } else {
 
2317
        return name;
 
2318
    }
 
2319
}
 
2320
 
 
2321
 
 
2322
 
 
2323
 
 
2324
/* ---------------------------------------------------------------------
 
2325
 * Public API
 
2326
 * --------------------------------------------------------------------- */
 
2327
 
 
2328
 
 
2329
/* ---------------------------------------------------------------------
 
2330
 * NR_RegGetUsername - Gets a copy of the current username
 
2331
 *
 
2332
 * Parameters:
 
2333
 *   A variable which, on exit will contain an alloc'ed string which is a
 
2334
 *   copy of the current username.
 
2335
 *
 
2336
 * DO NOT USE -- OBSOLETE
 
2337
 * ---------------------------------------------------------------------
 
2338
 */
 
2339
 
 
2340
VR_INTERFACE(REGERR) NR_RegGetUsername(char **name)
 
2341
{
 
2342
    /* XXX: does this need locking? */
 
2343
 
 
2344
    if ( name == NULL )
 
2345
        return REGERR_PARAM;
 
2346
 
 
2347
    *name = XP_STRDUP(nr_GetUsername());
 
2348
 
 
2349
    if ( NULL == *name )
 
2350
        return REGERR_MEMORY;
 
2351
 
 
2352
    return REGERR_OK;
 
2353
}
 
2354
 
 
2355
 
 
2356
/* ---------------------------------------------------------------------
 
2357
 * NR_RegSetBufferSize - Set the buffer size
 
2358
 *
 
2359
 * Parameters:
 
2360
 *     name     - name of the current user
 
2361
 *
 
2362
 * Output:
 
2363
 * ---------------------------------------------------------------------
 
2364
 */
 
2365
 
 
2366
VR_INTERFACE(int) NR_RegSetBufferSize( HREG hReg, int bufsize )
 
2367
{
 
2368
    int      newSize;
 
2369
 
 
2370
    PR_Lock( reglist_lock );
 
2371
 
 
2372
    newSize = nr_RegSetBufferSize( hReg, bufsize );
 
2373
 
 
2374
    PR_Unlock(reglist_lock);
 
2375
 
 
2376
    return newSize;
 
2377
}
 
2378
 
 
2379
 
 
2380
/* ---------------------------------------------------------------------
 
2381
 * NR_RegSetUsername - Set the current username
 
2382
 * 
 
2383
 * If the current user profile name is not set then trying to use
 
2384
 * HKEY_CURRENT_USER will result in an error.
 
2385
 *
 
2386
 * Parameters:
 
2387
 *     name     - name of the current user
 
2388
 *
 
2389
 * Output:
 
2390
 * ---------------------------------------------------------------------
 
2391
 */
 
2392
 
 
2393
VR_INTERFACE(REGERR) NR_RegSetUsername(const char *name)
 
2394
{
 
2395
    char *tmp;
 
2396
 
 
2397
    if ( name == NULL || *name == '\0' )
 
2398
        return REGERR_PARAM;
 
2399
 
 
2400
    tmp = XP_STRDUP(name);
 
2401
    if (NULL == tmp) {
 
2402
        return REGERR_MEMORY;
 
2403
    }
 
2404
 
 
2405
    PR_Lock( reglist_lock );
 
2406
 
 
2407
    XP_FREEIF(user_name);
 
2408
    user_name = tmp;
 
2409
 
 
2410
/* XXX: changing the username should go through and clear out the current.user
 
2411
   for each open registry. */
 
2412
 
 
2413
    PR_Unlock( reglist_lock );
 
2414
  
 
2415
    return REGERR_OK;
 
2416
}
 
2417
 
 
2418
 
 
2419
 
 
2420
 
 
2421
#ifndef STANDALONE_REGISTRY
 
2422
/* ---------------------------------------------------------------------
 
2423
 * NR_RegGetUniqueName
 
2424
 * 
 
2425
 * Returns a unique name that can be used for anonymous key/value names
 
2426
 *
 
2427
 * Parameters:
 
2428
 *     hReg     - handle of open registry
 
2429
 *     outbuf   - where to put the string
 
2430
 *     buflen   - how big the buffer is
 
2431
 * ---------------------------------------------------------------------
 
2432
 */
 
2433
VR_INTERFACE(REGERR) NR_RegGetUniqueName(HREG hReg, char* outbuf, uint32 buflen)
 
2434
{
 
2435
    PRUint64    one;
 
2436
    REGERR      err;
 
2437
    REGFILE*    reg;
 
2438
    static PRUint64 uniqkey;
 
2439
 
 
2440
    /* verify parameters */
 
2441
    err = VERIFY_HREG( hReg );
 
2442
    if ( err != REGERR_OK )
 
2443
        return err;
 
2444
 
 
2445
    reg = ((REGHANDLE*)hReg)->pReg;
 
2446
 
 
2447
    if ( !outbuf )
 
2448
        return REGERR_PARAM;
 
2449
 
 
2450
    if ( buflen <= (sizeof(PRUint64)*2) )
 
2451
        return REGERR_BUFTOOSMALL;
 
2452
 
 
2453
    if ( LL_IS_ZERO(uniqkey) )
 
2454
        uniqkey = PR_Now();
 
2455
 
 
2456
    PR_snprintf(outbuf,buflen,"%llx",uniqkey);
 
2457
 
 
2458
    /* increment counter for next time */
 
2459
    LL_I2L(one,1);
 
2460
    LL_ADD(uniqkey, uniqkey, one);
 
2461
 
 
2462
    return REGERR_OK;
 
2463
}
 
2464
#endif
 
2465
 
 
2466
 
 
2467
       
 
2468
       
 
2469
/* ---------------------------------------------------------------------
 
2470
 * NR_RegOpen - Open a netscape XP registry
 
2471
 *
 
2472
 * Parameters:
 
2473
 *    filename   - registry file to open. NULL or ""  opens the standard
 
2474
 *                 local registry.
 
2475
 *    hReg       - OUT: handle to opened registry
 
2476
 *
 
2477
 * Output:
 
2478
 * ---------------------------------------------------------------------
 
2479
 */
 
2480
VR_INTERFACE(REGERR) NR_RegOpen( const char *filename, HREG *hReg )
 
2481
{
 
2482
    REGERR    status = REGERR_OK;
 
2483
 
 
2484
#if !defined(STANDALONE_REGISTRY)
 
2485
    /* you must call NR_StartupRegistry() first */
 
2486
    if ( regStartCount <= 0 )
 
2487
        return REGERR_FAIL;
 
2488
#endif
 
2489
 
 
2490
    PR_Lock(reglist_lock);
 
2491
 
 
2492
    status = nr_RegOpen( filename, hReg );
 
2493
 
 
2494
    PR_Unlock(reglist_lock);
 
2495
 
 
2496
    return status;
 
2497
 
 
2498
}   /* NR_RegOpen */
 
2499
 
 
2500
 
 
2501
 
 
2502
 
 
2503
/* ---------------------------------------------------------------------
 
2504
 * NR_RegClose - Close a netscape XP registry
 
2505
 *
 
2506
 * Parameters:
 
2507
 *    hReg     - handle of open registry to be closed.
 
2508
 *
 
2509
 * After calling this routine the handle is no longer valid
 
2510
 * ---------------------------------------------------------------------
 
2511
 */
 
2512
VR_INTERFACE(REGERR) NR_RegClose( HREG hReg )
 
2513
{
 
2514
    REGERR      err = REGERR_OK;
 
2515
 
 
2516
    PR_Lock( reglist_lock );
 
2517
 
 
2518
    err = nr_RegClose( hReg );
 
2519
 
 
2520
    PR_Unlock(reglist_lock);
 
2521
 
 
2522
    return err;
 
2523
 
 
2524
}   /* NR_RegClose */
 
2525
 
 
2526
 
 
2527
 
 
2528
 
 
2529
/* ---------------------------------------------------------------------
 
2530
 * NR_RegFlush - Manually flush data in a netscape XP registry
 
2531
 *
 
2532
 * Parameters:
 
2533
 *    hReg     - handle of open registry to be flushed.
 
2534
 * ---------------------------------------------------------------------
 
2535
 */
 
2536
VR_INTERFACE(REGERR) NR_RegFlush( HREG hReg )
 
2537
{
 
2538
    REGERR      err;
 
2539
    REGFILE*    reg;
 
2540
 
 
2541
    /* verify parameters */
 
2542
    err = VERIFY_HREG( hReg );
 
2543
    if ( err != REGERR_OK )
 
2544
        return err;
 
2545
 
 
2546
    reg = ((REGHANDLE*)hReg)->pReg;
 
2547
 
 
2548
    /* can't flush a read-only registry */
 
2549
    if ( reg->readOnly )
 
2550
        return REGERR_READONLY;
 
2551
 
 
2552
    /* lock the registry file */
 
2553
    err = nr_Lock( reg );
 
2554
    if ( err == REGERR_OK )
 
2555
    {
 
2556
        if ( reg->hdrDirty ) {
 
2557
            nr_WriteHdr( reg );
 
2558
        }
 
2559
 
 
2560
        XP_FileFlush( reg->fh );
 
2561
 
 
2562
        /* unlock the registry */
 
2563
        nr_Unlock( reg );
 
2564
    }
 
2565
 
 
2566
    return err;
 
2567
 
 
2568
} /* NR_RegFlush */
 
2569
 
 
2570
 
 
2571
 
 
2572
 
 
2573
/* ---------------------------------------------------------------------
 
2574
 * NR_RegIsWritable - Check read/write status of open registry
 
2575
 *
 
2576
 * Parameters:
 
2577
 *    hReg     - handle of open registry to query
 
2578
 * ---------------------------------------------------------------------
 
2579
 */
 
2580
VR_INTERFACE(REGERR) NR_RegIsWritable( HREG hReg )
 
2581
{
 
2582
    REGERR      err;
 
2583
    REGFILE*    reg;
 
2584
 
 
2585
    /* verify parameters */
 
2586
    err = VERIFY_HREG( hReg );
 
2587
    if ( err != REGERR_OK )
 
2588
        return err;
 
2589
 
 
2590
    reg = ((REGHANDLE*)hReg)->pReg;
 
2591
 
 
2592
    if ( reg->readOnly )
 
2593
        return REGERR_READONLY;
 
2594
    else
 
2595
        return REGERR_OK;
 
2596
 
 
2597
}   /* NR_RegIsWritable */
 
2598
 
 
2599
 
 
2600
 
 
2601
/* ---------------------------------------------------------------------
 
2602
 * NR_RegAddKey - Add a key node to the registry
 
2603
 *
 
2604
 *      This routine is simply a wrapper to perform user input
 
2605
 *      validation and translation from HREG and standard key
 
2606
 *      values into the internal format
 
2607
 *
 
2608
 * Parameters:
 
2609
 *    hReg     - handle of open registry
 
2610
 *    key      - registry key obtained from NR_RegGetKey(),
 
2611
 *               or one of the standard top-level keys
 
2612
 *    path     - relative path of key to be added.  Intermediate
 
2613
 *               nodes will also be added if necessary.
 
2614
 * ---------------------------------------------------------------------
 
2615
 */
 
2616
VR_INTERFACE(REGERR) NR_RegAddKey( HREG hReg, RKEY key, char *path, RKEY *newKey )
 
2617
{
 
2618
    REGERR      err;
 
2619
    REGOFF      start;
 
2620
    REGFILE*    reg;
 
2621
 
 
2622
    /* prevent use of return value in case errors aren't checked */
 
2623
    if ( newKey != NULL )
 
2624
        *newKey = 0;
 
2625
 
 
2626
    /* verify parameters */
 
2627
    err = VERIFY_HREG( hReg );
 
2628
    if ( err != REGERR_OK )
 
2629
        return err;
 
2630
 
 
2631
    reg = ((REGHANDLE*)hReg)->pReg;
 
2632
 
 
2633
    if ( path == NULL || *path == '\0' || reg == NULL )
 
2634
        return REGERR_PARAM;
 
2635
 
 
2636
    /* lock the registry file */
 
2637
    err = nr_Lock( reg );
 
2638
    if ( err == REGERR_OK )
 
2639
    {
 
2640
        /* ... don't allow additional children of ROOTKEY */
 
2641
        start = nr_TranslateKey( reg, key );
 
2642
        if ( start != 0 && start != reg->hdr.root )
 
2643
        {
 
2644
            err = nr_RegAddKey( reg, start, path, newKey, FALSE );
 
2645
        }
 
2646
        else
 
2647
            err = REGERR_PARAM;
 
2648
 
 
2649
        /* unlock the registry */
 
2650
        nr_Unlock( reg );
 
2651
    }
 
2652
 
 
2653
    return err;
 
2654
}   /* NR_RegAddKey */
 
2655
 
 
2656
 
 
2657
 
 
2658
 
 
2659
/* ---------------------------------------------------------------------
 
2660
 * NR_RegAddKeyRaw - Add a key node to the registry
 
2661
 *
 
2662
 *      This routine is different from NR_RegAddKey() in that it takes 
 
2663
 *      a keyname rather than a path.
 
2664
 *
 
2665
 * Parameters:
 
2666
 *    hReg     - handle of open registry
 
2667
 *    key      - registry key obtained from NR_RegGetKey(),
 
2668
 *               or one of the standard top-level keys
 
2669
 *    keyname  - name of key to be added. No parsing of this
 
2670
 *               name happens.
 
2671
 *    newkey   - if not null the RKEY of the new key is returned
 
2672
 * ---------------------------------------------------------------------
 
2673
 */
 
2674
VR_INTERFACE(REGERR) NR_RegAddKeyRaw( HREG hReg, RKEY key, char *keyname, RKEY *newKey )
 
2675
{
 
2676
    REGERR      err;
 
2677
    REGOFF      start;
 
2678
    REGFILE*    reg;
 
2679
 
 
2680
    /* prevent use of return value in case errors aren't checked */
 
2681
    if ( newKey != NULL )
 
2682
        *newKey = 0;
 
2683
 
 
2684
    /* verify parameters */
 
2685
    err = VERIFY_HREG( hReg );
 
2686
    if ( err != REGERR_OK )
 
2687
        return err;
 
2688
 
 
2689
    reg = ((REGHANDLE*)hReg)->pReg;
 
2690
 
 
2691
    if ( keyname == NULL || *keyname == '\0' || reg == NULL )
 
2692
        return REGERR_PARAM;
 
2693
 
 
2694
    /* lock the registry file */
 
2695
    err = nr_Lock( reg );
 
2696
    if ( err == REGERR_OK )
 
2697
    {
 
2698
        /* ... don't allow additional children of ROOTKEY */
 
2699
        start = nr_TranslateKey( reg, key );
 
2700
        if ( start != 0 && start != reg->hdr.root ) 
 
2701
        {
 
2702
            err = nr_RegAddKey( reg, start, keyname, newKey, TRUE );
 
2703
        }
 
2704
        else
 
2705
            err = REGERR_PARAM;
 
2706
 
 
2707
        /* unlock the registry */
 
2708
        nr_Unlock( reg );
 
2709
    }
 
2710
 
 
2711
    return err;
 
2712
}   /* NR_RegAddKeyRaw */
 
2713
 
 
2714
 
 
2715
 
 
2716
 
 
2717
/* ---------------------------------------------------------------------
 
2718
 * NR_RegDeleteKey - Delete the specified key
 
2719
 *
 
2720
 * Note that delete simply orphans blocks and makes no attempt
 
2721
 * to reclaim space in the file. Use NR_RegPack()
 
2722
 *
 
2723
 * Cannot be used to delete keys with child keys
 
2724
 *
 
2725
 * Parameters:
 
2726
 *    hReg     - handle of open registry
 
2727
 *    key      - starting node RKEY, typically one of the standard ones.
 
2728
 *    path     - relative path of key to delete
 
2729
 * ---------------------------------------------------------------------
 
2730
 */
 
2731
VR_INTERFACE(REGERR) NR_RegDeleteKey( HREG hReg, RKEY key, char *path )
 
2732
{
 
2733
    REGERR      err;
 
2734
    REGFILE*    reg;
 
2735
 
 
2736
    /* verify parameters */
 
2737
    err = VERIFY_HREG( hReg );
 
2738
    if ( err != REGERR_OK )
 
2739
        return err;
 
2740
 
 
2741
    reg = ((REGHANDLE*)hReg)->pReg;
 
2742
 
 
2743
    /* lock registry */
 
2744
    err = nr_Lock( reg );
 
2745
    if ( err == REGERR_OK )
 
2746
    {
 
2747
        err = nr_RegDeleteKey( reg, key, path, FALSE );
 
2748
        nr_Unlock( reg );
 
2749
    }
 
2750
 
 
2751
    return err;
 
2752
}   /* NR_RegDeleteKey */
 
2753
 
 
2754
 
 
2755
 
 
2756
 
 
2757
/* ---------------------------------------------------------------------
 
2758
 * NR_RegDeleteKeyRaw - Delete the specified raw key
 
2759
 *
 
2760
 * Note that delete simply orphans blocks and makes no attempt
 
2761
 * to reclaim space in the file. Use NR_RegPack()
 
2762
 *
 
2763
 * Parameters:
 
2764
 *    hReg     - handle of open registry
 
2765
 *    key      - RKEY or parent to the raw key you wish to delete
 
2766
 *    keyname  - name of child key to delete
 
2767
 * ---------------------------------------------------------------------
 
2768
 */
 
2769
VR_INTERFACE(REGERR) NR_RegDeleteKeyRaw( HREG hReg, RKEY key, char *keyname )
 
2770
{
 
2771
    REGERR      err;
 
2772
    REGFILE*    reg;
 
2773
 
 
2774
    /* verify parameters */
 
2775
    err = VERIFY_HREG( hReg );
 
2776
    if ( err != REGERR_OK )
 
2777
        return err;
 
2778
 
 
2779
    reg = ((REGHANDLE*)hReg)->pReg;
 
2780
 
 
2781
    /* lock registry */
 
2782
    err = nr_Lock( reg );
 
2783
    if ( err == REGERR_OK )
 
2784
    {
 
2785
        err = nr_RegDeleteKey( reg, key, keyname, TRUE );
 
2786
        nr_Unlock( reg );
 
2787
    }
 
2788
 
 
2789
    return err;
 
2790
}   /* NR_RegDeleteKeyRaw */
 
2791
 
 
2792
 
 
2793
 
 
2794
 
 
2795
/* ---------------------------------------------------------------------
 
2796
 * NR_RegGetKey - Get the RKEY value of a node from its path
 
2797
 *
 
2798
 * Parameters:
 
2799
 *    hReg     - handle of open registry
 
2800
 *    key      - starting node RKEY, typically one of the standard ones.
 
2801
 *    path     - relative path of key to find.  (a blank path just gives you
 
2802
 *               the starting key--useful for verification, VersionRegistry)
 
2803
 *    result   - if successful the RKEY of the specified sub-key
 
2804
 * ---------------------------------------------------------------------
 
2805
 */
 
2806
VR_INTERFACE(REGERR) NR_RegGetKey( HREG hReg, RKEY key, const char *path, RKEY *result )
 
2807
{
 
2808
    REGERR      err;
 
2809
    REGOFF      start;
 
2810
    REGFILE*    reg;
 
2811
    REGDESC     desc;
 
2812
 
 
2813
    XP_ASSERT( regStartCount > 0 );
 
2814
 
 
2815
    /* prevent use of return value in case errors aren't checked */
 
2816
    if ( result != NULL )
 
2817
        *result = (RKEY)0;
 
2818
 
 
2819
    /* verify parameters */
 
2820
    err = VERIFY_HREG( hReg );
 
2821
    if ( err != REGERR_OK )
 
2822
        return err;
 
2823
 
 
2824
    if ( path == NULL || result == NULL )
 
2825
        return REGERR_PARAM;
 
2826
 
 
2827
    reg = ((REGHANDLE*)hReg)->pReg;
 
2828
 
 
2829
    /* lock registry */
 
2830
    err = nr_Lock( reg );
 
2831
    if ( err == REGERR_OK )
 
2832
    {
 
2833
        start = nr_TranslateKey( reg, key );
 
2834
        if ( start != 0 )
 
2835
        {
 
2836
            /* find the specified key ( if it's valid )*/
 
2837
            err = nr_Find( reg, start, path, &desc, 0, 0, FALSE );
 
2838
            if ( err == REGERR_OK ) {
 
2839
                *result = (RKEY)desc.location;
 
2840
            }
 
2841
        }
 
2842
        else {
 
2843
            err = REGERR_PARAM;
 
2844
        }
 
2845
 
 
2846
        nr_Unlock( reg );
 
2847
    }
 
2848
 
 
2849
    return err;
 
2850
 
 
2851
}   /* NR_RegGetKey */
 
2852
 
 
2853
 
 
2854
 
 
2855
 
 
2856
/* ---------------------------------------------------------------------
 
2857
 * NR_RegGetKeyRaw - Get the RKEY value of a node from its keyname
 
2858
 *
 
2859
 * Parameters:
 
2860
 *    hReg     - handle of open registry
 
2861
 *    key      - starting node RKEY, typically one of the standard ones.
 
2862
 *    keyname  - keyname of key to find.  (a blank keyname just gives you
 
2863
 *               the starting key--useful for verification, VersionRegistry)
 
2864
 *    result   - if successful the RKEY of the specified sub-key
 
2865
 * ---------------------------------------------------------------------
 
2866
 */
 
2867
VR_INTERFACE(REGERR) NR_RegGetKeyRaw( HREG hReg, RKEY key, char *keyname, RKEY *result )
 
2868
{
 
2869
    REGERR      err;
 
2870
    REGOFF      start;
 
2871
    REGFILE*    reg;
 
2872
    REGDESC     desc;
 
2873
 
 
2874
    XP_ASSERT( regStartCount > 0 );
 
2875
 
 
2876
    /* prevent use of return value in case errors aren't checked */
 
2877
    if ( result != NULL )
 
2878
        *result = (RKEY)0;
 
2879
 
 
2880
    /* verify parameters */
 
2881
    err = VERIFY_HREG( hReg );
 
2882
    if ( err != REGERR_OK )
 
2883
        return err;
 
2884
 
 
2885
    if ( keyname == NULL || result == NULL )
 
2886
        return REGERR_PARAM;
 
2887
 
 
2888
    reg = ((REGHANDLE*)hReg)->pReg;
 
2889
 
 
2890
    /* lock registry */
 
2891
    err = nr_Lock( reg );
 
2892
    if ( err == REGERR_OK )
 
2893
    {
 
2894
        start = nr_TranslateKey( reg, key );
 
2895
        if ( start != 0 )
 
2896
        {
 
2897
            /* find the specified key ( if it's valid )*/
 
2898
            err = nr_Find( reg, start, keyname, &desc, 0, 0, TRUE );
 
2899
            if ( err == REGERR_OK ) {
 
2900
                *result = (RKEY)desc.location;
 
2901
            }
 
2902
        }
 
2903
        else {
 
2904
            err = REGERR_PARAM;
 
2905
        }
 
2906
 
 
2907
        nr_Unlock( reg );
 
2908
    }
 
2909
 
 
2910
    return err;
 
2911
 
 
2912
}   /* NR_RegGetKeyRaw */
 
2913
 
 
2914
 
 
2915
 
 
2916
 
 
2917
/* ---------------------------------------------------------------------
 
2918
 * NR_RegGetEntryInfo - Get some basic info about the entry data
 
2919
 *
 
2920
 * Parameters:
 
2921
 *    hReg     - handle of open registry
 
2922
 *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
 
2923
 *    name     - name of entry
 
2924
 *    info     - return: Entry info object
 
2925
 * ---------------------------------------------------------------------
 
2926
 */
 
2927
VR_INTERFACE(REGERR) NR_RegGetEntryInfo( HREG hReg, RKEY key, char *name, 
 
2928
                            REGINFO *info )
 
2929
{
 
2930
    REGERR      err;
 
2931
    REGFILE*    reg;
 
2932
    REGDESC     desc;
 
2933
    
 
2934
    XP_ASSERT( regStartCount > 0 );
 
2935
 
 
2936
    /* verify parameters */
 
2937
    err = VERIFY_HREG( hReg );
 
2938
    if ( err != REGERR_OK )
 
2939
        return err;
 
2940
 
 
2941
    if ( name == NULL || *name == '\0' || info == NULL || key == 0 )
 
2942
        return REGERR_PARAM;
 
2943
 
 
2944
    reg = ((REGHANDLE*)hReg)->pReg;
 
2945
 
 
2946
    err = nr_Lock( reg );
 
2947
    if ( err == REGERR_OK )
 
2948
    {
 
2949
        /* read starting desc */
 
2950
        err = nr_ReadDesc( reg, key, &desc);
 
2951
        if ( err == REGERR_OK ) 
 
2952
        {
 
2953
            /* if the named entry exists */
 
2954
            err = nr_FindAtLevel( reg, desc.value, name, &desc, NULL );
 
2955
            if ( err == REGERR_OK ) 
 
2956
            {
 
2957
                /* ... return the values */
 
2958
                if ( info->size == sizeof(REGINFO) )
 
2959
                {
 
2960
                    info->entryType   = desc.type;
 
2961
                    info->entryLength = desc.valuelen;
 
2962
                }
 
2963
                else
 
2964
                {
 
2965
                    /* uninitialized (maybe invalid) REGINFO structure */
 
2966
                    err = REGERR_PARAM;
 
2967
                }
 
2968
            }
 
2969
        }
 
2970
 
 
2971
        nr_Unlock( reg );
 
2972
    }
 
2973
 
 
2974
    return err;
 
2975
 
 
2976
}   /* NR_RegGetEntryInfo */
 
2977
 
 
2978
 
 
2979
 
 
2980
       
 
2981
/* ---------------------------------------------------------------------
 
2982
 * NR_RegGetEntryString - Get the UTF string value associated with the
 
2983
 *                       named entry of the specified key.
 
2984
 *
 
2985
 * Parameters:
 
2986
 *    hReg     - handle of open registry
 
2987
 *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
 
2988
 *    name     - name of entry
 
2989
 *    buffer   - destination for string
 
2990
 *    bufsize  - size of buffer
 
2991
 * ---------------------------------------------------------------------
 
2992
 */
 
2993
VR_INTERFACE(REGERR) NR_RegGetEntryString( HREG  hReg, RKEY  key, char  *name,
 
2994
                            char  *buffer, uint32 bufsize)
 
2995
{
 
2996
    REGERR      err;
 
2997
    REGFILE*    reg;
 
2998
    REGDESC     desc;
 
2999
 
 
3000
    XP_ASSERT( regStartCount > 0 );
 
3001
 
 
3002
    /* verify parameters */
 
3003
    err = VERIFY_HREG( hReg );
 
3004
    if ( err != REGERR_OK )
 
3005
        return err;
 
3006
 
 
3007
    if ( name==NULL || *name=='\0' || buffer==NULL || bufsize==0 || key==0 )
 
3008
        return REGERR_PARAM;
 
3009
 
 
3010
    reg = ((REGHANDLE*)hReg)->pReg;
 
3011
 
 
3012
    err = nr_Lock( reg );
 
3013
    if ( err == REGERR_OK )
 
3014
    {
 
3015
        /* read starting desc */
 
3016
        err = nr_ReadDesc( reg, key, &desc);
 
3017
        if ( err == REGERR_OK ) 
 
3018
        {
 
3019
            /* if the named entry exists */
 
3020
            err = nr_FindAtLevel( reg, desc.value, name, &desc, NULL );
 
3021
            if ( err == REGERR_OK ) 
 
3022
            {
 
3023
                /* read the string */
 
3024
                if ( desc.type == REGTYPE_ENTRY_STRING_UTF ) 
 
3025
                {
 
3026
                    err = nr_ReadData( reg, &desc, bufsize, buffer );
 
3027
                    /* prevent run-away strings */
 
3028
                    buffer[bufsize-1] = '\0';
 
3029
                }
 
3030
                else {
 
3031
                    err = REGERR_BADTYPE;
 
3032
                }
 
3033
            }
 
3034
        }
 
3035
 
 
3036
        nr_Unlock( reg );
 
3037
    }
 
3038
 
 
3039
    return err;
 
3040
 
 
3041
}   /* NR_RegGetEntryString */
 
3042
 
 
3043
 
 
3044
 
 
3045
 
 
3046
/* ---------------------------------------------------------------------
 
3047
 * NR_RegGetEntry - Get the value data associated with the
 
3048
 *                  named entry of the specified key.
 
3049
 *
 
3050
 * Parameters:
 
3051
 *    hReg     - handle of open registry
 
3052
 *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
 
3053
 *    name     - name of entry
 
3054
 *    buffer   - destination for data
 
3055
 *    size     - in:  size of buffer
 
3056
 *               out: size of actual data (incl. \0 term. for strings)
 
3057
 * ---------------------------------------------------------------------
 
3058
 */
 
3059
VR_INTERFACE(REGERR) NR_RegGetEntry( HREG hReg, RKEY key, char *name,
 
3060
    void *buffer, uint32 *size )
 
3061
{
 
3062
    REGERR      err;
 
3063
    REGFILE*    reg;
 
3064
    REGDESC     desc;
 
3065
    char        *tmpbuf = NULL;  /* malloc a tmp buffer to convert XP int arrays */
 
3066
    uint32      nInt;
 
3067
    uint32      *pISrc;
 
3068
    uint32      *pIDest;
 
3069
    XP_Bool     needFree = FALSE;
 
3070
 
 
3071
    XP_ASSERT( regStartCount > 0 );
 
3072
 
 
3073
    /* verify parameters */
 
3074
    err = VERIFY_HREG( hReg );
 
3075
    if ( err != REGERR_OK )
 
3076
        return err;
 
3077
 
 
3078
    if ( name==NULL || *name=='\0' || buffer==NULL || size==NULL || key==0 )
 
3079
        return REGERR_PARAM;
 
3080
 
 
3081
    reg = ((REGHANDLE*)hReg)->pReg;
 
3082
 
 
3083
    err = nr_Lock( reg );
 
3084
    if ( err == REGERR_OK )
 
3085
    {
 
3086
        /* read starting desc */
 
3087
        err = nr_ReadDesc( reg, key, &desc);
 
3088
        if ( err == REGERR_OK )
 
3089
        {
 
3090
            /* if the named entry exists */
 
3091
            err = nr_FindAtLevel( reg, desc.value, name, &desc, NULL );
 
3092
            if ( err == REGERR_OK )
 
3093
            {
 
3094
                if ( desc.valuelen > *size ) {
 
3095
                    err = REGERR_BUFTOOSMALL;
 
3096
                }
 
3097
                else if ( desc.valuelen == 0 ) {
 
3098
                    err = REGERR_FAIL;
 
3099
                }
 
3100
                else switch (desc.type)
 
3101
                {
 
3102
                /* platform independent array of 32-bit integers */
 
3103
                case REGTYPE_ENTRY_INT32_ARRAY:
 
3104
                    tmpbuf = (char*)XP_ALLOC( desc.valuelen );
 
3105
                    if ( tmpbuf != NULL ) 
 
3106
                    {
 
3107
                        needFree = TRUE;
 
3108
                        err = nr_ReadData( reg, &desc, desc.valuelen, tmpbuf );
 
3109
                        if ( REGERR_OK == err )
 
3110
                        {
 
3111
                            /* convert int array */
 
3112
                            nInt = (desc.valuelen / INTSIZE);
 
3113
                            pISrc = (uint32*)tmpbuf;
 
3114
                            pIDest = (uint32*)buffer;
 
3115
                            for(; nInt > 0; nInt--, pISrc++, pIDest++) {
 
3116
                                *pIDest = nr_ReadLong((char*)pISrc);
 
3117
                            }
 
3118
                        }
 
3119
                    }
 
3120
                    else
 
3121
                        err = REGERR_MEMORY;
 
3122
                    break;
 
3123
 
 
3124
                case REGTYPE_ENTRY_STRING_UTF:
 
3125
                    tmpbuf = (char*)buffer;
 
3126
                    err = nr_ReadData( reg, &desc, *size, tmpbuf );
 
3127
                    /* prevent run-away strings */
 
3128
                    tmpbuf[(*size)-1] = '\0';
 
3129
                    break;
 
3130
 
 
3131
                case REGTYPE_ENTRY_FILE:
 
3132
 
 
3133
                    err = nr_ReadData( reg, &desc, *size, (char*)buffer );
 
3134
#if defined(XP_MAC) || defined(XP_MACOSX)
 
3135
                    if (err == 0)
 
3136
                    {
 
3137
                        tmpbuf = nr_PathFromMacAlias(buffer, *size);
 
3138
                        if (tmpbuf == NULL) 
 
3139
                        {
 
3140
                            buffer = NULL;
 
3141
                            err = REGERR_NOFILE; /* must match nr_GetPathname() in VerReg.c */
 
3142
                        }
 
3143
                        else 
 
3144
                        {
 
3145
                            needFree = TRUE;
 
3146
 
 
3147
                            if (XP_STRLEN(tmpbuf) < *size) /* leave room for \0 */
 
3148
                                XP_STRCPY(buffer, tmpbuf);
 
3149
                            else 
 
3150
                                err = REGERR_BUFTOOSMALL;
 
3151
                        }
 
3152
                    }
 
3153
#endif
 
3154
                    break;
 
3155
                
 
3156
                case REGTYPE_ENTRY_BYTES:
 
3157
                default:              /* return raw data for unknown types */
 
3158
                    err = nr_ReadData( reg, &desc, *size, (char*)buffer );
 
3159
                    break;
 
3160
                }
 
3161
 
 
3162
                /* return the actual data size */
 
3163
                *size = desc.valuelen;
 
3164
            }
 
3165
        }
 
3166
 
 
3167
        nr_Unlock( reg );
 
3168
    }
 
3169
 
 
3170
    if (needFree)
 
3171
        XP_FREE(tmpbuf);
 
3172
 
 
3173
    return err;
 
3174
 
 
3175
}   /* NR_RegGetEntry */
 
3176
 
 
3177
 
 
3178
 
 
3179
 
 
3180
/* ---------------------------------------------------------------------
 
3181
 * NR_RegSetEntryString - Store a UTF-8 string value associated with the
 
3182
 *                       named entry of the specified key.  Used for
 
3183
 *                       both creation and update.
 
3184
 *
 
3185
 * Parameters:
 
3186
 *    hReg     - handle of open registry
 
3187
 *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
 
3188
 *    name     - name of entry
 
3189
 *    buffer   - UTF-8 String to store
 
3190
 * ---------------------------------------------------------------------
 
3191
 */
 
3192
VR_INTERFACE(REGERR) NR_RegSetEntryString( HREG hReg, RKEY key, char *name,
 
3193
                                     char *buffer )
 
3194
{
 
3195
    REGERR      err;
 
3196
    REGFILE*    reg;
 
3197
    REGDESC     desc;
 
3198
    REGDESC     parent;
 
3199
 
 
3200
    XP_ASSERT( regStartCount > 0 );
 
3201
 
 
3202
    /* verify parameters */
 
3203
    err = VERIFY_HREG( hReg );
 
3204
    if ( err != REGERR_OK )
 
3205
        return err;
 
3206
 
 
3207
    if ( name == NULL || *name == '\0' || buffer == NULL || key == 0 )
 
3208
        return REGERR_PARAM;
 
3209
 
 
3210
    reg = ((REGHANDLE*)hReg)->pReg;
 
3211
 
 
3212
    /* lock registry */
 
3213
    err = nr_Lock( reg );
 
3214
    if ( err != REGERR_OK )
 
3215
        return err;
 
3216
 
 
3217
    /* read starting desc */
 
3218
    err = nr_ReadDesc( reg, key, &parent);
 
3219
    if ( err == REGERR_OK ) {
 
3220
 
 
3221
        /* if the named entry already exists */
 
3222
        err = nr_FindAtLevel( reg, parent.value, name, &desc, NULL );
 
3223
        if ( err == REGERR_OK ) {
 
3224
            /* then update the existing one */
 
3225
            err = nr_WriteString( reg, buffer, &desc );
 
3226
            if ( err == REGERR_OK ) {
 
3227
                desc.type = REGTYPE_ENTRY_STRING_UTF;
 
3228
                err = nr_WriteDesc( reg, &desc );
 
3229
            }
 
3230
        }
 
3231
        else if ( err == REGERR_NOFIND ) {
 
3232
            /* otherwise create a new entry */
 
3233
            err = nr_CreateEntryString( reg, &parent, name, buffer );
 
3234
        }
 
3235
        /* other errors fall through */
 
3236
    }
 
3237
 
 
3238
    /* unlock registry */
 
3239
    nr_Unlock( reg );
 
3240
 
 
3241
    return err;
 
3242
 
 
3243
}   /* NR_RegSetEntryString */
 
3244
 
 
3245
 
 
3246
 
 
3247
 
 
3248
/* ---------------------------------------------------------------------
 
3249
 * NR_RegSetEntry - Store value data associated with the named entry
 
3250
 *                  of the specified key.  Used for both creation and update.
 
3251
 *
 
3252
 * Parameters:
 
3253
 *    hReg     - handle of open registry
 
3254
 *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
 
3255
 *    name     - name of entry
 
3256
 *    type     - type of data to be stored
 
3257
 *    buffer   - data to store
 
3258
 *    size     - length of data to store in bytes
 
3259
 * ---------------------------------------------------------------------
 
3260
 */
 
3261
VR_INTERFACE(REGERR) NR_RegSetEntry( HREG hReg, RKEY key, char *name, uint16 type,
 
3262
    void *buffer, uint32 size )
 
3263
{
 
3264
    REGERR      err;
 
3265
    REGFILE*    reg;
 
3266
    REGDESC     desc;
 
3267
    REGDESC     parent;
 
3268
    char        *data = NULL;
 
3269
    uint32      nInt;
 
3270
    uint32      *pIDest;
 
3271
    uint32      *pISrc;
 
3272
    XP_Bool     needFree = FALSE;
 
3273
    int32       datalen = size;
 
3274
 
 
3275
    XP_ASSERT( regStartCount > 0 );
 
3276
 
 
3277
    /* verify parameters */
 
3278
    err = VERIFY_HREG( hReg );
 
3279
    if ( err != REGERR_OK )
 
3280
        return err;
 
3281
 
 
3282
    if ( name==NULL || *name=='\0' || buffer==NULL || size==0 || key==0 )
 
3283
        return REGERR_PARAM;
 
3284
 
 
3285
    reg = ((REGHANDLE*)hReg)->pReg;
 
3286
 
 
3287
    /* validate type and convert numerics to XP format */
 
3288
    switch (type)
 
3289
    {
 
3290
        case REGTYPE_ENTRY_BYTES:
 
3291
            data = (char*)buffer;
 
3292
            break;
 
3293
 
 
3294
        case REGTYPE_ENTRY_FILE:
 
3295
 
 
3296
#if defined(XP_MAC) || defined(XP_MACOSX)
 
3297
            nr_MacAliasFromPath(buffer, (void **)&data, &datalen);
 
3298
            if (data)
 
3299
                needFree = TRUE;
 
3300
#else
 
3301
            data = (char*)buffer;   
 
3302
#endif
 
3303
            break;
 
3304
 
 
3305
 
 
3306
        case REGTYPE_ENTRY_STRING_UTF:
 
3307
            data = (char*)buffer;
 
3308
            /* string must be null terminated */
 
3309
            if ( data[size-1] != '\0' )
 
3310
                return REGERR_PARAM;
 
3311
            break;
 
3312
 
 
3313
 
 
3314
        case REGTYPE_ENTRY_INT32_ARRAY:
 
3315
            /* verify no partial integers */
 
3316
            if ( (size % INTSIZE) != 0 )
 
3317
                return REGERR_PARAM;
 
3318
 
 
3319
            /* get a conversion buffer */
 
3320
            data = (char*)XP_ALLOC(size);
 
3321
            if ( data == NULL )
 
3322
                return REGERR_MEMORY;
 
3323
            else
 
3324
                needFree = TRUE;
 
3325
 
 
3326
            /* convert array to XP format */
 
3327
            nInt = ( size / INTSIZE );
 
3328
            pIDest = (uint32*)data;
 
3329
            pISrc  = (uint32*)buffer;
 
3330
 
 
3331
            for( ; nInt > 0; nInt--, pIDest++, pISrc++) {
 
3332
                nr_WriteLong( *pISrc, (char*)pIDest );
 
3333
            }
 
3334
            break;
 
3335
 
 
3336
 
 
3337
        default:
 
3338
            return REGERR_BADTYPE;
 
3339
    }
 
3340
 
 
3341
    /* lock registry */
 
3342
    err = nr_Lock( reg );
 
3343
    if ( REGERR_OK == err )
 
3344
    {
 
3345
        /* read starting desc */
 
3346
        err = nr_ReadDesc( reg, key, &parent);
 
3347
        if ( err == REGERR_OK ) 
 
3348
        {
 
3349
            /* if the named entry already exists */
 
3350
            err = nr_FindAtLevel( reg, parent.value, name, &desc, NULL );
 
3351
            if ( err == REGERR_OK ) 
 
3352
            {
 
3353
                /* then update the existing one */
 
3354
                err = nr_WriteData( reg, data, datalen, &desc );
 
3355
                if ( err == REGERR_OK ) 
 
3356
                {
 
3357
                    desc.type = type;
 
3358
                    err = nr_WriteDesc( reg, &desc );
 
3359
                }
 
3360
            }
 
3361
            else if ( err == REGERR_NOFIND ) 
 
3362
            {
 
3363
                /* otherwise create a new entry */
 
3364
                err = nr_CreateEntry( reg, &parent, name, type, data, datalen );
 
3365
            }
 
3366
            else {
 
3367
                /* other errors fall through */
 
3368
            }
 
3369
        }
 
3370
 
 
3371
        /* unlock registry */
 
3372
        nr_Unlock( reg );
 
3373
    }
 
3374
 
 
3375
    if (needFree)
 
3376
        XP_FREE(data);
 
3377
 
 
3378
    return err;
 
3379
 
 
3380
}   /* NR_RegSetEntry */
 
3381
 
 
3382
 
 
3383
 
 
3384
 
 
3385
/* ---------------------------------------------------------------------
 
3386
 * NR_RegDeleteEntry - Delete the named entry
 
3387
 *
 
3388
 * Parameters:
 
3389
 *    hReg     - handle of open registry
 
3390
 *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
 
3391
 *    name     - name of entry
 
3392
 * ---------------------------------------------------------------------
 
3393
 */
 
3394
VR_INTERFACE(REGERR) NR_RegDeleteEntry( HREG hReg, RKEY key, char *name )
 
3395
{
 
3396
    REGERR      err;
 
3397
    REGFILE*    reg;
 
3398
    REGDESC     desc;
 
3399
    REGDESC     parent;
 
3400
    REGOFF      offPrev;
 
3401
 
 
3402
    XP_ASSERT( regStartCount > 0 );
 
3403
 
 
3404
    /* verify parameters */
 
3405
    err = VERIFY_HREG( hReg );
 
3406
    if ( err != REGERR_OK )
 
3407
        return err;
 
3408
 
 
3409
    if ( name == NULL || *name == '\0' || key == 0)
 
3410
        return REGERR_PARAM;
 
3411
 
 
3412
    reg = ((REGHANDLE*)hReg)->pReg;
 
3413
 
 
3414
    /* lock registry */
 
3415
    err = nr_Lock( reg );
 
3416
    if ( err != REGERR_OK )
 
3417
        return err;
 
3418
 
 
3419
    /* read starting desc */
 
3420
    err = nr_ReadDesc( reg, key, &parent);
 
3421
    if ( err == REGERR_OK ) {
 
3422
 
 
3423
        /* look up the named entry */
 
3424
        err = nr_FindAtLevel( reg, parent.value, name, &desc, &offPrev );
 
3425
        if ( err == REGERR_OK ) {
 
3426
 
 
3427
            XP_ASSERT( TYPE_IS_ENTRY( desc.type ) );
 
3428
 
 
3429
            /* if entry is the head of a chain */
 
3430
            if ( offPrev == 0 ) {
 
3431
                /* hook parent key to next entry */
 
3432
                XP_ASSERT( parent.value == desc.location );
 
3433
                parent.value = desc.left;
 
3434
            }
 
3435
            else {
 
3436
                /* otherwise hook previous entry to next */
 
3437
                err = nr_ReadDesc( reg, offPrev, &parent );
 
3438
                parent.left = desc.left;
 
3439
            }
 
3440
            /* write out changed desc for previous node */
 
3441
            if ( err == REGERR_OK ) {
 
3442
                err = nr_WriteDesc( reg, &parent );
 
3443
                /* zap the deleted desc because an enum state may contain a
 
3444
                 * reference to a specific entry node
 
3445
                 */
 
3446
                if ( err == REGERR_OK ) {
 
3447
                    desc.type |= REGTYPE_DELETED;
 
3448
                    err = nr_WriteDesc( reg, &desc );
 
3449
                }
 
3450
            }
 
3451
        }
 
3452
    }
 
3453
 
 
3454
    /* unlock registry */
 
3455
    nr_Unlock( reg );
 
3456
 
 
3457
    return err;
 
3458
 
 
3459
}   /* NR_RegDeleteEntry */
 
3460
 
 
3461
 
 
3462
 
 
3463
 
 
3464
/* ---------------------------------------------------------------------
 
3465
 * NR_RegEnumSubkeys - Enumerate the subkey names for the specified key
 
3466
 *
 
3467
 * Returns REGERR_NOMORE at end of enumeration.
 
3468
 *
 
3469
 * Parameters:
 
3470
 *    hReg     - handle of open registry
 
3471
 *    key      - RKEY of key to enumerate--obtain with NR_RegGetKey()
 
3472
 *    eState   - enumerations state, must contain NULL to start
 
3473
 *    buffer   - location to store subkey names.  Once an enumeration
 
3474
 *               is started user must not modify contents since values
 
3475
 *               are built using the previous contents.
 
3476
 *    bufsize  - size of buffer for names
 
3477
 *    style    - 0 returns direct child keys only, REGENUM_DESCEND
 
3478
 *               returns entire sub-tree
 
3479
 * ---------------------------------------------------------------------
 
3480
 */
 
3481
VR_INTERFACE(REGERR) NR_RegEnumSubkeys( HREG hReg, RKEY key, REGENUM *state,
 
3482
                                    char *buffer, uint32 bufsize, uint32 style)
 
3483
{
 
3484
    REGERR      err;
 
3485
    REGFILE*    reg;
 
3486
    REGDESC     desc;
 
3487
 
 
3488
    XP_ASSERT( regStartCount > 0 );
 
3489
 
 
3490
    /* verify parameters */
 
3491
    err = VERIFY_HREG( hReg );
 
3492
    if ( err != REGERR_OK )
 
3493
        return err;
 
3494
 
 
3495
    if ( key == 0 || state == NULL || buffer == NULL )
 
3496
        return REGERR_PARAM;
 
3497
 
 
3498
    reg = ((REGHANDLE*)hReg)->pReg;
 
3499
 
 
3500
    /* lock registry */
 
3501
    err = nr_Lock( reg );
 
3502
    if ( err != REGERR_OK )
 
3503
        return err;
 
3504
 
 
3505
    desc.down     = 0; /* initialize to quiet warnings */
 
3506
    desc.location = 0;
 
3507
 
 
3508
    /* verify starting key */
 
3509
    key = nr_TranslateKey( reg, key );
 
3510
    if ( key == 0 )
 
3511
        err = REGERR_PARAM;
 
3512
    else if ( *state == 0 )
 
3513
        err = nr_ReadDesc( reg, key, &desc);
 
3514
    else
 
3515
        err = REGERR_OK;
 
3516
 
 
3517
    if ( err == REGERR_OK )
 
3518
    {
 
3519
        /* if in initial state and no children return now */
 
3520
        if ( *state == 0 && desc.down == 0 ) 
 
3521
        {
 
3522
            err = REGERR_NOMORE;
 
3523
        }
 
3524
        else switch ( style )
 
3525
        {
 
3526
          case REGENUM_CHILDREN:
 
3527
            *buffer = '\0';
 
3528
            if ( *state == 0 ) 
 
3529
            {
 
3530
                /* initial state: get first child (.down) */
 
3531
                err = nr_ReplaceName( reg, desc.down, buffer, bufsize, &desc );
 
3532
            }
 
3533
            else 
 
3534
            {
 
3535
                /* get sibling (.left) of current key */
 
3536
                err = nr_ReadDesc( reg, *state, &desc );
 
3537
                if ( err == REGERR_OK || REGERR_DELETED == err )
 
3538
                {
 
3539
                    /* it's OK for the current (state) node to be deleted */
 
3540
                    if ( desc.left != 0 ) 
 
3541
                    {
 
3542
                        err = nr_ReplaceName( reg, desc.left, 
 
3543
                                    buffer, bufsize, &desc );
 
3544
                    }
 
3545
                    else
 
3546
                        err = REGERR_NOMORE;
 
3547
                }
 
3548
            }
 
3549
            break;
 
3550
 
 
3551
 
 
3552
          case REGENUM_DESCEND:
 
3553
            if ( *state == 0 ) 
 
3554
            {
 
3555
                /* initial state */
 
3556
                *buffer = '\0';
 
3557
                err = nr_ReplaceName( reg, desc.down, buffer, bufsize, &desc );
 
3558
            }
 
3559
            else 
 
3560
            {
 
3561
                /* get last position */
 
3562
                err = nr_ReadDesc( reg, *state, &desc );
 
3563
                if ( REGERR_OK != err && REGERR_DELETED != err ) 
 
3564
                {
 
3565
                    /* it is OK for the state node to be deleted
 
3566
                     * (the *next* node MUST be "live", though).
 
3567
                     * bail out on any other error */
 
3568
                    break;
 
3569
                }
 
3570
 
 
3571
                if ( desc.down != 0 ) {
 
3572
                    /* append name of first child key */
 
3573
                    err = nr_CatName( reg, desc.down, buffer, bufsize, &desc );
 
3574
                }
 
3575
                else if ( desc.left != 0 ) {
 
3576
                    /* replace last segment with next sibling */
 
3577
                    err = nr_ReplaceName( reg, desc.left, 
 
3578
                                buffer, bufsize, &desc );
 
3579
                }
 
3580
                else {
 
3581
                  /* done with level, pop up as many times as necessary */
 
3582
                    while ( err == REGERR_OK ) 
 
3583
                    {
 
3584
                        if ( desc.parent != key && desc.parent != 0 ) 
 
3585
                        {
 
3586
                            err = nr_RemoveName( buffer );
 
3587
                            if ( err == REGERR_OK ) 
 
3588
                            {
 
3589
                                err = nr_ReadDesc( reg, desc.parent, &desc );
 
3590
                                if ( err == REGERR_OK && desc.left != 0 ) 
 
3591
                                {
 
3592
                                    err = nr_ReplaceName( reg, desc.left, 
 
3593
                                                buffer, bufsize, &desc );
 
3594
                                    break;  /* found a node */
 
3595
                                }
 
3596
                            }
 
3597
                        }
 
3598
                        else
 
3599
                            err = REGERR_NOMORE;
 
3600
                    }
 
3601
                }
 
3602
            }
 
3603
            break;
 
3604
 
 
3605
 
 
3606
          case REGENUM_DEPTH_FIRST:
 
3607
            if ( *state == 0 ) 
 
3608
            {
 
3609
                /* initial state */
 
3610
 
 
3611
                *buffer = '\0';
 
3612
                err = nr_ReplaceName( reg, desc.down, buffer, bufsize, &desc );
 
3613
                while ( REGERR_OK == err && desc.down != 0 )
 
3614
                {
 
3615
                    /* start as far down the tree as possible */
 
3616
                    err = nr_CatName( reg, desc.down, buffer, bufsize, &desc );
 
3617
                }
 
3618
            }
 
3619
            else 
 
3620
            {
 
3621
                /* get last position */
 
3622
                err = nr_ReadDesc( reg, *state, &desc );
 
3623
                if ( REGERR_OK != err && REGERR_DELETED != err ) 
 
3624
                {
 
3625
                    /* it is OK for the state node to be deleted
 
3626
                     * (the *next* node MUST be "live", though).
 
3627
                     * bail out on any other error */
 
3628
                    break;
 
3629
                }
 
3630
 
 
3631
                if ( desc.left != 0 )
 
3632
                {
 
3633
                    /* get sibling, then descend as far as possible */
 
3634
                    err = nr_ReplaceName(reg, desc.left, buffer,bufsize,&desc);
 
3635
 
 
3636
                    while ( REGERR_OK == err && desc.down != 0 ) 
 
3637
                    {
 
3638
                        err = nr_CatName(reg, desc.down, buffer,bufsize,&desc);
 
3639
                    }
 
3640
                }
 
3641
                else 
 
3642
                {
 
3643
                    /* pop up to parent */
 
3644
                    if ( desc.parent != key && desc.parent != 0 )
 
3645
                    {
 
3646
                        err = nr_RemoveName( buffer );
 
3647
                        if ( REGERR_OK == err )
 
3648
                        {
 
3649
                            /* validate parent key */
 
3650
                            err = nr_ReadDesc( reg, desc.parent, &desc );
 
3651
                        }
 
3652
                    }
 
3653
                    else 
 
3654
                        err = REGERR_NOMORE;
 
3655
                }
 
3656
            }
 
3657
            break;
 
3658
 
 
3659
 
 
3660
          default:
 
3661
            err = REGERR_PARAM;
 
3662
            break;
 
3663
        }
 
3664
    }
 
3665
 
 
3666
    /* set enum state to current key */
 
3667
    if ( err == REGERR_OK ) {
 
3668
        *state = desc.location;
 
3669
    }
 
3670
 
 
3671
    /* unlock registry */
 
3672
    nr_Unlock( reg );
 
3673
 
 
3674
    return err;
 
3675
 
 
3676
}   /* NR_RegEnumSubkeys */
 
3677
 
 
3678
 
 
3679
 
 
3680
 
 
3681
/* ---------------------------------------------------------------------
 
3682
 * NR_RegEnumEntries - Enumerate the entry names for the specified key
 
3683
 *
 
3684
 * Returns REGERR_NOMORE at end of enumeration.
 
3685
 *
 
3686
 * Parameters:
 
3687
 *    hReg     - handle of open registry
 
3688
 *    key      - RKEY of key that contains entry--obtain with NR_RegGetKey()
 
3689
 *    eState   - enumerations state, must contain NULL to start
 
3690
 *    buffer   - location to store entry names
 
3691
 *    bufsize  - size of buffer for names
 
3692
 *    info     - optional REGINFO for the entry. If not NULL must be 
 
3693
 *               initialized as in NR_RegGetEntryInfo()
 
3694
 * ---------------------------------------------------------------------
 
3695
 */
 
3696
VR_INTERFACE(REGERR) NR_RegEnumEntries( HREG hReg, RKEY key, REGENUM *state,
 
3697
                            char *buffer, uint32 bufsize, REGINFO *info )
 
3698
{
 
3699
    REGERR      err;
 
3700
    REGFILE*    reg;
 
3701
    REGDESC     desc;
 
3702
 
 
3703
    XP_ASSERT( regStartCount > 0 );
 
3704
 
 
3705
    /* verify parameters */
 
3706
    err = VERIFY_HREG( hReg );
 
3707
    if ( err != REGERR_OK )
 
3708
        return err;
 
3709
 
 
3710
    if ( key == 0 || state == NULL || buffer == NULL )
 
3711
        return REGERR_PARAM;
 
3712
 
 
3713
    reg = ((REGHANDLE*)hReg)->pReg;
 
3714
 
 
3715
    /* lock registry */
 
3716
    err = nr_Lock( reg );
 
3717
    if ( err != REGERR_OK )
 
3718
        return err;
 
3719
    
 
3720
    /* verify starting key */
 
3721
    err = nr_ReadDesc( reg, key, &desc);
 
3722
    if ( err == REGERR_OK )
 
3723
    {
 
3724
        if ( *state == 0 ) 
 
3725
        {
 
3726
            /* initial state--get first entry */
 
3727
            if ( desc.value != 0 ) 
 
3728
            {
 
3729
                *buffer = '\0';
 
3730
                err =  nr_ReplaceName( reg, desc.value, buffer, bufsize, &desc );
 
3731
            }
 
3732
            else  
 
3733
            { 
 
3734
                /* there *are* no entries */
 
3735
                err = REGERR_NOMORE;
 
3736
            }
 
3737
        }
 
3738
        else 
 
3739
        {
 
3740
            /* 'state' stores previous entry */
 
3741
            err = nr_ReadDesc( reg, *state, &desc );
 
3742
            if ( err == REGERR_OK  || err == REGERR_DELETED ) 
 
3743
            {
 
3744
                /* get next entry in chain */
 
3745
                if ( desc.left != 0 ) 
 
3746
                {
 
3747
                    *buffer = '\0';
 
3748
                    err =  nr_ReplaceName( reg, desc.left, buffer, bufsize, &desc );
 
3749
                }
 
3750
                else 
 
3751
                {
 
3752
                    /* at end of chain */
 
3753
                    err = REGERR_NOMORE;
 
3754
                }
 
3755
            }
 
3756
        }
 
3757
 
 
3758
        /* if we found an entry */
 
3759
        if ( err == REGERR_OK ) 
 
3760
        {
 
3761
            /* set enum state to current entry */
 
3762
            *state = desc.location;
 
3763
 
 
3764
            /* return REGINFO if requested */
 
3765
            if ( info != NULL && info->size >= sizeof(REGINFO) ) 
 
3766
            {
 
3767
                info->entryType   = desc.type;
 
3768
                info->entryLength = desc.valuelen;
 
3769
            }
 
3770
        }
 
3771
    }
 
3772
 
 
3773
    /* unlock registry */
 
3774
    nr_Unlock( reg );
 
3775
 
 
3776
    return err;
 
3777
 
 
3778
}   /* NR_RegEnumEntries */
 
3779
 
 
3780
 
 
3781
 
 
3782
 
 
3783
 
 
3784
/* --------------------------------------------------------------------
 
3785
 * Registry Packing
 
3786
 * --------------------------------------------------------------------
 
3787
 */
 
3788
#ifndef STANDALONE_REGISTRY
 
3789
#include "VerReg.h"
 
3790
 
 
3791
#ifdef RESURRECT_LATER
 
3792
static REGERR nr_createTempRegName( char *filename, uint32 filesize );
 
3793
static REGERR nr_addNodesToNewReg( HREG hReg, RKEY rootkey, HREG hRegNew, void *userData, nr_RegPackCallbackFunc fn );
 
3794
/* -------------------------------------------------------------------- */
 
3795
static REGERR nr_createTempRegName( char *filename, uint32 filesize )
 
3796
{
 
3797
    struct stat statbuf;
 
3798
    XP_Bool nameFound = FALSE;
 
3799
    char tmpname[MAX_PATH+1];
 
3800
    uint32 len;
 
3801
    int err;
 
3802
 
 
3803
    XP_STRCPY( tmpname, filename );
 
3804
    len = XP_STRLEN(tmpname);
 
3805
    if (len < filesize) {
 
3806
        tmpname[len-1] = '~';
 
3807
        tmpname[len] = '\0';
 
3808
        remove(tmpname);
 
3809
        if ( stat(tmpname, &statbuf) != 0 )
 
3810
            nameFound = TRUE;
 
3811
    }
 
3812
    len++;
 
3813
    while (!nameFound && len < filesize ) {
 
3814
        tmpname[len-1] = '~';
 
3815
        tmpname[len] = '\0';
 
3816
        remove(tmpname);
 
3817
        if ( stat(tmpname, &statbuf) != 0 )
 
3818
            nameFound = TRUE;
 
3819
        else
 
3820
            len++;
 
3821
    }  
 
3822
    if (nameFound) {
 
3823
        XP_STRCPY(filename, tmpname);
 
3824
        err = REGERR_OK;
 
3825
    } else {
 
3826
        err = REGERR_FAIL;
 
3827
    }
 
3828
   return err;
 
3829
}
 
3830
 
 
3831
static REGERR nr_addNodesToNewReg( HREG hReg, RKEY rootkey, HREG hRegNew, void *userData, nr_RegPackCallbackFunc fn )
 
3832
{
 
3833
    char keyname[MAXREGPATHLEN+1] = {0};
 
3834
    char entryname[MAXREGPATHLEN+1] = {0};
 
3835
    void *buffer;
 
3836
    uint32 bufsize = 2024;
 
3837
    uint32 datalen;
 
3838
    REGENUM state = 0;
 
3839
    REGENUM entrystate = 0;
 
3840
    REGINFO info;
 
3841
    int err = REGERR_OK;
 
3842
    int status = REGERR_OK;
 
3843
    RKEY key;
 
3844
    RKEY newKey;
 
3845
    REGFILE* reg;
 
3846
    REGFILE* regNew;
 
3847
    static int32 cnt = 0;
 
3848
    static int32 prevCnt = 0;
 
3849
 
 
3850
    reg = ((REGHANDLE*)hReg)->pReg;
 
3851
    regNew = ((REGHANDLE*)hRegNew)->pReg;
 
3852
 
 
3853
    buffer = XP_ALLOC(bufsize);
 
3854
    if ( buffer == NULL ) {
 
3855
        err = REGERR_MEMORY;
 
3856
        return err;
 
3857
    }
 
3858
 
 
3859
    while (err == REGERR_OK)
 
3860
    {
 
3861
        err = NR_RegEnumSubkeys( hReg, rootkey, &state, keyname, sizeof(keyname), REGENUM_DESCEND );
 
3862
        if ( err != REGERR_OK )
 
3863
            break;
 
3864
        err = NR_RegAddKey( hRegNew, rootkey, keyname, &newKey );
 
3865
        if ( err != REGERR_OK )
 
3866
            break;
 
3867
        cnt++;
 
3868
        if (cnt >= prevCnt + 15) 
 
3869
        {
 
3870
            fn(userData, regNew->hdr.avail, reg->hdr.avail);
 
3871
            prevCnt = cnt;
 
3872
        }
 
3873
        err = NR_RegGetKey( hReg, rootkey, keyname, &key );
 
3874
        if ( err != REGERR_OK )
 
3875
            break;
 
3876
        entrystate = 0;
 
3877
        status = REGERR_OK;
 
3878
        while (status == REGERR_OK) {
 
3879
            info.size = sizeof(REGINFO);
 
3880
            status = NR_RegEnumEntries( hReg, key, &entrystate, entryname, 
 
3881
                                        sizeof(entryname), &info );
 
3882
            if ( status == REGERR_OK ) {
 
3883
                XP_ASSERT( bufsize >= info.entryLength );
 
3884
                datalen = bufsize;
 
3885
                status = NR_RegGetEntry( hReg, key, entryname, buffer, &datalen );
 
3886
                XP_ASSERT( info.entryLength == datalen );
 
3887
                if ( status == REGERR_OK ) {
 
3888
                    /* copy entry */
 
3889
                    status = NR_RegSetEntry( hRegNew, newKey, entryname, 
 
3890
                                info.entryType, buffer, info.entryLength );
 
3891
                }
 
3892
            } 
 
3893
        }
 
3894
        if ( status != REGERR_NOMORE ) {
 
3895
            /* pass real error to outer loop */
 
3896
            err = status;
 
3897
        }
 
3898
    }
 
3899
 
 
3900
    if ( err == REGERR_NOMORE )
 
3901
        err = REGERR_OK;
 
3902
 
 
3903
    XP_FREEIF(buffer);
 
3904
    return err;
 
3905
}
 
3906
#endif /* RESURRECT_LATER */
 
3907
 
 
3908
 
 
3909
 
 
3910
/* ---------------------------------------------------------------------
 
3911
 * NR_RegPack    - Pack an open registry.  
 
3912
 *                Registry is locked the entire time.
 
3913
 *
 
3914
 * Parameters:
 
3915
 *    hReg     - handle of open registry to pack
 
3916
 * ---------------------------------------------------------------------
 
3917
 */
 
3918
VR_INTERFACE(REGERR) NR_RegPack( HREG hReg, void *userData, nr_RegPackCallbackFunc fn)
 
3919
{
 
3920
    return REGERR_FAIL; /* XXX resurrect after mozilla beta 1 */
 
3921
#if RESURRECT_LATER
 
3922
    XP_File  fh;
 
3923
    REGFILE* reg;
 
3924
    HREG hRegTemp;
 
3925
    char tempfilename[MAX_PATH+1] = {0};
 
3926
    char oldfilename[MAX_PATH+1] = {0};
 
3927
 
 
3928
    XP_Bool bCloseTempFile = FALSE;
 
3929
 
 
3930
    int err = REGERR_OK;
 
3931
    RKEY key;
 
3932
 
 
3933
    XP_ASSERT( regStartCount > 0 );
 
3934
    if ( regStartCount <= 0 )
 
3935
        return REGERR_FAIL;
 
3936
 
 
3937
    reg = ((REGHANDLE*)hReg)->pReg;
 
3938
 
 
3939
    /* lock registry */
 
3940
    err = nr_Lock( reg );
 
3941
    if ( err != REGERR_OK )
 
3942
        return err; 
 
3943
 
 
3944
    PR_Lock(reglist_lock); 
 
3945
    XP_STRCPY(tempfilename, reg->filename);
 
3946
    err = nr_createTempRegName(tempfilename, sizeof(tempfilename));
 
3947
    if ( err != REGERR_OK )
 
3948
        goto safe_exit; 
 
3949
     
 
3950
    /* force file creation */
 
3951
    fh = vr_fileOpen(tempfilename, XP_FILE_WRITE_BIN);
 
3952
    if ( !VALID_FILEHANDLE(fh) ) {
 
3953
        err = REGERR_FAIL;
 
3954
        goto safe_exit;
 
3955
    }
 
3956
    XP_FileClose(fh);
 
3957
 
 
3958
    err = NR_RegOpen(tempfilename, &hRegTemp);
 
3959
    if ( err != REGERR_OK )
 
3960
        goto safe_exit;
 
3961
    bCloseTempFile = TRUE;
 
3962
 
 
3963
    /* must open temp file first or we get the same name twice */
 
3964
    XP_STRCPY(oldfilename, reg->filename);
 
3965
    err = nr_createTempRegName(oldfilename, sizeof(oldfilename));
 
3966
    if ( err != REGERR_OK )
 
3967
        goto safe_exit; 
 
3968
     
 
3969
    key = ROOTKEY_PRIVATE;
 
3970
    err = nr_addNodesToNewReg( hReg, key, hRegTemp, userData, fn);
 
3971
    if ( err != REGERR_OK  )
 
3972
        goto safe_exit;
 
3973
    key = ROOTKEY_VERSIONS;
 
3974
    err = nr_addNodesToNewReg( hReg, key, hRegTemp, userData, fn);
 
3975
    if ( err != REGERR_OK  )
 
3976
        goto safe_exit;
 
3977
    key = ROOTKEY_COMMON;
 
3978
    err = nr_addNodesToNewReg( hReg, key, hRegTemp, userData, fn);
 
3979
    if ( err != REGERR_OK  )
 
3980
        goto safe_exit;
 
3981
    key = ROOTKEY_USERS;
 
3982
    err = nr_addNodesToNewReg( hReg, key, hRegTemp, userData, fn);
 
3983
    if ( err != REGERR_OK  )
 
3984
        goto safe_exit;
 
3985
 
 
3986
    err = NR_RegClose(hRegTemp);
 
3987
    bCloseTempFile = FALSE;
 
3988
  
 
3989
    /* close current reg file so we can rename it */
 
3990
    XP_FileClose(reg->fh);
 
3991
   
 
3992
    /* rename current reg file out of the way */
 
3993
    err = nr_RenameFile(reg->filename, oldfilename);
 
3994
    if ( err == -1 ) {
 
3995
        /* rename failed, get rid of the new registry and reopen the old one*/
 
3996
        remove(tempfilename);
 
3997
        reg->fh = vr_fileOpen(reg->filename, XP_FILE_UPDATE_BIN);
 
3998
        goto safe_exit;
 
3999
    }
 
4000
 
 
4001
    /* rename packed registry to the correct name */
 
4002
    err = nr_RenameFile(tempfilename, reg->filename);
 
4003
    if ( err == -1 ) {
 
4004
        /* failure, recover original registry */
 
4005
        err = nr_RenameFile(oldfilename, reg->filename);
 
4006
        remove(tempfilename);
 
4007
        reg->fh = vr_fileOpen(reg->filename, XP_FILE_UPDATE_BIN);
 
4008
        goto safe_exit;
 
4009
    
 
4010
    } else {
 
4011
        remove(oldfilename); 
 
4012
    }
 
4013
    reg->fh = vr_fileOpen(reg->filename, XP_FILE_UPDATE_BIN);
 
4014
 
 
4015
safe_exit:
 
4016
    if ( bCloseTempFile ) {
 
4017
        NR_RegClose(hRegTemp);
 
4018
    }
 
4019
    PR_Unlock( reglist_lock );
 
4020
    nr_Unlock(reg);
 
4021
    return err;
 
4022
#endif /* RESURRECT_LATER */
 
4023
}
 
4024
 
 
4025
 
 
4026
#ifdef XP_MAC
 
4027
#pragma export reset
 
4028
#endif
 
4029
 
 
4030
#endif /* STANDALONE_REGISTRY */
 
4031
 
 
4032
 
 
4033
 
 
4034
 
 
4035
 
 
4036
 
 
4037
/* ---------------------------------------------------------------------
 
4038
 * ---------------------------------------------------------------------
 
4039
 * Registry initialization and shut-down
 
4040
 * ---------------------------------------------------------------------
 
4041
 * ---------------------------------------------------------------------
 
4042
 */
 
4043
 
 
4044
#include "VerReg.h"
 
4045
 
 
4046
#ifndef STANDALONE_REGISTRY
 
4047
extern PRLock *vr_lock;
 
4048
#endif 
 
4049
 
 
4050
 
 
4051
 
 
4052
#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(STANDALONE_REGISTRY)
 
4053
extern XP_Bool bGlobalRegistry;
 
4054
#endif
 
4055
 
 
4056
#ifdef XP_MAC
 
4057
#pragma export on
 
4058
#endif
 
4059
 
 
4060
VR_INTERFACE(REGERR) NR_StartupRegistry(void)
 
4061
{
 
4062
    REGERR status = REGERR_OK;
 
4063
 
 
4064
#ifndef STANDALONE_REGISTRY
 
4065
    if ( reglist_lock == NULL ) {
 
4066
        reglist_lock = PR_NewLock();
 
4067
    }
 
4068
 
 
4069
    if ( reglist_lock != NULL ) {
 
4070
        PR_Lock( reglist_lock );
 
4071
    }
 
4072
    else {
 
4073
        XP_ASSERT( reglist_lock );
 
4074
        status = REGERR_FAIL;
 
4075
    }
 
4076
#endif
 
4077
 
 
4078
    if ( status == REGERR_OK )
 
4079
    {
 
4080
        ++regStartCount;
 
4081
        if ( regStartCount == 1 )
 
4082
        {
 
4083
            /* first time only initialization */
 
4084
            vr_findGlobalRegName();
 
4085
 
 
4086
#ifndef STANDALONE_REGISTRY
 
4087
            /* initialization for version registry */
 
4088
            vr_lock = PR_NewLock();
 
4089
            XP_ASSERT( vr_lock != NULL );
 
4090
#if defined(XP_UNIX) && !defined(XP_MACOSX)
 
4091
            bGlobalRegistry = ( getenv(UNIX_GLOBAL_FLAG) != NULL );
 
4092
#endif
 
4093
#endif 
 
4094
        } /* if ( regStartCount == 1 ) */
 
4095
 
 
4096
        PR_Unlock( reglist_lock );
 
4097
    }
 
4098
 
 
4099
    return status;
 
4100
}   /* NR_StartupRegistry */
 
4101
 
 
4102
VR_INTERFACE(void) NR_ShutdownRegistry(void)
 
4103
{
 
4104
    REGFILE* pReg;
 
4105
    XP_Bool  bDestroyLocks = FALSE;
 
4106
 
 
4107
    /* people should track whether NR_StartupRegistry() was successful
 
4108
     * and not call this if it fails... but they won't so we'll try to
 
4109
     * handle that case gracefully.
 
4110
     */
 
4111
#ifndef STANDALONE_REGISTRY
 
4112
    if ( reglist_lock == NULL ) 
 
4113
        return;  /* was not started successfully */
 
4114
#else
 
4115
    if ( regStartCount == 0 )
 
4116
        return;  /* was not started successfully */
 
4117
#endif
 
4118
 
 
4119
    PR_Lock( reglist_lock );
 
4120
 
 
4121
    --regStartCount;
 
4122
    if ( regStartCount == 0 )
 
4123
    {
 
4124
        /* shutdown for real. */
 
4125
 
 
4126
        /* close any forgotten open registries */
 
4127
        while ( RegList != NULL ) 
 
4128
        {
 
4129
            pReg = RegList;
 
4130
            if ( pReg->hdrDirty ) {
 
4131
                nr_WriteHdr( pReg );
 
4132
            }
 
4133
            nr_CloseFile( &(pReg->fh) );
 
4134
            nr_DeleteNode( pReg );
 
4135
        }
 
4136
    
 
4137
        XP_FREEIF(user_name);
 
4138
        XP_FREEIF(globalRegName);
 
4139
        XP_FREEIF(verRegName);
 
4140
 
 
4141
        bDestroyLocks = TRUE;
 
4142
    }
 
4143
 
 
4144
    PR_Unlock( reglist_lock );
 
4145
 
 
4146
#ifndef STANDALONE_REGISTRY    
 
4147
    if ( bDestroyLocks ) 
 
4148
    {
 
4149
        PR_DestroyLock( reglist_lock );
 
4150
        reglist_lock = NULL;
 
4151
 
 
4152
        PR_DestroyLock(vr_lock);
 
4153
        vr_lock = NULL;
 
4154
    }
 
4155
#endif
 
4156
 
 
4157
}   /* NR_ShutdownRegistry */
 
4158
 
 
4159
#ifdef XP_MAC
 
4160
#pragma export reset
 
4161
#endif
 
4162
 
 
4163
/* EOF: reg.c */