00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #ifdef HAVE_CONFIG_H
00024 #include "config.h"
00025 #endif
00026
00027 #include <stdio.h>
00028 #include <stdarg.h>
00029 #include <string.h>
00030 #include <stdlib.h>
00031 #include <errno.h>
00032
00033 #ifdef HAVE_STRINGS_H
00034 #include <strings.h>
00035 #endif
00036
00037 #ifdef HAVE_SYS_STAT_H
00038 #include <sys/stat.h>
00039 #endif
00040
00041 #ifdef HAVE_UNISTD_H
00042 #include <unistd.h>
00043 #endif
00044
00045 #ifdef WIN32
00046 #include <io.h>
00047 #endif
00048
00049 #ifdef HAVE_SYS_TYPES_H
00050 #include <sys/types.h>
00051 #endif
00052
00053 #ifdef HAVE_PWD_H
00054 #include <pwd.h>
00055 #endif
00056
00057 #ifdef HAVE_GRP_H
00058 #include <grp.h>
00059 #endif
00060
00061 #ifdef HAVE_LANGINFO_H
00062 #include <langinfo.h>
00063 #endif
00064
00065 #include "kdb.h"
00066 #include "kdbprivate.h"
00067
00077 size_t strblen(const char *s) {
00078 char *found=strchr(s,0);
00079 if (found) return found-s+1;
00080 return 0;
00081 }
00082
00083
00084
00373 Key *keyNew(const char *keyName, ...) {
00374 va_list va;
00375 Key *key;
00376 uint32_t action=0;
00377 uint8_t keyType=KEY_TYPE_UNDEFINED;
00378 uint8_t keyTypeBinary=0;
00379 size_t valueSize=0;
00380
00381 key=(Key *)malloc(sizeof(Key));
00382 if (!key) return 0;
00383 keyInit(key);
00384
00385 if (keyName) {
00386 size_t nameSize;
00387
00388 nameSize=keySetName(key,keyName);
00389 if (! nameSize) {
00390 free(key);
00391 return 0;
00392 }
00393
00394 va_start(va,keyName);
00395
00396 action=va_arg(va,uint32_t);
00397 while (action) {
00398 switch (action) {
00399 case KEY_SWITCH_TYPE:
00400
00401
00402
00403
00404 keyType=(uint8_t)va_arg(va,unsigned int);
00405
00406 if (keyType == KEY_TYPE_DIR) {
00407 mode_t mask=umask(0);
00408
00409 umask(mask);
00410 keySetDir(key,mask);
00411 keyType=KEY_TYPE_UNDEFINED;
00412 } else {
00413 keyTypeBinary=(KEY_TYPE_BINARY <= keyType &&
00414 keyType < KEY_TYPE_STRING);
00415
00416 keySetType(key,keyType);
00417 }
00418 break;
00419 case KEY_SWITCH_VALUE:
00420 if (keyType == KEY_TYPE_UNDEFINED)
00421 keyType=KEY_TYPE_STRING;
00422
00423 if (!keyTypeBinary) {
00424
00425 keySetString(key,va_arg(va,char *));
00426
00427
00428 keySetType(key,keyType);
00429 } else {
00430
00431 void *value=va_arg(va,void *);
00432 valueSize=va_arg(va,size_t);
00433 keySetRaw(key,value,valueSize);
00434 }
00435
00436 break;
00437 case KEY_SWITCH_UID:
00438 keySetUID(key,va_arg(va,uid_t));
00439 break;
00440 case KEY_SWITCH_GID:
00441 keySetGID(key,va_arg(va,gid_t));
00442 break;
00443 case KEY_SWITCH_MODE:
00444 keySetAccess(key,va_arg(va,mode_t));
00445 break;
00446 case KEY_SWITCH_UMODE:
00447 if (keyIsDir(key))
00448 keySetDir(key,va_arg(va,mode_t));
00449 else
00450 keySetUAccess(key,va_arg(va,mode_t));
00451 break;
00452 case KEY_SWITCH_DOMAIN:
00453 keySetOwner(key,va_arg(va,char *));
00454 break;
00455 case KEY_SWITCH_COMMENT:
00456 keySetComment(key,va_arg(va,char *));
00457 break;
00458 case KEY_SWITCH_NEEDSYNC: {
00459 int rc=0;
00460 rc=kdbGetKey(va_arg(va,KDBHandle),key);
00461 if (rc)
00462
00463
00464
00465 key->flags|=KEY_SWITCH_FLAG;
00466 } break;
00467 }
00468 action=va_arg(va,uint32_t);
00469 }
00470 va_end(va);
00471 }
00472 return key;
00473 }
00474
00475
00476
00477
00490 int keyDel(Key *key) {
00491 int rc;
00492
00493 rc=keyClose(key);
00494 free(key);
00495
00496 return rc;
00497 }
00498
00499
00500
00501
00502
00513 int keyIsInitialized(const Key *key) {
00514
00515 return ((key->flags & KEY_SWITCH_INITMASK)==KEY_SWITCH_INITIALIZED);
00516 }
00517
00518
00519
00520
00521
00522
00523
00569 char *keyNameGetOneLevel(const char *keyName, size_t *size) {
00570 char *real=(char *)keyName;
00571 size_t cursor=0;
00572 int escapeNext=0;
00573 int end=0;
00574
00575
00576 while (*real && *real == RG_KEY_DELIM) real++;
00577
00578
00579 while (real[cursor] && ! end) {
00580 switch (real[cursor]) {
00581 case '\\':
00582 escapeNext=1;
00583 break;
00584 case RG_KEY_DELIM:
00585 if (! escapeNext) end=1;
00586 default:
00587 escapeNext=0;
00588 }
00589 ++cursor;
00590 }
00591
00592
00593 if (end) --cursor;
00594
00595 *size=cursor;
00596 return real;
00597 }
00598
00599
00629 ssize_t keySetName(Key *key, const char *newName) {
00630 size_t length;
00631 size_t rootLength, userLength, systemLength, userDomainLength;
00632 size_t keyNameSize=1;
00633 char *p=0;
00634 size_t size=0;
00635
00636
00637 if (!newName || !(length=strblen(newName)-1)) {
00638 if (key->key) {
00639 free(key->key);
00640 key->key=0;
00641 }
00642 key->flags &= ~(KEY_SWITCH_NAME | KEY_SWITCH_NEEDSYNC |
00643 KEY_SWITCH_ISSYSTEM | KEY_SWITCH_ISUSER);
00644 return 0;
00645 }
00646
00647 rootLength=keyNameGetFullRootNameSize(newName)-1;
00648 if (!rootLength) {
00649 errno=KDB_RET_INVALIDKEY;
00650 return -1;
00651 }
00652 userLength=sizeof("user")-1;
00653 systemLength=sizeof("system")-1;
00654 userDomainLength=rootLength-userLength;
00655
00656 if (userDomainLength>0) --userDomainLength;
00657
00658 if (keyNameIsUser(newName)) {
00659
00660 if (length > userLength) {
00661
00662 if (*(newName+userLength)==':') {
00663
00664 if (userDomainLength > 0) {
00665 p=realloc(key->userDomain,userDomainLength+1);
00666 if (NULL==p) goto error_mem;
00667 key->userDomain=p;
00668 strncpy(key->userDomain,newName+userLength+1,userDomainLength);
00669 key->userDomain[userDomainLength]=0;
00670 key->flags |= KEY_SWITCH_DOMAIN;
00671 }
00672 keyNameSize+=length-userDomainLength-1;
00673 } else if (*(newName+userLength)!=RG_KEY_DELIM) {
00674
00675 errno=KDB_RET_INVALIDKEY;
00676 return -1;
00677 } else {
00678
00679 keyNameSize+=length;
00680 }
00681 } else {
00682
00683 keyNameSize+=userLength;
00684 }
00685
00686 if (!key->userDomain) {
00687 char *envVar;
00688
00689 envVar = getenv("USER");
00690 if ( envVar ) {
00691 keySetOwner(key, envVar);
00692 } else {
00693
00694 keySetOwner(key, NULL);
00695 }
00696 }
00697
00698 rootLength = userLength;
00699 key->flags |= KEY_SWITCH_ISUSER;
00700 key->flags &= ~KEY_SWITCH_ISSYSTEM;
00701 } else if (keyNameIsSystem(newName)) {
00702
00703 if (length > systemLength && *(newName+systemLength)!=RG_KEY_DELIM) {
00704
00705 errno=KDB_RET_INVALIDKEY;
00706 return -1;
00707 }
00708 keyNameSize+=length;
00709
00710 rootLength = systemLength;
00711 key->flags |= KEY_SWITCH_ISSYSTEM;
00712 key->flags &= ~KEY_SWITCH_ISUSER;
00713 } else {
00714
00715 errno=KDB_RET_INVALIDKEY;
00716 return -1;
00717 }
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730 p=malloc(keyNameSize);
00731 if (NULL==p) goto error_mem;
00732 if (key->key) free(key->key);
00733 key->key=p;
00734
00735
00736 if (!key->key) return -1;
00737
00738
00739 strncpy(key->key,newName,rootLength);
00740
00741
00742 p=(char *)newName;
00743 size=0;
00744 p=keyNameGetOneLevel(p+size,&size);
00745
00746
00747 keyNameSize=rootLength;
00748 while (*(p=keyNameGetOneLevel(p+size,&size))) {
00749
00750 key->key[keyNameSize]=RG_KEY_DELIM;
00751 keyNameSize++;
00752
00753
00754 memcpy(key->key+keyNameSize,p,size);
00755 keyNameSize+=size;
00756 }
00757 key->key[keyNameSize]=0;
00758
00759 key->flags |= KEY_SWITCH_NAME | KEY_SWITCH_NEEDSYNC;
00760
00761 return keyNameSize+1;
00762
00763 error_mem:
00764 errno=KDB_RET_NOMEM;
00765 return -1;
00766 }
00767
00768
00769
00770
00786 ssize_t keyAddBaseName(Key *key,const char *baseName) {
00787 size_t nameSize=0;
00788 size_t newSize=0;
00789 size_t size=0;
00790 char *p=0;
00791
00792 if (key->key) nameSize=strblen(key->key);
00793 if (baseName) newSize=strblen(baseName);
00794 else return nameSize;
00795
00796
00797
00798 if (newSize <= 1)
00799
00800 return nameSize;
00801
00802 if (key->key) {
00803 p=realloc(key->key,newSize+nameSize);
00804 if (NULL == p) {
00805 errno=KDB_RET_NOMEM;
00806 return -1;
00807 }
00808
00809
00810
00811 key->key=p;
00812
00813
00814 p=(char *)baseName;
00815 nameSize--;
00816 while (*(p=keyNameGetOneLevel(p+size,&size))) {
00817
00818 key->key[nameSize]=RG_KEY_DELIM;
00819 nameSize++;
00820
00821
00822 memcpy(key->key+nameSize,p,size);
00823 nameSize+=size;
00824 }
00825 key->key[nameSize]=0;
00826 return nameSize+1;
00827 } else return keySetName(key,baseName);
00828 }
00829
00830
00831
00832
00849 ssize_t keySetBaseName(Key *key, const char *baseName) {
00850 size_t newSize=0;
00851 size_t size=0;
00852 char *p=0;
00853 char *prevParent=0;
00854 size_t parentSize=0;
00855 size_t oldSize=0;
00856
00857 if (baseName) newSize=strblen(baseName);
00858
00859
00860 p=key->key;
00861 while (*(p=keyNameGetOneLevel(p+size,&size)))
00862 prevParent=p;
00863
00864 if (prevParent && prevParent!=key->key) {
00865
00866
00867 --prevParent;
00868
00869 oldSize=strblen(key->key);
00870 parentSize = prevParent-key->key;
00871
00872
00873
00874
00875
00876 key->key[parentSize]=0;
00877
00878
00879 if (newSize <= 1) return parentSize+1;
00880
00881
00882 if (oldSize < (newSize += parentSize + 1)) {
00883 p=realloc(key->key,newSize);
00884 if (NULL == p) {
00885 errno=KDB_RET_NOMEM;
00886 return -1;
00887 }
00888
00889 key->key=p;
00890 }
00891
00892
00893 size=0;
00894 p=(char *)baseName;
00895 while (*(p=keyNameGetOneLevel(p+size,&size))) {
00896
00897 key->key[parentSize]=RG_KEY_DELIM;
00898 parentSize++;
00899
00900
00901 memcpy(key->key+parentSize,p,size);
00902 parentSize+=size;
00903 }
00904 key->key[parentSize]=0;
00905 return parentSize+1;
00906 } else return keySetName(key,baseName);
00907 }
00908
00909
00910
00911
00920 ssize_t keyGetNameSize(const Key *key) {
00921 if (key->key) return strblen(key->key);
00922 else return 0;
00923 }
00924
00925
00926
00927
00938 ssize_t keyGetName(const Key *key, char *returnedName, size_t maxSize) {
00939 size_t bytes;
00940
00941 if (!key->key) {
00942 errno=KDB_RET_NOKEY;
00943 returnedName[0]=0;
00944 return 0;
00945 }
00946
00947 bytes=strblen(strncpy(returnedName,key->key,maxSize));
00948 if (maxSize < strblen(key->key)) {
00949 errno=KDB_RET_TRUNC;
00950 return -1;
00951 }
00952 return bytes;
00953 }
00954
00955
00956
00968 char *keyStealName(const Key *key) {
00969 return key->key;
00970 }
00971
00972
00973
00974
00975
00976
00984 ssize_t keyGetFullNameSize(const Key *key) {
00985 size_t returnedSize;
00986
00987 if (!key->key) return 0;
00988
00989 returnedSize=strblen(key->key);
00990
00991 if (keyNameIsUser(key->key) && key->userDomain)
00992 returnedSize+=strblen(key->userDomain);
00993
00994
00995
00996
00997
00998
00999
01000 return returnedSize;
01001 }
01002
01003
01004
01005
01015 ssize_t keyGetFullName(const Key *key, char *returnedName, size_t maxSize) {
01016 size_t userSize=sizeof("user")-1;
01017 size_t userDomainSize;
01018 ssize_t length;
01019 char *cursor;
01020
01021 length=keyGetFullNameSize(key);
01022 if (length == 0) {
01023 errno=KDB_RET_NOKEY;
01024 returnedName[0]=0;
01025 return length;
01026 }
01027 if (length < 0) return length;
01028 if (length > maxSize) {
01029 errno=KDB_RET_TRUNC;
01030 return -1;
01031 }
01032
01033 cursor=returnedName;
01034 if (keyIsUser(key)) {
01035 strncpy(cursor,key->key,userSize);
01036 cursor+=userSize;
01037 if (key->userDomain) {
01038 *cursor=':'; ++cursor;
01039 userDomainSize=strblen(key->userDomain)-1;
01040 strcpy(cursor,key->userDomain);
01041 cursor+=userDomainSize;
01042 }
01043 strcpy(cursor,key->key+userSize);
01044 } else strcpy(cursor,key->key);
01045
01046 return length;
01047 }
01048
01049
01050
01051
01052
01064 int keyNameGetNamespace(const char *keyName) {
01065 if (keyNameIsSystem(keyName)) return KEY_NS_SYSTEM;
01066 if (keyNameIsUser(keyName)) return KEY_NS_USER;
01067 return 0;
01068 }
01069
01070
01071
01082 int keyGetNamespace(const Key *key) {
01083 if (key->flags & KEY_SWITCH_ISSYSTEM) return KEY_NS_SYSTEM;
01084 if (key->flags & KEY_SWITCH_ISUSER) return KEY_NS_USER;
01085 return 0;
01086 }
01087
01088
01089
01099 int keyNameIsSystem(const char *keyName) {
01100 if (!strncmp("system",keyName,sizeof("system")-1)) return 1;
01101 return 0;
01102 }
01103
01104
01105
01114 int keyIsSystem(const Key *key) {
01115 return (key->flags & KEY_SWITCH_ISSYSTEM)?1:0;
01116 }
01117
01118
01119
01129 int keyNameIsUser(const char *keyName) {
01130 if (!strncmp("user",keyName,sizeof("user")-1)) return 1;
01131 return 0;
01132 }
01133
01134
01135
01144 int keyIsUser(const Key *key) {
01145 return (key->flags & KEY_SWITCH_ISUSER)?1:0;
01146 }
01147
01148
01149
01160 ssize_t keyNameGetFullRootNameSize(const char *keyName) {
01161 char *end;
01162 int length=strlen(keyName);
01163
01164 if (!length) return 0;
01165
01166
01167
01168
01169
01170
01171
01172
01173
01174
01175
01176 end=strchr(keyName,RG_KEY_DELIM);
01177 if (!end)
01178 end = (char *)keyName + length;
01179
01180 return end-keyName+1;
01181 }
01182
01183
01184
01195 ssize_t keyGetRootNameSize(const Key *key) {
01196 if (!key->key) return 0;
01197
01198 if (keyIsUser(key)) return sizeof("user");
01199 else return sizeof("system");
01200 }
01201
01202
01203
01221 ssize_t keyGetRootName(const Key *key, char *returned, size_t maxSize) {
01222 size_t size;
01223
01224 if (!key->key) {
01225 errno=KDB_RET_NOKEY;
01226 return -1;
01227 }
01228
01229 if (!(size=keyGetRootNameSize(key))) {
01230 errno=KDB_RET_NOKEY;
01231 return -1;
01232 }
01233
01234 if (maxSize < size) {
01235 errno=KDB_RET_TRUNC;
01236 return -1;
01237 } else strncpy(returned,key->key,size-1);
01238 returned[size-1]=0;
01239 return size;
01240 }
01241
01242
01243
01244
01245
01246
01262 ssize_t keyNameGetRootNameSize(const char *keyName) {
01263 int length=strlen(keyName);
01264
01265 if (!length) return 0;
01266
01267
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
01278
01279
01280
01281 if (keyNameIsUser(keyName)) return sizeof("user");
01282 else if (keyNameIsSystem(keyName)) return sizeof("system");
01283 else {
01284 errno=KDB_RET_INVALIDKEY;
01285 return -1;
01286 }
01287 }
01288
01289
01290
01291
01292
01304 ssize_t keyGetFullRootNameSize(const Key *key) {
01305 size_t size=0;
01306
01307 if (keyIsUser(key)) {
01308 if (key->userDomain) size=strblen(key->userDomain);
01309 else size=strblen(getenv("USER"));
01310
01311 return size+sizeof("user");
01312 } else {
01313 return keyNameGetRootNameSize(key->key);
01314 }
01315 }
01316
01317
01335 ssize_t keyGetFullRootName(const Key *key, char *returned, size_t maxSize) {
01336 size_t size;
01337 size_t rootSize;
01338 char *cursor;
01339
01340 if (!key->key) {
01341 errno=KDB_RET_NOKEY;
01342 return 0;
01343 }
01344
01345 if (!(size=keyGetFullRootNameSize(key))) {
01346 errno=KDB_RET_NOKEY;
01347 return 0;
01348 }
01349
01350 if (maxSize < size) {
01351 errno=KDB_RET_TRUNC;
01352 return -1;
01353 }
01354
01355 rootSize = keyGetRootNameSize(key)-1;
01356 strncpy(returned,key->key, rootSize);
01357 if (keyIsUser(key)) {
01358 cursor = returned + rootSize;
01359 *cursor = ':'; cursor++;
01360 if (key->userDomain)
01361 strncpy (cursor, key->userDomain, size - rootSize);
01362 else
01363 strncpy (cursor, getenv("USER"), size - rootSize);
01364 } else {
01365 returned[rootSize]=0;
01366 }
01367
01368 return size;
01369 }
01370
01371
01372
01373
01374
01375
01383 ssize_t keyGetParentNameSize(const Key *key) {
01384 char *parentNameEnd=0;
01385 char *p;
01386 size_t size;
01387
01388 if (!key->key) {
01389 errno=KDB_RET_NOKEY;
01390 return 0;
01391 }
01392
01393
01394
01395
01396
01397
01398
01399
01400
01401 p=key->key;
01402 size=0;
01403
01404
01405 while (*(p=keyNameGetOneLevel(p+size,&size))) parentNameEnd=p;
01406
01407
01408 if (!parentNameEnd || parentNameEnd==key->key) return 0;
01409
01410
01411
01412
01413
01414 return parentNameEnd - key->key;
01415 }
01416
01417
01418
01439 ssize_t keyGetParentName(const Key *key, char *returnedParent, size_t maxSize) {
01440 ssize_t parentSize;
01441
01442 parentSize=keyGetParentNameSize(key);
01443
01444 if (parentSize > maxSize) {
01445 errno=KDB_RET_TRUNC;
01446 return 0;
01447 } else strncpy(returnedParent,key->key,parentSize);
01448
01449 returnedParent[parentSize-1]=0;
01450
01451 return parentSize;
01452 }
01453
01454
01455
01456
01457
01458
01475 ssize_t keyNameGetBaseNameSize(const char *keyName) {
01476 char *p=(char *)keyName;
01477 char *base=0;
01478 size_t size=0;
01479 size_t baseSize=0;
01480
01481 while (*(p=keyNameGetOneLevel(p+size,&size))) {
01482 base=p;
01483 baseSize=size;
01484 }
01485
01486 if (base == keyName) return 0;
01487 else return baseSize+1;
01488 }
01489
01490
01491
01506 ssize_t keyGetBaseNameSize(const Key *key) {
01507 if (!key->key) return 0;
01508
01509 return keyNameGetBaseNameSize(key->key);
01510 }
01511
01512
01513
01529 ssize_t keyGetBaseName(const Key *key, char *returned, size_t maxSize) {
01530 size_t size=0;
01531 char *p=key->key;
01532 char *baseName=0;
01533 size_t baseSize=0;
01534
01535 while (*(p=keyNameGetOneLevel(p+size,&size))) {
01536 baseName=p;
01537 baseSize=size+1;
01538 }
01539
01540 if (!baseName || baseName==key->key) return 0;
01541
01542 if (maxSize < baseSize) {
01543 strncpy(returned,baseName,maxSize);
01544 errno=KDB_RET_TRUNC;
01545 return maxSize;
01546 } else {
01547 strncpy(returned,baseName,baseSize);
01548 return baseSize;
01549 }
01550 }
01551
01552
01562 char *keyStealBaseName(const Key *key) {
01563 char *p=key->key;
01564 char *base=0;
01565 size_t size=0;
01566
01567 while (*(p=keyNameGetOneLevel(p+size,&size))) base=p;
01568
01569 if (base != key->key) return base;
01570 else return 0;
01571 }
01572
01573
01574
01575
01576
01577
01578
01579
01580
01586 ssize_t keyGetDataSize(const Key *key) {
01587 return keyGetValueSize(key);
01588 }
01589
01590
01591
01602 ssize_t keyGetValueSize(const Key *key) {
01603 return key->dataSize;
01604 }
01605
01606
01607
01625 ssize_t keySetString(Key *key, const char *newStringValue) {
01626 ssize_t ret=newStringValue?strblen(newStringValue):0;
01627
01628 if (!newStringValue || !ret) ret=keySetRaw(key,0,0);
01629 else ret=keySetRaw(key,newStringValue,ret);
01630
01631 keySetType(key,KEY_TYPE_STRING);
01632
01633 return ret;
01634 }
01635
01636
01637
01662 ssize_t keyGetString(const Key *key, char *returnedString, size_t maxSize) {
01663 if (!key->data) {
01664 *returnedString=0;
01665 errno=KDB_RET_NODATA;
01666 return 0;
01667 }
01668
01669 if (key->dataSize > maxSize) {
01670 errno=KDB_RET_TRUNC;
01671 return -1;
01672 }
01673
01674 if (key->type < KEY_TYPE_STRING) {
01675 errno=KDB_RET_TYPEMISMATCH;
01676 return -1;
01677 }
01678
01679 strcpy(returnedString,key->data);
01680 return key->dataSize;
01681 }
01682
01683
01684
01685
01686
01687
01737 void *keyStealValue(const Key *key) {
01738 return key->data;
01739 }
01740
01741
01742
01743
01744
01745
01746
01747
01757 ssize_t keySetLink(Key *key, const char *target) {
01758 ssize_t ret=target?strblen(target):0;
01759
01760 if (!target || !ret) ret=keySetRaw(key,0,0);
01761 else ret=keySetRaw(key,target,ret);
01762
01763 keySetType(key,KEY_TYPE_LINK);
01764
01765 return ret;
01766 }
01767
01768
01769
01781 ssize_t keyGetLink(const Key *key, char *returnedTarget, size_t maxSize) {
01786 if (!key->data) {
01787 errno=KDB_RET_NODATA;
01788 return 0;
01789 }
01790
01791 if (key->type != KEY_TYPE_LINK) {
01792 errno=KDB_RET_TYPEMISMATCH;
01793 return -1;
01794 }
01795
01796 if (key->dataSize > maxSize) {
01797 errno=KDB_RET_TRUNC;
01798 return -1;
01799 }
01800
01801 strcpy(returnedTarget,key->data);
01802 return key->dataSize;
01803 }
01804
01805
01806
01816 int keyIsLink(const Key *key) {
01817 return (key->type==KEY_TYPE_LINK);
01818 }
01819
01820
01821
01831 int keyIsDir(const Key *key) {
01832 return (key->access & 0040000);
01833
01834 }
01835
01836
01837
01845 int keyIsBin(const Key *key) {
01846 return (KEY_TYPE_BINARY <= key->type && key->type < KEY_TYPE_STRING);
01847 }
01848
01849
01857 int keyIsString(const Key *key) {
01858 return (key->type >= KEY_TYPE_STRING);
01859 }
01860
01861
01862
01863
01873 ssize_t keyGetBinary(const Key *key, void *returnedValue, size_t maxSize) {
01874 if (!key->data) {
01875 errno=KDB_RET_NODATA;
01876 return 0;
01877 }
01878
01879 if (key->dataSize > maxSize) {
01880 errno=KDB_RET_TRUNC;
01881 return -1;
01882 }
01883
01884 memcpy(returnedValue,key->data,key->dataSize);
01885 return key->dataSize;
01886 }
01887
01888
01889
01908 ssize_t keySetBinary(Key *key, const void *newBinary, size_t dataSize) {
01909 ssize_t ret=keySetRaw(key,newBinary,dataSize);
01910
01911 keySetType(key,KEY_TYPE_BINARY);
01912
01913 return ret;
01914 }
01915
01916
01917
01926 int keyNeedsSync(const Key *key) {
01927
01928 return (key->flags & KEY_SWITCH_NEEDSYNC);
01929 }
01930
01931
01932
01942 uint8_t keyGetType(const Key *key) {
01943 return key->type;
01944 }
01945
01946
01947
02019 uint8_t keySetType(Key *key,uint8_t newType) {
02020 mode_t mask=0;
02021
02022 switch (newType) {
02023 case KEY_TYPE_DIR:
02024 mask=umask(0); umask(mask);
02025 keySetDir(key,mask);
02026 break;
02027 default:
02028 key->type=newType;
02029
02030
02031 }
02032 key->flags |= KEY_SWITCH_NEEDSYNC;
02033 return key->type;
02034 }
02035
02036
02063 uint8_t keySetDir(Key *key, mode_t customUmask) {
02064 mode_t dirSwitch=0111;
02065
02066
02067 key->access|=(dirSwitch & ~customUmask) | 0040000;
02068
02069
02070 key->flags |= KEY_SWITCH_NEEDSYNC;
02071
02072 return KEY_TYPE_DIR;
02073 }
02074
02075
02088 ssize_t keySetRaw(Key *key, const void *newBinary, size_t dataSize) {
02089 if (!dataSize || !newBinary) {
02090 if (key->data) {
02091 free(key->data);
02092 key->data=0;
02093 }
02094 key->dataSize = 0;
02095 key->flags &= ~(KEY_SWITCH_VALUE);
02096 key->flags |= KEY_SWITCH_NEEDSYNC;
02097 return 0;
02098 }
02099
02100 key->dataSize=dataSize;
02101 if (key->data) {
02102 char *p=0;
02103 p=realloc(key->data,key->dataSize);
02104 if (NULL==p) return -1;
02105 key->data=p;
02106 } else {
02107 key->data=malloc(key->dataSize);
02108 }
02109
02110 if (!key->data) return -1;
02111
02112 memcpy(key->data,newBinary,key->dataSize);
02113 key->flags |= KEY_SWITCH_VALUE | KEY_SWITCH_NEEDSYNC;
02114 return key->dataSize;
02115 }
02116
02117
02118
02119
02120
02121
02122
02123
02124
02125
02126
02127
02128
02129
02141 ssize_t keySetOwner(Key *key, const char *userDomain) {
02142 ssize_t size;
02143
02144 if (userDomain == 0) {
02145 if (key->userDomain) {
02146 free(key->userDomain);
02147 key->userDomain=0;
02148 }
02149 key->flags &= ~KEY_SWITCH_DOMAIN;
02150 return 0;
02151 }
02152
02153 if ((size=strblen(userDomain)) > 0) {
02154 if (key->userDomain) {
02155 char *p=0;
02156 p=realloc(key->userDomain,size);
02157 if (NULL==p) {
02158 errno=KDB_RET_NOMEM;
02159 return -1;
02160 }
02161 key->userDomain=p;
02162 } else {
02163 key->userDomain=malloc(size);
02164 }
02165 if (!key->userDomain) return -1;
02166
02167 strcpy(key->userDomain,userDomain);
02168 key->flags |= KEY_SWITCH_DOMAIN | KEY_SWITCH_NEEDSYNC;
02169 return size;
02170 } else if (key->userDomain) {
02171 free(key->userDomain);
02172 key->userDomain=0;
02173 key->flags &= ~(KEY_SWITCH_DOMAIN | KEY_SWITCH_NEEDSYNC);
02174 }
02175 return 0;
02176 }
02177
02178
02179
02187 ssize_t keyGetOwnerSize(const Key *key) {
02188 if (!key->userDomain) {
02189 errno=KDB_RET_NODOMAIN;
02190 return 0;
02191 }
02192
02193 return strblen(key->userDomain);
02194 }
02195
02196
02197
02218 ssize_t keyGetOwner(const Key *key, char *returned, size_t maxSize) {
02219 ssize_t bytes;
02220
02221 if (!key->userDomain) {
02222 errno=KDB_RET_NODOMAIN;
02223 return 0;
02224 }
02225
02226 if (maxSize < (bytes=strblen(key->userDomain))) {
02227 errno=KDB_RET_TRUNC;
02228 return -1;
02229 } else strcpy(returned,key->userDomain);
02230 return bytes;
02231 }
02232
02233
02234
02235
02245 char *keyStealOwner(const Key *key) {
02246 return key->userDomain;
02247 }
02248
02249
02250
02251
02252
02253
02254
02255
02256
02257
02258
02259
02271 ssize_t keySetComment(Key *key, const char *newComment) {
02272 ssize_t size;
02273
02274 if (newComment && (size=strblen(newComment)) > 0) {
02275 if (key->flags & KEY_SWITCH_COMMENT) {
02276 char *p=0;
02277 p=realloc(key->comment,size);
02278 if (NULL==p) {
02279 errno=KDB_RET_NOMEM;
02280 return -1;
02281 }
02282 key->comment=p;
02283 } else {
02284 key->comment=malloc(size);
02285 }
02286 if (!key->comment) return -1;
02287
02288 strcpy(key->comment,newComment);
02289 key->flags |= KEY_SWITCH_COMMENT | KEY_SWITCH_NEEDSYNC;
02290 return key->commentSize=size;
02291 } else if (key->flags & KEY_SWITCH_COMMENT) {
02292 free(key->comment);
02293 key->comment=0;
02294 key->flags &= ~(KEY_SWITCH_COMMENT | KEY_SWITCH_NEEDSYNC);
02295 }
02296 return key->commentSize=0;
02297 }
02298
02299
02300
02311 ssize_t keyGetCommentSize(const Key *key) {
02312 if (!key->comment) {
02313 errno=KDB_RET_NODESC;
02314 return 0;
02315 }
02316
02317 return strblen(key->comment);
02318 }
02319
02320
02321
02333 ssize_t keyGetComment(const Key *key, char *returnedDesc, size_t maxSize) {
02334 ssize_t bytes;
02335
02336 if (!key->comment) {
02337 errno=KDB_RET_NODESC;
02338 return 0;
02339 }
02340
02341 bytes=strblen(strncpy(returnedDesc,key->comment,maxSize));
02342 if (maxSize < strblen(key->comment)) {
02343 errno=KDB_RET_TRUNC;
02344 return -1;
02345 }
02346 return bytes;
02347 }
02348
02349
02360 char *keyStealComment(const Key *key) {
02361 return key->comment;
02362 }
02363
02364
02365
02366
02367
02368
02369
02370
02371
02372
02383 uid_t keyGetUID(const Key *key) {
02384 return key->uid;
02385 }
02386
02387
02388
02399 int keySetUID(Key *key, uid_t uid) {
02400 key->uid=uid;
02401 key->flags |= KEY_SWITCH_UID | KEY_SWITCH_NEEDSYNC;
02402
02403 return 0;
02404 }
02405
02406
02407
02415 gid_t keyGetGID(const Key *key) {
02416 return key->gid;
02417 }
02418
02419
02420
02428 int keySetGID(Key *key, gid_t gid) {
02429 key->gid=gid;
02430 key->flags |= KEY_SWITCH_GID | KEY_SWITCH_NEEDSYNC;
02431
02432 return 0;
02433 }
02434
02435
02436
02442 mode_t keyGetAccess(const Key *key) {
02443 return key->access;
02444 }
02445
02446
02447
02458 int keySetAccess(Key *key, mode_t mode) {
02459 key->access=mode;
02460 key->flags |= KEY_SWITCH_MODE | KEY_SWITCH_NEEDSYNC;
02461
02462 return 0;
02463 }
02464
02465
02466
02467
02468
02479 int keySetUAccess(Key *key, mode_t umask) {
02480 key->access=DEFFILEMODE & ~umask;
02481
02482 key->flags |= KEY_SWITCH_MODE | KEY_SWITCH_NEEDSYNC;
02483
02484 return 0;
02485 }
02486
02487
02488
02489
02490
02491
02492
02493
02494
02499 time_t keyGetMTime(const Key *key) {
02500 return key->mtime;
02501 }
02502
02503
02504
02509 time_t keyGetATime(const Key *key) {
02510 return key->atime;
02511 }
02512
02513
02514
02519 time_t keyGetCTime(const Key *key) {
02520 return key->ctime;
02521 }
02522
02523
02524
02525
02526
02527
02528
02529
02530
02531
02532
02533
02534 ssize_t keyGetRecordSize(const Key *key) {
02535 return key->recordSize;
02536 }
02537
02538
02544 Key *keyNext(Key *key) {
02545 return key->next;
02546 }
02547
02548
02549
02550
02551
02552
02553
02554
02572 int keyDup(const Key *source, Key *dest) {
02573
02574
02575 *dest=*source;
02576
02577
02578 dest->key=
02579 dest->comment=
02580 dest->userDomain=
02581 dest->data=0;
02582
02583
02584
02585 keySetName(dest,source->key);
02586 keySetOwner(dest,source->userDomain);
02587 keySetComment(dest,source->comment);
02588 keySetRaw(dest,source->data,source->dataSize);
02589
02590 dest->flags=source->flags | KEY_SWITCH_NEEDSYNC;
02591
02592 return 0;
02593 }
02594
02595
02596
02597
02598
02691 uint32_t keyCompare(const Key *key1, const Key *key2) {
02692 uint32_t ret=0;
02693
02694
02695
02696 if (key1->uid != key2->uid) ret|=KEY_SWITCH_UID;
02697 if (key1->gid != key2->gid) ret|=KEY_SWITCH_GID;
02698 if (key1->type != key2->type) ret|=KEY_SWITCH_TYPE;
02699 #if defined(S_IRWXU) && defined(S_IRWXG) && defined(S_IRWXO)
02700 if ((key1->access & (S_IRWXU|S_IRWXG|S_IRWXO)) !=
02701 (key2->access & (S_IRWXU|S_IRWXG|S_IRWXO))) ret|=KEY_SWITCH_MODE;
02702 #else
02703 if ((key1->access) !=
02704 (key2->access)) ret|=KEY_SWITCH_MODE;
02705 #endif
02706
02707
02708
02709 if (key1->key && key2->key) {
02710 if (strcmp(key1->key,key2->key)) ret|=KEY_SWITCH_NAME;
02711 } else
02712 if (key1->key || key2->key) ret|=KEY_SWITCH_NAME;
02713
02714 if (key1->comment && key2->comment) {
02715 if (strcmp(key1->comment,key2->comment)) ret|=KEY_SWITCH_COMMENT;
02716 } else
02717 if (key1->comment || key2->comment) ret|=KEY_SWITCH_COMMENT;
02718
02719 if (key1->userDomain && key2->userDomain) {
02720 if (strcmp(key1->userDomain,key2->userDomain)) ret|=KEY_SWITCH_DOMAIN;
02721 } else
02722 if (key1->userDomain || key2->userDomain) ret|=KEY_SWITCH_DOMAIN;
02723
02724
02725 ret|=
02726
02727 (key1->flags ^ key2->flags) &
02728
02729 (KEY_SWITCH_FLAG | KEY_SWITCH_NEEDSYNC);
02730
02731
02732 if (key1->dataSize != key2->dataSize) ret|=KEY_SWITCH_VALUE;
02733 else if (memcmp(key1->data,key2->data,
02734 (key1->dataSize<=key2->dataSize?key1->dataSize:key2->dataSize)))
02735 ret|=KEY_SWITCH_VALUE;
02736
02737 return ret;
02738 }
02739
02740
02741
02742
02743
02744
02745
02746
02747
02748
02749
02786 ssize_t keyToStream(const Key *key, FILE* stream, unsigned long options) {
02787 return keyToStreamBasename(key,stream,0,0,options);
02788 }
02789
02790
02791
02792
02793
02832 ssize_t keyToStreamBasename(const Key *key, FILE *stream, const char *parent,
02833 const size_t parentSize, unsigned long options) {
02834 ssize_t written=0;
02835 char buffer[800];
02836 struct passwd *pwd=0;
02837 struct group *grp=0;
02838
02839
02840 if (parent) {
02841
02842 int found;
02843 size_t skip=parentSize ? parentSize : strblen(parent)-1;
02844
02845 found=memcmp(parent,key->key,skip);
02846 if (found == 0) {
02847 while (*(key->key+skip) == RG_KEY_DELIM) ++skip;
02848
02849 if (*(key->key+skip) != 0)
02850 written+=fprintf(stream,"<key basename=\"%s\"",
02851 key->key+skip);
02852 }
02853 }
02854
02855 if (written == 0) {
02856 if (options & KDB_O_FULLNAME) {
02857 keyGetFullName(key,buffer,sizeof(buffer));
02858 written+=fprintf(stream,"<key name=\"%s\"", buffer);
02859 } else written+=fprintf(stream,"<key name=\"%s\"", key->key);
02860 }
02861
02862
02863 if (options & KDB_O_CONDENSED) written+=fprintf(stream," ");
02864 else written+=fprintf(stream,"\n ");
02865
02866
02867
02868
02869 if (options & KDB_O_NUMBERS) {
02870 written+=fprintf(stream,"type=\"%d\"", key->type);
02871 } else {
02872 buffer[0]=0;
02873
02874 switch (key->type) {
02875 case KEY_TYPE_STRING:
02876 strcpy(buffer,"string");
02877 break;
02878 case KEY_TYPE_BINARY:
02879 strcpy(buffer,"binary");
02880 break;
02881 case KEY_TYPE_LINK:
02882 strcpy(buffer,"link");
02883 break;
02884
02885
02886
02887 case KEY_TYPE_UNDEFINED:
02888 strcpy(buffer,"undefined");
02889 break;
02890 }
02891 if (buffer[0]) written+=fprintf(stream,"type=\"%s\"", buffer);
02892 else written+=fprintf(stream,"type=\"%d\"", key->type);
02893
02894 written+=fprintf(stream," ");
02895 if (keyIsDir(key)) written+=fprintf(stream,"isdir=\"yes\"");
02896 }
02897
02898 #ifdef HAVE_PWD_H
02899 if (keyIsUser(key)) {
02900 struct passwd *domainPwd=0;
02901 int uidMatch,gidMatch;
02902
02903 domainPwd=getpwnam(key->userDomain);
02904 pwd=getpwuid(key->uid);
02905 grp=getgrgid(key->gid);
02906
02907 uidMatch=(key->uid == domainPwd->pw_uid);
02908 gidMatch=(key->gid == domainPwd->pw_gid);
02909
02910 if (options & KDB_O_FULLUGID) {
02911 if (pwd && !(options & KDB_O_NUMBERS))
02912 written+=fprintf(stream," uid=\"%s\"",pwd->pw_name);
02913 else written+=fprintf(stream, " uid=\"%d\"",key->uid);
02914
02915 if (grp && !(options & KDB_O_NUMBERS))
02916 written+=fprintf(stream," gid=\"%s\"",grp->gr_name);
02917 else written+=fprintf(stream," gid=\"%d\"",key->gid);
02918 } else {
02919 if (!uidMatch) {
02920 if (pwd && !(options & KDB_O_NUMBERS))
02921 written+=fprintf(stream," uid=\"%s\"",pwd->pw_name);
02922 else written+=fprintf(stream, " uid=\"%d\"",key->uid);
02923 }
02924
02925 if (!gidMatch) {
02926 if (grp && !(options & KDB_O_NUMBERS))
02927 written+=fprintf(stream," gid=\"%s\"",grp->gr_name);
02928 else written+=fprintf(stream, " gid=\"%d\"",key->gid);
02929 }
02930 }
02931 } else {
02932 if (!(options & KDB_O_NUMBERS)) {
02933 pwd=getpwuid(key->uid);
02934 grp=getgrgid(key->gid);
02935 }
02936
02937
02938 if (pwd) written+=fprintf(stream," uid=\"%s\"",pwd->pw_name);
02939 else written+=fprintf(stream, " uid=\"%d\"",key->uid);
02940
02941 if (grp) written+=fprintf(stream," gid=\"%s\"",grp->gr_name);
02942 else written+=fprintf(stream, " gid=\"%d\"",key->gid);
02943 }
02944 #endif
02945
02946 #if defined(S_IRWXU) && defined(S_IRWXG) && defined(S_IRWXO)
02947 written+=fprintf(stream," mode=\"0%o\"",
02948 key->access & (S_IRWXU|S_IRWXG|S_IRWXO));
02949 #else
02950 written+=fprintf(stream," mode=\"0%o\"",
02951 key->access);
02952 #endif
02953
02954
02955 if (!key->data && !key->comment) {
02956 written+=fprintf(stream,"/>");
02957 if (!(options & KDB_O_CONDENSED))
02958 written+=fprintf(stream,"\n\n\n\n\n\n");
02959
02960 return written;
02961 } else {
02962 if (key->data) {
02963 if ((key->dataSize <= 16) &&
02964 key->type >= KEY_TYPE_STRING &&
02965 !strchr(key->data,'\n')) {
02966
02967
02968
02969
02970
02971 if (options & KDB_O_CONDENSED) written+=fprintf(stream," ");
02972 else written+=fprintf(stream,"\n ");
02973
02974 written+=fprintf(stream,"value=\"%s\"",(char *)key->data);
02975
02976 if (key->comment) written+=fprintf(stream,">\n");
02977 else {
02978 written+=fprintf(stream,"/>");
02979 if (!(options & KDB_O_CONDENSED))
02980 written+=fprintf(stream,"\n\n\n\n\n\n");
02981
02982 return written;
02983 }
02984 } else {
02985 written+=fprintf(stream,">");
02986 if (!(options & KDB_O_CONDENSED)) written+=fprintf(stream,"\n\n ");
02987
02988 written+=fprintf(stream,"<value>");
02989 if (key->type >= KEY_TYPE_STRING || key->type < KEY_TYPE_BINARY) {
02990 written+=fprintf(stream,"<![CDATA[");
02991 fflush(stream);
02992
02993 written+=write(fileno(stream),key->data,key->dataSize-1);
02994 written+=fprintf(stream,"]]>");
02995 } else {
02996
02997 char *encoded=malloc(3*key->dataSize);
02998 size_t encodedSize;
02999
03000 written+=fprintf(stream,"\n");
03001 encodedSize=encode(key->data,key->dataSize,encoded);
03002 fflush(stream);
03003 written+=write(fileno(stream),encoded,encodedSize);
03004 free(encoded);
03005 written+=fprintf(stream,"\n");
03006 }
03007
03008 written+=fprintf(stream,"</value>");
03009 }
03010 } else {
03011 if (key->comment) {
03012 written+=fprintf(stream,">");
03013 if (!(options & KDB_O_CONDENSED))
03014 written+=fprintf(stream,"\n");
03015 } else {
03016 written+=fprintf(stream,"/>");
03017 if (!(options & KDB_O_CONDENSED))
03018 written+=fprintf(stream,"\n\n\n\n\n\n");
03019
03020 return written;
03021 }
03022 }
03023 }
03024
03025 if (!(options & KDB_O_CONDENSED)) {
03026 written+=fprintf(stream,"\n");
03027 if (key->comment) written+=fprintf(stream," ");
03028 }
03029
03030 if (key->comment) {
03031 written+=fprintf(stream,"<comment><![CDATA[%s]]></comment>", key->comment);
03032 if (!(options & KDB_O_CONDENSED))
03033 written+=fprintf(stream,"\n");
03034 }
03035
03036 written+=fprintf(stream,"</key>");
03037
03038 if (!(options & KDB_O_CONDENSED))
03039 written+=fprintf(stream,"\n\n\n\n\n\n");
03040
03041 return written;
03042 }
03043
03044
03045
03046
03047
03048
03049
03050
03051
03052
03053
03054
03055
03056
03057
03058
03059
03060
03061
03062
03063
03064
03065
03066
03067
03068
03069
03070
03071
03072
03073
03085 int keySetFlag(Key *key) {
03086 key->flags|=KEY_SWITCH_FLAG;
03087
03088 return 0;
03089 }
03090
03091
03092
03104 int keyClearFlag(Key *key) {
03105 key->flags &= ~KEY_SWITCH_FLAG;
03106
03107 return 0;
03108 }
03109
03110
03111
03124 int keyGetFlag(const Key *key) {
03125 return (key->flags & KEY_SWITCH_FLAG) ? 1:0;
03126 }
03127
03128
03129
03130
03131
03132
03133
03134
03135
03136
03173 int keyInit(Key *key) {
03174 mode_t localUmask;
03175
03176 memset(key,0,sizeof(Key));
03177 key->type=KEY_TYPE_UNDEFINED;
03178
03179
03180 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
03181 key->uid=getuid();
03182 key->gid=getgid();
03183 #endif
03184
03185
03186
03187 localUmask=umask(0); umask(localUmask);
03188 keySetUAccess(key,localUmask);
03189
03190 key->flags = KEY_SWITCH_INITIALIZED;
03191
03192 return 0;
03193 }
03194
03195
03196
03197
03218 int keyClose(Key *key) {
03219 if (key->key) free(key->key);
03220 if (key->data) free(key->data);
03221 if (key->comment) free(key->comment);
03222 if (key->userDomain) free(key->userDomain);
03223 memset(key,0,sizeof(Key));
03224 return 0;
03225 }
03226
03227
03236 void *keySerialize(Key *key) {
03237 size_t metaInfoSize=0;
03238 size_t fullNameSize=0;
03239 void *serialized=0;
03240
03241 fullNameSize=keyGetFullNameSize(key);
03242
03243 metaInfoSize = KEY_METAINFO_SIZE(key);
03244
03245 key->recordSize=metaInfoSize + key->dataSize + key->commentSize
03246 + fullNameSize;
03247 serialized=malloc(key->recordSize);
03248 memset(serialized,0,key->recordSize);
03249
03250
03251 memcpy(serialized,key,metaInfoSize);
03252
03253
03254 if (key->comment) memcpy((char *)(serialized)+metaInfoSize,
03255 key->comment,key->commentSize);
03256
03257
03258 if (key->data) memcpy((char *)(serialized)+metaInfoSize+key->commentSize,
03259 key->data,key->dataSize);
03260
03261
03262 keyGetFullName(key,
03263 (char *)(serialized)+metaInfoSize+key->commentSize+key->dataSize,
03264 fullNameSize);
03265
03266 return serialized;
03267 }
03268
03279 Key *keyUnserialize(const void *serialized) {
03280 Key *key=0;
03281 size_t metaInfoSize=0;
03282
03283 if (!serialized) return 0;
03284
03285 key=keyNew(KEY_SWITCH_END);
03286
03287
03288 metaInfoSize = KEY_METAINFO_SIZE(key);
03289 memcpy(key,serialized,metaInfoSize);
03290
03291
03292 if (key->commentSize) {
03293 key->comment=malloc(key->commentSize);
03294 memcpy(key->comment,
03295 (char *)(serialized)+metaInfoSize,
03296 key->commentSize);
03297 }
03298
03299
03300 if (key->dataSize) {
03301 key->data=malloc(key->dataSize);
03302 memcpy(key->data,
03303 (char *)(serialized)+metaInfoSize+key->commentSize,
03304 key->dataSize);
03305 }
03306
03307
03308 keySetName(key,
03309 (char *)(serialized)+metaInfoSize+key->commentSize+key->dataSize);
03310
03311 return key;
03312 }