LCOV - code coverage report
Current view: top level - src - odb.cxx (source / functions) Coverage Total Hit
Test: coverage.info Lines: 28.1 % 6624 1862
Test Date: 2025-11-11 10:26:08 Functions: 51.9 % 183 95

            Line data    Source code
       1              : /********************************************************************\
       2              : 
       3              :   Name:         ODB.C
       4              :   Created by:   Stefan Ritt
       5              : 
       6              :   Contents:     MIDAS online database functions
       7              : 
       8              :   $Id$
       9              : 
      10              : \********************************************************************/
      11              : 
      12              : #undef NDEBUG // midas required assert() to be always enabled
      13              : 
      14              : /**dox***************************************************************/
      15              : /** @file odb.c
      16              : The Online Database file
      17              : */
      18              : 
      19              : /** @defgroup odbfunctionc ODB Functions (db_xxx)
      20              :  */
      21              : 
      22              : /**dox***************************************************************/
      23              : /** @addtogroup odbfunctionc
      24              : *
      25              :  *  @{  */
      26              : 
      27              : /**dox***************************************************************/
      28              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
      29              : 
      30              : #include "midas.h"
      31              : #include "msystem.h"
      32              : #include "mxml.h"
      33              : #include "git-revision.h"
      34              : #include "mstrlcpy.h"
      35              : #include <assert.h>
      36              : #include <signal.h>
      37              : #include <math.h>
      38              : 
      39              : #include "mjson.h"
      40              : 
      41              : #define CHECK_OPEN_RECORD 1
      42              : 
      43              : /*------------------------------------------------------------------*/
      44              : 
      45              : /********************************************************************\
      46              : *                                                                    *
      47              : *                 db_xxx  -  Database Functions                      *
      48              : *                                                                    *
      49              : \********************************************************************/
      50              : 
      51              : /* Globals */
      52              : 
      53              : #ifdef LOCAL_ROUTINES
      54              : static DATABASE *_database;
      55              : static INT _database_entries = 0;
      56              : #endif
      57              : 
      58              : static RECORD_LIST *_record_list;
      59              : static INT _record_list_entries = 0;
      60              : 
      61              : static WATCH_LIST *_watch_list;
      62              : static INT _watch_list_entries = 0;
      63              : 
      64              : INT db_save_xml_key(HNDLE hDB, HNDLE hKey, INT level, MXML_WRITER * writer);
      65              : 
      66              : /*------------------------------------------------------------------*/
      67              : 
      68              : #ifdef LOCAL_ROUTINES
      69              : typedef struct db_err_msg_struct db_err_msg;
      70              : static void db_msg(db_err_msg** msg, INT message_type, const char *filename, INT line, const char *routine, const char *format, ...) MATTRPRINTF(6,7);
      71              : static void db_print_msg(const db_err_msg* msg);
      72              : static void db_flush_msg(db_err_msg** msg);
      73              : static INT db_find_key_locked(const DATABASE_HEADER *pheader, HNDLE hKey, const char *key_name, HNDLE * subhKey, db_err_msg** msg);
      74              : static const KEY* db_find_pkey_locked(const DATABASE_HEADER *pheader, const KEY* pkey, const char *key_name, int *pstatus, db_err_msg** msg);
      75              : static std::string db_get_path_locked(const DATABASE_HEADER* pheader, HNDLE hKey);
      76              : static std::string db_get_path_locked(const DATABASE_HEADER* pheader, const KEY *pkey);
      77              : static int db_scan_tree_locked(const DATABASE_HEADER* pheader, const KEY* pkey, int level, int(*callback) (const DATABASE_HEADER*, const KEY*, int, void*, db_err_msg**), void *info, db_err_msg** msg);
      78              : static int db_set_mode_wlocked(DATABASE_HEADER*,KEY*,WORD mode,int recurse,db_err_msg**);
      79              : static const KEY* db_resolve_link_locked(const DATABASE_HEADER*, const KEY*,int *pstatus, db_err_msg**);
      80              : static int db_notify_clients_locked(const DATABASE_HEADER* pheader, HNDLE hDB, HNDLE hKeyMod, int index, BOOL bWalk, db_err_msg** msg);
      81              : static int db_create_key_wlocked(DATABASE_HEADER* pheader, KEY* parentKey, const char *key_name, DWORD type, KEY** pnewkey, db_err_msg** msg);
      82              : static int db_set_value_wlocked(DATABASE_HEADER* pheader, HNDLE hDB, KEY* pkey_root, const char *key_name, const void *data, INT data_size, INT num_values, DWORD type, db_err_msg** msg);
      83              : static INT db_get_data_locked(DATABASE_HEADER* pheader, const KEY* pkey, int idx, void *data, INT * buf_size, DWORD type, db_err_msg** msg);
      84              : static INT db_set_data_wlocked(DATABASE_HEADER* pheader, KEY* pkey, const void *data, INT data_size, INT num_values, DWORD type, const char* caller, db_err_msg** msg);
      85              : static INT db_set_data_index_wlocked(DATABASE_HEADER* pheader, KEY* pkey, int idx, const void *data, INT data_size, DWORD type, const char* caller, db_err_msg** msg);
      86              : static INT db_check_set_data_locked(DATABASE_HEADER* pheader, const KEY* pkey, const void *data, INT data_size, INT num_values, DWORD type, const char* caller, db_err_msg** msg);
      87              : static INT db_check_set_data_index_locked(DATABASE_HEADER* pheader, const KEY* pkey, int idx, const void *data, INT data_size, DWORD type, const char* caller, db_err_msg** msg);
      88              : static int db_remove_open_record_wlocked(DATABASE* pdb, DATABASE_HEADER* pheader, HNDLE hKey);
      89              : #endif // LOCAL_ROUTINES
      90              : 
      91              : /*------------------------------------------------------------------*/
      92              : 
      93              : /********************************************************************\
      94              : *                                                                    *
      95              : *            db_msg_xxx error message handling                       *
      96              : *                                                                    *
      97              : \********************************************************************/
      98              : 
      99              : #ifdef LOCAL_ROUTINES
     100              : 
     101              : struct db_err_msg_struct
     102              : {
     103              :    db_err_msg *next = NULL;
     104              :    int message_type = 0;
     105              :    std::string filename;
     106              :    int line = 0;
     107              :    std::string routine;
     108              :    std::string text;
     109              : };
     110              : 
     111              : static db_err_msg* _last_error_message = NULL; // for debuging core dumps
     112              : 
     113            0 : void db_print_msg(const db_err_msg* msg)
     114              : {
     115            0 :    while (msg != NULL) {
     116            0 :       printf("db_err_msg: %p, next %p, type %d, file \'%s:%d\', function \'%s\': %s\n", msg, msg->next, msg->message_type, msg->filename.c_str(), msg->line, msg->routine.c_str(), msg->text.c_str());
     117            0 :       msg = msg->next;
     118              :    }
     119            0 : }
     120              : 
     121            7 : void db_msg(db_err_msg** msgp, INT message_type, const char *filename, INT line, const char *routine, const char *format, ...)
     122              : {
     123            7 :    if (!msgp)
     124            0 :       return;
     125              :    
     126              :    va_list argptr;
     127              :    char message[1000];
     128              : 
     129              :    /* print argument list into message */
     130            7 :    va_start(argptr, format);
     131            7 :    vsnprintf(message, sizeof(message)-1, format, argptr);
     132            7 :    va_end(argptr);
     133            7 :    message[sizeof(message)-1] = 0; // ensure string is NUL-terminated
     134              : 
     135            7 :    db_err_msg* msg = new db_err_msg;
     136              : 
     137            7 :    msg->next = NULL;
     138            7 :    msg->message_type = message_type;
     139            7 :    msg->filename = filename;
     140            7 :    msg->line = line;
     141            7 :    msg->routine = routine;
     142            7 :    msg->text = message;
     143              : 
     144            7 :    _last_error_message = msg;
     145              : 
     146              :    //printf("new message:\n");
     147              :    //db_print_msg(msg);
     148              : 
     149            7 :    if (*msgp == NULL) {
     150            7 :       *msgp = msg;
     151            7 :       return;
     152              :    }
     153              : 
     154              :    // append new message to the end of the list
     155            0 :    db_err_msg *m = (*msgp);
     156            0 :    while (m->next != NULL) {
     157            0 :       m = m->next;
     158              :    }
     159            0 :    assert(m->next == NULL);
     160            0 :    m->next = msg;
     161              : 
     162              :    //printf("Message list with added new message:\n");
     163              :    //db_print_msg(*msgp);
     164            0 :    return;
     165              : }
     166              : 
     167            7 : void db_flush_msg(db_err_msg** msgp)
     168              : {
     169            7 :    db_err_msg *msg = *msgp;
     170            7 :    *msgp = NULL;
     171              : 
     172              :    if (/* DISABLES CODE */ (0)) {
     173              :       printf("db_flush_msg: %p\n", msg);
     174              :       db_print_msg(msg);
     175              :    }
     176              : 
     177           14 :    while (msg != NULL) {
     178            7 :       cm_msg(msg->message_type, msg->filename.c_str(), msg->line, msg->routine.c_str(), "%s", msg->text.c_str());
     179            7 :       db_err_msg* next = msg->next;
     180            7 :       msg->message_type = 0;
     181            7 :       msg->next = NULL;
     182            7 :       delete msg;
     183            7 :       msg = next;
     184              :    }
     185            7 : }
     186              : 
     187              : #endif // LOCAL_ROUTINES
     188              : 
     189              : /*------------------------------------------------------------------*/
     190              : 
     191              : #ifdef LOCAL_ROUTINES
     192              : 
     193              : /********************************************************************\
     194              : *                                                                    *
     195              : *            Shared Memory Allocation                                *
     196              : *                                                                    *
     197              : \********************************************************************/
     198              : 
     199              : static bool db_validate_key_offset(const DATABASE_HEADER * pheader, int offset);
     200              : 
     201              : /*------------------------------------------------------------------*/
     202          190 : static void *malloc_key(DATABASE_HEADER * pheader, INT size, const char* caller)
     203              : {
     204          190 :    FREE_DESCRIP *pfree, *pfound, *pprev = NULL;
     205              : 
     206          190 :    if (size == 0)
     207            0 :       return NULL;
     208              : 
     209              :    /* quadword alignment for alpha CPU */
     210          190 :    size = ALIGN8(size);
     211              : 
     212              :    //printf("malloc_key(%d) from [%s]\n", size, caller);
     213              : 
     214          190 :    if (!db_validate_key_offset(pheader, pheader->first_free_key)) {
     215            0 :       return NULL;
     216              :    }
     217              : 
     218              :    /* search for free block */
     219          190 :    pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
     220              : 
     221              :    //printf("try free block %p size %d, next %d\n", pfree, pfree->size, pfree->next_free);
     222              : 
     223          307 :    while (pfree->size < size && pfree->next_free) {
     224          117 :       if (!db_validate_key_offset(pheader, pfree->next_free)) {
     225            0 :          return NULL;
     226              :       }
     227          117 :       pprev = pfree;
     228          117 :       pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
     229              :       //printf("pfree %p size %d next_free %d\n", pfree, pfree->size, pfree->next_free);
     230              :    }
     231              : 
     232              :    //printf("found free block %p size %d\n", pfree, pfree->size);
     233              : 
     234              :    /* return if not enough memory */
     235          190 :    if (pfree->size < size)
     236            0 :       return 0;
     237              : 
     238          190 :    pfound = pfree;
     239              : 
     240              :    /* if found block is first in list, correct pheader */
     241          190 :    if (pfree == (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key)) {
     242           77 :       if (size < pfree->size) {
     243              :          /* free block is only used partially */
     244           75 :          pheader->first_free_key += size;
     245           75 :          pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
     246              : 
     247           75 :          pfree->size = pfound->size - size;
     248           75 :          pfree->next_free = pfound->next_free;
     249              :       } else {
     250              :          /* free block is used totally */
     251            2 :          pheader->first_free_key = pfree->next_free;
     252              :       }
     253              :    } else {
     254              :       /* check if free block is used totally */
     255          113 :       if (pfound->size - size < (int) sizeof(FREE_DESCRIP)) {
     256              :          /* skip block totally */
     257            4 :          pprev->next_free = pfound->next_free;
     258              :       } else {
     259              :          /* decrease free block */
     260          109 :          pfree = (FREE_DESCRIP *) ((char *) pfound + size);
     261              : 
     262          109 :          pfree->size = pfound->size - size;
     263          109 :          pfree->next_free = pfound->next_free;
     264              : 
     265          109 :          pprev->next_free = (POINTER_T) pfree - (POINTER_T) pheader;
     266              :       }
     267              :    }
     268              : 
     269          190 :    assert((void*)pfound != (void*)pheader);
     270              : 
     271          190 :    memset(pfound, 0, size);
     272              : 
     273          190 :    return pfound;
     274              : }
     275              : 
     276              : /*------------------------------------------------------------------*/
     277           77 : static void free_key(DATABASE_HEADER * pheader, void *address, INT size)
     278              : {
     279              :    FREE_DESCRIP *pfree, *pprev, *pnext;
     280              : 
     281           77 :    if (size == 0)
     282            0 :       return;
     283              : 
     284           77 :    assert(address != pheader);
     285              : 
     286              :    /* quadword alignment for alpha CPU */
     287           77 :    size = ALIGN8(size);
     288              : 
     289           77 :    pfree = (FREE_DESCRIP *) address;
     290           77 :    pprev = NULL;
     291              : 
     292              :    /* clear current block */
     293           77 :    memset(address, 0, size);
     294              : 
     295              :    /* if key comes before first free block, adjust pheader */
     296           77 :    if ((POINTER_T) address - (POINTER_T) pheader < pheader->first_free_key) {
     297           10 :       pfree->size = size;
     298           10 :       pfree->next_free = pheader->first_free_key;
     299           10 :       pheader->first_free_key = (POINTER_T) address - (POINTER_T) pheader;
     300              :    } else {
     301              :       /* find last free block before current block */
     302           67 :       pprev = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
     303              : 
     304          106 :       while (pprev->next_free < (POINTER_T) address - (POINTER_T) pheader) {
     305           39 :          if (pprev->next_free <= 0) {
     306            0 :             cm_msg(MERROR, "free_key", "database is corrupted: pprev=%p, pprev->next_free=%d", pprev, pprev->next_free);
     307            0 :             return;
     308              :          }
     309           39 :          pprev = (FREE_DESCRIP *) ((char *) pheader + pprev->next_free);
     310              :       }
     311              : 
     312           67 :       pfree->size = size;
     313           67 :       pfree->next_free = pprev->next_free;
     314              : 
     315           67 :       pprev->next_free = (POINTER_T) pfree - (POINTER_T) pheader;
     316              :    }
     317              : 
     318              :    /* try to melt adjacent free blocks after current block */
     319           77 :    pnext = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
     320           77 :    if ((POINTER_T) pnext == (POINTER_T) pfree + pfree->size) {
     321           31 :       pfree->size += pnext->size;
     322           31 :       pfree->next_free = pnext->next_free;
     323              : 
     324           31 :       memset(pnext, 0, pnext->size);
     325              :    }
     326              : 
     327              :    /* try to melt adjacent free blocks before current block */
     328           77 :    if (pprev && pprev->next_free == (POINTER_T) pprev - (POINTER_T) pheader + pprev->size) {
     329           35 :       pprev->size += pfree->size;
     330           35 :       pprev->next_free = pfree->next_free;
     331              : 
     332           35 :       memset(pfree, 0, pfree->size);
     333              :    }
     334              : }
     335              : 
     336          125 : static int validate_free_data(DATABASE_HEADER * pheader, int free_data)
     337              : {
     338          125 :    if (free_data <= 0)
     339            0 :       return 0;
     340              : 
     341          125 :    if (free_data < (int)sizeof(DATABASE_HEADER)) {
     342              :       //printf("validate_free_data: failed: %d is inside the database header 0..%d\n", free_data, (int)sizeof(DATABASE_HEADER));
     343            0 :       return 0;
     344              :    }
     345              : 
     346          125 :    if (free_data < (int)sizeof(DATABASE_HEADER) + pheader->key_size) {
     347              :       //printf("validate_free_data: failed: %d is inside key space %d..%d\n", free_data, (int)sizeof(DATABASE_HEADER), (int)sizeof(DATABASE_HEADER) + pheader->key_size);
     348            0 :       return 0;
     349              :    }
     350              : 
     351          125 :    if (free_data > (int)sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size) {
     352              :       //printf("validate_free_data: failed: %d is beyound end of odb %d+%d+%d = %d\n", free_data, (int)sizeof(DATABASE_HEADER), pheader->key_size, pheader->data_size, (int)sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size);
     353            0 :       return 0;
     354              :    }
     355              : 
     356          125 :    return 1;
     357              : }
     358              : 
     359              : /*------------------------------------------------------------------*/
     360          114 : static void *malloc_data(DATABASE_HEADER * pheader, INT size)
     361              : {
     362          114 :    if (size == 0)
     363            0 :       return NULL;
     364              : 
     365          114 :    assert(size > 0);
     366              : 
     367              :    /* quadword alignment for alpha CPU */
     368          114 :    size = ALIGN8(size);
     369              : 
     370              :    /* smallest allocation size is 8 bytes to make sure we can always create a new FREE_DESCRIP in free_data() */
     371          114 :    assert(size >= (int)sizeof(FREE_DESCRIP));
     372              : 
     373          114 :    if (!validate_free_data(pheader, pheader->first_free_data)) {
     374            0 :       return NULL;
     375              :    }
     376              : 
     377              :    /* search for free block */
     378          114 :    FREE_DESCRIP *pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
     379          114 :    FREE_DESCRIP *pprev = NULL;
     380          114 :    FREE_DESCRIP *pfound = NULL;
     381              : 
     382              :    while (1) {
     383              :       //printf("malloc_data: pprev %p,  pfree %p, next %d, size %d, want %d\n", pprev, pfree, pfree->next_free, pfree->size, size);
     384              : 
     385          117 :       if (pfree->size >= size) {
     386              :          // we will use this block
     387          114 :          pfound = pfree;
     388          114 :          break;
     389              :       }
     390              : 
     391            3 :       if (!pfree->next_free) {
     392              :          // no more free blocks
     393            0 :          return NULL;
     394              :       }
     395              : 
     396            3 :       if (!validate_free_data(pheader, pfree->next_free)) {
     397              :          // next_free is invalid
     398              :          //printf("malloc_data: pprev %p,  pfree %p, next %d, size %d, next is invalid\n", pprev, pfree, pfree->next_free, pfree->size);
     399            0 :          return NULL;
     400              :       }
     401            3 :       pprev = pfree;
     402            3 :       pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
     403              :    }
     404              : 
     405              :    //printf("malloc_data: pprev %p, pfound %p, size %d, want %d\n", pprev, pfound, pfound->size, size);
     406              : 
     407          114 :    assert(pfound != NULL);
     408          114 :    assert(size <= pfound->size);
     409              : 
     410              :    /* if found block is first in list, correct pheader */
     411          114 :    if (!pprev) {
     412          111 :       if (size < pfree->size) {
     413              :          /* free block is only used partially */
     414          106 :          pheader->first_free_data += size;
     415          106 :          pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
     416              : 
     417          106 :          pfree->size = pfound->size - size;
     418          106 :          pfree->next_free = pfound->next_free;
     419              :       } else {
     420              :          /* free block is used totally */
     421            5 :          pheader->first_free_data = pfree->next_free;
     422              :       }
     423              :    } else {
     424              :       /* check if free block is used totally */
     425            3 :       if (pfound->size - size < (int) sizeof(FREE_DESCRIP)) {
     426              :          /* delete this free block from the free blocks chain */
     427            0 :          pprev->next_free = pfound->next_free;
     428              :       } else {
     429              :          /* decrease free block */
     430            3 :          pfree = (FREE_DESCRIP *) ((char *) pfound + size);
     431              : 
     432            3 :          pfree->size = pfound->size - size;
     433            3 :          pfree->next_free = pfound->next_free;
     434              : 
     435            3 :          pprev->next_free = (POINTER_T) pfree - (POINTER_T) pheader;
     436              :       }
     437              :    }
     438              : 
     439          114 :    assert((void*)pfound != (void*)pheader);
     440              : 
     441              :    /* zero memeory */
     442          114 :    memset(pfound, 0, size);
     443              : 
     444          114 :    return pfound;
     445              : }
     446              : 
     447              : /*------------------------------------------------------------------*/
     448           45 : static int free_data(DATABASE_HEADER * pheader, void *address, INT size, const char* caller)
     449              : {
     450           45 :    if (size == 0)
     451            0 :       return DB_SUCCESS;
     452              : 
     453           45 :    assert(address != pheader);
     454              : 
     455              :    /* quadword alignment for alpha CPU */
     456           45 :    size = ALIGN8(size);
     457              : 
     458              :    /* smallest allocation size is 8 bytes to make sure we can always create a new FREE_DESCRIP in free_data() */
     459           45 :    assert(size >= (int)sizeof(FREE_DESCRIP));
     460              : 
     461           45 :    FREE_DESCRIP *pprev = NULL;
     462           45 :    FREE_DESCRIP *pfree = (FREE_DESCRIP *) address;
     463           45 :    int pfree_offset = (POINTER_T) address - (POINTER_T) pheader;
     464              : 
     465              :    /* clear current block */
     466           45 :    memset(address, 0, size);
     467              : 
     468           45 :    if (pheader->first_free_data == 0) {
     469              :       /* if free list is empty, create the first free block, adjust pheader */
     470            0 :       pfree->size = size;
     471            0 :       pfree->next_free = 0;
     472            0 :       pheader->first_free_data = pfree_offset;
     473              :       /* nothing else to do */
     474            0 :       return DB_SUCCESS;
     475           45 :    } else if ((POINTER_T) address - (POINTER_T) pheader < pheader->first_free_data) {
     476              :       /* if data comes before first free block, create new free block, adjust pheader */
     477           11 :       pfree->size = size;
     478           11 :       pfree->next_free = pheader->first_free_data;
     479           11 :       pheader->first_free_data = pfree_offset;
     480              :       /* maybe merge next free block into the new free block */
     481              :       //printf("free_data: created new first free block, maybe merge with old first free block\n");
     482              :    } else {
     483              :       /* find last free block before current block */
     484           34 :       pprev = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
     485              : 
     486           42 :       while (pprev->next_free < pfree_offset) {
     487            8 :          if (pprev->next_free == 0) {
     488              :             /* add new free block at the end of the chain of free blocks */
     489              :             //printf("free_data: adding new free block at the very end\n");
     490            0 :             break;
     491              :          }
     492            8 :          if (!validate_free_data(pheader, pprev->next_free)) {
     493            0 :             cm_msg(MERROR, "free_data", "database is corrupted: pprev=%p, pprev->next_free=%d in free_data(%p,%p,%d) from %s", pprev, pprev->next_free, pheader, address, size, caller);
     494            0 :             return DB_CORRUPTED;
     495              :          }
     496              : 
     497            8 :          pprev = (FREE_DESCRIP *) ((char *) pheader + pprev->next_free);
     498              :       }
     499              : 
     500           34 :       pfree->size = size;
     501           34 :       pfree->next_free = pprev->next_free;
     502              : 
     503           34 :       pprev->next_free = pfree_offset;
     504              :    }
     505              : 
     506              :    /* try to melt adjacent free blocks after current block */
     507           45 :    FREE_DESCRIP *pnext = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
     508           45 :    if ((POINTER_T) pnext == (POINTER_T) pfree + pfree->size) {
     509              :       //printf("free_data: merging first and second free block\n");
     510            6 :       pfree->size += pnext->size;
     511            6 :       pfree->next_free = pnext->next_free;
     512              : 
     513            6 :       memset(pnext, 0, pnext->size);
     514              :    }
     515              : 
     516              :    /* try to melt adjacent free blocks before current block */
     517           45 :    if (pprev && pprev->next_free == (POINTER_T) pprev - (POINTER_T) pheader + pprev->size) {
     518              :       //printf("free_data: merging pprev and pfree\n");
     519           31 :       pprev->size += pfree->size;
     520           31 :       pprev->next_free = pfree->next_free;
     521              : 
     522           31 :       memset(pfree, 0, pfree->size);
     523              :    }
     524              : 
     525           45 :    return DB_SUCCESS;
     526              : }
     527              : 
     528              : /*------------------------------------------------------------------*/
     529           27 : static void *realloc_data(DATABASE_HEADER * pheader, void *address, INT old_size, INT new_size, const char* caller)
     530              : {
     531           27 :    void *tmp = NULL;
     532              : 
     533           27 :    if (old_size) {
     534              :       int status;
     535            2 :       tmp = malloc(old_size);
     536            2 :       if (tmp == NULL) {
     537            0 :          cm_msg(MERROR, "realloc_data", "cannot malloc(%d), called from %s", old_size, caller);
     538            0 :          return NULL;
     539              :       }
     540              : 
     541            2 :       memcpy(tmp, address, old_size);
     542              : 
     543            2 :       status = free_data(pheader, address, old_size, caller);
     544            2 :       if (status != DB_SUCCESS) {
     545            0 :          free(tmp);
     546            0 :          cm_msg(MERROR, "realloc_data", "cannot free_data(%p, %d), called from %s", address, old_size, caller);
     547            0 :          return NULL;
     548              :       }
     549              :    }
     550              : 
     551           27 :    void *pnew = malloc_data(pheader, new_size);
     552              : 
     553           27 :    if (!pnew) {
     554            0 :       if (tmp)
     555            0 :          free(tmp);
     556            0 :       cm_msg(MERROR, "realloc_data", "cannot malloc_data(%d), called from %s", new_size, caller);
     557            0 :       return NULL;
     558              :    }
     559              : 
     560           27 :    if (old_size) {
     561            2 :       memcpy(pnew, tmp, old_size < new_size ? old_size : new_size);
     562            2 :       free(tmp);
     563              :    }
     564              : 
     565           27 :    return pnew;
     566              : }
     567              : 
     568              : #endif // LOCAL_ROUTINES
     569              : 
     570              : /*------------------------------------------------------------------*/
     571            0 : char *strcomb(const char **list)
     572              : /* convert list of strings into single string to be used by db_paste() */
     573              : {
     574              :    INT i, j;
     575              :    static char *str = NULL;
     576              : 
     577              :    /* counter number of chars */
     578            0 :    for (i = 0, j = 0; list[i]; i++)
     579            0 :       j += strlen(list[i]) + 1;
     580            0 :    j += 1;
     581              : 
     582            0 :    if (str == NULL)
     583            0 :       str = (char *) malloc(j);
     584              :    else
     585            0 :       str = (char *) realloc(str, j);
     586              : 
     587            0 :    assert(str != NULL);
     588              : 
     589            0 :    str[0] = 0;
     590            0 :    for (i = 0; list[i]; i++) {
     591            0 :       strcat(str, list[i]);
     592            0 :       strcat(str, "\n");
     593              :    }
     594              : 
     595            0 :    return str;
     596              : }
     597              : 
     598              : /*------------------------------------------------------------------*/
     599              : 
     600            2 : std::string strcomb1(const char **list)
     601              : /* convert list of strings into single string to be used by db_paste() */
     602              : {
     603            2 :    std::string s;
     604              : 
     605           24 :    for (int i = 0; list[i]; i++) {
     606           22 :       s += list[i];
     607           22 :       s += "\n";
     608              :    }
     609              : 
     610            2 :    return s;
     611            0 : }
     612              : 
     613              : /*------------------------------------------------------------------*/
     614              : 
     615              : struct print_key_info_buf
     616              : {
     617              :    int alloc_size;
     618              :    int used;
     619              :    char* buf;
     620              : };
     621              : 
     622            0 : static void add_to_buf(struct print_key_info_buf* buf, const char* s)
     623              : {
     624            0 :    int len = strlen(s);
     625            0 :    if (buf->used + len + 10 > buf->alloc_size) {
     626            0 :       int new_size = 1024 + 2*buf->alloc_size + len;
     627              :       //printf("realloc %d->%d, used %d, adding %d\n", buf->alloc_size, new_size, buf->used, len);
     628            0 :       buf->buf = (char*)realloc(buf->buf, new_size);
     629            0 :       assert(buf->buf != NULL);
     630            0 :       buf->alloc_size = new_size;
     631              :    }
     632              : 
     633            0 :    memcpy(buf->buf + buf->used, s, len);
     634            0 :    buf->used += len;
     635            0 :    buf->buf[buf->used] = 0; // zero-terminate the string
     636            0 : }
     637              : 
     638              : #ifdef LOCAL_ROUTINES
     639              : 
     640            0 : static INT print_key_info(HNDLE hDB, HNDLE hKey, KEY * pkey, INT level, void *info)
     641              : {
     642            0 :    struct print_key_info_buf* buf = (struct print_key_info_buf*)info;
     643              :    int i;
     644              : 
     645              :    char str[256];
     646              : 
     647            0 :    sprintf(str, "%08X  %08X  %08X    ",
     648            0 :            (int) (hKey - sizeof(DATABASE_HEADER)),
     649            0 :            (int) (pkey->data - sizeof(DATABASE_HEADER)), (int) pkey->total_size);
     650              : 
     651            0 :    assert(strlen(str)+10 < sizeof(str));
     652              : 
     653            0 :    for (i = 0; i < level; i++)
     654            0 :       strcat(str, "  ");
     655              : 
     656            0 :    assert(strlen(str)+10 < sizeof(str));
     657              : 
     658            0 :    strcat(str, pkey->name);
     659            0 :    strcat(str, "\n");
     660              : 
     661            0 :    assert(strlen(str)+10 < sizeof(str));
     662              : 
     663              :    //printf("str [%s]\n", str);
     664              : 
     665            0 :    add_to_buf(buf, str);
     666              : 
     667            0 :    return SUCCESS;
     668              : }
     669              : 
     670              : static bool db_validate_data_offset(const DATABASE_HEADER * pheader, int offset);
     671              : 
     672            0 : INT db_show_mem(HNDLE hDB, char **result, BOOL verbose)
     673              : {
     674              :    INT total_size_key, total_size_data;
     675              : 
     676              :    struct print_key_info_buf buf;
     677            0 :    buf.buf = NULL;
     678            0 :    buf.used = 0;
     679            0 :    buf.alloc_size = 0;
     680              : 
     681            0 :    db_lock_database(hDB);
     682              : 
     683            0 :    DATABASE_HEADER *pheader = _database[hDB - 1].database_header;
     684              : 
     685              :    char str[256];
     686              : 
     687            0 :    sprintf(str, "Database header size is 0x%04X, all following values are offset by this!\n", (int)sizeof(DATABASE_HEADER));
     688            0 :    add_to_buf(&buf, str);
     689            0 :    sprintf(str, "Key area  0x00000000 - 0x%08X, size %d bytes\n",  pheader->key_size - 1, pheader->key_size);
     690            0 :    add_to_buf(&buf, str);
     691            0 :    sprintf(str, "Data area 0x%08X - 0x%08X, size %d bytes\n\n",    pheader->key_size, pheader->key_size + pheader->data_size, pheader->data_size);
     692            0 :    add_to_buf(&buf, str);
     693              : 
     694            0 :    add_to_buf(&buf, "Keylist:\n");
     695            0 :    add_to_buf(&buf, "--------\n");
     696            0 :    total_size_key = 0;
     697              : 
     698            0 :    if (!db_validate_key_offset(pheader, pheader->first_free_key)) {
     699            0 :       add_to_buf(&buf, "ODB is corrupted: pheader->first_free_key is invalid\n");
     700            0 :       db_unlock_database(hDB);
     701            0 :       if (result) {
     702            0 :          *result = buf.buf;
     703              :       } else {
     704            0 :          free(buf.buf);
     705              :       }
     706            0 :       return DB_CORRUPTED;
     707              :    }
     708              : 
     709            0 :    FREE_DESCRIP *pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
     710              : 
     711            0 :    while ((POINTER_T) pfree != (POINTER_T) pheader) {
     712            0 :       total_size_key += pfree->size;
     713            0 :       sprintf(str, "Free block at 0x%08X, size 0x%08X, next 0x%08X\n",
     714            0 :               (int) ((POINTER_T) pfree - (POINTER_T) pheader - sizeof(DATABASE_HEADER)),
     715            0 :               pfree->size, pfree->next_free ? (int) (pfree->next_free - sizeof(DATABASE_HEADER)) : 0);
     716            0 :       add_to_buf(&buf, str);
     717            0 :       if (!db_validate_key_offset(pheader, pfree->next_free)) {
     718            0 :          add_to_buf(&buf, "ODB is corrupted: next_free is invalid!");
     719            0 :          break;
     720              :       }
     721            0 :       pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
     722              :    }
     723              : 
     724            0 :    sprintf(str, "\nFree Key area: %d bytes out of %d bytes\n", total_size_key, pheader->key_size);
     725            0 :    add_to_buf(&buf, str);
     726              :    
     727            0 :    add_to_buf(&buf, "\nData:\n");
     728            0 :    add_to_buf(&buf, "-----\n");
     729            0 :    total_size_data = 0;
     730              : 
     731            0 :    if (!db_validate_data_offset(pheader, pheader->first_free_data)) {
     732            0 :       add_to_buf(&buf, "ODB is corrupted: pheader->first_free_data is invalid\n");
     733            0 :       db_unlock_database(hDB);
     734            0 :       if (result) {
     735            0 :          *result = buf.buf;
     736              :       } else {
     737            0 :          free(buf.buf);
     738              :       }
     739            0 :       return DB_CORRUPTED;
     740              :    }
     741              : 
     742            0 :    pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
     743              : 
     744            0 :    while ((POINTER_T) pfree != (POINTER_T) pheader) {
     745            0 :       total_size_data += pfree->size;
     746            0 :       sprintf(str, "Free block at 0x%08X, size 0x%08X, next 0x%08X\n",
     747            0 :               (int) ((POINTER_T) pfree - (POINTER_T) pheader - sizeof(DATABASE_HEADER)),
     748            0 :               pfree->size, pfree->next_free ? (int) (pfree->next_free - sizeof(DATABASE_HEADER)) : 0);
     749            0 :       add_to_buf(&buf, str);
     750            0 :       if (!db_validate_data_offset(pheader, pfree->next_free)) {
     751            0 :          add_to_buf(&buf, "ODB is corrupted: next_free is invalid!");
     752            0 :          break;
     753              :       }
     754            0 :       pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
     755              :    }
     756              : 
     757            0 :    sprintf(str, "\nFree Data area: %d bytes out of %d bytes\n", total_size_data, pheader->data_size);
     758            0 :    add_to_buf(&buf, str);
     759              : 
     760            0 :    sprintf(str, "\nFree: %1d (%1.1lf%%) keylist, %1d (%1.1lf%%) data\n",
     761              :            total_size_key,
     762            0 :            100 * (double) total_size_key / pheader->key_size,
     763            0 :            total_size_data, 100 * (double) total_size_data / pheader->data_size);
     764            0 :    add_to_buf(&buf, str);
     765              : 
     766            0 :    if (verbose) {
     767            0 :       add_to_buf(&buf, "\n\n");
     768            0 :       add_to_buf(&buf, "Key       Data      Size\n");
     769            0 :       add_to_buf(&buf, "----------------------------\n");
     770            0 :       db_scan_tree(hDB, pheader->root_key, 0, print_key_info, &buf);
     771              :    }
     772              : 
     773            0 :    sprintf(str, "\nTotal ODB size: %d (0x%08X) Bytes, %lg MiB\n",
     774            0 :            pheader->key_size + pheader->data_size, pheader->key_size + pheader->data_size,
     775            0 :            ((pheader->key_size + pheader->data_size) / 1E6));
     776            0 :    add_to_buf(&buf, str);
     777            0 :    db_unlock_database(hDB);
     778              : 
     779            0 :    if (result) {
     780            0 :       *result = buf.buf;
     781              :    } else {
     782            0 :       free(buf.buf);
     783              :    }
     784              : 
     785            0 :    return DB_SUCCESS;
     786              : }
     787              : 
     788            0 : INT db_get_free_mem(HNDLE hDB, INT *key_size, INT *data_size)
     789              : {
     790              :    DATABASE_HEADER *pheader;
     791              :    FREE_DESCRIP *pfree;
     792              :    
     793            0 :    *data_size = 0;
     794            0 :    *key_size = 0;
     795              : 
     796            0 :    db_lock_database(hDB);
     797              :    
     798            0 :    pheader = _database[hDB - 1].database_header;
     799              :    
     800            0 :    pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
     801              :    
     802            0 :    while ((POINTER_T) pfree != (POINTER_T) pheader) {
     803            0 :       *key_size += pfree->size;
     804            0 :       pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
     805              :    }
     806              :    
     807            0 :    *data_size = 0;
     808            0 :    pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
     809              :    
     810            0 :    while ((POINTER_T) pfree != (POINTER_T) pheader) {
     811            0 :       *data_size += pfree->size;
     812            0 :       pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
     813              :    }
     814              : 
     815            0 :    db_unlock_database(hDB);
     816            0 :    return DB_SUCCESS;
     817              : }
     818              : 
     819              : #endif // LOCAL_ROUTINES
     820              : 
     821              : // Method to check if a given string is valid UTF-8.  Returns 1 if it is.
     822              : // This method was taken from stackoverflow user Christoph, specifically
     823              : // http://stackoverflow.com/questions/1031645/how-to-detect-utf-8-in-plain-c
     824          438 : static bool is_utf8(const char * string)
     825              : {
     826          438 :    if (!string)
     827            0 :       return false;
     828              : 
     829          438 :    return ss_is_valid_utf8(string);
     830              : }
     831              : 
     832              : #ifdef LOCAL_ROUTINES
     833              : 
     834              : /*------------------------------------------------------------------*/
     835          410 : static int db_validate_name(const char* name, int maybe_path, const char* caller_name, db_err_msg** msg)
     836              : {
     837              :    //printf("db_validate_name [%s] length %d, maybe_path %d from %s\n", name, (int)strlen(name), maybe_path, caller_name);
     838              : 
     839          410 :    if (name == NULL) {
     840            1 :       db_msg(msg, MERROR, "db_validate_name", "Invalid name passed to %s: should not be NULL", caller_name);
     841            1 :       return DB_INVALID_NAME;
     842              :    }
     843              : 
     844          409 :    size_t len = strlen(name);
     845              : 
     846          409 :    if (len < 1) {
     847            4 :       db_msg(msg, MERROR, "db_validate_name", "Invalid name passed to %s: should not be an empty string", caller_name);
     848            4 :       return DB_INVALID_NAME;
     849              :    }
     850              : 
     851          405 :    if (strchr(name, '[')) {
     852            1 :       db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not contain \"[\"", name, caller_name);
     853            1 :       return DB_INVALID_NAME;
     854              :    }
     855              :    
     856          404 :    if (name[0] == ' ') {
     857            0 :       db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not start with a space", name, caller_name);
     858            0 :       return DB_INVALID_NAME;
     859              :    }
     860              : 
     861          404 :    if (name[len-1] == ' ') {
     862            0 :       db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not end with a space", name, caller_name);
     863            0 :       return DB_INVALID_NAME;
     864              :    }
     865              : 
     866          404 :    if (strchr(name, ']')) {
     867            1 :       db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not contain \"[\"", name, caller_name);
     868            1 :       return DB_INVALID_NAME;
     869              :    }
     870              :    
     871          403 :    if (!is_utf8(name)) {
     872            0 :       db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: invalid unicode UTF-8 encoding", name, caller_name);
     873            0 :       return DB_INVALID_NAME;
     874              :    }
     875              :    
     876          403 :    if (!maybe_path) {
     877          269 :       if (strchr(name, '/')) {
     878            0 :          db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not contain \"/\"", name, caller_name);
     879            0 :          return DB_INVALID_NAME;
     880              :       }
     881              : 
     882          269 :       if (strlen(name) >= NAME_LENGTH) {
     883            0 :          db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: length %d should be less than %d bytes", name, caller_name, (int)strlen(name), NAME_LENGTH);
     884            0 :          return DB_INVALID_NAME;
     885              :       }
     886              :    }
     887              : 
     888              :    //if (strcmp(name, "test")==0)
     889              :    //return DB_INVALID_NAME;
     890              : 
     891          403 :    return DB_SUCCESS;
     892              : }
     893              : 
     894              : /*------------------------------------------------------------------*/
     895         9607 : static bool db_validate_key_offset(const DATABASE_HEADER * pheader, int offset)
     896              : /* check if key offset lies in valid range */
     897              : {
     898         9607 :    if (offset != 0 && offset < (int) sizeof(DATABASE_HEADER))
     899            0 :       return false;
     900              : 
     901         9607 :    if (offset > (int) sizeof(DATABASE_HEADER) + pheader->key_size)
     902            0 :       return false;
     903              : 
     904         9607 :    return true;
     905              : }
     906              : 
     907         1519 : static bool db_validate_data_offset(const DATABASE_HEADER * pheader, int offset)
     908              : /* check if data offset lies in valid range */
     909              : {
     910         1519 :    if (offset != 0 && offset < (int) sizeof(DATABASE_HEADER))
     911            0 :       return false;
     912              : 
     913         1519 :    if (offset > (int) sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size)
     914            0 :       return false;
     915              : 
     916         1519 :    return true;
     917              : }
     918              : 
     919         1173 : static bool db_validate_hkey(const DATABASE_HEADER * pheader, HNDLE hKey)
     920              : {
     921         1173 :    if (hKey == 0) {
     922            0 :       cm_msg(MERROR, "db_validate_hkey", "Error: invalid zero hkey %d", hKey);
     923            0 :       return false;
     924              :    }
     925         1173 :    if (!db_validate_key_offset(pheader, hKey)) {
     926            0 :       cm_msg(MERROR, "db_validate_hkey", "Error: invalid hkey %d", hKey);
     927            0 :       return false;
     928              :    }
     929         1173 :    return true;
     930              : }
     931              : 
     932         5101 : static const KEY* db_get_pkey(const DATABASE_HEADER* pheader, HNDLE hKey, int* pstatus, const char* caller, db_err_msg **msg)
     933              : {
     934         5101 :    BOOL hKey_is_root_key = FALSE;
     935              : 
     936         5101 :    if (!hKey) {
     937          253 :       hKey_is_root_key = TRUE;
     938          253 :       hKey = pheader->root_key;
     939              :    }
     940              : 
     941              :    /* check if hKey argument is correct */
     942         5101 :    if (hKey == 0) {
     943            0 :       if (pstatus)
     944            0 :          *pstatus = DB_INVALID_HANDLE;
     945            0 :       return NULL;
     946              :    }
     947              : 
     948              :    /* check if hKey argument is correct */
     949         5101 :    if (!db_validate_key_offset(pheader, hKey)) {
     950            0 :       if (pstatus)
     951            0 :          *pstatus = DB_INVALID_HANDLE;
     952            0 :       return NULL;
     953              :    }
     954              : 
     955         5101 :    const KEY* pkey = (const KEY *) ((char *) pheader + hKey);
     956              : 
     957         5101 :    if (pkey->type < 1 || pkey->type >= TID_LAST) {
     958            0 :       DWORD tid = pkey->type;
     959            0 :       if (hKey_is_root_key) {
     960            0 :          db_msg(msg, MERROR, caller, "db_get_pkey: root_key hkey %d invalid key type %d, database root directory is corrupted", hKey, tid);
     961            0 :          if (pstatus)
     962            0 :             *pstatus = DB_CORRUPTED;
     963            0 :          return NULL;
     964              :       } else {
     965            0 :          std::string path = db_get_path_locked(pheader, hKey);
     966            0 :          db_msg(msg, MERROR, caller, "db_get_pkey: hkey %d path \"%s\" invalid key type %d", hKey, path.c_str(), tid);
     967            0 :       }
     968            0 :       if (pstatus)
     969            0 :          *pstatus = DB_NO_KEY;
     970            0 :       return NULL;
     971              :    }
     972              : 
     973         5101 :    if (pkey->name[0] == 0) {
     974            0 :       std::string path = db_get_path_locked(pheader, hKey);
     975            0 :       db_msg(msg, MERROR, caller, "db_get_pkey: hkey %d path \"%s\" invalid name \"%s\" is empty", hKey, path.c_str(), pkey->name);
     976            0 :       if (pstatus)
     977            0 :          *pstatus = DB_NO_KEY;
     978            0 :       return NULL;
     979            0 :    }
     980              : 
     981         5101 :    return pkey;
     982              : }
     983              : 
     984         1285 : static const KEYLIST* db_get_pkeylist(const DATABASE_HEADER* pheader, HNDLE hKey, const KEY* pkey, const char* caller, db_err_msg **msg, bool kludge_repair = false)
     985              : {
     986         1285 :    if (pkey->type != TID_KEY) {
     987            0 :       std::string path = db_get_path_locked(pheader, hKey);
     988            0 :       db_msg(msg, MERROR, caller, "db_get_pkeylist: hkey %d path \"%s\" unexpected call to db_get_pkeylist(), not a subdirectory, pkey->type %d", hKey, path.c_str(), pkey->type);
     989            0 :       return NULL;
     990            0 :    }
     991              : 
     992         1285 :    if (!hKey) {
     993            0 :       hKey = pheader->root_key;
     994              :    }
     995              : 
     996         1285 :    if (!db_validate_data_offset(pheader, pkey->data)) {
     997            0 :       std::string path = db_get_path_locked(pheader, hKey);
     998            0 :       db_msg(msg, MERROR, caller, "hkey %d path \"%s\" invalid pkey->data %d", hKey, path.c_str(), pkey->data);
     999            0 :       return NULL;
    1000            0 :    }
    1001              : 
    1002         1285 :    const KEYLIST *pkeylist = (const KEYLIST *) ((char *) pheader + pkey->data);
    1003              : 
    1004         1285 :    if (pkeylist->parent != hKey) {
    1005            0 :       std::string path = db_get_path_locked(pheader, hKey);
    1006            0 :       db_msg(msg, MERROR, caller, "hkey %d path \"%s\" invalid pkeylist->parent %d should be hkey %d", hKey, path.c_str(), pkeylist->parent, hKey);
    1007            0 :       return NULL;
    1008            0 :    }
    1009              : 
    1010         1285 :    if (pkeylist->first_key == 0 && pkeylist->num_keys != 0) {
    1011            0 :       if (!kludge_repair) {
    1012            0 :          std::string path = db_get_path_locked(pheader, hKey);
    1013            0 :          db_msg(msg, MERROR, caller, "hkey %d path \"%s\" invalid pkeylist->first_key %d should be non zero for num_keys %d", hKey, path.c_str(), pkeylist->first_key, pkeylist->num_keys);
    1014            0 :          return NULL;
    1015            0 :       }
    1016              : 
    1017              :       // FIXME: this repair should be done in db_validate_and_repair_key()
    1018              : 
    1019            0 :       std::string path = db_get_path_locked(pheader, hKey);
    1020            0 :       db_msg(msg, MERROR, caller, "hkey %d path \"%s\" repaired invalid num_keys %d when pkeylist->first_key is zero", hKey, path.c_str(), pkeylist->num_keys);
    1021            0 :       ((KEYLIST*)pkeylist)->num_keys = 0;
    1022            0 :    }
    1023              : 
    1024         1285 :    return pkeylist;
    1025              : }
    1026              : 
    1027         1868 : static HNDLE db_pkey_to_hkey(const DATABASE_HEADER* pheader, const KEY* pkey)
    1028              : {
    1029         1868 :    return (POINTER_T) pkey - (POINTER_T) pheader;
    1030              : }
    1031              : 
    1032          150 : static const KEY* db_get_parent(const DATABASE_HEADER* pheader, const KEY* pkey, int* pstatus, const char* caller, db_err_msg **msg)
    1033              : {
    1034          150 :    if (pkey->parent_keylist == 0) {
    1035              :       // they asked for the parent of "/", return "/"
    1036            5 :       return db_get_pkey(pheader, pheader->root_key, pstatus, caller, msg);
    1037              :    }
    1038              : 
    1039          145 :    if (!db_validate_data_offset(pheader, pkey->parent_keylist)) {
    1040            0 :       db_msg(msg, MERROR, caller, "hkey %d path \"%s\" invalid pkey->parent %d", db_pkey_to_hkey(pheader, pkey), db_get_path_locked(pheader, pkey).c_str(), pkey->parent_keylist);
    1041            0 :       if (pstatus)
    1042            0 :          *pstatus = DB_CORRUPTED;
    1043            0 :       return NULL;
    1044              :    }
    1045              : 
    1046          145 :    const KEYLIST *pkeylist = (const KEYLIST *) ((char *) pheader + pkey->parent_keylist);
    1047              : 
    1048          145 :    if (pkeylist->first_key == 0 && pkeylist->num_keys != 0) {
    1049            0 :       db_msg(msg, MERROR, caller, "hkey %d path \"%s\" invalid parent pkeylist->first_key %d should be non zero for num_keys %d", db_pkey_to_hkey(pheader, pkey), db_get_path_locked(pheader, pkey).c_str(), pkeylist->first_key, pkeylist->num_keys);
    1050            0 :       if (pstatus)
    1051            0 :          *pstatus = DB_CORRUPTED;
    1052            0 :       return NULL;
    1053              :    }
    1054              : 
    1055          145 :    return db_get_pkey(pheader, pkeylist->parent, pstatus, caller, msg);
    1056              : }
    1057              : 
    1058          252 : static const KEY* db_enum_first_locked(const DATABASE_HEADER *pheader, const KEY* pkey, db_err_msg **msg)
    1059              : {
    1060          252 :    HNDLE hKey = db_pkey_to_hkey(pheader, pkey);
    1061              : 
    1062          252 :    if (pkey->type != TID_KEY) {
    1063            0 :       std::string path = db_get_path_locked(pheader, hKey);
    1064            0 :       db_msg(msg, MERROR, "db_enum_first_locked", "hkey %d path \"%s\" tid %d is not a directory", hKey, path.c_str(), pkey->type);
    1065            0 :       return NULL;
    1066            0 :    }
    1067              :    
    1068          252 :    const KEYLIST *pkeylist = db_get_pkeylist(pheader, hKey, pkey, "db_find_key", msg);
    1069              : 
    1070          252 :    if (!pkeylist) {
    1071              :       // error
    1072            0 :       return NULL;
    1073              :    }
    1074              : 
    1075          252 :    if (pkeylist->num_keys == 0) {
    1076              :       // empty directory
    1077           39 :       return NULL;
    1078              :    }
    1079              : 
    1080          213 :    if (pkeylist->first_key == 0) {
    1081              :       // empty directory
    1082            0 :       return NULL;
    1083              :    }
    1084              : 
    1085          213 :    return db_get_pkey(pheader, pkeylist->first_key, NULL, "db_enum_first_locked", msg);
    1086              : }
    1087              : 
    1088          786 : static const KEY* db_enum_next_locked(const DATABASE_HEADER *pheader, const KEY* pdir, const KEY* pkey, db_err_msg **msg)
    1089              : {
    1090          786 :    if (pkey->next_key == 0)
    1091          164 :       return NULL;
    1092              :    
    1093          622 :    return db_get_pkey(pheader, pkey->next_key, NULL, "db_enum_next_locked", msg);
    1094              : }
    1095              : 
    1096            0 : static const KEY* db_resolve_link_locked(const DATABASE_HEADER* pheader, const KEY* pkey, int* pstatus, db_err_msg** msg)
    1097              : {
    1098            0 :    if (pkey->type != TID_LINK)
    1099            0 :       return pkey;
    1100              : 
    1101              :    // FIXME: need to validate pkey->data
    1102              : 
    1103            0 :    if (*((char *) pheader + pkey->data) == '/') {
    1104            0 :       return db_find_pkey_locked(pheader, NULL, (char*)pheader + pkey->data, pstatus, msg);
    1105              :    } else {
    1106            0 :       return db_find_pkey_locked(pheader, pkey, (char*)pheader + pkey->data, pstatus, msg);
    1107              :    }
    1108              : }
    1109              : 
    1110              : /*
    1111              : static void db_print_pkey(const DATABASE_HEADER * pheader, const KEY* pkey, int recurse = 0, const char *path = NULL, HNDLE parenthkeylist = 0)
    1112              : {
    1113              :    HNDLE hkey = db_pkey_to_hkey(pheader, pkey);
    1114              : 
    1115              :    std::string xpath;
    1116              :    if (path == NULL) {
    1117              :       xpath = db_get_path_locked(pheader, hkey);
    1118              :       path = xpath.c_str();
    1119              :    }
    1120              : 
    1121              :    printf("path \"%s\", parenthkey %d, hkey %d, name \"%s\", type %d, parent %d, data %d, total_size %d", path, parenthkeylist, hkey, pkey->name, pkey->type, pkey->parent_keylist, pkey->data, pkey->total_size);
    1122              : 
    1123              :    if (pkey->type != TID_KEY) {
    1124              :       printf("\n");
    1125              :    } else {
    1126              :       const KEYLIST *pkeylist = db_get_pkeylist(pheader, hkey, pkey, "db_validate_key", NULL);
    1127              : 
    1128              :       if (pkeylist) {
    1129              :          printf(", pkeylist parent %d, num_keys %d, first_key %d", pkeylist->parent, pkeylist->num_keys, pkeylist->first_key);
    1130              :          printf("\n");
    1131              :       }
    1132              :    }
    1133              : }
    1134              : 
    1135              : static void db_print_hkey(const DATABASE_HEADER * pheader, HNDLE hkey, int recurse = 0, const char *path = NULL, HNDLE parenthkeylist = 0)
    1136              : {
    1137              :    const KEY *pkey = db_get_pkey(pheader, hkey, NULL, "db_print_key", NULL);
    1138              : 
    1139              :    if (!pkey) {
    1140              :       return;
    1141              :    }
    1142              : 
    1143              :    db_print_pkey(pheader, pkey, recurse, path, parenthkeylist);
    1144              : }
    1145              : */
    1146              : 
    1147           70 : static bool db_validate_and_repair_key_wlocked(DATABASE_HEADER * pheader, int recurse, const char *path, HNDLE parenthkeylist, HNDLE hkey, KEY * pkey, db_err_msg **msg)
    1148              : {
    1149              :    int status;
    1150           70 :    bool flag = true;
    1151              : 
    1152              :    //printf("path \"%s\", parenthkey %d, hkey %d, pkey->name \"%s\", type %d\n", path, parenthkeylist, hkey, pkey->name, pkey->type);
    1153              : 
    1154              :    //std::string xpath = db_get_path_locked(pheader, hkey);
    1155              :    //if (xpath != path)
    1156              :    //   printf("hkey %d, path \"%s\" vs \"%s\"\n", hkey, path, xpath.c_str());
    1157              : 
    1158              :    //db_print_key(pheader, 0, path, parenthkeylist, hkey);
    1159              : 
    1160           70 :    if (hkey==0 || !db_validate_key_offset(pheader, hkey)) {
    1161            0 :       db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid hkey", hkey, path);
    1162            0 :       return false;
    1163              :    }
    1164              : 
    1165              :    /* check key type */
    1166           70 :    if (pkey->type <= 0 || pkey->type >= TID_LAST) {
    1167            0 :       db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", name \"%s\", invalid key type %d", hkey, path, pkey->name, pkey->type);
    1168            0 :       return false;
    1169              :    }
    1170              : 
    1171              :    /* check key name */
    1172           70 :    status = db_validate_name(pkey->name, FALSE, "db_validate_key", msg);
    1173           70 :    if (status != DB_SUCCESS) {
    1174              :       char newname[NAME_LENGTH];
    1175            0 :       sprintf(newname, "%p", pkey);
    1176            0 :       db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\": invalid name \"%s\" replaced with \"%s\"", hkey, path, pkey->name, newname);
    1177            0 :       mstrlcpy(pkey->name, newname, sizeof(pkey->name));
    1178            0 :       flag = false;
    1179              :       //return false;
    1180              :    }
    1181              : 
    1182              :    /* check parent */
    1183           70 :    if (pkey->parent_keylist != parenthkeylist) {
    1184            0 :       db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", name \"%s\", invalid parent_keylist %d should be %d", hkey, path, pkey->name, pkey->parent_keylist, parenthkeylist);
    1185            0 :       return false;
    1186              :    }
    1187              : 
    1188           70 :    if (!db_validate_data_offset(pheader, pkey->data)) {
    1189            0 :       db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid data offset 0x%08X is invalid", hkey, path, pkey->data - (int)sizeof(DATABASE_HEADER));
    1190            0 :       return false;
    1191              :    }
    1192              : 
    1193              :    /* check key sizes */
    1194           70 :    if ((pkey->total_size < 0) || (pkey->total_size > pheader->data_size)) {
    1195            0 :       db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->total_size %d", hkey, path, pkey->total_size);
    1196            0 :       return false;
    1197              :    }
    1198              : 
    1199           70 :    if ((pkey->item_size < 0) || (pkey->item_size > pheader->data_size)) {
    1200            0 :       db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->item_size: %d", hkey, path, pkey->item_size);
    1201            0 :       return false;
    1202              :    }
    1203              : 
    1204           70 :    if ((pkey->num_values < 0) || (pkey->num_values > pheader->data_size)) {
    1205            0 :       db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->num_values: %d", hkey, path, pkey->num_values);
    1206            0 :       return false;
    1207              :    }
    1208              : 
    1209              :    /* check and correct key size */
    1210           70 :    if (pkey->total_size != pkey->item_size * pkey->num_values) {
    1211            0 :       db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", corrected pkey->total_size from %d to %d*%d=%d", hkey, path, pkey->total_size, pkey->item_size, pkey->num_values, pkey->item_size * pkey->num_values);
    1212            0 :       pkey->total_size = pkey->item_size * pkey->num_values;
    1213            0 :       flag = false;
    1214              :    }
    1215              : 
    1216              :    /* check and correct key size */
    1217           70 :    if (pkey->data == 0 && pkey->total_size != 0) {
    1218            0 :       db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", pkey->data is zero, corrected pkey->num_values %d and pkey->total_size %d to be zero, should be zero", hkey, path, pkey->num_values, pkey->total_size);
    1219            0 :       pkey->num_values = 0;
    1220            0 :       pkey->total_size = 0;
    1221            0 :       flag = false;
    1222              :    }
    1223              : 
    1224           70 :    if (pkey->type == TID_STRING || pkey->type == TID_LINK) {
    1225            7 :       const char* s = (char*)pheader + pkey->data;
    1226            7 :       if (!is_utf8(s)) {
    1227            0 :          db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", string value is not valid UTF-8", hkey, path);
    1228              :          //flag = false;
    1229              :       }
    1230              :    }
    1231              : 
    1232              :    /* check for empty link */
    1233           70 :    if (pkey->type == TID_LINK) {
    1234              :       // minimum symlink length is 3 bytes:
    1235              :       // one byte "/"
    1236              :       // one byte odb entry name
    1237              :       // one byte "\0"
    1238            0 :       if (pkey->total_size <= 2) {
    1239            0 :          db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK is an empty link", hkey, path);
    1240            0 :          flag = false;
    1241              :          //return false;
    1242              :       }
    1243              :    }
    1244              : 
    1245              :    /* check for too long link */
    1246           70 :    if (pkey->type == TID_LINK) {
    1247            0 :       if (pkey->total_size >= MAX_ODB_PATH) {
    1248            0 :          db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK length %d exceeds MAX_ODB_PATH %d", hkey, path, pkey->total_size, MAX_ODB_PATH);
    1249            0 :          flag = false;
    1250              :          //return false;
    1251              :       }
    1252              :    }
    1253              : 
    1254              :    /* check for link loop */
    1255           70 :    if (pkey->type == TID_LINK) {
    1256            0 :       const char* link = (char*)pheader + pkey->data;
    1257            0 :       int link_len = strlen(link);
    1258            0 :       int path_len = strlen(path);
    1259            0 :       if (link_len == path_len) {
    1260              :          // check for link to itself
    1261            0 :          if (equal_ustring(link, path)) {
    1262            0 :             db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK to \"%s\" is a link to itself", hkey, path, link);
    1263            0 :             flag = false;
    1264              :             //return false;
    1265              :          }
    1266            0 :       } else if (link_len < path_len) {
    1267              :          // check for link to the "path" subdirectory
    1268              :          char tmp[MAX_ODB_PATH];
    1269            0 :          memcpy(tmp, path, link_len);
    1270            0 :          tmp[link_len] = 0;
    1271            0 :          if (equal_ustring(link, tmp) && path[link_len] == DIR_SEPARATOR) {
    1272            0 :             db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK to \"%s\" is a loop", hkey, path, link);
    1273            0 :             flag = false;
    1274              :             //return false;
    1275              :          }
    1276              :       }
    1277              :    }
    1278              : 
    1279              :    /* check access mode */
    1280           70 :    if ((pkey->access_mode & ~(MODE_READ | MODE_WRITE | MODE_DELETE | MODE_EXCLUSIVE | MODE_ALLOC))) {
    1281            0 :       db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->access_mode %d", hkey, path, pkey->access_mode);
    1282            0 :       flag = false;
    1283              :       //return false;
    1284              :    }
    1285              : 
    1286              :    /* check if access time is in the future */
    1287           70 :    if (pkey->last_written > 0 && ((DWORD)pkey->last_written > ss_time() + 3600)) {
    1288            0 :       db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->last_written time %d", hkey, path, pkey->last_written);
    1289            0 :       flag = false;
    1290              :       //return false;
    1291              :    }
    1292              : 
    1293           70 :    if (pkey->type == TID_KEY) {
    1294           24 :       bool pkeylist_ok = true;
    1295              :       // FIXME: notice the kludged repair of pkeylist! K.O.
    1296           24 :       const KEYLIST *pkeylist = db_get_pkeylist(pheader, hkey, pkey, "db_validate_key", msg, true);
    1297              : 
    1298           24 :       if (!pkeylist) {
    1299            0 :          db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->data %d", hkey, path, pkey->data);
    1300            0 :          flag = false;
    1301              :       } else {
    1302           24 :          if (pkeylist->parent != hkey) {
    1303            0 :             db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_KEY invalid pkeylist->parent %d is not hkey %d", hkey, path, pkeylist->parent, hkey);
    1304            0 :             flag = false;
    1305            0 :             pkeylist_ok = false;
    1306              :          }
    1307              : 
    1308           24 :          if (pkeylist->num_keys < 0 || pkeylist->num_keys > pheader->key_size) {
    1309            0 :             db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_KEY invalid pkeylist->num_keys %d", hkey, path, pkeylist->num_keys);
    1310            0 :             flag = false;
    1311            0 :             pkeylist_ok = false;
    1312              :          }
    1313              :          
    1314           24 :          if (pkeylist->num_keys == 0 && pkeylist->first_key == 0) {
    1315              :             // empty key
    1316           20 :          } else if (pkeylist->first_key == 0 || !db_validate_key_offset(pheader, pkeylist->first_key)) {
    1317            0 :             db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_KEY invalid pkeylist->first_key %d", hkey, path, pkeylist->first_key);
    1318            0 :             flag = false;
    1319            0 :             pkeylist_ok = false;
    1320              :          }
    1321              :          
    1322           24 :          if (pkeylist_ok) {
    1323              :             //printf("hkey %d, path \"%s\", pkey->data %d, pkeylist parent %d, num_keys %d, first_key %d: ", hkey, path, pkey->data, pkeylist->parent, pkeylist->num_keys, pkeylist->first_key);
    1324              :             
    1325           24 :             HNDLE subhkey = pkeylist->first_key;
    1326              : 
    1327           24 :             int count = 0;
    1328           93 :             while (subhkey != 0) {
    1329           69 :                KEY* subpkey = (KEY*)db_get_pkey(pheader, subhkey, NULL, "db_validate_key", msg);
    1330           69 :                if (!subpkey) {
    1331            0 :                   db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_KEY invalid subhkey %d", hkey, path, subhkey);
    1332            0 :                   pkeylist_ok = false;
    1333            0 :                   flag = false;
    1334            0 :                   break;
    1335              :                }
    1336              : 
    1337           69 :                std::string buf;
    1338           69 :                buf += path;
    1339           69 :                buf += "/";
    1340           69 :                buf += subpkey->name;
    1341              :                
    1342              :                //printf("pkey %p, next %d, name [%s], path %s\n", subpkey, subpkey->next_key, subpkey->name, buf.c_str());
    1343              :                
    1344           69 :                if (recurse) {
    1345           64 :                   flag &= db_validate_and_repair_key_wlocked(pheader, recurse + 1, buf.c_str(), pkey->data, subhkey, subpkey, msg);
    1346              :                }
    1347              : 
    1348           69 :                count++;
    1349           69 :                subhkey = subpkey->next_key;
    1350           69 :             }
    1351              : 
    1352           24 :             if (count != pkeylist->num_keys) {
    1353            0 :                db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", repaired TID_KEY mismatch of pkeylist->num_keys %d against key chain length %d", hkey, path, pkeylist->num_keys, count);
    1354            0 :                ((KEYLIST*)pkeylist)->num_keys = count;
    1355            0 :                flag = false;
    1356            0 :                pkeylist_ok = false;
    1357              :             }
    1358              :          }
    1359              :       }
    1360              :    }
    1361              : 
    1362           70 :    return flag;
    1363              : }
    1364              : 
    1365              : /*------------------------------------------------------------------*/
    1366              : 
    1367           13 : DATABASE_CLIENT* db_get_my_client_locked(DATABASE* pdb)
    1368              : {
    1369           13 :    assert(pdb);
    1370           13 :    assert(pdb->database_header);
    1371              : 
    1372           13 :    DATABASE_HEADER *pheader = pdb->database_header;
    1373              : 
    1374           13 :    int idx = pdb->client_index;
    1375              : 
    1376           13 :    if (idx < 0 || idx >= pheader->max_client_index || idx >= MAX_CLIENTS) {
    1377            0 :       cm_msg(MERROR, "db_get_my_client_locked", "My client index %d in ODB is invalid: out of range 0..%d. Maybe this client was removed by a timeout, see midas.log. Cannot continue, aborting...", idx, pheader->max_client_index-1);
    1378            0 :       ss_semaphore_release(pdb->semaphore);
    1379            0 :       abort();
    1380              :    }
    1381              : 
    1382           13 :    DATABASE_CLIENT* pclient = &pheader->client[idx]; // safe to dereference on "idx"
    1383              : 
    1384           13 :    if (pclient->name[0] == 0) {
    1385            0 :       cm_msg(MERROR, "db_get_my_client_locked", "My client index %d in ODB is invalid: client name is blank. Maybe this client was removed by a timeout, see midas.log. Cannot continue, aborting...", idx);
    1386            0 :       ss_semaphore_release(pdb->semaphore);
    1387            0 :       abort();
    1388              :    }
    1389              : 
    1390           13 :    int pid = getpid();
    1391              : 
    1392           13 :    if (pclient->pid != pid) {
    1393            0 :       cm_msg(MERROR, "db_get_my_client_locked", "My client index %d in ODB is invalid: pid mismatch, my pid is %d, but ODB has %d. Maybe this client was removed by a timeout, see midas.log. Cannot continue, aborting...", idx, pid, pclient->pid);
    1394            0 :       ss_semaphore_release(pdb->semaphore);
    1395            0 :       abort();
    1396              :    }
    1397              : 
    1398              :    //printf("db_get_my_client_locked: idx %d, name [%s], pid %d\n", idx, pclient->name, pid);
    1399              : 
    1400              :    //if (strcmp(pclient->name, "mdump") == 0) {
    1401              :    //   ss_semaphore_release(pdb->semaphore);
    1402              :    //   for (int i=0; i<15; i++) {
    1403              :    //      printf("sleep %d\n", i);
    1404              :    //      ::sleep(1);
    1405              :    //   }
    1406              :    //   abort();
    1407              :    //}
    1408              :    
    1409           13 :    return pclient;
    1410              : }
    1411              : 
    1412              : /*------------------------------------------------------------------*/
    1413            3 : static void db_validate_sizes()
    1414              : {
    1415              :    /* validate size of data structures (miscompiled, 32/64 bit mismatch, etc */
    1416              : 
    1417              :    if (0) {
    1418              : #define S(x) printf("assert(sizeof(%-20s) == %6d);\n", #x, (int)sizeof(x))
    1419              :       // basic data types
    1420              :       S(char *);
    1421              :       S(char);
    1422              :       S(int);
    1423              :       S(long int);
    1424              :       S(float);
    1425              :       S(double);
    1426              :       S(BOOL);
    1427              :       S(WORD);
    1428              :       S(DWORD);
    1429              :       S(INT);
    1430              :       S(POINTER_T);
    1431              :       S(midas_thread_t);
    1432              :       // data buffers
    1433              :       S(EVENT_REQUEST);
    1434              :       S(BUFFER_CLIENT);
    1435              :       S(BUFFER_HEADER);
    1436              :       // history files
    1437              :       S(HIST_RECORD);
    1438              :       S(DEF_RECORD);
    1439              :       S(INDEX_RECORD);
    1440              :       S(TAG);
    1441              :       // ODB shared memory structures
    1442              :       S(KEY);
    1443              :       S(KEYLIST);
    1444              :       S(OPEN_RECORD);
    1445              :       S(DATABASE_CLIENT);
    1446              :       S(DATABASE_HEADER);
    1447              :       // misc structures
    1448              :       S(EVENT_HEADER);
    1449              :       S(RUNINFO);
    1450              :       S(EQUIPMENT_INFO);
    1451              :       S(EQUIPMENT_STATS);
    1452              :       S(BANK_HEADER);
    1453              :       S(BANK);
    1454              :       S(BANK32);
    1455              :       S(ANA_OUTPUT_INFO);
    1456              :       S(PROGRAM_INFO);
    1457              :       S(ALARM_CLASS);
    1458              :       S(ALARM);
    1459              :       //S(CHN_SETTINGS);
    1460              :       //S(CHN_STATISTICS);
    1461              : #undef S
    1462              :    }
    1463              : 
    1464              : #if 0
    1465              :    EQUIPMENT_INFO eq;
    1466              :    printf("EQUIPMENT_INFO offset of event_id: %d\n", (int)((char*)&eq.event_id - (char*)&eq));
    1467              :    printf("EQUIPMENT_INFO offset of eq_type: %d\n", (int)((char*)&eq.eq_type - (char*)&eq));
    1468              :    printf("EQUIPMENT_INFO offset of event_limit: %d\n", (int)((char*)&eq.event_limit - (char*)&eq));
    1469              :    printf("EQUIPMENT_INFO offset of num_subevents: %d\n", (int)((char*)&eq.num_subevents - (char*)&eq));
    1470              :    printf("EQUIPMENT_INFO offset of status: %d\n", (int)((char*)&eq.status - (char*)&eq));
    1471              :    printf("EQUIPMENT_INFO offset of hidden: %d\n", (int)((char*)&eq.hidden - (char*)&eq));
    1472              : #endif
    1473              : 
    1474              :    assert(sizeof(UINT8)  == 1);
    1475              :    assert(sizeof(INT8)   == 1);
    1476              :    assert(sizeof(UINT16) == 2);
    1477              :    assert(sizeof(INT16)  == 2);
    1478              :    assert(sizeof(UINT32) == 4);
    1479              :    assert(sizeof(INT32)  == 4);
    1480              :    assert(sizeof(UINT64) == 8);
    1481              :    assert(sizeof(INT64)  == 8);
    1482              :    
    1483              : #ifdef OS_LINUX
    1484              :    assert(sizeof(EVENT_REQUEST) == 16); // ODB v3
    1485              :    assert(sizeof(BUFFER_CLIENT) == 256);
    1486              :    assert(sizeof(BUFFER_HEADER) == 16444);
    1487              :    assert(sizeof(HIST_RECORD) == 20);
    1488              :    assert(sizeof(DEF_RECORD) == 40);
    1489              :    assert(sizeof(INDEX_RECORD) == 12);
    1490              :    assert(sizeof(TAG) == 40);
    1491              :    assert(sizeof(KEY) == 68);
    1492              :    assert(sizeof(KEYLIST) == 12);
    1493              :    assert(sizeof(OPEN_RECORD) == 8);
    1494              :    assert(sizeof(DATABASE_CLIENT) == 2112);
    1495              :    assert(sizeof(DATABASE_HEADER) == 135232);
    1496              :    assert(sizeof(EVENT_HEADER) == 16);
    1497              :    //assert(sizeof(EQUIPMENT_INFO) == 696); has been moved to dynamic checking inside mhttpd.c
    1498              :    assert(sizeof(EQUIPMENT_STATS) == 24);
    1499              :    assert(sizeof(BANK_HEADER) == 8);
    1500              :    assert(sizeof(BANK) == 8);
    1501              :    assert(sizeof(BANK32) == 12);
    1502              :    assert(sizeof(ANA_OUTPUT_INFO) == 792);
    1503              :    assert(sizeof(PROGRAM_INFO) == 316);
    1504              :    assert(sizeof(ALARM) == 460);
    1505              :    assert(sizeof(ALARM_CLASS) == 352);
    1506              :    //assert(sizeof(CHN_SETTINGS) == 648); // ODB v3
    1507              :    //assert(sizeof(CHN_STATISTICS) == 56);        // ODB v3
    1508              : #endif
    1509            3 : }
    1510              : 
    1511              : typedef struct {
    1512              :    DATABASE_HEADER * pheader;
    1513              :    int max_keys;
    1514              :    int num_keys;
    1515              :    HNDLE* hkeys;
    1516              :    int* counts;
    1517              :    int* modes;
    1518              :    int num_modified;
    1519              : } UPDATE_OPEN_RECORDS;
    1520              : 
    1521           67 : static int db_update_open_record_wlocked(const DATABASE_HEADER* xpheader, const KEY* xpkey, int level, void* voidp, db_err_msg **msg)
    1522              : {
    1523           67 :    int found = 0;
    1524           67 :    int count = 0;
    1525              :    int status;
    1526              :    int k;
    1527           67 :    UPDATE_OPEN_RECORDS *uorp = (UPDATE_OPEN_RECORDS *)voidp;
    1528              : 
    1529           67 :    KEY* pkey = (KEY*)xpkey; // drop "const": we already have "allow_write"
    1530              : 
    1531           67 :    HNDLE hKey = db_pkey_to_hkey(uorp->pheader, pkey);
    1532              : 
    1533           67 :    for (k=0; k<uorp->num_keys; k++)
    1534            0 :       if (uorp->hkeys[k] == hKey) {
    1535            0 :          found = 1;
    1536            0 :          count = uorp->counts[k];
    1537            0 :          break;
    1538              :       }
    1539              : 
    1540           67 :    if (pkey->notify_count == 0 && !found)
    1541           67 :       return DB_SUCCESS; // no open record here
    1542              : 
    1543            0 :    std::string path = db_get_path_locked(uorp->pheader, hKey);
    1544            0 :    if (path == "") {
    1545            0 :       db_msg(msg, MINFO, "db_update_open_record", "Invalid hKey %d", hKey);
    1546            0 :       return DB_SUCCESS;
    1547              :    }
    1548              : 
    1549              :    //if (!db_validate_hkey(uorp->pheader, hKey)) {
    1550              :    //   cm_msg(MINFO, "db_update_open_record", "Invalid hKey %d", hKey);
    1551              :    //   return DB_SUCCESS;
    1552              :    //}
    1553              :    //
    1554              :    //KEY* pkey = (KEY *) ((char *) uorp->pheader + hKey);
    1555              : 
    1556              :    //printf("path [%s], type %d, notify_count %d\n", path, pkey->type, pkey->notify_count);
    1557              : 
    1558              :    // extra check: are we looking at the same key?
    1559              :    //assert(xkey->notify_count == pkey->notify_count);
    1560              : 
    1561              : #if 0
    1562              :    printf("%s, notify_count %d, found %d, our count %d\n", path, pkey->notify_count, found, count);
    1563              : #endif
    1564              :    
    1565            0 :    if (pkey->notify_count==0 && found) {
    1566            0 :       db_msg(msg, MINFO, "db_update_open_record", "Added missing open record flag to \"%s\"", path.c_str());
    1567            0 :       pkey->notify_count = count;
    1568            0 :       uorp->num_modified++;
    1569            0 :       return DB_SUCCESS;
    1570              :    }
    1571              : 
    1572            0 :    if (pkey->notify_count!=0 && !found) {
    1573            0 :       db_msg(msg, MINFO, "db_update_open_record", "Removed open record flag from \"%s\"", path.c_str());
    1574            0 :       pkey->notify_count = 0;
    1575            0 :       uorp->num_modified++;
    1576              : 
    1577              :       if (pkey->access_mode | MODE_EXCLUSIVE) {
    1578            0 :          status = db_set_mode_wlocked(uorp->pheader, pkey, (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 1, msg);
    1579            0 :          if (status != DB_SUCCESS) {
    1580            0 :             db_msg(msg, MERROR, "db_update_open_record", "Cannot remove exclusive access mode from \"%s\", db_set_mode() status %d", path.c_str(), status);
    1581            0 :             return DB_SUCCESS;
    1582              :          }
    1583            0 :          db_msg(msg, MINFO, "db_update_open_record", "Removed exclusive access mode from \"%s\"", path.c_str());
    1584              :       }
    1585            0 :       return DB_SUCCESS;
    1586              :    }
    1587              : 
    1588            0 :    if (pkey->notify_count != uorp->counts[k]) {
    1589            0 :       db_msg(msg, MINFO, "db_update_open_record", "Updated notify_count of \"%s\" from %d to %d", path.c_str(), pkey->notify_count, count);
    1590            0 :       pkey->notify_count = count;
    1591            0 :       uorp->num_modified++;
    1592            0 :       return DB_SUCCESS;
    1593              :    }
    1594              : 
    1595            0 :    return DB_SUCCESS;
    1596            0 : }
    1597              : 
    1598            3 : static int db_validate_open_records_wlocked(DATABASE_HEADER* pheader, db_err_msg** msg)
    1599              : {
    1600            3 :    int status = DB_SUCCESS;
    1601              :    UPDATE_OPEN_RECORDS uor;
    1602              :    int i, j, k;
    1603              : 
    1604            3 :    uor.max_keys = MAX_CLIENTS*MAX_OPEN_RECORDS;
    1605            3 :    uor.num_keys = 0;
    1606            3 :    uor.hkeys = (HNDLE*)calloc(uor.max_keys, sizeof(HNDLE));
    1607            3 :    uor.counts = (int*)calloc(uor.max_keys, sizeof(int));
    1608            3 :    uor.modes = (int*)calloc(uor.max_keys, sizeof(int));
    1609            3 :    uor.num_modified = 0;
    1610              : 
    1611            3 :    assert(uor.hkeys != NULL);
    1612            3 :    assert(uor.counts != NULL);
    1613            3 :    assert(uor.modes != NULL);
    1614              : 
    1615            3 :    uor.pheader = pheader;
    1616              : 
    1617            6 :    for (i = 0; i < pheader->max_client_index; i++) {
    1618            3 :       DATABASE_CLIENT* pclient = &pheader->client[i];
    1619            3 :       for (j = 0; j < pclient->max_index; j++)
    1620            0 :          if (pclient->open_record[j].handle) {
    1621            0 :             int found = 0;
    1622            0 :             for (k=0; k<uor.num_keys; k++) {
    1623            0 :                if (uor.hkeys[k] == pclient->open_record[j].handle) {
    1624            0 :                   uor.counts[k]++;
    1625            0 :                   found = 1;
    1626            0 :                   break;
    1627              :                }
    1628              :             }
    1629            0 :             if (!found) {
    1630            0 :                uor.hkeys[uor.num_keys] = pclient->open_record[j].handle;
    1631            0 :                uor.counts[uor.num_keys] = 1;
    1632            0 :                uor.modes[uor.num_keys] = pclient->open_record[j].access_mode;
    1633            0 :                uor.num_keys++;
    1634              :             }
    1635              :          }
    1636              :    }
    1637              : 
    1638              : #if 0
    1639              :    for (i=0; i<uor.num_keys; i++)
    1640              :       printf("index %d, handle %d, count %d, access mode %d\n", i, uor.hkeys[i], uor.counts[i], uor.modes[i]);
    1641              : #endif
    1642              : 
    1643            3 :    const KEY* proot = db_get_pkey(pheader, 0, &status, "db_validate_open_record", msg);
    1644              : 
    1645            3 :    if (proot) {
    1646            3 :       db_scan_tree_locked(pheader, proot, 0, db_update_open_record_wlocked, &uor, msg);
    1647              :    }
    1648              : 
    1649            3 :    if (uor.num_modified) {
    1650            0 :       db_msg(msg, MINFO, "db_validate_open_records", "Corrected %d ODB entries", uor.num_modified);
    1651              :    }
    1652              : 
    1653            3 :    free(uor.hkeys);
    1654            3 :    free(uor.counts);
    1655            3 :    free(uor.modes);
    1656              : 
    1657            3 :    return status;
    1658              : }
    1659              : 
    1660              : /*------------------------------------------------------------------*/
    1661            3 : static bool db_validate_and_repair_db_wlocked(DATABASE_HEADER * pheader, db_err_msg **msg)
    1662              : {
    1663            3 :    int total_size_key = 0;
    1664            3 :    int total_size_data = 0;
    1665              :    double ratio;
    1666              :    FREE_DESCRIP *pfree;
    1667            3 :    bool flag = true;
    1668              : 
    1669              :    /* validate size of data structures (miscompiled, 32/64 bit mismatch, etc */
    1670              : 
    1671            3 :    db_validate_sizes();
    1672              : 
    1673              :    /* validate the key free list */
    1674              : 
    1675            3 :    if (!db_validate_key_offset(pheader, pheader->first_free_key)) {
    1676            0 :       db_msg(msg, MERROR, "db_validate_db", "Error: database corruption, invalid pheader->first_free_key 0x%08X", pheader->first_free_key - (int)sizeof(DATABASE_HEADER));
    1677            0 :       return false;
    1678              :    }
    1679              : 
    1680            3 :    pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
    1681              : 
    1682            8 :    while ((POINTER_T) pfree != (POINTER_T) pheader) {
    1683              : 
    1684            5 :       if (pfree->next_free != 0 && !db_validate_key_offset(pheader, pfree->next_free)) {
    1685            0 :          db_msg(msg, MERROR, "db_validate_db", "Warning: database corruption, invalid key area next_free 0x%08X", pfree->next_free - (int)sizeof(DATABASE_HEADER));
    1686            0 :          flag = false;
    1687            0 :          break;
    1688              :       }
    1689              : 
    1690            5 :       total_size_key += pfree->size;
    1691            5 :       FREE_DESCRIP *nextpfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
    1692              : 
    1693            5 :       if (pfree->next_free != 0 && nextpfree == pfree) {
    1694            0 :          db_msg(msg, MERROR, "db_validate_db", "Warning: database corruption, key area next_free 0x%08X is same as current free %p, truncating the free list", pfree->next_free, pfree - (int)sizeof(DATABASE_HEADER));
    1695            0 :          pfree->next_free = 0;
    1696            0 :          flag = false;
    1697            0 :          break;
    1698              :          //return false;
    1699              :       }
    1700              : 
    1701            5 :       pfree = nextpfree;
    1702              :    }
    1703              : 
    1704            3 :    ratio = ((double) (pheader->key_size - total_size_key)) / ((double) pheader->key_size);
    1705            3 :    if (ratio > 0.9)
    1706            0 :       db_msg(msg, MERROR, "db_validate_db", "Warning: database key area is %.0f%% full", ratio * 100.0);
    1707              : 
    1708            3 :    if (total_size_key > pheader->key_size) {
    1709            0 :       db_msg(msg, MERROR, "db_validate_db", "Error: database corruption, total_key_size 0x%08X bigger than pheader->key_size 0x%08X", total_size_key, pheader->key_size);
    1710            0 :       flag = false;
    1711              :    }
    1712              : 
    1713              :    /* validate the data free list */
    1714              : 
    1715            3 :    if (!db_validate_data_offset(pheader, pheader->first_free_data)) {
    1716            0 :       db_msg(msg, MERROR, "db_validate_db", "Error: database corruption, invalid pheader->first_free_data 0x%08X", pheader->first_free_data - (int)sizeof(DATABASE_HEADER));
    1717            0 :       return false;
    1718              :    }
    1719              : 
    1720              :    //printf("pheader %p, first_free_data %d, key size %d, data size %d\n", pheader, pheader->first_free_data, pheader->key_size, pheader->data_size);
    1721              : 
    1722            3 :    pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
    1723              : 
    1724            8 :    while ((POINTER_T) pfree != (POINTER_T) pheader) {
    1725              : 
    1726            5 :       if (pfree->next_free != 0 && !db_validate_data_offset(pheader, pfree->next_free)) {
    1727            0 :          db_msg(msg, MERROR, "db_validate_db", "Warning: database corruption, invalid data area next_free 0x%08X", pfree->next_free - (int)sizeof(DATABASE_HEADER));
    1728            0 :          flag = false;
    1729            0 :          break;
    1730              :          //return false;
    1731              :       }
    1732              : 
    1733            5 :       total_size_data += pfree->size;
    1734            5 :       FREE_DESCRIP *nextpfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
    1735              : 
    1736            5 :       if (pfree->next_free != 0 && nextpfree == pfree) {
    1737            0 :          db_msg(msg, MERROR, "db_validate_db", "Warning: database corruption, data area next_free 0x%08X is same as current free %p, truncating the free list", pfree->next_free, pfree - (int)sizeof(DATABASE_HEADER));
    1738            0 :          pfree->next_free = 0;
    1739            0 :          flag = false;
    1740            0 :          break;
    1741              :          //return false;
    1742              :       }
    1743              : 
    1744            5 :       pfree = nextpfree;
    1745              :    }
    1746              : 
    1747            3 :    ratio = ((double) (pheader->data_size - total_size_data)) / ((double) pheader->data_size);
    1748            3 :    if (ratio > 0.9)
    1749            0 :       db_msg(msg, MERROR, "db_validate_db", "Warning: database data area is %.0f%% full", ratio * 100.0);
    1750              : 
    1751            3 :    if (total_size_data > pheader->data_size) {
    1752            0 :       db_msg(msg, MERROR, "db_validate_db", "Error: database corruption, total_size_data 0x%08X bigger than pheader->data_size 0x%08X", total_size_key, pheader->data_size);
    1753            0 :       flag = false;
    1754              :       //return false;
    1755              :    }
    1756              : 
    1757              :    /* validate the tree of keys, starting from the root key */
    1758              : 
    1759            3 :    if (!db_validate_key_offset(pheader, pheader->root_key)) {
    1760            0 :       db_msg(msg, MERROR, "db_validate_db", "Error: database corruption, pheader->root_key 0x%08X is invalid", pheader->root_key - (int)sizeof(DATABASE_HEADER));
    1761            0 :       return false;
    1762              :    }
    1763              : 
    1764            3 :    flag &= db_validate_and_repair_key_wlocked(pheader, 1, "", 0, pheader->root_key, (KEY *) ((char *) pheader + pheader->root_key), msg);
    1765              : 
    1766            3 :    if (!flag) {
    1767            0 :       db_msg(msg, MERROR, "db_validate_db", "Error: ODB corruption detected, see previous messages");
    1768              :    }
    1769              : 
    1770            3 :    return flag;
    1771              : }
    1772              : 
    1773              : #endif // LOCAL_ROUTINES
    1774              : 
    1775              : /**dox***************************************************************/
    1776              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    1777              : 
    1778              : /********************************************************************/
    1779              : /**
    1780              : Open an online database
    1781              : @param database_name     Database name.
    1782              : @param database_size     Initial size of database if not existing
    1783              : @param client_name       Name of this application
    1784              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    1785              : @return DB_SUCCESS, DB_CREATED, DB_INVALID_NAME, DB_NO_MEMORY,
    1786              :         DB_MEMSIZE_MISMATCH, DB_NO_SEMAPHORE, DB_INVALID_PARAM,
    1787              :         RPC_NET_ERROR
    1788              : */
    1789            3 : INT db_open_database(const char *xdatabase_name, INT database_size, HNDLE * hDB, const char *client_name)
    1790              : {
    1791            3 :    if (rpc_is_remote())
    1792            0 :       return rpc_call(RPC_DB_OPEN_DATABASE, xdatabase_name, database_size, hDB, client_name);
    1793              : 
    1794              : #ifdef LOCAL_ROUTINES
    1795              :    {
    1796              :    INT i, status;
    1797              :    HNDLE handle;
    1798              :    DATABASE_CLIENT *pclient;
    1799              :    BOOL shm_created;
    1800              :    DATABASE_HEADER *pheader;
    1801              :    KEY *pkey;
    1802              :    KEYLIST *pkeylist;
    1803              :    FREE_DESCRIP *pfree;
    1804              :    BOOL call_watchdog;
    1805              :    DWORD timeout;
    1806              :    char database_name[NAME_LENGTH];
    1807              : 
    1808              :    /* restrict name length */
    1809            3 :    mstrlcpy(database_name, xdatabase_name, NAME_LENGTH);
    1810              : 
    1811            3 :    int odb_size_limit = 100*1000*1000;
    1812            3 :    if (database_size < 0 || database_size > odb_size_limit) {
    1813            0 :       cm_msg(MERROR, "db_open_database", "invalid database size: %d bytes. ODB size limit is %d bytes", database_size, odb_size_limit);
    1814            3 :       return DB_INVALID_PARAM;
    1815              :    }
    1816              : 
    1817            3 :    if (strlen(client_name) >= NAME_LENGTH) {
    1818            0 :       cm_msg(MERROR, "db_open_database", "client name \'%s\' is longer than %d characters", client_name, NAME_LENGTH-1);
    1819            0 :       return DB_INVALID_PARAM;
    1820              :    }
    1821              : 
    1822            3 :    if (strchr(client_name, '/') != NULL) {
    1823            0 :       cm_msg(MERROR, "db_open_database", "client name \'%s\' should not contain the slash \'/\' character", client_name);
    1824            0 :       return DB_INVALID_PARAM;
    1825              :    }
    1826              : 
    1827              :    /* allocate new space for the new database descriptor */
    1828            3 :    if (_database_entries == 0) {
    1829            3 :       _database = (DATABASE *) malloc(sizeof(DATABASE));
    1830            3 :       assert(_database != NULL);
    1831            3 :       memset(_database, 0, sizeof(DATABASE));
    1832            3 :       if (_database == NULL) {
    1833            0 :          *hDB = 0;
    1834            0 :          return DB_NO_MEMORY;
    1835              :       }
    1836              : 
    1837            3 :       _database_entries = 1;
    1838            3 :       i = 0;
    1839              :    } else {
    1840              :       /* check if database already open */
    1841            0 :       for (i = 0; i < _database_entries; i++)
    1842            0 :          if (_database[i].attached && equal_ustring(_database[i].name, database_name)) {
    1843              :             /* check if database belongs to this thread */
    1844            0 :             *hDB = i + 1;
    1845            0 :             return DB_SUCCESS;
    1846              :          }
    1847              : 
    1848              :       /* check for a deleted entry */
    1849            0 :       for (i = 0; i < _database_entries; i++)
    1850            0 :          if (!_database[i].attached)
    1851            0 :             break;
    1852              : 
    1853              :       /* if not found, create new one */
    1854            0 :       if (i == _database_entries) {
    1855            0 :          _database = (DATABASE *) realloc(_database, sizeof(DATABASE) * (_database_entries + 1));
    1856            0 :          assert(_database != NULL);
    1857              : 
    1858            0 :          memset(&_database[_database_entries], 0, sizeof(DATABASE));
    1859              : 
    1860            0 :          _database_entries++;
    1861            0 :          if (_database == NULL) {
    1862            0 :             _database_entries--;
    1863            0 :             *hDB = 0;
    1864            0 :             return DB_NO_MEMORY;
    1865              :          }
    1866              :       }
    1867              :    }
    1868              : 
    1869            3 :    handle = (HNDLE) i;
    1870              : 
    1871              :    /* open shared memory region */
    1872            3 :    void* shm_adr = NULL;
    1873            3 :    size_t shm_size = 0;
    1874              :    HNDLE shm_handle;
    1875              :    
    1876            3 :    status = ss_shm_open(database_name, sizeof(DATABASE_HEADER) + 2 * ALIGN8(database_size / 2), &shm_adr, &shm_size, &shm_handle, TRUE);
    1877              : 
    1878            3 :    if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
    1879            0 :       *hDB = 0;
    1880            0 :       return DB_INVALID_NAME;
    1881              :    }
    1882              : 
    1883            3 :    _database[handle].shm_adr  = shm_adr;
    1884            3 :    _database[handle].shm_size = shm_size;
    1885            3 :    _database[handle].shm_handle = shm_handle;
    1886              : 
    1887            3 :    _database[handle].database_header = (DATABASE_HEADER *) shm_adr;
    1888              : 
    1889              :    /* shortcut to header */
    1890            3 :    pheader = _database[handle].database_header;
    1891              : 
    1892              :    /* save name */
    1893            3 :    strcpy(_database[handle].name, database_name);
    1894              : 
    1895            3 :    shm_created = (status == SS_CREATED);
    1896              : 
    1897              :    /* clear memeory for debugging */
    1898              :    /* memset(pheader, 0, sizeof(DATABASE_HEADER) + 2*ALIGN8(database_size/2)); */
    1899              : 
    1900            3 :    if (shm_created && pheader->name[0] == 0) {
    1901              :       /* setup header info if database was created */
    1902            1 :       memset(pheader, 0, sizeof(DATABASE_HEADER) + 2 * ALIGN8(database_size / 2));
    1903              : 
    1904            1 :       strcpy(pheader->name, database_name);
    1905            1 :       pheader->version = DATABASE_VERSION;
    1906            1 :       pheader->key_size = ALIGN8(database_size / 2);
    1907            1 :       pheader->data_size = ALIGN8(database_size / 2);
    1908            1 :       pheader->root_key = sizeof(DATABASE_HEADER);
    1909            1 :       pheader->first_free_key = sizeof(DATABASE_HEADER);
    1910            1 :       pheader->first_free_data = sizeof(DATABASE_HEADER) + pheader->key_size;
    1911              : 
    1912              :       /* set up free list */
    1913            1 :       pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
    1914            1 :       pfree->size = pheader->key_size;
    1915            1 :       pfree->next_free = 0;
    1916              : 
    1917            1 :       pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
    1918            1 :       pfree->size = pheader->data_size;
    1919            1 :       pfree->next_free = 0;
    1920              : 
    1921              :       /* create root key */
    1922            1 :       pkey = (KEY *) malloc_key(pheader, sizeof(KEY), "db_open_database_A");
    1923            1 :       assert(pkey);
    1924              : 
    1925              :       /* set key properties */
    1926            1 :       pkey->type = TID_KEY;
    1927            1 :       pkey->num_values = 1;
    1928            1 :       pkey->access_mode = MODE_READ | MODE_WRITE | MODE_DELETE;
    1929            1 :       strcpy(pkey->name, "root");
    1930            1 :       pkey->parent_keylist = 0;
    1931              : 
    1932              :       /* create keylist */
    1933            1 :       pkeylist = (KEYLIST *) malloc_key(pheader, sizeof(KEYLIST), "db_open_database_B");
    1934            1 :       assert(pkeylist);
    1935              : 
    1936              :       /* store keylist in data field */
    1937            1 :       pkey->data = (POINTER_T) pkeylist - (POINTER_T) pheader;
    1938            1 :       pkey->item_size = sizeof(KEYLIST);
    1939            1 :       pkey->total_size = sizeof(KEYLIST);
    1940              : 
    1941            1 :       pkeylist->parent = (POINTER_T) pkey - (POINTER_T) pheader;
    1942            1 :       pkeylist->num_keys = 0;
    1943            1 :       pkeylist->first_key = 0;
    1944              :    }
    1945              : 
    1946              :    /* check database version */
    1947            3 :    if (pheader->version != DATABASE_VERSION) {
    1948            0 :       cm_msg(MERROR, "db_open_database",
    1949              :              "Different database format: Shared memory is %d, program is %d", pheader->version, DATABASE_VERSION);
    1950            0 :       return DB_VERSION_MISMATCH;
    1951              :    }
    1952              : 
    1953              :    /* check database size vs shared memory size */
    1954            3 :    if (_database[handle].shm_size < (int)sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size) {
    1955            0 :       cm_msg(MERROR, "db_open_database", "Invalid database, shared memory size %d is smaller than database size %d (header: %d, key area: %d, data area: %d). Delete this shared memory (odbedit -R), create a new odb (odbinit) and reload it from the last odb save file.", _database[handle].shm_size, (int)sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size, (int)sizeof(DATABASE_HEADER), pheader->key_size, pheader->data_size);
    1956            0 :       return DB_VERSION_MISMATCH;
    1957              :    }
    1958              : 
    1959              :    /* check root key */
    1960            3 :    if (!db_validate_key_offset(pheader, pheader->root_key)) {
    1961            0 :       cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key offset %d is invalid", pheader->root_key);
    1962            0 :       return DB_VERSION_MISMATCH;
    1963              :    } else {
    1964            3 :       pkey = (KEY*)((char*)pheader + pheader->root_key);
    1965              : 
    1966            3 :       if (pkey->type != TID_KEY) {
    1967            0 :          cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key type %d is not TID_KEY", pkey->type);
    1968            0 :          return DB_VERSION_MISMATCH;
    1969              :       }
    1970              : 
    1971            3 :       if (strcmp(pkey->name, "root") != 0) {
    1972            0 :          cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key name \"%s\" is not \"root\"", pkey->name);
    1973            0 :          return DB_VERSION_MISMATCH;
    1974              :       }
    1975              : 
    1976              :       // what if we are connecting to an incompatible ODB?
    1977              :       // A call to db_validate_and_repair_key() maybe will
    1978              :       // corrupt it here. But we have no choice,
    1979              :       // if we skip it here and continue,
    1980              :       // db_validate_and_repair_db() will call it later anyway... K.O.
    1981              : 
    1982            3 :       db_err_msg* msg = NULL;
    1983            3 :       bool ok = db_validate_and_repair_key_wlocked(pheader, 0, "", 0, pheader->root_key, pkey, &msg);
    1984            3 :       if (msg)
    1985            0 :          db_flush_msg(&msg);
    1986            3 :       if (!ok) {
    1987            0 :          cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key is invalid");
    1988            0 :          return DB_VERSION_MISMATCH;
    1989              :       }
    1990              :    }
    1991              : 
    1992              :    /* set default mutex and semaphore timeout */
    1993            3 :    _database[handle].timeout = 10000;
    1994              : 
    1995              :    /* create mutexes for the database */
    1996            3 :    status = ss_mutex_create(&_database[handle].mutex, TRUE);
    1997            3 :    if (status != SS_SUCCESS && status != SS_CREATED) {
    1998            0 :       *hDB = 0;
    1999            0 :       return DB_NO_SEMAPHORE;
    2000              :    }
    2001              : 
    2002              :    /* create semaphore for the database */
    2003            3 :    status = ss_semaphore_create(database_name, &(_database[handle].semaphore));
    2004            3 :    if (status != SS_SUCCESS && status != SS_CREATED) {
    2005            0 :       *hDB = 0;
    2006            0 :       return DB_NO_SEMAPHORE;
    2007              :    }
    2008            3 :    _database[handle].lock_cnt = 0;
    2009              : 
    2010            3 :    _database[handle].protect = FALSE;
    2011            3 :    _database[handle].protect_read = FALSE;
    2012            3 :    _database[handle].protect_write = FALSE;
    2013              : 
    2014              :    /* first lock database */
    2015            3 :    status = db_lock_database(handle + 1);
    2016            3 :    if (status != DB_SUCCESS)
    2017            0 :       return status;
    2018              : 
    2019              :    /* we have the database locked, without write protection */
    2020              : 
    2021              :    /*
    2022              :     Now we have a DATABASE_HEADER, so let's setup a CLIENT
    2023              :     structure in that database. The information there can also
    2024              :     be seen by other processes.
    2025              :     */
    2026              : 
    2027              :    /*
    2028              :     update the client count
    2029              :     */
    2030            3 :    int num_clients = 0;
    2031            3 :    int max_client_index = 0;
    2032          195 :    for (i = 0; i < MAX_CLIENTS; i++) {
    2033          192 :       if (pheader->client[i].pid == 0)
    2034          192 :          continue;
    2035            0 :       num_clients++;
    2036            0 :       max_client_index = i + 1;
    2037              :    }
    2038            3 :    pheader->num_clients = num_clients;
    2039            3 :    pheader->max_client_index = max_client_index;
    2040              : 
    2041              :    /*fprintf(stderr,"num_clients: %d, max_client: %d\n",pheader->num_clients,pheader->max_client_index); */
    2042              : 
    2043              :    /* remove dead clients */
    2044          195 :    for (i = 0; i < MAX_CLIENTS; i++) {
    2045          192 :       if (pheader->client[i].pid == 0)
    2046          192 :          continue;
    2047            0 :       if (!ss_pid_exists(pheader->client[i].pid)) {
    2048              :          char client_name_tmp[NAME_LENGTH];
    2049              :          int client_pid;
    2050              : 
    2051            0 :          mstrlcpy(client_name_tmp, pheader->client[i].name, sizeof(client_name_tmp));
    2052            0 :          client_pid = pheader->client[i].pid;
    2053              : 
    2054              :          // removed: /* decrement notify_count for open records and clear exclusive mode */
    2055              :          // open records are corrected later, by db_validate_open_records()
    2056              : 
    2057              :          /* clear entry from client structure in database header */
    2058            0 :          memset(&(pheader->client[i]), 0, sizeof(DATABASE_CLIENT));
    2059              : 
    2060            0 :          cm_msg(MERROR, "db_open_database", "Removed ODB client \'%s\', index %d because process pid %d does not exists", client_name_tmp, i, client_pid);
    2061              :       }
    2062              :    }
    2063              : 
    2064              :    /*
    2065              :     Look for empty client slot
    2066              :     */
    2067            3 :    for (i = 0; i < MAX_CLIENTS; i++)
    2068            3 :       if (pheader->client[i].pid == 0)
    2069            3 :          break;
    2070              : 
    2071            3 :    if (i == MAX_CLIENTS) {
    2072            0 :       db_unlock_database(handle + 1);
    2073            0 :       *hDB = 0;
    2074            0 :       cm_msg(MERROR, "db_open_database", "maximum number of clients exceeded");
    2075            0 :       return DB_NO_SLOT;
    2076              :    }
    2077              : 
    2078              :    /* store slot index in _database structure */
    2079            3 :    _database[handle].client_index = i;
    2080              : 
    2081              :    /*
    2082              :     Save the index of the last client of that database so that later only
    2083              :     the clients 0..max_client_index-1 have to be searched through.
    2084              :     */
    2085            3 :    pheader->num_clients++;
    2086            3 :    if (i + 1 > pheader->max_client_index)
    2087            3 :       pheader->max_client_index = i + 1;
    2088              : 
    2089              :    /* setup database header and client structure */
    2090            3 :    pclient = &pheader->client[i];
    2091              : 
    2092            3 :    memset(pclient, 0, sizeof(DATABASE_CLIENT));
    2093            3 :    mstrlcpy(pclient->name, client_name, sizeof(pclient->name));
    2094            3 :    pclient->pid = ss_getpid();
    2095            3 :    pclient->num_open_records = 0;
    2096              : 
    2097            3 :    ss_suspend_get_odb_port(&pclient->port);
    2098              : 
    2099            3 :    pclient->last_activity = ss_millitime();
    2100              : 
    2101            3 :    cm_get_watchdog_params(&call_watchdog, &timeout);
    2102            3 :    pclient->watchdog_timeout = timeout;
    2103              : 
    2104              :    /* check ODB for corruption */
    2105            3 :    db_err_msg* msg = NULL;
    2106            3 :    bool ok = db_validate_and_repair_db_wlocked(pheader, &msg);
    2107            3 :    if (msg)
    2108            0 :       db_flush_msg(&msg);
    2109            3 :    if (!ok) {
    2110              :       /* do not treat corrupted odb as a fatal error- allow the user
    2111              :        to preceed at own risk- the database is already corrupted,
    2112              :        so no further harm can possibly be made. */
    2113              :       /*
    2114              :        db_unlock_database(handle + 1);
    2115              :        *hDB = 0;
    2116              :        return DB_CORRUPTED;
    2117              :        */
    2118              :    }
    2119              : 
    2120              :    /* setup _database entry */
    2121            3 :    _database[handle].database_data = _database[handle].database_header + 1;
    2122            3 :    _database[handle].attached = TRUE;
    2123            3 :    _database[handle].protect = FALSE;
    2124            3 :    _database[handle].protect_read = FALSE;
    2125            3 :    _database[handle].protect_write = FALSE;
    2126              : 
    2127            3 :    *hDB = (handle + 1);
    2128              : 
    2129            3 :    status = db_validate_open_records_wlocked(pheader, &msg);
    2130            3 :    if (status != DB_SUCCESS) {
    2131            0 :       db_unlock_database(handle + 1);
    2132            0 :       if (msg)
    2133            0 :          db_flush_msg(&msg);
    2134            0 :       cm_msg(MERROR, "db_open_database", "Error: db_validate_open_records() status %d", status);
    2135            0 :       return status;
    2136              :    }
    2137              : 
    2138            3 :    db_unlock_database(handle + 1);
    2139              : 
    2140            3 :    if (msg)
    2141            0 :       db_flush_msg(&msg);
    2142              : 
    2143            3 :    if (shm_created)
    2144            3 :       return DB_CREATED;
    2145              :    }
    2146              : #endif                          /* LOCAL_ROUTINES */
    2147              : 
    2148            0 :    return DB_SUCCESS;
    2149              : }
    2150              : 
    2151              : /********************************************************************/
    2152              : /**
    2153              : Close a database
    2154              : @param   hDB          ODB handle obtained via cm_get_experiment_database().
    2155              : @return DB_SUCCESS, DB_INVALID_HANDLE, RPC_NET_ERROR
    2156              : */
    2157            3 : INT db_close_database(HNDLE hDB)
    2158              : {
    2159            3 :    if (rpc_is_remote())
    2160            0 :       return rpc_call(RPC_DB_CLOSE_DATABASE, hDB);
    2161              : 
    2162              : #ifdef LOCAL_ROUTINES
    2163              :    else {
    2164              :       INT destroy_flag, i, j;
    2165              :       char xname[256];
    2166              : 
    2167            3 :       if (hDB > _database_entries || hDB <= 0) {
    2168            0 :          cm_msg(MERROR, "db_close_database", "invalid database handle %d", hDB);
    2169            0 :          return DB_INVALID_HANDLE;
    2170              :       }
    2171              : 
    2172              :       /* flush database to disk */
    2173            3 :       db_flush_database(hDB);
    2174              : 
    2175              :       /* first lock database */
    2176            3 :       db_lock_database(hDB);
    2177              : 
    2178            3 :       DATABASE* pdb = &_database[hDB - 1];
    2179              : 
    2180            3 :       if (!pdb->attached) {
    2181            0 :          db_unlock_database(hDB);
    2182            0 :          cm_msg(MERROR, "db_close_database", "database not attached");
    2183            0 :          return DB_INVALID_HANDLE;
    2184              :       }
    2185              : 
    2186            3 :       DATABASE_HEADER *pheader = pdb->database_header;
    2187            3 :       DATABASE_CLIENT *pclient = db_get_my_client_locked(pdb);
    2188              : 
    2189            3 :       db_allow_write_locked(&_database[hDB-1], "db_close_database");
    2190              : 
    2191              :       /* close all open records */
    2192            3 :       for (i = 0; i < pclient->max_index; i++)
    2193            0 :          if (pclient->open_record[i].handle)
    2194            0 :             db_remove_open_record_wlocked(pdb, pheader, pclient->open_record[i].handle);
    2195              : 
    2196              :       /* mark entry in _database as empty */
    2197            3 :       pdb->attached = FALSE;
    2198              : 
    2199              :       /* clear entry from client structure in database header */
    2200            3 :       memset(pclient, 0, sizeof(DATABASE_CLIENT));
    2201              : 
    2202              :       /* calculate new max_client_index entry */
    2203          195 :       for (i = MAX_CLIENTS - 1; i >= 0; i--)
    2204          192 :          if (pheader->client[i].pid != 0)
    2205            0 :             break;
    2206            3 :       pheader->max_client_index = i + 1;
    2207              : 
    2208              :       /* count new number of clients */
    2209          195 :       for (i = MAX_CLIENTS - 1, j = 0; i >= 0; i--)
    2210          192 :          if (pheader->client[i].pid != 0)
    2211            0 :             j++;
    2212            3 :       pheader->num_clients = j;
    2213              : 
    2214            3 :       destroy_flag = (pheader->num_clients == 0);
    2215              : 
    2216              :       /* flush shared memory to disk before it gets deleted */
    2217            3 :       if (destroy_flag)
    2218            3 :          ss_shm_flush(pheader->name, pdb->shm_adr, pdb->shm_size, pdb->shm_handle, true);
    2219              : 
    2220            3 :       mstrlcpy(xname, pheader->name, sizeof(xname));
    2221              : 
    2222              :       /* unmap shared memory, delete it if we are the last */
    2223            3 :       ss_shm_close(xname, pdb->shm_adr, pdb->shm_size, pdb->shm_handle, destroy_flag);
    2224              : 
    2225            3 :       pheader = NULL; // after ss_shm_close(), pheader points nowhere
    2226            3 :       pdb->database_header = NULL; // ditto
    2227              : 
    2228              :       /* unlock database */
    2229            3 :       db_unlock_database(hDB);
    2230              : 
    2231              :       /* delete semaphore */
    2232            3 :       ss_semaphore_delete(pdb->semaphore, destroy_flag);
    2233              : 
    2234              :       /* delete mutex */
    2235            3 :       ss_mutex_delete(pdb->mutex);
    2236              : 
    2237              :       /* update _database_entries */
    2238            3 :       if (hDB == _database_entries)
    2239            3 :          _database_entries--;
    2240              : 
    2241            3 :       if (_database_entries > 0)
    2242            0 :          _database = (DATABASE *) realloc(_database, sizeof(DATABASE) * (_database_entries));
    2243              :       else {
    2244            3 :          free(_database);
    2245            3 :          _database = NULL;
    2246              :       }
    2247              : 
    2248              :       /* if we are the last one, also delete other semaphores */
    2249            3 :       if (destroy_flag) {
    2250              :          //extern INT _semaphore_elog, _semaphore_alarm, _semaphore_history, _semaphore_msg;
    2251              :          extern INT _semaphore_elog, _semaphore_alarm, _semaphore_history;
    2252              : 
    2253            3 :          if (_semaphore_elog)
    2254            3 :             ss_semaphore_delete(_semaphore_elog, TRUE);
    2255            3 :          if (_semaphore_alarm)
    2256            3 :             ss_semaphore_delete(_semaphore_alarm, TRUE);
    2257            3 :          if (_semaphore_history)
    2258            3 :             ss_semaphore_delete(_semaphore_history, TRUE);
    2259              :          //if (_semaphore_msg)
    2260              :          //   ss_semaphore_delete(_semaphore_msg, TRUE);
    2261              :       }
    2262              : 
    2263              :    }
    2264              : #endif                          /* LOCAL_ROUTINES */
    2265              : 
    2266            3 :    return DB_SUCCESS;
    2267              : }
    2268              : 
    2269              : /**dox***************************************************************/
    2270              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    2271              : 
    2272              : /*------------------------------------------------------------------*/
    2273            3 : INT db_flush_database(HNDLE hDB)
    2274              : /********************************************************************\
    2275              : 
    2276              :   Routine: db_flush_database
    2277              : 
    2278              :   Purpose: Flushes the shared memory of a database to its disk file.
    2279              : 
    2280              :   Input:
    2281              :     HNDLE  hDB              Handle to the database, which is used as
    2282              :                             an index to the _database array.
    2283              : 
    2284              :   Output:
    2285              :     none
    2286              : 
    2287              :   Function value:
    2288              :     DB_SUCCESS              Successful completion
    2289              :     DB_INVALID_HANDLE       Database handle is invalid
    2290              :     RPC_NET_ERROR           Network error
    2291              : 
    2292              : \********************************************************************/
    2293              : {
    2294            3 :    if (rpc_is_remote())
    2295            0 :       return rpc_call(RPC_DB_FLUSH_DATABASE, hDB);
    2296              : 
    2297              : #ifdef LOCAL_ROUTINES
    2298              :    else {
    2299              :       int status, size;
    2300            3 :       uint32_t flush_period = 60;
    2301            3 :       uint32_t last_flush = 0;
    2302              : 
    2303            3 :       if (hDB > _database_entries || hDB <= 0) {
    2304            0 :          cm_msg(MERROR, "db_flush_database", "invalid database handle");
    2305            0 :          return DB_INVALID_HANDLE;
    2306              :       }
    2307              : 
    2308              :       /* create keys if not present */
    2309            3 :       size = sizeof(flush_period);
    2310            3 :       db_get_value(hDB, 0, "/System/Flush/Flush period", &flush_period, &size, TID_UINT32, true);
    2311            3 :       size = sizeof(last_flush);
    2312            3 :       db_get_value(hDB, 0, "/System/Flush/Last flush", &last_flush, &size, TID_UINT32, true);
    2313              : 
    2314              :       HNDLE hkey;
    2315            3 :       status = db_find_key(hDB, 0, "/System/Flush/Last flush", &hkey);
    2316            3 :       if (status != DB_SUCCESS) {
    2317            0 :          cm_msg(MERROR, "db_flush_database", "Cannot obtain key /System/Flush/Last flush");
    2318            0 :          return DB_INVALID_HANDLE;
    2319              :       }
    2320              : 
    2321            3 :       db_lock_database(hDB);
    2322            3 :       DATABASE *pdb = &_database[hDB - 1];
    2323            3 :       DATABASE_HEADER *pheader = pdb->database_header;
    2324              : 
    2325            3 :       if (!_database[hDB - 1].attached) {
    2326            0 :          db_unlock_database(hDB);
    2327            0 :          cm_msg(MERROR, "db_flush_database", "invalid database handle");
    2328            0 :          return DB_INVALID_HANDLE;
    2329              :       }
    2330              : 
    2331            3 :       db_err_msg *msg = nullptr;
    2332              : 
    2333            3 :       const KEY *pkey = db_get_pkey(pheader, hkey, &status, "db_flush_database", &msg);
    2334              : 
    2335            3 :       if (!pkey) {
    2336            0 :          db_unlock_database(hDB);
    2337            0 :          if (msg)
    2338            0 :             db_flush_msg(&msg);
    2339            0 :          return CM_NO_CLIENT;
    2340              :       }
    2341              : 
    2342            3 :       size = sizeof(last_flush);
    2343            3 :       status = db_get_data_locked(pheader, pkey, 0, &last_flush, &size, TID_UINT32, &msg);
    2344              : 
    2345              :       /* only flush if period has expired */
    2346            3 :       if (ss_time() > last_flush + flush_period) {
    2347            1 :          db_allow_write_locked(pdb, "db_flush_database");
    2348              : 
    2349              :          /* update last flush time in ODB */
    2350            1 :          last_flush = ss_time();
    2351            1 :          db_set_value_wlocked(pheader, hDB, 0, "/System/Flush/Last flush", &last_flush, sizeof(last_flush), 1, TID_UINT32, &msg);
    2352              : 
    2353              :          /* flush shared memory to disk */
    2354            1 :          ss_shm_flush(pheader->name, _database[hDB - 1].shm_adr, _database[hDB - 1].shm_size, _database[hDB - 1].shm_handle, false);
    2355              :       }
    2356              : 
    2357            3 :       db_unlock_database(hDB);
    2358              :    }
    2359              : #endif                          /* LOCAL_ROUTINES */
    2360              : 
    2361            3 :    return DB_SUCCESS;
    2362              : }
    2363              : 
    2364              : /*------------------------------------------------------------------*/
    2365            2 : INT db_close_all_databases(void)
    2366              : /********************************************************************\
    2367              : 
    2368              :   Routine: db_close_all_databases
    2369              : 
    2370              :   Purpose: Close all open databases and open records
    2371              : 
    2372              :   Input:
    2373              :     none
    2374              : 
    2375              :   Output:
    2376              :     none
    2377              : 
    2378              :   Function value:
    2379              :     DB_SUCCESS              Successful completion
    2380              : 
    2381              : \********************************************************************/
    2382              : {
    2383              :    INT status;
    2384              : 
    2385            2 :    if (rpc_is_remote()) {
    2386            0 :       status = rpc_call(RPC_DB_CLOSE_ALL_DATABASES);
    2387            0 :       if (status != DB_SUCCESS)
    2388            0 :          return status;
    2389              :    }
    2390              : 
    2391            2 :    db_close_all_records();
    2392            2 :    db_unwatch_all();
    2393              : 
    2394              : #ifdef LOCAL_ROUTINES
    2395              :    {
    2396              :       INT i;
    2397              : 
    2398            4 :       for (i = _database_entries; i > 0; i--)
    2399            2 :          db_close_database(i);
    2400              :    }
    2401              : #endif                          /* LOCAL_ROUTINES */
    2402              :    
    2403            2 :    return DB_SUCCESS;
    2404              : }
    2405              : 
    2406              : /*------------------------------------------------------------------*/
    2407            2 : INT db_set_client_name(HNDLE hDB, const char *client_name)
    2408              : /********************************************************************\
    2409              : 
    2410              :   Routine: db_set_client_name
    2411              : 
    2412              :   Purpose: Set client name for a database. Used by cm_connect_experiment
    2413              :            if a client name is duplicate and changed.
    2414              : 
    2415              :   Input:
    2416              :     INT  hDB                Handle to database
    2417              :     char *client_name       Name of this application
    2418              : 
    2419              :   Output:
    2420              : 
    2421              :   Function value:
    2422              :     DB_SUCCESS              Successful completion
    2423              :     RPC_NET_ERROR           Network error
    2424              : 
    2425              : \********************************************************************/
    2426              : {
    2427            2 :    if (rpc_is_remote())
    2428            0 :       return rpc_call(RPC_DB_SET_CLIENT_NAME, hDB, client_name);
    2429              : 
    2430              : #ifdef LOCAL_ROUTINES
    2431              :    {
    2432            2 :       if (hDB > _database_entries || hDB <= 0) {
    2433            0 :          cm_msg(MERROR, "db_set_client_name", "invalid database handle %d", hDB);
    2434            0 :          return DB_INVALID_HANDLE;
    2435              :       }
    2436              : 
    2437            2 :       db_lock_database(hDB);
    2438              :       
    2439            2 :       DATABASE *pdb = &_database[hDB - 1];
    2440            2 :       DATABASE_CLIENT *pclient = db_get_my_client_locked(pdb);
    2441            2 :       mstrlcpy(pclient->name, client_name, sizeof(pclient->name));
    2442              : 
    2443            2 :       db_unlock_database(hDB);
    2444              :    }
    2445              : #endif                          /* LOCAL_ROUTINES */
    2446              : 
    2447            2 :    return DB_SUCCESS;
    2448              : }
    2449              : 
    2450              : /**dox***************************************************************/
    2451              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    2452              : 
    2453              : /********************************************************************/
    2454              : /**
    2455              : Lock a database for exclusive access via system semaphore calls.
    2456              : @param hDB   Handle to the database to lock
    2457              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TIMEOUT
    2458              : */
    2459              : 
    2460         1103 : INT db_lock_database(HNDLE hDB)
    2461              : {
    2462              : #ifdef LOCAL_ROUTINES
    2463              :    int status;
    2464              : 
    2465         1103 :    if (hDB > _database_entries || hDB <= 0) {
    2466            0 :       cm_msg(MERROR, "db_lock_database", "invalid database handle %d, aborting...", hDB);
    2467            0 :       abort();
    2468              :       return DB_INVALID_HANDLE;
    2469              :    }
    2470              : 
    2471              :    /* obtain access mutex in multi-thread applications */
    2472         1103 :    status = ss_mutex_wait_for(_database[hDB - 1].mutex, _database[hDB - 1].timeout);
    2473         1103 :    if (status != SS_SUCCESS) {
    2474            0 :       cm_msg(MERROR, "db_lock_database", "cannot lock ODB mutex, timeout %d ms, status %d, aborting...", _database[hDB - 1].timeout, status);
    2475            0 :       abort();
    2476              :    }
    2477              : 
    2478              :    /* protect this function against recursive call from signal handlers */
    2479         1103 :    if (_database[hDB - 1].inside_lock_unlock) {
    2480            0 :       fprintf(stderr, "db_lock_database: Detected recursive call to db_{lock,unlock}_database() while already inside db_{lock,unlock}_database(). Maybe this is a call from a signal handler. Cannot continue, aborting...\n");
    2481            0 :       abort();
    2482              :    }
    2483              : 
    2484         1103 :    _database[hDB - 1].inside_lock_unlock = 1;
    2485              : 
    2486              :    //static int x = 0;
    2487              :    //x++;
    2488              :    //if (x > 5000) {
    2489              :    //   printf("inside db_lock_database(), press Ctrl-C now!\n");
    2490              :    //   sleep(5);
    2491              :    //}
    2492              : 
    2493              :    // test recursive locking
    2494              :    // static int out=0;
    2495              :    // out++;
    2496              :    // printf("HERE %d!\n", out);
    2497              :    // if (out>10) abort();
    2498              :    // db_lock_database(hDB);
    2499              :    // printf("OUT %d!\n", out);
    2500              : 
    2501         1103 :    if (_database[hDB - 1].lock_cnt == 0) {
    2502          791 :       _database[hDB - 1].lock_cnt = 1;
    2503              :       /* wait max. 5 minutes for semaphore (required if locking process is being debugged) */
    2504          791 :       status = ss_semaphore_wait_for(_database[hDB - 1].semaphore, _database[hDB - 1].timeout);
    2505          791 :       if (status == SS_TIMEOUT) {
    2506            0 :          cm_msg(MERROR, "db_lock_database", "cannot lock ODB semaphore, timeout %d ms, aborting...", _database[hDB - 1].timeout);
    2507              :          //exit(1); // exit() calls ataxit() handlers, including midas disconnect experiment which will crash because ODB locking is hosed. cannot continue, must abort(). K.O. 13-OCT-2024.
    2508            0 :          abort();
    2509              :       }
    2510          791 :       if (status != SS_SUCCESS) {
    2511            0 :          cm_msg(MERROR, "db_lock_database", "cannot lock ODB semaphore, timeout %d ms, ss_semaphore_wait_for() status %d, aborting...", _database[hDB - 1].timeout, status);
    2512            0 :          abort();
    2513              :       }
    2514              :    } else {
    2515          312 :       _database[hDB - 1].lock_cnt++; // we have already the lock (recursive call), so just increase counter
    2516              :    }
    2517              : 
    2518              : #ifdef CHECK_LOCK_COUNT
    2519              :    {
    2520              :       char str[256];
    2521              : 
    2522              :       sprintf(str, "db_lock_database, lock_cnt=%d", _database[hDB - 1].lock_cnt);
    2523              :       ss_stack_history_entry(str);
    2524              :    }
    2525              : #endif
    2526              : 
    2527         1103 :    if (_database[hDB - 1].protect) {
    2528            0 :       if (_database[hDB - 1].database_header == NULL) {
    2529              :          int status;
    2530            0 :          assert(!_database[hDB - 1].protect_read);
    2531            0 :          assert(!_database[hDB - 1].protect_write);
    2532            0 :          status = ss_shm_unprotect(_database[hDB - 1].shm_handle, &_database[hDB - 1].shm_adr, _database[hDB - 1].shm_size, TRUE, FALSE, "db_lock_database");
    2533            0 :          if (status != SS_SUCCESS) {
    2534            0 :             cm_msg(MERROR, "db_lock_database", "cannot lock ODB, ss_shm_unprotect(TRUE,FALSE) failed with status %d, aborting...", status);
    2535            0 :             cm_msg_flush_buffer();
    2536            0 :             ss_semaphore_release(_database[hDB - 1].semaphore);
    2537            0 :             abort();
    2538              :          }
    2539            0 :          _database[hDB - 1].database_header = (DATABASE_HEADER *) _database[hDB - 1].shm_adr;
    2540            0 :          _database[hDB - 1].protect_read = TRUE;
    2541            0 :          _database[hDB - 1].protect_write = FALSE;
    2542              :       }
    2543              :    }
    2544              : 
    2545         1103 :    _database[hDB - 1].inside_lock_unlock = 0;
    2546              : 
    2547              : #endif                          /* LOCAL_ROUTINES */
    2548              :    
    2549         1103 :    return DB_SUCCESS;
    2550              : }
    2551              : 
    2552              : #ifdef LOCAL_ROUTINES
    2553          327 : INT db_allow_write_locked(DATABASE* p, const char* caller_name)
    2554              : {
    2555          327 :    assert(p);
    2556          327 :    if (p->protect && !p->protect_write) {
    2557              :       int status;
    2558            0 :       assert(p->lock_cnt > 0);
    2559            0 :       assert(p->database_header != NULL);
    2560            0 :       assert(p->protect_read);
    2561            0 :       status = ss_shm_unprotect(p->shm_handle, &p->shm_adr, p->shm_size, TRUE, TRUE, caller_name);
    2562            0 :       if (status != SS_SUCCESS) {
    2563            0 :          cm_msg(MERROR, "db_allow_write_locked", "cannot write to ODB, ss_shm_unprotect(TRUE,TRUE) failed with status %d, aborting...", status);
    2564            0 :          cm_msg_flush_buffer();
    2565            0 :          ss_semaphore_release(p->semaphore);
    2566            0 :          abort();
    2567              :       }
    2568            0 :       p->database_header = (DATABASE_HEADER *) p->shm_adr;
    2569            0 :       p->protect_read = TRUE;
    2570            0 :       p->protect_write = TRUE;
    2571              :    }
    2572          327 :    return DB_SUCCESS;
    2573              : }
    2574              : #endif                          /* LOCAL_ROUTINES */
    2575              : 
    2576              : /********************************************************************/
    2577              : /**
    2578              : Unlock a database via system semaphore calls.
    2579              : @param hDB   Handle to the database to unlock
    2580              : @return DB_SUCCESS, DB_INVALID_HANDLE
    2581              : */
    2582         1103 : INT db_unlock_database(HNDLE hDB)
    2583              : {
    2584              : #ifdef LOCAL_ROUTINES
    2585              : 
    2586         1103 :    if (hDB > _database_entries || hDB <= 0) {
    2587            0 :       cm_msg(MERROR, "db_unlock_database", "invalid database handle %d", hDB);
    2588            0 :       return DB_INVALID_HANDLE;
    2589              :    }
    2590              : #ifdef CHECK_LOCK_COUNT
    2591              :    {
    2592              :       char str[256];
    2593              : 
    2594              :       sprintf(str, "db_unlock_database, lock_cnt=%d", _database[hDB - 1].lock_cnt);
    2595              :       ss_stack_history_entry(str);
    2596              :    }
    2597              : #endif
    2598              : 
    2599              :    /* protect this function against recursive call from signal handlers */
    2600         1103 :    if (_database[hDB - 1].inside_lock_unlock) {
    2601            0 :       fprintf(stderr, "db_unlock_database: Detected recursive call to db_{lock,unlock}_database() while already inside db_{lock,unlock}_database(). Maybe this is a call from a signal handler. Cannot continue, aborting...\n");
    2602            0 :       abort();
    2603              :    }
    2604              : 
    2605         1103 :    _database[hDB - 1].inside_lock_unlock = 1;
    2606              : 
    2607              :    //static int x = 0;
    2608              :    //x++;
    2609              :    //if (x > 5000) {
    2610              :    //   printf("inside db_unlock_database(), press Ctrl-C now!\n");
    2611              :    //   sleep(5);
    2612              :    //}
    2613              : 
    2614         1103 :    if (_database[hDB - 1].lock_cnt == 1) {
    2615          791 :       ss_semaphore_release(_database[hDB - 1].semaphore);
    2616              : 
    2617          791 :       if (_database[hDB - 1].protect && _database[hDB - 1].database_header) {
    2618              :          int status;
    2619            0 :          assert(_database[hDB - 1].protect_read);
    2620            0 :          assert(_database[hDB - 1].database_header);
    2621            0 :          _database[hDB - 1].database_header = NULL;
    2622            0 :          status = ss_shm_protect(_database[hDB - 1].shm_handle, _database[hDB - 1].shm_adr, _database[hDB - 1].shm_size);
    2623            0 :          if (status != SS_SUCCESS) {
    2624            0 :             cm_msg(MERROR, "db_unlock_database", "cannot unlock ODB, ss_shm_protect() failed with status %d, aborting...", status);
    2625            0 :             cm_msg_flush_buffer();
    2626            0 :             abort();
    2627              :          }
    2628            0 :          _database[hDB - 1].protect_read = FALSE;
    2629            0 :          _database[hDB - 1].protect_write = FALSE;
    2630              :       }
    2631              :    }
    2632              : 
    2633         1103 :    assert(_database[hDB - 1].lock_cnt > 0);
    2634         1103 :    _database[hDB - 1].lock_cnt--;
    2635              : 
    2636         1103 :    _database[hDB - 1].inside_lock_unlock = 0;
    2637              : 
    2638              :    /* release mutex for multi-thread applications */
    2639         1103 :    ss_mutex_release(_database[hDB - 1].mutex);
    2640              : 
    2641              : #endif                          /* LOCAL_ROUTINES */
    2642         1103 :    return DB_SUCCESS;
    2643              : }
    2644              : 
    2645              : /********************************************************************/
    2646              : #if 0
    2647              : INT db_get_lock_cnt(HNDLE hDB)
    2648              : {
    2649              : #ifdef LOCAL_ROUTINES
    2650              : 
    2651              :    /* return zero if no ODB is open or we run remotely */
    2652              :    if (_database_entries == 0)
    2653              :       return 0;
    2654              : 
    2655              :    if (hDB > _database_entries || hDB <= 0) {
    2656              :       cm_msg(MERROR, "db_get_lock_cnt", "invalid database handle %d, aborting...", hDB);
    2657              :       fprintf(stderr, "db_get_lock_cnt: invalid database handle %d, aborting...\n", hDB);
    2658              :       abort();
    2659              :       return DB_INVALID_HANDLE;
    2660              :    }
    2661              : 
    2662              :    return _database[hDB - 1].lock_cnt;
    2663              : #else
    2664              :    return 0;
    2665              : #endif
    2666              : }
    2667              : #endif
    2668              : 
    2669            4 : INT db_set_lock_timeout(HNDLE hDB, int timeout_millisec)
    2670              : {
    2671              : #ifdef LOCAL_ROUTINES
    2672              : 
    2673              :    /* return zero if no ODB is open or we run remotely */
    2674            4 :    if (_database_entries == 0)
    2675            0 :       return 0;
    2676              : 
    2677            4 :    if (hDB > _database_entries || hDB <= 0) {
    2678            0 :       cm_msg(MERROR, "db_set_lock_timeout", "invalid database handle %d", hDB);
    2679            0 :       return 0;
    2680              :    }
    2681              : 
    2682            4 :    if (timeout_millisec > 0) {
    2683            2 :       _database[hDB - 1].timeout = timeout_millisec;
    2684              :    }
    2685              : 
    2686            4 :    return _database[hDB - 1].timeout;
    2687              : #else
    2688              :    return 0;
    2689              : #endif
    2690              : }
    2691              : 
    2692              : #ifdef LOCAL_ROUTINES
    2693              : 
    2694              : /**
    2695              : Update last activity time
    2696              : */
    2697            0 : INT db_update_last_activity(DWORD millitime)
    2698              : {
    2699            0 :    bool found = false;
    2700            0 :    int pid = ss_getpid();
    2701            0 :    for (int i = 0; i < _database_entries; i++) {
    2702            0 :       if (_database[i].attached) {
    2703            0 :          db_lock_database(i + 1);
    2704            0 :          db_allow_write_locked(&_database[i], "db_update_last_activity");
    2705            0 :          assert(_database[i].database_header);
    2706              :          /* update the last_activity entry to show that we are alive */
    2707            0 :          for (int j=0; j<_database[i].database_header->max_client_index; j++) {
    2708            0 :             DATABASE_CLIENT* pdbclient = _database[i].database_header->client + j;
    2709              :             //printf("client %d pid %d vs our pid %d\n", j, pdbclient->pid, pid);
    2710            0 :             if (pdbclient->pid == pid) {
    2711            0 :                pdbclient->last_activity = millitime;
    2712            0 :                found = true;
    2713              :             }
    2714              :          }
    2715            0 :          db_unlock_database(i + 1);
    2716              :       }
    2717              :    }
    2718              : 
    2719            0 :    if (!found) {
    2720            0 :       cm_msg(MERROR, "db_update_last_activity", "Did not find this client in any database. Maybe this client was removed by a timeout, see midas.log. Cannot continue, aborting...");
    2721            0 :       abort();
    2722              :    }
    2723              :    
    2724            0 :    return DB_SUCCESS;
    2725              : }
    2726              : 
    2727              : #endif // LOCAL_ROUTINES
    2728              : 
    2729              : #ifdef LOCAL_ROUTINES
    2730            0 : static void db_delete_client_wlocked(DATABASE_HEADER* pheader, int jclient, db_err_msg** msg)
    2731              : {
    2732            0 :    DATABASE_CLIENT* pdbclient = &pheader->client[jclient];
    2733              : 
    2734              :    /* decrement notify_count for open records and clear exclusive mode */
    2735              :    int k;
    2736            0 :    for (k = 0; k < pdbclient->max_index; k++)
    2737            0 :       if (pdbclient->open_record[k].handle) {
    2738            0 :          KEY* pkey = (KEY *) ((char *) pheader + pdbclient->open_record[k].handle);
    2739            0 :          if (pkey->notify_count > 0)
    2740            0 :             pkey->notify_count--;
    2741              :          
    2742            0 :          if (pdbclient->open_record[k].access_mode & MODE_WRITE)
    2743            0 :             db_set_mode_wlocked(pheader, pkey, (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2, msg);
    2744              :       }
    2745              :    
    2746              :    /* clear entry from client structure in buffer header */
    2747            0 :    memset(pdbclient, 0, sizeof(DATABASE_CLIENT));
    2748              :    
    2749              :    /* calculate new max_client_index entry */
    2750            0 :    for (k = MAX_CLIENTS - 1; k >= 0; k--)
    2751            0 :       if (pheader->client[k].pid != 0)
    2752            0 :          break;
    2753            0 :    pheader->max_client_index = k + 1;
    2754              :    
    2755              :    /* count new number of clients */
    2756              :    int nc;
    2757            0 :    for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
    2758            0 :       if (pheader->client[k].pid != 0)
    2759            0 :          nc++;
    2760            0 :    pheader->num_clients = nc;
    2761            0 : }
    2762              : #endif
    2763              : 
    2764              : #ifdef LOCAL_ROUTINES
    2765            2 : static int db_delete_client_info_wlocked(HNDLE hDB, DATABASE_HEADER* pheader, int pid, db_err_msg** msg)
    2766              : {
    2767            2 :    if (!pid)
    2768            2 :       pid = ss_getpid();
    2769              : 
    2770              :    char str[256];
    2771            2 :    int status = 0;
    2772              : 
    2773            2 :    sprintf(str, "System/Clients/%0d", pid);
    2774            2 :    KEY* pkey = (KEY*)db_find_pkey_locked(pheader, NULL, str, &status, msg);
    2775            2 :    if (!pkey) {
    2776            0 :       return status;
    2777              :    }
    2778              :    
    2779            2 :    status = db_set_mode_wlocked(pheader, pkey, MODE_READ | MODE_WRITE | MODE_DELETE, 2, msg);
    2780            2 :    HNDLE hKey = db_pkey_to_hkey(pheader, pkey);
    2781            2 :    status = db_delete_key1(hDB, hKey, 1, TRUE);
    2782            2 :    int32_t data = 0;
    2783            2 :    db_set_value_wlocked(pheader, hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT32, msg);
    2784              : 
    2785            2 :    return status;
    2786              : }
    2787              : #endif
    2788              : 
    2789              : /********************************************************************/
    2790              : /**
    2791              : Delete client info from database
    2792              : @param hDB               Database handle
    2793              : @param pid               PID of entry to delete, zero for this process.
    2794              : @return CM_SUCCESS
    2795              : */
    2796            2 : int db_delete_client_info(HNDLE hDB, int pid)
    2797              : {
    2798              : #ifdef LOCAL_ROUTINES
    2799            2 :    if (hDB > _database_entries || hDB <= 0) {
    2800            0 :       cm_msg(MERROR, "db_delete_client_info", "invalid database handle");
    2801            0 :       return DB_INVALID_HANDLE;
    2802              :    }
    2803              :    
    2804            2 :    if (!_database[hDB - 1].attached) {
    2805            0 :       cm_msg(MERROR, "db_delete_client_info", "invalid database handle");
    2806            0 :       return DB_INVALID_HANDLE;
    2807              :    }
    2808              :    
    2809              :    /* lock database */
    2810            2 :    db_lock_database(hDB);
    2811              : 
    2812            2 :    DATABASE *pdb = &_database[hDB - 1];
    2813            2 :    DATABASE_HEADER *pheader = pdb->database_header;
    2814              : 
    2815            2 :    db_allow_write_locked(pdb, "db_delete_client_info");
    2816              : 
    2817            2 :    db_err_msg* msg = NULL;
    2818              : 
    2819            2 :    int status = db_delete_client_info_wlocked(hDB, pheader, pid, &msg);
    2820              :    
    2821            2 :    db_unlock_database(hDB);
    2822              : 
    2823            2 :    if (msg)
    2824            0 :       db_flush_msg(&msg);
    2825              : 
    2826            2 :    return status;
    2827              : #else
    2828              :    return DB_SUCCESS;
    2829              : #endif
    2830              : }
    2831              : 
    2832            0 : void db_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
    2833              : {
    2834              : #ifdef LOCAL_ROUTINES
    2835              :    int i;
    2836              :    /* check online databases */
    2837            0 :    for (i = 0; i < _database_entries; i++) {
    2838            0 :       DATABASE *pdb = &_database[i];
    2839            0 :       if (!pdb->attached) // empty slot
    2840            0 :          continue;
    2841              : 
    2842            0 :       db_lock_database(i + 1);
    2843            0 :       db_allow_write_locked(pdb, "db_cleanup");
    2844              : 
    2845            0 :       DATABASE_HEADER* pheader = pdb->database_header;
    2846            0 :       DATABASE_CLIENT* pclient = db_get_my_client_locked(pdb);
    2847              :       
    2848              :       /* update the last_activity entry to show that we are alive */
    2849            0 :       pclient->last_activity = actual_time;
    2850              :       
    2851              :       /* don't check other clients if interval is stange */
    2852            0 :       if (wrong_interval) {
    2853            0 :          db_unlock_database(i + 1);
    2854            0 :          continue;
    2855              :       }
    2856              :       
    2857            0 :       db_err_msg *msg = NULL;
    2858              :       
    2859              :       /* now check other clients */
    2860              :       int j;
    2861            0 :       for (j = 0; j < pheader->max_client_index; j++) {
    2862            0 :          DATABASE_CLIENT *pdbclient = &pheader->client[j];
    2863            0 :          int client_pid = pdbclient->pid;
    2864            0 :          if (client_pid == 0) // empty slot
    2865            0 :             continue;
    2866              :          
    2867            0 :          if (!ss_pid_exists(client_pid)) {
    2868            0 :             db_msg(&msg, MINFO, "db_cleanup", "Client \'%s\' on database \'%s\' pid %d does not exist and db_cleanup called by %s removed it", pdbclient->name, pheader->name, client_pid, who);
    2869              :             
    2870            0 :             db_delete_client_wlocked(pheader, j, &msg);
    2871            0 :             db_delete_client_info_wlocked(i+1, pheader, client_pid, &msg);
    2872              :             
    2873            0 :             continue;
    2874              :          }
    2875              :          
    2876              :          /* now make again the check with the buffer locked */
    2877            0 :          actual_time = ss_millitime();
    2878            0 :          if ((pdbclient->watchdog_timeout &&
    2879            0 :               actual_time > pdbclient->last_activity &&
    2880            0 :               actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout)
    2881              :              ) {
    2882            0 :             db_msg(&msg, MINFO, "db_cleanup", "Client \'%s\' on database \'%s\' pid %d timed out and db_cleanup called by %s removed it (idle %1.1lfs,TO %1.0lfs)",
    2883            0 :                    pdbclient->name, pheader->name, client_pid,
    2884              :                    who,
    2885            0 :                    (actual_time - pdbclient->last_activity) / 1000.0,
    2886            0 :                    pdbclient->watchdog_timeout / 1000.0);
    2887              :             
    2888            0 :             db_delete_client_wlocked(pheader, j, &msg);
    2889            0 :             db_delete_client_info_wlocked(i+1, pheader, client_pid, &msg);
    2890              :          }
    2891              :       }
    2892              :       
    2893            0 :       db_unlock_database(i + 1);
    2894            0 :       if (msg)
    2895            0 :          db_flush_msg(&msg);
    2896              :    }
    2897              : #endif
    2898            0 : }
    2899              : 
    2900              : #ifdef LOCAL_ROUTINES
    2901              : 
    2902            0 : void db_cleanup2(const char* client_name, int ignore_timeout, DWORD actual_time,  const char *who)
    2903              : {
    2904              :    /* check online databases */
    2905              :    int i;
    2906            0 :    for (i = 0; i < _database_entries; i++) {
    2907            0 :       if (_database[i].attached) {
    2908              :          /* update the last_activity entry to show that we are alive */
    2909              :          
    2910            0 :          db_lock_database(i + 1);
    2911              : 
    2912            0 :          db_err_msg *msg = NULL;
    2913              : 
    2914            0 :          DATABASE* pdb = &_database[i];
    2915              : 
    2916            0 :          db_allow_write_locked(pdb, "db_cleanup2");
    2917              : 
    2918            0 :          DWORD now = ss_millitime();
    2919              : 
    2920            0 :          DATABASE_HEADER* pheader = pdb->database_header;
    2921            0 :          DATABASE_CLIENT* pclient = db_get_my_client_locked(pdb);
    2922            0 :          pclient->last_activity = now;
    2923              :          
    2924              :          /* now check other clients */
    2925              :          int j;
    2926            0 :          for (j = 0; j < pheader->max_client_index; j++) {
    2927            0 :             DATABASE_CLIENT* pdbclient = &pheader->client[j];
    2928            0 :             if (j == pdb->client_index) // do not check ourselves
    2929            0 :                continue;
    2930            0 :             if (!pdbclient->pid) // empty slot
    2931            0 :                continue;
    2932            0 :             if ((client_name == NULL || client_name[0] == 0
    2933            0 :                  || strncmp(pdbclient->name, client_name, strlen(client_name)) == 0)) {
    2934            0 :                int client_pid = pdbclient->pid;
    2935              : 
    2936            0 :                if (!ss_pid_exists(client_pid)) {
    2937            0 :                   db_msg(&msg, MINFO, "db_cleanup2", "Client \'%s\' on database \'%s\' pid %d does not exist and db_cleanup2 called by %s removed it",
    2938            0 :                          pdbclient->name, pheader->name,
    2939              :                          client_pid,
    2940              :                          who);
    2941              : 
    2942            0 :                   db_delete_client_wlocked(pheader, j, &msg);
    2943            0 :                   db_delete_client_info_wlocked(i+1, pheader, client_pid, &msg);
    2944              : 
    2945              :                   /* go again though whole list */
    2946            0 :                   j = 0;
    2947            0 :                   continue;
    2948              :                }
    2949              : 
    2950              :                DWORD interval;
    2951            0 :                if (ignore_timeout)
    2952            0 :                   interval = 2 * WATCHDOG_INTERVAL;
    2953              :                else
    2954            0 :                   interval = pdbclient->watchdog_timeout;
    2955              :                
    2956              :                /* If client process has no activity, clear its buffer entry. */
    2957              :                
    2958            0 :                if ((interval > 0 && now - pdbclient->last_activity > interval)) {
    2959            0 :                   db_msg(&msg, MINFO, "db_cleanup2", "Client \'%s\' on database \'%s\' timed out and db_cleanup2 called by %s removed it (idle %1.1lfs,TO %1.0lfs)",
    2960            0 :                          pdbclient->name, pheader->name,
    2961              :                          who,
    2962            0 :                          (now - pdbclient->last_activity) / 1000.0, interval / 1000.0);
    2963              : 
    2964            0 :                   db_delete_client_wlocked(pheader, j, &msg);
    2965            0 :                   db_delete_client_info_wlocked(i+1, pheader, client_pid, &msg);
    2966              :                   
    2967              :                   /* go again though whole list */
    2968            0 :                   j = 0;
    2969            0 :                   continue;
    2970              :                }
    2971              :             }
    2972              :          }
    2973              :          
    2974            0 :          db_unlock_database(i + 1);
    2975            0 :          if (msg)
    2976            0 :             db_flush_msg(&msg);
    2977              :       }
    2978              :    }
    2979            0 : }
    2980              : 
    2981            4 : void db_set_watchdog_params(DWORD timeout)
    2982              : {
    2983              :    /* set watchdog flag of all open databases */
    2984            8 :    for (int i = _database_entries; i > 0; i--) {
    2985              :       
    2986            4 :       db_lock_database(i);
    2987              : 
    2988            4 :       DATABASE* pdb = &_database[i - 1];
    2989              : 
    2990            4 :       if (!pdb->attached) {
    2991            0 :          db_unlock_database(i);
    2992            0 :          continue;
    2993              :       }
    2994              :       
    2995            4 :       DATABASE_CLIENT *pclient = db_get_my_client_locked(pdb);
    2996              :       
    2997            4 :       db_allow_write_locked(pdb, "db_set_watchdog_params");
    2998              :       
    2999              :       /* clear entry from client structure in buffer header */
    3000            4 :       pclient->watchdog_timeout = timeout;
    3001              :       
    3002              :       /* show activity */
    3003            4 :       pclient->last_activity = ss_millitime();
    3004              :       
    3005            4 :       db_unlock_database(i);
    3006              :    }
    3007            4 : }
    3008              : 
    3009              : /********************************************************************/
    3010              : /**
    3011              : Return watchdog information about specific client
    3012              : @param    hDB              ODB handle
    3013              : @param    client_name     ODB client name
    3014              : @param    timeout         Timeout for this application in seconds
    3015              : @param    last            Last time watchdog was called in msec
    3016              : @return   CM_SUCCESS, CM_NO_CLIENT, DB_INVALID_HANDLE
    3017              : */
    3018              : 
    3019            0 : INT db_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD * timeout, DWORD * last)
    3020              : {
    3021            0 :    if (hDB > _database_entries || hDB <= 0) {
    3022            0 :       cm_msg(MERROR, "db_get_watchdog_info", "invalid database handle");
    3023            0 :       return DB_INVALID_HANDLE;
    3024              :    }
    3025              :    
    3026            0 :    if (!_database[hDB - 1].attached) {
    3027            0 :       cm_msg(MERROR, "db_get_watchdog_info", "invalid database handle");
    3028            0 :       return DB_INVALID_HANDLE;
    3029              :    }
    3030              :    
    3031              :    /* lock database */
    3032            0 :    db_lock_database(hDB);
    3033              : 
    3034            0 :    DATABASE *pdb = &_database[hDB - 1];
    3035            0 :    DATABASE_HEADER *pheader = pdb->database_header;
    3036              :    
    3037              :    /* find client */
    3038            0 :    for (int i = 0; i < pheader->max_client_index; i++) {
    3039            0 :       DATABASE_CLIENT *pclient = &pheader->client[i];
    3040            0 :       if (pclient->pid && equal_ustring(pclient->name, client_name)) {
    3041            0 :          *timeout = pclient->watchdog_timeout;
    3042            0 :          *last = ss_millitime() - pclient->last_activity;
    3043            0 :          db_unlock_database(hDB);
    3044            0 :          return DB_SUCCESS;
    3045              :       }
    3046              :    }
    3047              :    
    3048            0 :    *timeout = *last = 0;
    3049              : 
    3050            0 :    db_unlock_database(hDB);
    3051              : 
    3052            0 :    return CM_NO_CLIENT;
    3053              : }
    3054              : 
    3055              : /********************************************************************/
    3056              : /**
    3057              : Check if a client with a /system/client/xxx entry has
    3058              : a valid entry in the ODB client table. If not, remove
    3059              : that client from the /system/client tree.
    3060              : @param   hDB               Handle to online database
    3061              : @param   hKeyClient        Handle to client key
    3062              : @return  CM_SUCCESS, CM_NO_CLIENT
    3063              : */
    3064            0 : INT db_check_client(HNDLE hDB, HNDLE hKeyClient)
    3065              : {
    3066            0 :    if (hDB > _database_entries || hDB <= 0) {
    3067            0 :       cm_msg(MERROR, "db_check_client", "invalid database handle");
    3068            0 :       return DB_INVALID_HANDLE;
    3069              :    }
    3070              :    
    3071            0 :    if (!_database[hDB - 1].attached) {
    3072            0 :       cm_msg(MERROR, "db_check_client", "invalid database handle");
    3073            0 :       return DB_INVALID_HANDLE;
    3074              :    }
    3075              : 
    3076              :    int status;
    3077              :    
    3078            0 :    db_lock_database(hDB);
    3079              : 
    3080            0 :    db_err_msg* msg = NULL;
    3081              : 
    3082            0 :    DATABASE *pdb = &_database[hDB - 1];
    3083            0 :    DATABASE_HEADER *pheader = pdb->database_header;
    3084              :    
    3085            0 :    const KEY* pkey = db_get_pkey(pheader, hKeyClient, &status, "db_check_client", &msg);
    3086              : 
    3087            0 :    if (!pkey) {
    3088            0 :       db_unlock_database(hDB);
    3089            0 :       if (msg)
    3090            0 :          db_flush_msg(&msg);
    3091            0 :       return CM_NO_CLIENT;
    3092              :    }
    3093              :    
    3094            0 :    int client_pid = atoi(pkey->name);
    3095              : 
    3096            0 :    const KEY* pkey_name = db_find_pkey_locked(pheader, pkey, "Name", &status, &msg);
    3097              : 
    3098            0 :    if (!pkey_name) {
    3099            0 :       db_unlock_database(hDB);
    3100            0 :       if (msg)
    3101            0 :          db_flush_msg(&msg);
    3102            0 :       return CM_NO_CLIENT;
    3103              :    }
    3104              :    
    3105              :    char name[256];
    3106            0 :    name[0] = 0;
    3107            0 :    int size = sizeof(name);
    3108            0 :    status = db_get_data_locked(pheader, pkey_name, 0, name, &size, TID_STRING, &msg);
    3109              :    
    3110              :    //fprintf(stderr, "db_check_client: hkey %d, status %d, pid %d, name \'%s\'\n", hKeyClient, status, client_pid, name);
    3111              :    
    3112            0 :    if (status != DB_SUCCESS) {
    3113            0 :       db_unlock_database(hDB);
    3114            0 :       if (msg)
    3115            0 :          db_flush_msg(&msg);
    3116            0 :       return CM_NO_CLIENT;
    3117              :    }
    3118              :    
    3119            0 :    bool dead = false;
    3120            0 :    bool found = false;
    3121              :    
    3122              :    /* loop through clients */
    3123            0 :    for (int i = 0; i < pheader->max_client_index; i++) {
    3124            0 :       DATABASE_CLIENT *pclient = &pheader->client[i];
    3125            0 :       if (pclient->pid == client_pid) {
    3126            0 :          found = true;
    3127            0 :          break;
    3128              :       }
    3129              :    }
    3130              :    
    3131            0 :    if (found) {
    3132              :       /* check that the client is still running: PID still exists */
    3133            0 :       if (!ss_pid_exists(client_pid)) {
    3134            0 :          dead = true;
    3135              :       }
    3136              :    }
    3137              : 
    3138            0 :    status = DB_SUCCESS;
    3139              :    
    3140            0 :    if (!found || dead) {
    3141              :       /* client not found : delete ODB stucture */
    3142              :       
    3143            0 :       db_allow_write_locked(pdb, "db_check_client");
    3144              :       
    3145            0 :       status = db_delete_client_info_wlocked(hDB, pheader, client_pid, &msg);
    3146              :       
    3147            0 :       if (status != DB_SUCCESS)
    3148            0 :          db_msg(&msg, MERROR, "db_check_client", "Cannot delete client info for client \'%s\', pid %d, db_delete_client_info() status %d", name, client_pid, status);
    3149            0 :       else if (!found)
    3150            0 :          db_msg(&msg, MINFO, "db_check_client", "Deleted entry \'/System/Clients/%d\' for client \'%s\' because it is not connected to ODB", client_pid, name);
    3151            0 :       else if (dead)
    3152            0 :          db_msg(&msg, MINFO, "db_check_client", "Deleted entry \'/System/Clients/%d\' for client \'%s\' because process pid %d does not exists", client_pid, name, client_pid);
    3153              :       
    3154            0 :       status = CM_NO_CLIENT;
    3155              :    }
    3156              : 
    3157            0 :    db_unlock_database(hDB);
    3158            0 :    if (msg)
    3159            0 :       db_flush_msg(&msg);
    3160              : 
    3161            0 :    return status;
    3162              : }
    3163              : 
    3164              : #endif // LOCAL_ROUTINES
    3165              : 
    3166              : /********************************************************************/
    3167              : /**
    3168              : Protect a database for read/write access outside of the \b db_xxx functions
    3169              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    3170              : @return DB_SUCCESS, DB_INVALID_HANDLE
    3171              : */
    3172            0 : INT db_protect_database(HNDLE hDB)
    3173              : {
    3174            0 :    if (rpc_is_remote())
    3175            0 :       return DB_SUCCESS;
    3176              : 
    3177              : #ifdef LOCAL_ROUTINES
    3178            0 :    if (hDB > _database_entries || hDB <= 0) {
    3179            0 :       cm_msg(MERROR, "db_protect_database", "invalid database handle %d", hDB);
    3180            0 :       return DB_INVALID_HANDLE;
    3181              :    }
    3182              : 
    3183            0 :    _database[hDB - 1].protect = TRUE;
    3184            0 :    ss_shm_protect(_database[hDB - 1].shm_handle, _database[hDB - 1].database_header, _database[hDB - 1].shm_size);
    3185            0 :    _database[hDB - 1].database_header = NULL;
    3186              : #endif                          /* LOCAL_ROUTINES */
    3187            0 :    return DB_SUCCESS;
    3188              : }
    3189              : 
    3190              : /*---- helper routines ---------------------------------------------*/
    3191              : 
    3192         1301 : const char *extract_key(const char *key_list, char *key_name, int key_name_length)
    3193              : {
    3194         1301 :    int i = 0;
    3195              : 
    3196         1301 :    if (*key_list == '/')
    3197          982 :       key_list++;
    3198              : 
    3199        14849 :    while (*key_list && *key_list != '/' && ++i < key_name_length)
    3200        13548 :       *key_name++ = *key_list++;
    3201         1301 :    *key_name = 0;
    3202              : 
    3203         1301 :    return key_list;
    3204              : }
    3205              : 
    3206         4627 : BOOL equal_ustring(const char *str1, const char *str2)
    3207              : {
    3208         4627 :    if (str1 == NULL && str2 != NULL)
    3209            0 :       return FALSE;
    3210         4627 :    if (str1 != NULL && str2 == NULL)
    3211            0 :       return FALSE;
    3212         4627 :    if (str1 == NULL && str2 == NULL)
    3213            0 :       return TRUE;
    3214         4627 :    if (strlen(str1) != strlen(str2))
    3215         3158 :       return FALSE;
    3216              : 
    3217        12397 :    while (*str1)
    3218        11419 :       if (toupper(*str1++) != toupper(*str2++))
    3219          491 :          return FALSE;
    3220              : 
    3221          978 :    if (*str2)
    3222            0 :       return FALSE;
    3223              : 
    3224          978 :    return TRUE;
    3225              : }
    3226              : 
    3227            0 : BOOL ends_with_ustring(const char *str, const char *suffix)
    3228              : {
    3229            0 :    int len_str = strlen(str);
    3230            0 :    int len_suffix = strlen(suffix);
    3231              : 
    3232              :    // suffix is longer than the string
    3233            0 :    if (len_suffix > len_str)
    3234            0 :       return FALSE;
    3235              : 
    3236            0 :    return equal_ustring(str + len_str - len_suffix, suffix);
    3237              : }
    3238              : 
    3239              : //utility function to match string against wildcard pattern
    3240            0 : BOOL strmatch(char* pattern, char* str){
    3241            0 :    switch(*pattern){
    3242            0 :       case 0:
    3243            0 :          if(*str == 0){
    3244              :             //end of pattern
    3245            0 :             return true;
    3246              :          }else
    3247            0 :             return false;
    3248            0 :       case '*':
    3249              :          {
    3250            0 :             int i=0;
    3251              :             // check for end of the string
    3252            0 :             if(pattern[1] == 0) return true;
    3253              :             // loop on all possible matches
    3254            0 :             while(str[i] != 0) if(strmatch(pattern + 1, str+(i++))) return true;
    3255            0 :             return false;
    3256              :          }
    3257            0 :       case '?':
    3258            0 :          if(*str == 0){
    3259              :             //end of string
    3260            0 :             return false;
    3261              :          } else {
    3262            0 :             return strmatch(pattern+1, str+1);
    3263              :          }
    3264            0 :       default:
    3265            0 :          if(*str == 0){
    3266              :             //end of string
    3267            0 :             return false;
    3268            0 :          } else if(toupper(*str) == toupper(*pattern)){
    3269              :             //recursion
    3270            0 :             return strmatch(pattern+1, str+1);
    3271              :          } else {
    3272            0 :             return false;
    3273              :          }
    3274              :    }
    3275              : }
    3276              : 
    3277              : //utility function to extract array indexes
    3278            0 : void strarrayindex(char* odbpath, int* index1, int* index2){
    3279              :    char* pc;
    3280              : 
    3281            0 :    *index1 = *index2 = 0;
    3282            0 :    if (odbpath[strlen(odbpath) - 1] == ']') {
    3283            0 :       if (strchr(odbpath, '[')) {
    3284            0 :          if (*(strchr(odbpath, '[') + 1) == '*')
    3285            0 :             *index1 = -1;
    3286            0 :          else if (strchr((strchr(odbpath, '[') + 1), '.') ||
    3287            0 :                   strchr((strchr(odbpath, '[') + 1), '-')) {
    3288            0 :             *index1 = atoi(strchr(odbpath, '[') + 1);
    3289            0 :             pc = strchr(odbpath, '[') + 1;
    3290            0 :             while (*pc != '.' && *pc != '-')
    3291            0 :                pc++;
    3292            0 :             while (*pc == '.' || *pc == '-')
    3293            0 :                pc++;
    3294            0 :             *index2 = atoi(pc);
    3295              :          } else
    3296            0 :             *index1 = atoi(strchr(odbpath, '[') + 1);
    3297              :       }
    3298              : 
    3299              :       //remove everything after bracket
    3300            0 :       *strchr(odbpath, '[') = 0;
    3301              :    }
    3302            0 : }
    3303              : 
    3304              : /********************************************************************/
    3305              : /**
    3306              : Create a new key in a database
    3307              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    3308              : @param hKey  Key handle to start with, 0 for root
    3309              : @param key_name    Name of key in the form "/key/key/key"
    3310              : @param type        Type of key, one of TID_xxx (see @ref Midas_Data_Types)
    3311              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_INVALID_PARAM, DB_FULL, DB_KEY_EXIST, DB_NO_ACCESS
    3312              : */
    3313           68 : INT db_create_key(HNDLE hDB, HNDLE hKey, const char *key_name, DWORD type)
    3314              : {
    3315              : 
    3316           68 :    if (rpc_is_remote())
    3317            0 :       return rpc_call(RPC_DB_CREATE_KEY, hDB, hKey, key_name, type);
    3318              : 
    3319              : #ifdef LOCAL_ROUTINES
    3320              :    {
    3321              :       int status;
    3322              : 
    3323           68 :       if (hDB > _database_entries || hDB <= 0) {
    3324            0 :          cm_msg(MERROR, "db_create_key", "invalid database handle");
    3325            0 :          return DB_INVALID_HANDLE;
    3326              :       }
    3327              : 
    3328           68 :       if (!_database[hDB - 1].attached) {
    3329            0 :          cm_msg(MERROR, "db_create_key", "invalid database handle");
    3330            0 :          return DB_INVALID_HANDLE;
    3331              :       }
    3332              : 
    3333              :       /* lock database */
    3334           68 :       db_lock_database(hDB);
    3335              : 
    3336           68 :       DATABASE_HEADER* pheader = _database[hDB - 1].database_header;
    3337              : 
    3338           68 :       db_err_msg *msg = NULL;
    3339              :       
    3340           68 :       KEY* pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_create_key", &msg);
    3341              : 
    3342           68 :       if (!pkey) {
    3343            0 :          db_unlock_database(hDB);
    3344            0 :          if (msg)
    3345            0 :             db_flush_msg(&msg);
    3346            0 :          return DB_INVALID_HANDLE;
    3347              :       }
    3348              : 
    3349           68 :       db_allow_write_locked(&_database[hDB-1], "db_create_key");
    3350              : 
    3351           68 :       KEY* newpkey = NULL;
    3352              : 
    3353           68 :       status = db_create_key_wlocked(pheader, pkey, key_name, type, &newpkey, &msg);
    3354              : 
    3355           68 :       db_unlock_database(hDB);
    3356              : 
    3357           68 :       if (msg)
    3358            7 :          db_flush_msg(&msg);
    3359              : 
    3360           68 :       return status;
    3361              :    }
    3362              : #endif                          /* LOCAL_ROUTINES */
    3363              : 
    3364              :    return DB_SUCCESS;
    3365              : }
    3366              : 
    3367              : #ifdef LOCAL_ROUTINES
    3368              : 
    3369          138 : int db_create_key_wlocked(DATABASE_HEADER* pheader, KEY* parentKey, const char *key_name, DWORD type, KEY** pnewkey, db_err_msg** msg)
    3370              : {
    3371              :    int status;
    3372              : 
    3373          138 :    if (pnewkey)
    3374          138 :       *pnewkey = NULL;
    3375              : 
    3376          138 :    if (parentKey== NULL) {
    3377            0 :       parentKey = (KEY*) db_get_pkey(pheader, pheader->root_key, &status, "db_create_key_wlocked", msg);
    3378            0 :       if (!parentKey) {
    3379            0 :          return status;
    3380              :       }
    3381              :    }
    3382              :    
    3383          138 :    status = db_validate_name(key_name, TRUE, "db_create_key_wlocked", msg);
    3384          138 :    if (status != DB_SUCCESS) {
    3385            4 :       return status;
    3386              :    }
    3387              :    
    3388              :    /* check type */
    3389          134 :    if (type <= 0 || type >= TID_LAST) {
    3390            0 :       db_msg(msg, MERROR, "db_create_key", "invalid key type %d to create \'%s\' in \'%s\'", type, key_name, db_get_path_locked(pheader, parentKey).c_str());
    3391            0 :       return DB_INVALID_PARAM;
    3392              :    }
    3393              : 
    3394          134 :    const KEY* pdir = parentKey;
    3395              :    
    3396          134 :    if (pdir->type != TID_KEY) {
    3397            0 :       db_msg(msg, MERROR, "db_create_key", "cannot create \'%s\' in \'%s\' tid is %d, not a directory", key_name, db_get_path_locked(pheader, pdir).c_str(), pdir->type);
    3398            0 :       return DB_NO_KEY;
    3399              :    }
    3400              : 
    3401          134 :    KEY* pcreated = NULL;
    3402              : 
    3403          134 :    const char* pkey_name = key_name;
    3404              :    do {
    3405              :       // key name is limited to NAME_LENGTH, but buffer has to be slightly longer
    3406              :       // to prevent truncation before db_validate_name checks for correct length. K.O.
    3407              :       char name[NAME_LENGTH+100];
    3408              :       
    3409              :       /* extract single key from key_name */
    3410          202 :       pkey_name = extract_key(pkey_name, name, sizeof(name));
    3411              : 
    3412          202 :       status = db_validate_name(name, FALSE, "db_create_key", msg);
    3413          202 :       if (status != DB_SUCCESS) {
    3414            4 :          return status;
    3415              :       }
    3416              :       
    3417              :       /* do not allow empty names, like '/dir/dir//dir/' */
    3418          199 :       if (name[0] == 0) {
    3419            0 :          return DB_INVALID_PARAM;
    3420              :       }
    3421              :       
    3422              :       /* check if parent or current directory */
    3423          199 :       if (strcmp(name, "..") == 0) {
    3424            0 :          pdir = db_get_parent(pheader, pdir, &status, "db_create_key", msg);
    3425            0 :          if (!pdir) {
    3426            0 :             return status;
    3427              :          }
    3428            0 :          continue;
    3429              :       }
    3430          199 :       if (strcmp(name, ".") == 0)
    3431            0 :          continue;
    3432              : 
    3433          199 :       KEY* pprev = NULL;
    3434          199 :       const KEY* pitem = db_enum_first_locked(pheader, pdir, msg);
    3435              : 
    3436          779 :       while (pitem) {
    3437          629 :          if (equal_ustring(name, pitem->name)) {
    3438           49 :             break;
    3439              :          }
    3440          580 :          pprev = (KEY*)pitem;
    3441          580 :          pitem = db_enum_next_locked(pheader, pdir, pitem, msg);
    3442              :       }
    3443              :       
    3444          199 :       if (!pitem) {
    3445              :          /* not found: create new key */
    3446              : 
    3447              :          /* check parent for write access */
    3448          150 :          const KEY* pkeyparent = db_get_parent(pheader, pdir, &status, "db_create_key", msg);
    3449          150 :          if (!pkeyparent) {
    3450            0 :             return status;
    3451              :          }
    3452              : 
    3453          150 :          if (!(pkeyparent->access_mode & MODE_WRITE) || (pkeyparent->access_mode & MODE_EXCLUSIVE)) {
    3454            0 :             return DB_NO_ACCESS;
    3455              :          }
    3456              :          
    3457          150 :          KEYLIST* pkeylist = (KEYLIST*)db_get_pkeylist(pheader, db_pkey_to_hkey(pheader, pdir), pdir, "db_create_key_wlocked", msg);
    3458              :          
    3459          150 :          if (!pkeylist) {
    3460            0 :             return DB_CORRUPTED;
    3461              :          }
    3462              :          
    3463          150 :          pkeylist->num_keys++;
    3464              :          
    3465          150 :          if (*pkey_name == '/' || type == TID_KEY) {
    3466              :             /* create new key with keylist */
    3467           38 :             KEY* pkey = (KEY *) malloc_key(pheader, sizeof(KEY), "db_create_key_subdir");
    3468              :             
    3469           38 :             if (pkey == NULL) {
    3470            0 :                if (pkeylist->num_keys > 0)
    3471            0 :                   pkeylist->num_keys--;
    3472              : 
    3473            0 :                db_msg(msg, MERROR, "db_create_key", "online database full while creating \'%s\' in \'%s\'", key_name, db_get_path_locked(pheader, parentKey).c_str());
    3474            0 :                return DB_FULL;
    3475              :             }
    3476              : 
    3477              :             /* append key to key list */
    3478           38 :             if (pprev)
    3479           25 :                pprev->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
    3480              :             else
    3481           13 :                pkeylist->first_key = (POINTER_T) pkey - (POINTER_T) pheader;
    3482              :             
    3483              :             /* set key properties */
    3484           38 :             pkey->type = TID_KEY;
    3485           38 :             pkey->num_values = 1;
    3486           38 :             pkey->access_mode = MODE_READ | MODE_WRITE | MODE_DELETE;
    3487           38 :             mstrlcpy(pkey->name, name, sizeof(pkey->name));
    3488           38 :             pkey->parent_keylist = (POINTER_T) pkeylist - (POINTER_T) pheader;
    3489              :             
    3490              :             /* find space for new keylist */
    3491           38 :             pkeylist = (KEYLIST *) malloc_key(pheader, sizeof(KEYLIST), "db_create_key_B");
    3492              :             
    3493           38 :             if (pkeylist == NULL) {
    3494            0 :                db_msg(msg, MERROR, "db_create_key", "online database full while creating \'%s\' in \'%s'", key_name, db_get_path_locked(pheader, parentKey).c_str());
    3495            0 :                return DB_FULL;
    3496              :             }
    3497              :             
    3498              :             /* store keylist in data field */
    3499           38 :             pkey->data = (POINTER_T) pkeylist - (POINTER_T) pheader;
    3500           38 :             pkey->item_size = sizeof(KEYLIST);
    3501           38 :             pkey->total_size = sizeof(KEYLIST);
    3502              :             
    3503           38 :             pkeylist->parent = (POINTER_T) pkey - (POINTER_T) pheader;
    3504           38 :             pkeylist->num_keys = 0;
    3505           38 :             pkeylist->first_key = 0;
    3506              : 
    3507           38 :             pcreated = pkey;
    3508           38 :             pdir = pkey; // descend into newly created subdirectory
    3509           38 :          } else {
    3510              :             /* create new key with data */
    3511          112 :             KEY* pkey = (KEY *) malloc_key(pheader, sizeof(KEY), "db_create_key_data");
    3512              :             
    3513          112 :             if (pkey == NULL) {
    3514            0 :                if (pkeylist->num_keys > 0)
    3515            0 :                   pkeylist->num_keys--;
    3516              : 
    3517            0 :                db_msg(msg, MERROR, "db_create_key", "online database full while creating \'%s\' in \'%s\'", key_name, db_get_path_locked(pheader, parentKey).c_str());
    3518            0 :                return DB_FULL;
    3519              :             }
    3520              :             
    3521              :             /* append key to key list */
    3522          112 :             if (pprev)
    3523           89 :                pprev->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
    3524              :             else
    3525           23 :                pkeylist->first_key = (POINTER_T) pkey - (POINTER_T) pheader;
    3526              :             
    3527          112 :             pkey->type = type;
    3528          112 :             pkey->num_values = 1;
    3529          112 :             pkey->access_mode = MODE_READ | MODE_WRITE | MODE_DELETE;
    3530          112 :             mstrlcpy(pkey->name, name, sizeof(pkey->name));
    3531          112 :             pkey->parent_keylist = (POINTER_T) pkeylist - (POINTER_T) pheader;
    3532              :             
    3533              :             /* allocate data */
    3534          112 :             pkey->item_size = rpc_tid_size(type);
    3535          112 :             if (pkey->item_size > 0) {
    3536           87 :                void* data = malloc_data(pheader, pkey->item_size);
    3537           87 :                if (data == NULL) {
    3538            0 :                   pkey->total_size = 0;
    3539            0 :                   db_msg(msg, MERROR, "db_create_key", "online database full while creating \'%s\' in \'%s\'", key_name, db_get_path_locked(pheader, parentKey).c_str());
    3540            0 :                   return DB_FULL;
    3541              :                }
    3542              :                
    3543           87 :                pkey->total_size = pkey->item_size;
    3544           87 :                pkey->data = (POINTER_T)data - (POINTER_T)pheader;
    3545              :             } else {
    3546              :                /* first data is empty */
    3547           25 :                pkey->item_size = 0;
    3548           25 :                pkey->total_size = 0;
    3549           25 :                pkey->data = 0;
    3550              :             }
    3551          112 :             pcreated = pkey;
    3552              :          }
    3553              :       } else {
    3554              :          /* key found: descend */
    3555              :          
    3556              :          /* resolve links */
    3557           49 :          if (pitem->type == TID_LINK && pkey_name[0]) {
    3558            0 :             pdir = db_resolve_link_locked(pheader, pitem, &status, msg);
    3559            0 :             if (!pdir) {
    3560            0 :                return status;
    3561              :             }
    3562            0 :             continue;
    3563              :          }
    3564              :          
    3565           49 :          if (!(*pkey_name == '/')) {
    3566            1 :             if (pitem->type != type) {
    3567            0 :                db_msg(msg, MERROR, "db_create_key", "object of type %d already exists at \"%s\" while creating \'%s\' of type %d in \'%s\'", pitem->type, db_get_path_locked(pheader, pitem).c_str(), key_name, type, db_get_path_locked(pheader, parentKey).c_str());
    3568            0 :                return DB_TYPE_MISMATCH;
    3569              :             }
    3570              :             //db_print_pkey(pheader, pitem);
    3571              : 
    3572            1 :             if (pnewkey)
    3573            1 :                *pnewkey = (KEY*)pitem;
    3574              : 
    3575            1 :             return DB_KEY_EXIST;
    3576              :          }
    3577              :          
    3578           48 :          if (pitem->type != TID_KEY) {
    3579            0 :             db_msg(msg, MERROR, "db_create_key", "path element \"%s\" in \"%s\" is not a subdirectory at \"%s\" while creating \'%s\' in \'%s\'", name, key_name, db_get_path_locked(pheader, pitem).c_str(), key_name, db_get_path_locked(pheader, parentKey).c_str());
    3580            0 :             return DB_TYPE_MISMATCH;
    3581              :          }
    3582              :          
    3583           48 :          pdir = pitem;
    3584              :       }
    3585          198 :    } while (*pkey_name == '/');
    3586              : 
    3587          130 :    assert(pcreated != NULL);
    3588              :    
    3589          130 :    if (pnewkey)
    3590          130 :       *pnewkey = pcreated;
    3591              : 
    3592          130 :    return DB_SUCCESS;
    3593              : }
    3594              : 
    3595              : #endif /* LOCAL_ROUTINES */
    3596              : 
    3597              : /********************************************************************/
    3598              : /**
    3599              : Create a link to a key or set the destination of and existing link.
    3600              : @param hDB           ODB handle obtained via cm_get_experiment_database().
    3601              : @param hKey          Key handle to start with, 0 for root
    3602              : @param link_name     Name of key in the form "/key/key/key"
    3603              : @param destination   Destination of link in the form "/key/key/key"
    3604              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_FULL, DB_KEY_EXIST, DB_NO_ACCESS, DB_INVALID_NAME
    3605              : */
    3606           11 : INT db_create_link(HNDLE hDB, HNDLE hKey, const char *link_name, const char *destination)
    3607              : {
    3608              :    HNDLE hkey;
    3609              :    int status;
    3610              : 
    3611           11 :    if (rpc_is_remote())
    3612            0 :       return rpc_call(RPC_DB_CREATE_LINK, hDB, hKey, link_name, destination);
    3613              : 
    3614           11 :    if (destination == NULL) {
    3615            0 :       cm_msg(MERROR, "db_create_link", "link destination name is NULL");
    3616            0 :       return DB_INVALID_NAME;
    3617              :    }
    3618              : 
    3619           11 :    if (destination[0] != '/') {
    3620            1 :       cm_msg(MERROR, "db_create_link", "link destination name \'%s\' should start with \'/\', relative links are forbidden", destination);
    3621            1 :       return DB_INVALID_NAME;
    3622              :    }
    3623              : 
    3624           10 :    if (strlen(destination) < 1) {
    3625            0 :       cm_msg(MERROR, "db_create_link", "link destination name \'%s\' is too short", destination);
    3626            0 :       return DB_INVALID_NAME;
    3627              :    }
    3628              : 
    3629           10 :    if ((destination[0] == '/') && (destination[1] == 0)) {
    3630            0 :       cm_msg(MERROR, "db_create_link", "links to \"/\" are forbidden");
    3631            0 :       return DB_INVALID_NAME;
    3632              :    }
    3633              : 
    3634              :    /* check if destination exists */
    3635           10 :    status = db_find_key(hDB, hKey, destination, &hkey);
    3636           10 :    if (status != DB_SUCCESS) {
    3637            2 :       cm_msg(MERROR, "db_create_link", "Link destination \"%s\" does not exist", destination);
    3638            2 :       return DB_NO_KEY;
    3639              :    }
    3640              : 
    3641              :    //printf("db_create_link: [%s] hkey %d\n", destination, hkey);
    3642              : 
    3643              :    /* check if link already exists */
    3644            8 :    status = db_find_link(hDB, hKey, link_name, &hkey);
    3645            8 :    if (status != DB_SUCCESS) {
    3646              :       // create new link
    3647            8 :       status = db_set_value(hDB, hKey, link_name, destination, strlen(destination) + 1, 1, TID_LINK);
    3648              :    } else {
    3649              :       // check if key is TID_LINK
    3650              :       KEY key;
    3651            0 :       db_get_key(hDB, hkey, &key);
    3652            0 :       if (key.type != TID_LINK) {
    3653            0 :          cm_msg(MERROR, "db_create_link", "Existing key \"%s\" is not a link", link_name);
    3654            0 :          return DB_KEY_EXIST;
    3655              :       }
    3656              : 
    3657              :       // modify existing link
    3658            0 :       status = db_set_link_data(hDB, hkey, destination, strlen(destination)+1, 1, TID_LINK);
    3659              :    }
    3660              : 
    3661            8 :    return status;
    3662              : }
    3663              : 
    3664              : /********************************************************************/
    3665              : /**
    3666              : Delete a subtree, using level information (only called internally by db_delete_key())
    3667              : @internal
    3668              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    3669              : @param hKey  Key handle to start with, 0 for root
    3670              : @param level            Recursion level, must be zero when
    3671              : @param follow_links     Follow links when TRUE called from a user routine
    3672              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_OPEN_RECORD
    3673              : */
    3674          256 : INT db_delete_key1(HNDLE hDB, HNDLE hKey, INT level, BOOL follow_links)
    3675              : {
    3676              : #ifdef LOCAL_ROUTINES
    3677              :    {
    3678              :       DATABASE_HEADER *pheader;
    3679              :       KEYLIST *pkeylist;
    3680              :       KEY *pkey, *pnext_key, *pkey_tmp;
    3681              :       HNDLE hKeyLink;
    3682              :       BOOL deny_delete;
    3683              :       INT status;
    3684          256 :       BOOL locked = FALSE;
    3685              : 
    3686          256 :       if (hDB > _database_entries || hDB <= 0) {
    3687            0 :          cm_msg(MERROR, "db_delete_key1", "invalid database handle");
    3688          196 :          return DB_INVALID_HANDLE;
    3689              :       }
    3690              : 
    3691          256 :       if (!_database[hDB - 1].attached) {
    3692            0 :          cm_msg(MERROR, "db_delete_key1", "invalid database handle");
    3693            0 :          return DB_INVALID_HANDLE;
    3694              :       }
    3695              : 
    3696          256 :       if (hKey < (int) sizeof(DATABASE_HEADER)) {
    3697            0 :          cm_msg(MERROR, "db_delete_key1", "invalid key handle");
    3698            0 :          return DB_INVALID_HANDLE;
    3699              :       }
    3700              : 
    3701              :       /* lock database at the top level */
    3702          256 :       if (level == 0) {
    3703           13 :          db_lock_database(hDB);
    3704           13 :          locked = TRUE;
    3705              :       }
    3706              : 
    3707          256 :       pheader = _database[hDB - 1].database_header;
    3708              : 
    3709              :       /* check if hKey argument is correct */
    3710          256 :       if (!db_validate_hkey(pheader, hKey)) {
    3711            0 :          if (locked)
    3712            0 :             db_unlock_database(hDB);
    3713            0 :          return DB_INVALID_HANDLE;
    3714              :       }
    3715              : 
    3716          256 :       pkey = (KEY *) ((char *) pheader + hKey);
    3717              : 
    3718              :       /* check if someone has opened key or parent */
    3719          256 :       if (level == 0)
    3720              :          do {
    3721              : #ifdef CHECK_OPEN_RECORD
    3722           45 :             if (pkey->notify_count) {
    3723            0 :                if (locked)
    3724            0 :                   db_unlock_database(hDB);
    3725            0 :                return DB_OPEN_RECORD;
    3726              :             }
    3727              : #endif
    3728           45 :             if (pkey->parent_keylist == 0)
    3729           13 :                break;
    3730              : 
    3731           32 :             pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
    3732              :             // FIXME: validate pkeylist->parent
    3733           32 :             pkey = (KEY *) ((char *) pheader + pkeylist->parent);
    3734              :          } while (TRUE);
    3735              : 
    3736          256 :       pkey = (KEY *) ((char *) pheader + hKey);
    3737          256 :       pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
    3738              : 
    3739          256 :       deny_delete = FALSE;
    3740              : 
    3741              :       /* first recures subtree for keys */
    3742          256 :       if (pkey->type == TID_KEY && pkeylist->first_key) {
    3743          110 :          pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
    3744              : 
    3745              :          do {
    3746          139 :             pnext_key = (KEY *) (POINTER_T) pkey->next_key; // FIXME: what is this casting of hKey to pointer?
    3747              : 
    3748          139 :             status = db_delete_key1(hDB, (POINTER_T) pkey - (POINTER_T) pheader, level + 1, follow_links);
    3749              : 
    3750          139 :             if (status == DB_NO_ACCESS)
    3751            0 :                deny_delete = TRUE;
    3752              : 
    3753          139 :             if (pnext_key) {
    3754              :                // FIXME: validate pnext_key
    3755           29 :                pkey = (KEY *) ((char *) pheader + (POINTER_T) pnext_key);
    3756              :             }
    3757          139 :          } while (pnext_key);
    3758              :       }
    3759              : 
    3760              :       /* follow links if requested */
    3761          256 :       if (pkey->type == TID_LINK && follow_links) {
    3762          103 :          status = db_find_key1(hDB, 0, (char *) pheader + pkey->data, &hKeyLink);
    3763          103 :          if (status == DB_SUCCESS && follow_links < 100)
    3764          102 :             db_delete_key1(hDB, hKeyLink, level + 1, follow_links + 1);
    3765              : 
    3766          103 :          if (follow_links == 100)
    3767            1 :             cm_msg(MERROR, "db_delete_key1", "try to delete cyclic link");
    3768              :       }
    3769              : 
    3770              :       /* check if hKey argument is correct */
    3771          256 :       if (!db_validate_hkey(pheader, hKey)) {
    3772            0 :          if (locked)
    3773            0 :             db_unlock_database(hDB);
    3774            0 :          return DB_INVALID_HANDLE;
    3775              :       }
    3776              : 
    3777          256 :       pkey = (KEY *) ((char *) pheader + hKey);
    3778              : 
    3779              :       /* return if key was already deleted by cyclic link */
    3780          256 :       if (pkey->parent_keylist == 0) {
    3781          196 :          if (locked)
    3782            0 :             db_unlock_database(hDB);
    3783          196 :          return DB_SUCCESS;
    3784              :       }
    3785              : 
    3786              :       /* now delete key */
    3787           60 :       if (hKey != pheader->root_key) {
    3788           60 :          if (!(pkey->access_mode & MODE_DELETE) || deny_delete) {
    3789            0 :             if (locked)
    3790            0 :                db_unlock_database(hDB);
    3791            0 :             return DB_NO_ACCESS;
    3792              :          }
    3793              : #ifdef CHECK_OPEN_RECORD
    3794           60 :          if (pkey->notify_count) {
    3795            0 :             if (locked)
    3796            0 :                db_unlock_database(hDB);
    3797            0 :             return DB_OPEN_RECORD;
    3798              :          }
    3799              : #endif
    3800           60 :          db_allow_write_locked(&_database[hDB - 1], "db_delete_key1");
    3801              : 
    3802              :          /* delete key data */
    3803           60 :          if (pkey->type == TID_KEY)
    3804           17 :             free_key(pheader, (char *) pheader + pkey->data, pkey->total_size);
    3805              :          else
    3806           43 :             free_data(pheader, (char *) pheader + pkey->data, pkey->total_size, "db_delete_key1");
    3807              : 
    3808              :          /* unlink key from list */
    3809           60 :          pnext_key = (KEY *) (POINTER_T) pkey->next_key;
    3810           60 :          pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
    3811              : 
    3812           60 :          if ((KEY *) ((char *) pheader + pkeylist->first_key) == pkey) {
    3813              :             /* key is first in list */
    3814           49 :             pkeylist->first_key = (POINTER_T) pnext_key;
    3815              :          } else {
    3816              :             /* find predecessor */
    3817           11 :             pkey_tmp = (KEY *) ((char *) pheader + pkeylist->first_key);
    3818           38 :             while ((KEY *) ((char *) pheader + pkey_tmp->next_key) != pkey)
    3819           27 :                pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
    3820           11 :             pkey_tmp->next_key = (POINTER_T) pnext_key;
    3821              :          }
    3822              : 
    3823              :          /* delete key */
    3824           60 :          free_key(pheader, pkey, sizeof(KEY));
    3825           60 :          pkeylist->num_keys--;
    3826              :       }
    3827              : 
    3828           60 :       if (locked)
    3829           13 :          db_unlock_database(hDB);
    3830              :    }
    3831              : #endif                          /* LOCAL_ROUTINES */
    3832              : 
    3833           60 :    return DB_SUCCESS;
    3834              : }
    3835              : 
    3836              : /********************************************************************/
    3837              : /**
    3838              : Delete a subtree in a database starting from a key (including this key).
    3839              : \code
    3840              : ...
    3841              :     status = db_find_link(hDB, 0, str, &hkey);
    3842              :     if (status != DB_SUCCESS)
    3843              :     {
    3844              :       cm_msg(MINFO,"my_delete"," "Cannot find key %s", str);
    3845              :       return;
    3846              :     }
    3847              : 
    3848              :     status = db_delete_key(hDB, hkey, FALSE);
    3849              :     if (status != DB_SUCCESS)
    3850              :     {
    3851              :       cm_msg(MERROR,"my_delete"," "Cannot delete key %s", str);
    3852              :       return;
    3853              :     }
    3854              :   ...
    3855              : \endcode
    3856              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    3857              : @param hKey         for key where search starts, zero for root.
    3858              : @param follow_links Follow links when TRUE.
    3859              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_ACCESS, DB_OPEN_RECORD
    3860              : */
    3861           13 : INT db_delete_key(HNDLE hDB, HNDLE hKey, BOOL follow_links)
    3862              : {
    3863           13 :    if (rpc_is_remote())
    3864            0 :       return rpc_call(RPC_DB_DELETE_KEY, hDB, hKey, follow_links);
    3865              : 
    3866           13 :    return db_delete_key1(hDB, hKey, 0, follow_links);
    3867              : }
    3868              : 
    3869              : #ifdef LOCAL_ROUTINES
    3870          377 : static const KEY* db_find_pkey_locked(const DATABASE_HEADER *pheader, const KEY* pkey, const char *key_name, int *pstatus, db_err_msg **msg)
    3871              : {
    3872          377 :    if (pkey == NULL) {
    3873            7 :       pkey = db_get_pkey(pheader, pheader->root_key, pstatus, "db_find_key", msg);
    3874            7 :       if (!pkey) {
    3875            0 :          return NULL;
    3876              :       }
    3877              :    }
    3878              : 
    3879          377 :    HNDLE hKey = db_pkey_to_hkey(pheader, pkey);
    3880              : 
    3881          377 :    if (pkey->type != TID_KEY) {
    3882            0 :       DWORD tid = pkey->type;
    3883            0 :       std::string path = db_get_path_locked(pheader, hKey);
    3884            0 :       db_msg(msg, MERROR, "db_find_key", "hkey %d path \"%s\" tid %d is not a directory, looking for \"%s\"", hKey, path.c_str(), tid, key_name);
    3885            0 :       if (pstatus)
    3886            0 :          *pstatus = DB_NO_KEY;
    3887            0 :       return NULL;
    3888            0 :    }
    3889              :    
    3890          377 :    if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
    3891            0 :       if (!(pkey->access_mode & MODE_READ)) {
    3892            0 :          if (pstatus)
    3893            0 :             *pstatus = DB_NO_ACCESS;
    3894            0 :          return NULL;
    3895              :       }
    3896            0 :       return pkey;
    3897              :    }
    3898              : 
    3899          377 :    const KEYLIST *pkeylist = db_get_pkeylist(pheader, hKey, pkey, "db_find_key", msg);
    3900          377 :    if (!pkeylist) {
    3901            0 :       if (pstatus)
    3902            0 :          *pstatus = DB_CORRUPTED;
    3903            0 :       return NULL;
    3904              :    }
    3905              : 
    3906          377 :    HNDLE last_good_hkey = hKey;
    3907              : 
    3908          377 :    const char *pkey_name = key_name;
    3909              :    do {
    3910          774 :       assert(pkeylist!=NULL); // should never happen!
    3911              : 
    3912              :       char str[MAX_ODB_PATH];
    3913              :          
    3914              :       /* extract single subkey from key_name */
    3915          774 :       pkey_name = extract_key(pkey_name, str, sizeof(str));
    3916              : 
    3917              :       /* strip trailing '[n]' */
    3918          774 :       if (strchr(str, '[') && str[strlen(str) - 1] == ']')
    3919            0 :          *strchr(str, '[') = 0;
    3920              : 
    3921              :       /* check if parent or current directory */
    3922          774 :       if (strcmp(str, "..") == 0) {
    3923            0 :          if (pkey->parent_keylist) {
    3924            0 :             pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
    3925              :             // FIXME: validate pkeylist->parent
    3926            0 :             pkey = (KEY *) ((char *) pheader + pkeylist->parent);
    3927              :          }
    3928            0 :          continue;
    3929              :       }
    3930          774 :       if (strcmp(str, ".") == 0)
    3931            0 :          continue;
    3932              : 
    3933          774 :       last_good_hkey = hKey;
    3934              : 
    3935          774 :       hKey = pkeylist->first_key;
    3936              : 
    3937          774 :       if (hKey == 0) {
    3938              :          // empty subdirectory
    3939           12 :          if (pstatus)
    3940           12 :             *pstatus = DB_NO_KEY;
    3941          118 :          return NULL;
    3942              :       }
    3943              : 
    3944              :       int i;
    3945         2421 :       for (i = 0; i < pkeylist->num_keys; i++) {
    3946         2421 :          pkey = db_get_pkey(pheader, hKey, pstatus, "db_find_key", msg);
    3947              :          
    3948         2421 :          if (!pkey) {
    3949            0 :             std::string path = db_get_path_locked(pheader, last_good_hkey);
    3950            0 :             db_msg(msg, MERROR, "db_find_key", "hkey %d path \"%s\" invalid subdirectory entry hkey %d, looking for \"%s\"", last_good_hkey, path.c_str(), hKey, key_name);
    3951            0 :             return NULL;
    3952            0 :          }
    3953              : 
    3954         2421 :          if (!db_validate_key_offset(pheader, pkey->next_key)) {
    3955            0 :             std::string path = db_get_path_locked(pheader, hKey);
    3956            0 :             db_msg(msg, MERROR, "db_find_key", "hkey %d path \"%s\" invalid next_key %d, looking for \"%s\"", hKey, path.c_str(), pkey->next_key, key_name);
    3957            0 :             if (pstatus)
    3958            0 :                *pstatus = DB_CORRUPTED;
    3959            0 :             return NULL;
    3960            0 :          }
    3961              : 
    3962         2421 :          if (equal_ustring(str, pkey->name))
    3963          658 :             break;
    3964              : 
    3965         1763 :          if (pkey->next_key == 0) {
    3966          104 :             if (pstatus)
    3967          104 :                *pstatus = DB_NO_KEY;
    3968          104 :             return NULL;
    3969              :          }
    3970              : 
    3971         1659 :          hKey = pkey->next_key;
    3972              :       }
    3973              : 
    3974          658 :       if (i == pkeylist->num_keys) {
    3975            0 :          if (pstatus)
    3976            0 :             *pstatus = DB_NO_KEY;
    3977            0 :          return NULL;
    3978              :       }
    3979              : 
    3980              :       /* resolve links */
    3981          658 :       if (pkey->type == TID_LINK) {
    3982              :          /* copy destination, strip '/' */
    3983            2 :          mstrlcpy(str, (char *) pheader + pkey->data, sizeof(str));
    3984            2 :          if (str[strlen(str) - 1] == '/')
    3985            0 :             str[strlen(str) - 1] = 0;
    3986              : 
    3987              :          /* if link is pointer to array index, return link instead of destination */
    3988            2 :          if (str[strlen(str) - 1] == ']')
    3989            0 :             break;
    3990              : 
    3991              :          /* append rest of key name if existing */
    3992            2 :          if (pkey_name[0]) {
    3993            0 :             mstrlcat(str, pkey_name, sizeof(str));
    3994            0 :             return db_find_pkey_locked(pheader, NULL, str, pstatus, msg);
    3995              :          } else {
    3996              :             /* if last key in chain is a link, return its destination */
    3997            2 :             int status = 0;
    3998            2 :             const KEY *plink = db_find_pkey_locked(pheader, NULL, str, &status, msg);
    3999            2 :             if (pstatus) {
    4000            2 :                if (status == DB_NO_KEY)
    4001            1 :                   *pstatus = DB_INVALID_LINK;
    4002              :                else
    4003            1 :                   *pstatus = status;
    4004              :             }
    4005            2 :             return plink;
    4006              :          }
    4007              :       }
    4008              : 
    4009              :       /* key found: check if last in chain */
    4010          656 :       if (*pkey_name == '/') {
    4011          397 :          if (pkey->type != TID_KEY) {
    4012            0 :             if (pstatus)
    4013            0 :                *pstatus = DB_NO_KEY;
    4014            0 :             return NULL;
    4015              :          }
    4016              :       }
    4017              : 
    4018          656 :       if (pkey->type == TID_KEY) {
    4019              :          /* descend one level */
    4020          438 :          pkeylist = db_get_pkeylist(pheader, hKey, pkey, "db_find_key", msg);
    4021              :          
    4022          438 :          if (!pkeylist) {
    4023            0 :             if (pstatus)
    4024            0 :                *pstatus = DB_CORRUPTED;
    4025            0 :             return NULL;
    4026              :          }
    4027              :       } else {
    4028          218 :          pkeylist = NULL;
    4029              :       }
    4030              : 
    4031          656 :    } while (*pkey_name == '/' && *(pkey_name + 1));
    4032              : 
    4033          259 :    return pkey;
    4034              : }
    4035              : 
    4036          219 : static int db_find_key_locked(const DATABASE_HEADER *pheader, HNDLE hKey, const char *key_name, HNDLE *subhKey, db_err_msg **msg)
    4037              : {
    4038              :    int status;
    4039          219 :    const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_find_key", msg);
    4040          219 :    if (!pkey) {
    4041            0 :       if (subhKey)
    4042            0 :          *subhKey = 0;
    4043            0 :       return status;
    4044              :    }
    4045              : 
    4046          219 :    const KEY* plink = db_find_pkey_locked(pheader, pkey, key_name, &status, msg);
    4047              : 
    4048          219 :    if (!plink) {
    4049           28 :       *subhKey = 0;
    4050           28 :       return status;
    4051              :    }
    4052              : 
    4053          191 :    *subhKey = db_pkey_to_hkey(pheader, plink);
    4054              : 
    4055          191 :    return DB_SUCCESS;
    4056              : }
    4057              : #endif /* LOCAL_ROUTINES */
    4058              : 
    4059              : /********************************************************************/
    4060              : /**
    4061              : Returns key handle for a key with a specific name.
    4062              : 
    4063              : Keys can be accessed by their name including the directory
    4064              : or by a handle. A key handle is an internal offset to the shared memory
    4065              : where the ODB lives and allows a much faster access to a key than via its
    4066              : name.
    4067              : 
    4068              : The function db_find_key() must be used to convert a key name to a handle.
    4069              : Most other database functions use this key handle in various operations.
    4070              : \code
    4071              : HNDLE hkey, hsubkey;
    4072              : // use full name, start from root
    4073              : db_find_key(hDB, 0, "/Runinfo/Run number", &hkey);
    4074              : // start from subdirectory
    4075              : db_find_key(hDB, 0, "/Runinfo", &hkey);
    4076              : db_find_key(hdb, hkey, "Run number", &hsubkey);
    4077              : \endcode
    4078              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    4079              : @param hKey Handle for key where search starts, zero for root.
    4080              : @param key_name Name of key to search, can contain directories.
    4081              : @param subhKey Returned handle of key, zero if key cannot be found.
    4082              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_ACCESS, DB_NO_KEY
    4083              : */
    4084          219 : INT db_find_key(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE * subhKey)
    4085              : {
    4086          219 :    if (rpc_is_remote())
    4087            0 :       return rpc_call(RPC_DB_FIND_KEY, hDB, hKey, key_name, subhKey);
    4088              : 
    4089              : #ifdef LOCAL_ROUTINES
    4090              :    {
    4091              :       DATABASE_HEADER *pheader;
    4092              :       INT status;
    4093              : 
    4094          219 :       *subhKey = 0;
    4095              : 
    4096          219 :       if (hDB > _database_entries || hDB <= 0) {
    4097            0 :          cm_msg(MERROR, "db_find_key", "invalid database handle");
    4098            0 :          return DB_INVALID_HANDLE;
    4099              :       }
    4100              : 
    4101          219 :       if (!_database[hDB - 1].attached) {
    4102            0 :          cm_msg(MERROR, "db_find_key", "invalid database handle");
    4103            0 :          return DB_INVALID_HANDLE;
    4104              :       }
    4105              : 
    4106          219 :       db_err_msg *msg = NULL;
    4107              : 
    4108          219 :       db_lock_database(hDB);
    4109              : 
    4110          219 :       pheader = _database[hDB - 1].database_header;
    4111              :       
    4112          219 :       status = db_find_key_locked(pheader, hKey, key_name, subhKey, &msg);
    4113              :    
    4114          219 :       db_unlock_database(hDB);
    4115              : 
    4116          219 :       if (msg)
    4117            0 :          db_flush_msg(&msg);
    4118              : 
    4119          219 :       return status;
    4120              :    }
    4121              : #endif /* LOCAL_ROUTINES */
    4122              : 
    4123              :    return DB_SUCCESS;
    4124              : }
    4125              : 
    4126              : /**dox***************************************************************/
    4127              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    4128              : 
    4129              : /*------------------------------------------------------------------*/
    4130          103 : INT db_find_key1(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE * subhKey)
    4131              : /********************************************************************\
    4132              : 
    4133              :   Routine: db_find_key1
    4134              : 
    4135              :   Purpose: Same as db_find_key, but without DB locking
    4136              : 
    4137              :   Input:
    4138              :     HNDLE  bufer_handle     Handle to the database
    4139              :     HNDLE  hKey             Key handle to start the search
    4140              :     char   *key_name        Name of key in the form "/key/key/key"
    4141              : 
    4142              :   Output:
    4143              :     INT    *handle          Key handle
    4144              : 
    4145              :   Function value:
    4146              :     DB_SUCCESS              Successful completion
    4147              :     DB_INVALID_HANDLE       Database handle is invalid
    4148              :     DB_NO_KEY               Key doesn't exist
    4149              :     DB_NO_ACCESS            No access to read key
    4150              : 
    4151              : \********************************************************************/
    4152              : {
    4153          103 :    if (rpc_is_remote())
    4154            0 :       return rpc_call(RPC_DB_FIND_KEY, hDB, hKey, key_name, subhKey);
    4155              : 
    4156              : #ifdef LOCAL_ROUTINES
    4157              :    {
    4158              :       DATABASE_HEADER *pheader;
    4159              :       KEYLIST *pkeylist;
    4160              :       KEY *pkey;
    4161              :       const char *pkey_name;
    4162              :       INT i;
    4163              : 
    4164          103 :       *subhKey = 0;
    4165              : 
    4166          103 :       if (hDB > _database_entries || hDB <= 0) {
    4167            0 :          cm_msg(MERROR, "db_find_key", "invalid database handle");
    4168            0 :          return DB_INVALID_HANDLE;
    4169              :       }
    4170              : 
    4171          103 :       if (!_database[hDB - 1].attached) {
    4172            0 :          cm_msg(MERROR, "db_find_key", "invalid database handle");
    4173            0 :          return DB_INVALID_HANDLE;
    4174              :       }
    4175              : 
    4176          103 :       pheader = _database[hDB - 1].database_header;
    4177          103 :       if (!hKey)
    4178          103 :          hKey = pheader->root_key;
    4179              : 
    4180              :       /* check if hKey argument is correct */
    4181          103 :       if (!db_validate_hkey(pheader, hKey)) {
    4182            0 :          return DB_INVALID_HANDLE;
    4183              :       }
    4184              : 
    4185          103 :       pkey = (KEY *) ((char *) pheader + hKey);
    4186              : 
    4187          103 :       if (pkey->type != TID_KEY) {
    4188            0 :          cm_msg(MERROR, "db_find_key", "key has no subkeys");
    4189            0 :          *subhKey = 0;
    4190            0 :          return DB_NO_KEY;
    4191              :       }
    4192          103 :       pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
    4193              : 
    4194          103 :       if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
    4195            0 :          if (!(pkey->access_mode & MODE_READ)) {
    4196            0 :             *subhKey = 0;
    4197            0 :             return DB_NO_ACCESS;
    4198              :          }
    4199              : 
    4200            0 :          *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
    4201              : 
    4202            0 :          return DB_SUCCESS;
    4203              :       }
    4204              : 
    4205          103 :       pkey_name = key_name;
    4206              :       do {
    4207              :          char str[MAX_ODB_PATH];
    4208              : 
    4209              :          /* extract single subkey from key_name */
    4210          206 :          pkey_name = extract_key(pkey_name, str, sizeof(str));
    4211              : 
    4212              :          /* check if parent or current directory */
    4213          206 :          if (strcmp(str, "..") == 0) {
    4214            0 :             if (pkey->parent_keylist) {
    4215            0 :                pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
    4216              :                // FIXME: validate pkeylist->parent
    4217            0 :                pkey = (KEY *) ((char *) pheader + pkeylist->parent);
    4218              :             }
    4219            0 :             continue;
    4220              :          }
    4221          206 :          if (strcmp(str, ".") == 0)
    4222            0 :             continue;
    4223              : 
    4224              :          /* check if key is in keylist */
    4225              :          // FIXME: validate pkeylist->first_key
    4226          206 :          pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
    4227              : 
    4228         1030 :          for (i = 0; i < pkeylist->num_keys; i++) {
    4229         1030 :             if (equal_ustring(str, pkey->name))
    4230          206 :                break;
    4231              : 
    4232              :             // FIXME: validate pkey->next_key
    4233          824 :             pkey = (KEY *) ((char *) pheader + pkey->next_key);
    4234              :          }
    4235              : 
    4236          206 :          if (i == pkeylist->num_keys) {
    4237            0 :             *subhKey = 0;
    4238            1 :             return DB_NO_KEY;
    4239              :          }
    4240              : 
    4241              :          /* resolve links */
    4242          206 :          if (pkey->type == TID_LINK) {
    4243              :             /* copy destination, strip '/' */
    4244            1 :             strcpy(str, (char *) pheader + pkey->data);
    4245            1 :             if (str[strlen(str) - 1] == '/')
    4246            0 :                str[strlen(str) - 1] = 0;
    4247              : 
    4248              :             /* append rest of key name if existing */
    4249            1 :             if (pkey_name[0]) {
    4250            0 :                strcat(str, pkey_name);
    4251            0 :                return db_find_key1(hDB, 0, str, subhKey);
    4252              :             } else {
    4253              :                /* if last key in chain is a link, return its destination */
    4254            1 :                return db_find_link1(hDB, 0, str, subhKey);
    4255              :             }
    4256              :          }
    4257              : 
    4258              :          /* key found: check if last in chain */
    4259          205 :          if (*pkey_name == '/') {
    4260          103 :             if (pkey->type != TID_KEY) {
    4261            0 :                *subhKey = 0;
    4262            0 :                return DB_NO_KEY;
    4263              :             }
    4264              :          }
    4265              : 
    4266              :          /* descend one level */
    4267          205 :          pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
    4268              : 
    4269          205 :       } while (*pkey_name == '/' && *(pkey_name + 1));
    4270              : 
    4271          102 :       *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
    4272              :    }
    4273              : #endif                          /* LOCAL_ROUTINES */
    4274              : 
    4275          102 :    return DB_SUCCESS;
    4276              : }
    4277              : 
    4278              : /*------------------------------------------------------------------*/
    4279          107 : INT db_find_link(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE * subhKey)
    4280              : /********************************************************************\
    4281              : 
    4282              :   Routine: db_find_link
    4283              : 
    4284              :   Purpose: Find a key or link by name and return its handle
    4285              :            (internal address). The only difference of this routine
    4286              :            compared with db_find_key is that if the LAST key in
    4287              :            the chain is a link, it is NOT evaluated. Links not being
    4288              :            the last in the chain are evaluated.
    4289              : 
    4290              :   Input:
    4291              :     HNDLE  bufer_handle     Handle to the database
    4292              :     HNDLE  hKey       Key handle to start the search
    4293              :     char   *key_name        Name of key in the form "/key/key/key"
    4294              : 
    4295              :   Output:
    4296              :     INT    *handle          Key handle
    4297              : 
    4298              :   Function value:
    4299              :     DB_SUCCESS              Successful completion
    4300              :     DB_INVALID_HANDLE       Database handle is invalid
    4301              :     DB_NO_KEY               Key doesn't exist
    4302              :     DB_NO_ACCESS            No access to read key
    4303              : 
    4304              : \********************************************************************/
    4305              : {
    4306          107 :    if (rpc_is_remote())
    4307            0 :       return rpc_call(RPC_DB_FIND_LINK, hDB, hKey, key_name, subhKey);
    4308              : 
    4309              : #ifdef LOCAL_ROUTINES
    4310              :    {
    4311              :       DATABASE_HEADER *pheader;
    4312              :       KEYLIST *pkeylist;
    4313              :       KEY *pkey;
    4314              :       const char *pkey_name;
    4315              :       INT i;
    4316              : 
    4317          107 :       *subhKey = 0;
    4318              : 
    4319          107 :       if (hDB > _database_entries || hDB <= 0) {
    4320            0 :          cm_msg(MERROR, "db_find_link", "Invalid database handle");
    4321            0 :          return DB_INVALID_HANDLE;
    4322              :       }
    4323              : 
    4324          107 :       if (!_database[hDB - 1].attached) {
    4325            0 :          cm_msg(MERROR, "db_find_link", "invalid database handle");
    4326            0 :          return DB_INVALID_HANDLE;
    4327              :       }
    4328              : 
    4329          107 :       db_lock_database(hDB);
    4330              : 
    4331          107 :       pheader = _database[hDB - 1].database_header;
    4332          107 :       if (!hKey)
    4333           11 :          hKey = pheader->root_key;
    4334              : 
    4335              :       /* check if hKey argument is correct */
    4336          107 :       if (!db_validate_hkey(pheader, hKey)) {
    4337            0 :          db_unlock_database(hDB);
    4338            0 :          return DB_INVALID_HANDLE;
    4339              :       }
    4340              : 
    4341          107 :       pkey = (KEY *) ((char *) pheader + hKey);
    4342              : 
    4343          107 :       if (pkey->type != TID_KEY) {
    4344            0 :          db_unlock_database(hDB);
    4345            0 :          cm_msg(MERROR, "db_find_link", "key has no subkeys");
    4346            0 :          return DB_NO_KEY;
    4347              :       }
    4348          107 :       pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
    4349              : 
    4350          107 :       if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
    4351            0 :          if (!(pkey->access_mode & MODE_READ)) {
    4352            0 :             *subhKey = 0;
    4353            0 :             db_unlock_database(hDB);
    4354            0 :             return DB_NO_ACCESS;
    4355              :          }
    4356              : 
    4357            0 :          *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
    4358              : 
    4359            0 :          db_unlock_database(hDB);
    4360            0 :          return DB_SUCCESS;
    4361              :       }
    4362              : 
    4363          107 :       pkey_name = key_name;
    4364              :       do {
    4365              :          char str[MAX_ODB_PATH];
    4366              : 
    4367              :          /* extract single subkey from key_name */
    4368          117 :          pkey_name = extract_key(pkey_name, str, sizeof(str));
    4369              : 
    4370              :          /* check if parent or current directory */
    4371          117 :          if (strcmp(str, "..") == 0) {
    4372            0 :             if (pkey->parent_keylist) {
    4373            0 :                pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
    4374              :                // FIXME: validate pkeylist->parent
    4375            0 :                pkey = (KEY *) ((char *) pheader + pkeylist->parent);
    4376              :             }
    4377            0 :             continue;
    4378              :          }
    4379          117 :          if (strcmp(str, ".") == 0)
    4380            0 :             continue;
    4381              : 
    4382              :          /* check if key is in keylist */
    4383              :          // FIXME: validate pkeylist->first_key
    4384          117 :          pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
    4385              : 
    4386          540 :          for (i = 0; i < pkeylist->num_keys; i++) {
    4387          481 :             if (!db_validate_key_offset(pheader, pkey->next_key)) {
    4388            0 :                int pkey_next_key = pkey->next_key;
    4389            0 :                db_unlock_database(hDB);
    4390            0 :                cm_msg(MERROR, "db_find_link", "Warning: database corruption, key \"%s\", next_key 0x%08X is invalid", key_name, pkey_next_key - (int)sizeof(DATABASE_HEADER));
    4391            0 :                *subhKey = 0;
    4392           59 :                return DB_CORRUPTED;
    4393              :             }
    4394              : 
    4395          481 :             if (equal_ustring(str, pkey->name))
    4396           58 :                break;
    4397              : 
    4398          423 :             pkey = (KEY *) ((char *) pheader + pkey->next_key); // FIXME: pkey->next_key could be zero
    4399              :          }
    4400              : 
    4401          117 :          if (i == pkeylist->num_keys) {
    4402           59 :             *subhKey = 0;
    4403           59 :             db_unlock_database(hDB);
    4404           59 :             return DB_NO_KEY;
    4405              :          }
    4406              : 
    4407              :          /* resolve links if not last in chain */
    4408           58 :          if (pkey->type == TID_LINK && *pkey_name == '/') {
    4409              :             /* copy destination, strip '/' */
    4410            0 :             strcpy(str, (char *) pheader + pkey->data);
    4411            0 :             if (str[strlen(str) - 1] == '/')
    4412            0 :                str[strlen(str) - 1] = 0;
    4413              : 
    4414              :             /* append rest of key name */
    4415            0 :             strcat(str, pkey_name);
    4416            0 :             db_unlock_database(hDB);
    4417            0 :             return db_find_link(hDB, 0, str, subhKey);
    4418              :          }
    4419              : 
    4420              :          /* key found: check if last in chain */
    4421           58 :          if ((*pkey_name == '/')) {
    4422           10 :             if (pkey->type != TID_KEY) {
    4423            0 :                *subhKey = 0;
    4424            0 :                db_unlock_database(hDB);
    4425            0 :                return DB_NO_KEY;
    4426              :             }
    4427              :          }
    4428              : 
    4429              :          /* descend one level */
    4430           58 :          pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
    4431              : 
    4432           58 :       } while (*pkey_name == '/' && *(pkey_name + 1));
    4433              : 
    4434           48 :       *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
    4435              : 
    4436           48 :       db_unlock_database(hDB);
    4437              :    }
    4438              : #endif                          /* LOCAL_ROUTINES */
    4439              : 
    4440           48 :    return DB_SUCCESS;
    4441              : }
    4442              : 
    4443              : /*------------------------------------------------------------------*/
    4444            1 : INT db_find_link1(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE * subhKey)
    4445              : /********************************************************************\
    4446              : 
    4447              :   Routine: db_find_link1
    4448              : 
    4449              :   Purpose: Same ad db_find_link, but without DB locking
    4450              : 
    4451              :   Input:
    4452              :     HNDLE  bufer_handle     Handle to the database
    4453              :     HNDLE  hKey       Key handle to start the search
    4454              :     char   *key_name        Name of key in the form "/key/key/key"
    4455              : 
    4456              :   Output:
    4457              :     INT    *handle          Key handle
    4458              : 
    4459              :   Function value:
    4460              :     DB_SUCCESS              Successful completion
    4461              :     DB_INVALID_HANDLE       Database handle is invalid
    4462              :     DB_NO_KEY               Key doesn't exist
    4463              :     DB_NO_ACCESS            No access to read key
    4464              : 
    4465              : \********************************************************************/
    4466              : {
    4467            1 :    if (rpc_is_remote())
    4468            0 :       return rpc_call(RPC_DB_FIND_LINK, hDB, hKey, key_name, subhKey);
    4469              : 
    4470              : #ifdef LOCAL_ROUTINES
    4471              :    {
    4472              :       DATABASE_HEADER *pheader;
    4473              :       KEYLIST *pkeylist;
    4474              :       KEY *pkey;
    4475              :       const char *pkey_name;
    4476              :       INT i;
    4477              : 
    4478            1 :       *subhKey = 0;
    4479              : 
    4480            1 :       if (hDB > _database_entries || hDB <= 0) {
    4481            0 :          cm_msg(MERROR, "db_find_link", "Invalid database handle");
    4482            0 :          return DB_INVALID_HANDLE;
    4483              :       }
    4484              : 
    4485            1 :       if (!_database[hDB - 1].attached) {
    4486            0 :          cm_msg(MERROR, "db_find_link", "invalid database handle");
    4487            0 :          return DB_INVALID_HANDLE;
    4488              :       }
    4489              : 
    4490            1 :       pheader = _database[hDB - 1].database_header;
    4491            1 :       if (!hKey)
    4492            1 :          hKey = pheader->root_key;
    4493              : 
    4494              :       /* check if hKey argument is correct */
    4495            1 :       if (!db_validate_hkey(pheader, hKey)) {
    4496            0 :          return DB_INVALID_HANDLE;
    4497              :       }
    4498              : 
    4499            1 :       pkey = (KEY *) ((char *) pheader + hKey);
    4500              : 
    4501            1 :       if (pkey->type != TID_KEY) {
    4502            0 :          cm_msg(MERROR, "db_find_link", "key has no subkeys");
    4503            0 :          return DB_NO_KEY;
    4504              :       }
    4505            1 :       pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
    4506              : 
    4507            1 :       if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
    4508            0 :          if (!(pkey->access_mode & MODE_READ)) {
    4509            0 :             *subhKey = 0;
    4510            0 :             return DB_NO_ACCESS;
    4511              :          }
    4512              : 
    4513            0 :          *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
    4514              : 
    4515            0 :          return DB_SUCCESS;
    4516              :       }
    4517              : 
    4518            1 :       pkey_name = key_name;
    4519              :       do {
    4520              :          char str[MAX_ODB_PATH];
    4521              :          /* extract single subkey from key_name */
    4522            2 :          pkey_name = extract_key(pkey_name, str, sizeof(str));
    4523              : 
    4524              :          /* check if parent or current directory */
    4525            2 :          if (strcmp(str, "..") == 0) {
    4526            0 :             if (pkey->parent_keylist) {
    4527            0 :                pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
    4528              :                // FIXME: validate pkeylist->parent
    4529            0 :                pkey = (KEY *) ((char *) pheader + pkeylist->parent);
    4530              :             }
    4531            0 :             continue;
    4532              :          }
    4533            2 :          if (strcmp(str, ".") == 0)
    4534            0 :             continue;
    4535              : 
    4536              :          /* check if key is in keylist */
    4537              :          // FIXME: validate pkeylist->first_key
    4538            2 :          pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
    4539              : 
    4540            9 :          for (i = 0; i < pkeylist->num_keys; i++) {
    4541            9 :             if (!db_validate_key_offset(pheader, pkey->next_key)) {
    4542            0 :                cm_msg(MERROR, "db_find_link1", "Warning: database corruption, key \"%s\", next_key 0x%08X is invalid", key_name, pkey->next_key - (int)sizeof(DATABASE_HEADER));
    4543            0 :                *subhKey = 0;
    4544            0 :                return DB_CORRUPTED;
    4545              :             }
    4546              : 
    4547            9 :             if (equal_ustring(str, pkey->name))
    4548            2 :                break;
    4549              : 
    4550            7 :             pkey = (KEY *) ((char *) pheader + pkey->next_key); // FIXME: pkey->next_key could be zero
    4551              :          }
    4552              : 
    4553            2 :          if (i == pkeylist->num_keys) {
    4554            0 :             *subhKey = 0;
    4555            0 :             return DB_NO_KEY;
    4556              :          }
    4557              : 
    4558              :          /* resolve links if not last in chain */
    4559            2 :          if (pkey->type == TID_LINK && *pkey_name == '/') {
    4560              :             /* copy destination, strip '/' */
    4561            0 :             strcpy(str, (char *) pheader + pkey->data);
    4562            0 :             if (str[strlen(str) - 1] == '/')
    4563            0 :                str[strlen(str) - 1] = 0;
    4564              : 
    4565              :             /* append rest of key name */
    4566            0 :             strcat(str, pkey_name);
    4567            0 :             return db_find_link1(hDB, 0, str, subhKey);
    4568              :          }
    4569              : 
    4570              :          /* key found: check if last in chain */
    4571            2 :          if ((*pkey_name == '/')) {
    4572            1 :             if (pkey->type != TID_KEY) {
    4573            0 :                *subhKey = 0;
    4574            0 :                return DB_NO_KEY;
    4575              :             }
    4576              :          }
    4577              : 
    4578              :          /* descend one level */
    4579            2 :          pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
    4580              : 
    4581            2 :       } while (*pkey_name == '/' && *(pkey_name + 1));
    4582              : 
    4583            1 :       *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
    4584              :    }
    4585              : #endif                          /* LOCAL_ROUTINES */
    4586              : 
    4587            1 :    return DB_SUCCESS;
    4588              : }
    4589              : 
    4590              : /*------------------------------------------------------------------*/
    4591            0 : INT db_find_keys(HNDLE hDB, HNDLE hKeyRoot, char* odbpath, std::vector<HNDLE> &hKeyVector)
    4592              : /********************************************************************\
    4593              : 
    4594              :   Routine: db_find_keys
    4595              : 
    4596              :   Purpose: finds all ODB keys matching an odb path
    4597              :            '*' and '?' wildcard expansion available
    4598              : 
    4599              :   Input:
    4600              :     HNDLE               hDB              Handle to the database
    4601              :     HNDLE               hKeyRoot         Key handle to start the search
    4602              :     char                *odbpath         Pattern of keys in the form "/key/key/key", can include '*' and '?'
    4603              : 
    4604              :   Output:
    4605              :     std::vector<HNDLE>  &hKeyVector      Key handle
    4606              : 
    4607              :   Function value:
    4608              :     DB_SUCCESS              Successful completion
    4609              :     DB_INVALID_HANDLE       Database handle is invalid
    4610              :     DB_NO_KEY               No access to any key
    4611              :     DB_NO_ACCESS            No access to a read key
    4612              : 
    4613              : \********************************************************************/
    4614              : {
    4615            0 :       HNDLE hKey, hSubKey=0;
    4616              :       KEY key;
    4617              :       INT status;
    4618            0 :       char *pattern = NULL;
    4619            0 :       char *subkeypath = NULL;
    4620            0 :       char *parentkeypath = NULL;
    4621              :       char localpath[256];
    4622              : 
    4623              :       //make work on a local copy od odbpath to allow recursion
    4624            0 :       strcpy(localpath, odbpath);
    4625            0 :       parentkeypath = localpath;
    4626              : 
    4627            0 :       char *wildcard = strpbrk(localpath, "*?");
    4628            0 :       if(wildcard){
    4629              :          //wildcard found
    4630            0 :          subkeypath = strchr(wildcard, '/');
    4631            0 :          if(subkeypath){
    4632              :             //truncate the string at slash
    4633            0 :             *subkeypath = 0;
    4634            0 :             subkeypath++;
    4635              :          }
    4636            0 :          parentkeypath = strrchr(localpath, '/');
    4637            0 :          if(parentkeypath){
    4638              :             //truncate there too
    4639            0 :             *parentkeypath = 0;
    4640            0 :             pattern = parentkeypath+1;
    4641            0 :             if((parentkeypath-1) == localpath){
    4642              :                //path starts with '/', no parent path
    4643            0 :                parentkeypath = NULL;
    4644              :             } else {
    4645            0 :                parentkeypath = localpath;
    4646              :             }
    4647              :          } else {
    4648              :             //wildcard at top level, start with pattern
    4649            0 :             pattern = localpath;
    4650            0 :             parentkeypath = NULL;
    4651              :          }
    4652              :       }
    4653              : 
    4654              :       //if available search for parent key path
    4655            0 :       if(parentkeypath){
    4656            0 :          status = db_find_key(hDB, hKeyRoot, parentkeypath, &hKey);
    4657            0 :          if (status != DB_SUCCESS)
    4658            0 :             return status;
    4659              :       } else {
    4660            0 :          hKey = hKeyRoot;
    4661              :       }
    4662              : 
    4663              :       //if a pattern is found
    4664            0 :       if(pattern){
    4665              :          //try match all subkeys
    4666            0 :          status = DB_NO_KEY;
    4667            0 :          for (int i=0 ; ; i++)
    4668              :          {
    4669            0 :             db_enum_key(hDB, hKey, i, &hSubKey);
    4670            0 :             if (!hSubKey)
    4671            0 :                break; // end of list reached
    4672            0 :             db_get_key(hDB, hSubKey, &key);
    4673              : 
    4674            0 :             if(strmatch(pattern, key.name)){
    4675              :                //match
    4676            0 :                if(!subkeypath){
    4677              :                   //found
    4678            0 :                   hKeyVector.push_back(hSubKey);
    4679              : 
    4680            0 :                } else if (key.type == TID_KEY){
    4681              :                   //recurse with hSubKey as root key and subkeypath as path
    4682            0 :                   int subkeystatus = db_find_keys(hDB, hSubKey, subkeypath, hKeyVector);
    4683            0 :                   if (subkeystatus != DB_NO_KEY)
    4684            0 :                      status = subkeystatus;
    4685              : 
    4686            0 :                   if (status != DB_SUCCESS && status != DB_NO_KEY)
    4687            0 :                      break;
    4688              :                }
    4689              :             }
    4690            0 :          }
    4691              : 
    4692            0 :          return status;
    4693              :       } else {
    4694              :          //no pattern: hKey matches!
    4695            0 :          db_get_key(hDB, hKey, &key);
    4696            0 :          hKeyVector.push_back(hKey);
    4697              : 
    4698            0 :          return DB_SUCCESS;
    4699              :       }
    4700              : 
    4701              : }
    4702              : 
    4703              : /*------------------------------------------------------------------*/
    4704            0 : INT db_get_parent(HNDLE hDB, HNDLE hKey, HNDLE * parenthKey)
    4705              : /********************************************************************\
    4706              : 
    4707              :   Routine: db_get_parent
    4708              : 
    4709              :   Purpose: return an handle to the parent key
    4710              : 
    4711              :   Input:
    4712              :     HNDLE  bufer_handle     Handle to the database
    4713              :     HNDLE  hKey       Key handle of the key
    4714              : 
    4715              :   Output:
    4716              :     INT    *handle          Parent key handle
    4717              : 
    4718              :   Function value:
    4719              :     DB_SUCCESS              Successful completion
    4720              : 
    4721              : \********************************************************************/
    4722              : {
    4723            0 :    if (rpc_is_remote())
    4724            0 :       return rpc_call(RPC_DB_GET_PARENT, hDB, hKey, parenthKey);
    4725              : 
    4726              : #ifdef LOCAL_ROUTINES
    4727              :    {
    4728              : 
    4729              :       DATABASE_HEADER *pheader;
    4730              :       const KEY *pkey;
    4731              : 
    4732            0 :       if (hDB > _database_entries || hDB <= 0) {
    4733            0 :          cm_msg(MERROR, "db_get_parent", "invalid database handle");
    4734            0 :          return DB_INVALID_HANDLE;
    4735              :       }
    4736              : 
    4737            0 :       if (!_database[hDB - 1].attached) {
    4738            0 :          cm_msg(MERROR, "db_get_parent", "invalid database handle");
    4739            0 :          return DB_INVALID_HANDLE;
    4740              :       }
    4741              : 
    4742            0 :       if (hKey < (int) sizeof(DATABASE_HEADER)) {
    4743            0 :          cm_msg(MERROR, "db_get_parent", "invalid key handle");
    4744            0 :          return DB_INVALID_HANDLE;
    4745              :       }
    4746              : 
    4747            0 :       db_lock_database(hDB);
    4748              : 
    4749            0 :       pheader = _database[hDB - 1].database_header;
    4750            0 :       pkey = (const KEY *) ((char *) pheader + hKey);
    4751              : 
    4752              :       /* find parent key */
    4753            0 :       const KEYLIST *pkeylist = (const KEYLIST *) ((char *) pheader + pkey->parent_keylist);
    4754              : 
    4755            0 :       if (!db_validate_hkey(pheader, pkeylist->parent)) {
    4756            0 :          db_unlock_database(hDB);
    4757            0 :          return DB_INVALID_HANDLE;
    4758              :       }
    4759              : 
    4760            0 :       pkey = (const KEY *) ((char *) pheader + pkeylist->parent);
    4761              : 
    4762            0 :       *parenthKey = (POINTER_T) pkey - (POINTER_T) pheader;
    4763              : 
    4764            0 :       db_unlock_database(hDB);
    4765              :    }
    4766              : #endif
    4767              : 
    4768            0 :    return DB_SUCCESS;
    4769              : }
    4770              : 
    4771              : /*------------------------------------------------------------------*/
    4772            0 : INT db_scan_tree(HNDLE hDB, HNDLE hKey, INT level, INT(*callback) (HNDLE, HNDLE, KEY *, INT, void *), void *info)
    4773              : /********************************************************************\
    4774              : 
    4775              :   Routine: db_scan_tree
    4776              : 
    4777              :   Purpose: Scan a subtree recursively and call 'callback' for each key
    4778              : 
    4779              :   Input:
    4780              :     HNDLE  hDB              Handle to the database
    4781              :     HNDLE  hKeyRoot         Key to start scan from, 0 for root
    4782              :     INT    level            Recursion level
    4783              :     void   *callback        Callback routine called with params:
    4784              :                               hDB   Copy of hDB
    4785              :                               hKey  Copy of hKey
    4786              :                               key   Key associated with hKey
    4787              :                               INT   Recursion level
    4788              :                               info  Copy of *info
    4789              :     void   *info            Optional data copied to callback routine
    4790              : 
    4791              :   Output:
    4792              :     implicit via callback
    4793              : 
    4794              :   Function value:
    4795              :     DB_SUCCESS              Successful completion
    4796              :     <all error codes of db_get_key>
    4797              : 
    4798              : \********************************************************************/
    4799              : {
    4800              :    HNDLE hSubkey;
    4801              :    KEY key;
    4802              :    INT i, status;
    4803              : 
    4804            0 :    status = db_get_link(hDB, hKey, &key);
    4805            0 :    if (status != DB_SUCCESS)
    4806            0 :       return status;
    4807              : 
    4808            0 :    status = callback(hDB, hKey, &key, level, info);
    4809            0 :    if (status == 0)
    4810            0 :       return status;
    4811              : 
    4812            0 :    if (key.type == TID_KEY) {
    4813            0 :       for (i = 0;; i++) {
    4814            0 :          db_enum_link(hDB, hKey, i, &hSubkey);
    4815              : 
    4816            0 :          if (!hSubkey)
    4817            0 :             break;
    4818              : 
    4819            0 :          db_scan_tree(hDB, hSubkey, level + 1, callback, info);
    4820              :       }
    4821              :    }
    4822              : 
    4823            0 :    return DB_SUCCESS;
    4824              : }
    4825              : 
    4826              : #ifdef LOCAL_ROUTINES
    4827              : 
    4828           67 : int db_scan_tree_locked(const DATABASE_HEADER* pheader, const KEY* pkey, int level, int(*callback) (const DATABASE_HEADER* pheader, const KEY *, int, void *, db_err_msg** msg), void *info, db_err_msg** msg)
    4829              : {
    4830           67 :    assert(pkey != NULL);
    4831           67 :    assert(level < MAX_ODB_PATH);
    4832              : 
    4833           67 :    int status = callback(pheader, pkey, level, info, msg);
    4834           67 :    if (status == 0)
    4835            0 :       return status;
    4836              : 
    4837           67 :    if (pkey->type == TID_KEY) {
    4838           21 :       const KEY* subkey = db_enum_first_locked(pheader, pkey, msg);
    4839           85 :       while (subkey != NULL) {
    4840           64 :          db_scan_tree_locked(pheader, subkey, level + 1, callback, info, msg);
    4841           64 :          subkey = db_enum_next_locked(pheader, pkey, subkey, msg);
    4842              :       }
    4843              :    }
    4844              : 
    4845           67 :    return DB_SUCCESS;
    4846              : }
    4847              : 
    4848              : #endif // LOCAL_ROUTINES
    4849              : 
    4850              : /*------------------------------------------------------------------*/
    4851            8 : INT db_scan_tree_link(HNDLE hDB, HNDLE hKey, INT level, void (*callback) (HNDLE, HNDLE, KEY *, INT, void *), void *info)
    4852              : /********************************************************************\
    4853              : 
    4854              :   Routine: db_scan_tree_link
    4855              : 
    4856              :   Purpose: Scan a subtree recursively and call 'callback' for each key.
    4857              :            Similar to db_scan_tree but without following links.
    4858              : 
    4859              :   Input:
    4860              :     HNDLE  hDB              Handle to the database
    4861              :     HNDLE  hKeyRoot         Key to start scan from, 0 for root
    4862              :     INT    level            Recursion level
    4863              :     void   *callback        Callback routine called with params:
    4864              :                               hDB   Copy of hDB
    4865              :                               hKey  Copy of hKey
    4866              :                               key   Key associated with hKey
    4867              :                               INT   Recursion level
    4868              :                               info  Copy of *info
    4869              :     void   *info            Optional data copied to callback routine
    4870              : 
    4871              :   Output:
    4872              :     implicit via callback
    4873              : 
    4874              :   Function value:
    4875              :     DB_SUCCESS              Successful completion
    4876              :     <all error codes of db_get_key>
    4877              : 
    4878              : \********************************************************************/
    4879              : {
    4880              :    HNDLE hSubkey;
    4881              :    KEY key;
    4882              :    INT i, status;
    4883              : 
    4884            8 :    status = db_get_key(hDB, hKey, &key);
    4885            8 :    if (status != DB_SUCCESS)
    4886            0 :       return status;
    4887              : 
    4888            8 :    callback(hDB, hKey, &key, level, info);
    4889              : 
    4890            8 :    if (key.type == TID_KEY) {
    4891            8 :       for (i = 0;; i++) {
    4892            8 :          db_enum_link(hDB, hKey, i, &hSubkey);
    4893              : 
    4894            8 :          if (!hSubkey)
    4895            4 :             break;
    4896              : 
    4897            4 :          db_scan_tree_link(hDB, hSubkey, level + 1, callback, info);
    4898              :       }
    4899              :    }
    4900              : 
    4901            8 :    return DB_SUCCESS;
    4902              : }
    4903              : 
    4904              : #ifdef LOCAL_ROUTINES
    4905              : /*------------------------------------------------------------------*/
    4906            4 : static std::string db_get_path_locked(const DATABASE_HEADER* pheader, const KEY* pkey)
    4907              : {
    4908            8 :    std::string path = "";
    4909              :    while (1) {
    4910              :       //printf("db_get_path_locked: hkey %d, pkey name \"%s\", type %d, parent %d, path \"%s\"\n", hKey, pkey->name, pkey->type, pkey->parent_keylist, path.c_str());
    4911              : 
    4912              :       /* check key type */
    4913           14 :       if (pkey->type <= 0 || pkey->type >= TID_LAST) {
    4914              :          char buf[256];
    4915            0 :          sprintf(buf, "(INVALID_KEY_TYPE_%d)", pkey->type);
    4916            0 :          std::string xpath;
    4917            0 :          xpath += buf;
    4918            0 :          if (path.length() > 0) {
    4919            0 :             xpath += "/";
    4920            0 :             xpath += path;
    4921              :          }
    4922            0 :          return xpath;
    4923            0 :       }
    4924              : 
    4925              :       /* add key name in front of path */
    4926           14 :       std::string str = path;
    4927           14 :       path = "";
    4928           14 :       if (pkey->name[0] == 0) {
    4929            0 :          path += "(EMPTY_NAME)";
    4930              :       } else {
    4931           14 :          path += pkey->name;
    4932              :       }
    4933           14 :       if (str.length() > 0)
    4934           10 :          path += "/";
    4935           14 :       path += str;
    4936              : 
    4937           14 :       if (!pkey->parent_keylist) {
    4938            0 :          return path;
    4939              :       }
    4940              :       
    4941           14 :       if (!db_validate_data_offset(pheader, pkey->parent_keylist)) {
    4942            0 :          return "(INVALID_PARENT_KEYLIST)/" + path;
    4943              :       }
    4944              : 
    4945              :       /* find parent key */
    4946           14 :       const KEYLIST* pkeylist = (const KEYLIST *) ((char *) pheader + pkey->parent_keylist);
    4947              : 
    4948           14 :       if (pkeylist->parent == pheader->root_key) {
    4949            4 :          return "/" + path;
    4950              :       }
    4951              : 
    4952           10 :       if (pkeylist->parent == 0) {
    4953            0 :          return "(NULL_PARENT)/" + path;
    4954              :       }
    4955              : 
    4956           10 :       if (!db_validate_key_offset(pheader, pkeylist->parent)) {
    4957            0 :          return "(INVALID_PARENT)/" + path;
    4958              :       }
    4959              : 
    4960           10 :       pkey = (const KEY *) ((char *) pheader + pkeylist->parent);
    4961           24 :    };
    4962              : 
    4963              :    /* NOT REACHED */
    4964            4 : }
    4965              : 
    4966              : /*------------------------------------------------------------------*/
    4967            4 : static std::string db_get_path_locked(const DATABASE_HEADER* pheader, HNDLE hKey)
    4968              : {
    4969              :    //printf("db_get_path_locked: hkey %d\n", hKey);
    4970              : 
    4971            4 :    if (!hKey)
    4972            0 :       hKey = pheader->root_key;
    4973              : 
    4974            4 :    if (hKey == pheader->root_key) {
    4975            0 :       return "/";
    4976              :    }
    4977              : 
    4978              :    /* check if hKey argument is correct */
    4979            4 :    if (hKey == 0) {
    4980            0 :       return "(ZERO_HKEY)";
    4981              :    }
    4982              : 
    4983              :    /* check if hKey argument is correct */
    4984            4 :    if (!db_validate_key_offset(pheader, hKey)) {
    4985            0 :       return "(INVALID_HKEY)";
    4986              :    }
    4987              : 
    4988            4 :    const KEY* pkey = (const KEY *) ((char *) pheader + hKey);
    4989              : 
    4990            4 :    return db_get_path_locked(pheader, pkey);
    4991              : }
    4992              : #endif /* LOCAL_ROUTINES */
    4993              : 
    4994              : /*------------------------------------------------------------------*/
    4995            0 : INT db_get_path(HNDLE hDB, HNDLE hKey, char *path, INT buf_size)
    4996              : /********************************************************************\
    4997              : 
    4998              :   Routine: db_get_path
    4999              : 
    5000              :   Purpose: Get full path of a key
    5001              : 
    5002              :   Input:
    5003              :     HNDLE  hDB              Handle to the database
    5004              :     HNDLE  hKey             Key handle
    5005              :     INT    buf_size         Maximum size of path buffer (including
    5006              :                             trailing zero)
    5007              : 
    5008              :   Output:
    5009              :     char   path[buf_size]   Path string
    5010              : 
    5011              :   Function value:
    5012              :     DB_SUCCESS              Successful completion
    5013              :     DB_INVALID_HANDLE       Database handle is invalid
    5014              :     DB_NO_MEMORY            path buffer is to small to contain full
    5015              :                             string
    5016              : 
    5017              : \********************************************************************/
    5018              : {
    5019            0 :    if (rpc_is_remote())
    5020            0 :       return rpc_call(RPC_DB_GET_PATH, hDB, hKey, path, buf_size);
    5021              : 
    5022              : #ifdef LOCAL_ROUTINES
    5023              :    {
    5024              :       DATABASE_HEADER *pheader;
    5025              : 
    5026            0 :       if (hDB > _database_entries || hDB <= 0) {
    5027            0 :          cm_msg(MERROR, "db_get_path", "invalid database handle");
    5028            0 :          return DB_INVALID_HANDLE;
    5029              :       }
    5030              : 
    5031            0 :       if (!_database[hDB - 1].attached) {
    5032            0 :          cm_msg(MERROR, "db_get_path", "invalid database handle");
    5033            0 :          return DB_INVALID_HANDLE;
    5034              :       }
    5035              : 
    5036            0 :       db_lock_database(hDB);
    5037              : 
    5038            0 :       pheader = _database[hDB - 1].database_header;
    5039              : 
    5040            0 :       std::string xpath = db_get_path_locked(pheader, hKey);
    5041              : 
    5042            0 :       db_unlock_database(hDB);
    5043              : 
    5044            0 :       mstrlcpy(path, xpath.c_str(), buf_size);
    5045              : 
    5046            0 :       return DB_SUCCESS;
    5047            0 :    }
    5048              : #endif /* LOCAL_ROUTINES */
    5049              : 
    5050              :    return DB_SUCCESS;
    5051              : }
    5052              : 
    5053              : /*------------------------------------------------------------------*/
    5054            4 : std::string db_get_path(HNDLE hDB, HNDLE hKey)
    5055              : /********************************************************************\
    5056              : 
    5057              :   Routine: db_get_path
    5058              : 
    5059              :   Purpose: Get full path of a key
    5060              : 
    5061              :   Input:
    5062              :     HNDLE  hDB              Handle to the database
    5063              :     HNDLE  hKey             Key handle
    5064              : 
    5065              :   Function value:           Path string
    5066              : 
    5067              : \********************************************************************/
    5068              : {
    5069            4 :    if (rpc_is_remote()) {
    5070              :       char path[MAX_ODB_PATH];
    5071            0 :       int status = rpc_call(RPC_DB_GET_PATH, hDB, hKey, path, sizeof(path));
    5072            0 :       if (status != DB_SUCCESS) {
    5073            0 :          sprintf(path, "(RPC_DB_GET_PATH status %d)", status);
    5074              :       }
    5075            0 :       return path;
    5076              :    }
    5077              : 
    5078              : #ifdef LOCAL_ROUTINES
    5079              :    {
    5080              :       DATABASE_HEADER *pheader;
    5081              : 
    5082            4 :       if (hDB > _database_entries || hDB <= 0) {
    5083            0 :          cm_msg(MERROR, "db_get_path", "invalid database handle");
    5084            0 :          return "(DB_INVALID_HANDLE)";
    5085              :       }
    5086              : 
    5087            4 :       if (!_database[hDB - 1].attached) {
    5088            0 :          cm_msg(MERROR, "db_get_path", "invalid database handle");
    5089            0 :          return "(DB_INVALID_HANDLE)";
    5090              :       }
    5091              : 
    5092            4 :       db_lock_database(hDB);
    5093              : 
    5094            4 :       pheader = _database[hDB - 1].database_header;
    5095              : 
    5096            4 :       std::string xpath = db_get_path_locked(pheader, hKey);
    5097              : 
    5098            4 :       db_unlock_database(hDB);
    5099              : 
    5100            4 :       return xpath;
    5101            4 :    }
    5102              : #endif /* LOCAL_ROUTINES */
    5103              : 
    5104              :    return "(no LOCAL_ROUTINES)";
    5105              : }
    5106              : 
    5107              : #ifdef LOCAL_ROUTINES
    5108              : /*------------------------------------------------------------------*/
    5109            0 : static int db_find_open_records(HNDLE hDB, HNDLE hKey, KEY * key, INT level, void *xresult)
    5110              : {
    5111              :    /* check if this key has notify count set */
    5112            0 :    if (key->notify_count) {
    5113              : 
    5114            0 :       db_lock_database(hDB);
    5115              : 
    5116            0 :       DATABASE_HEADER *pheader = _database[hDB - 1].database_header;
    5117              : 
    5118            0 :       std::string path = db_get_path_locked(pheader, hKey);
    5119              : 
    5120            0 :       std::string line = msprintf("%s open %d times by", path.c_str(), key->notify_count);
    5121              : 
    5122              :       //printf("path [%s] key.name [%s]\n", path, key->name);
    5123              : 
    5124            0 :       int count = 0;
    5125            0 :       for (int i = 0; i < pheader->max_client_index; i++) {
    5126            0 :          DATABASE_CLIENT *pclient = &pheader->client[i];
    5127            0 :          for (int j = 0; j < pclient->max_index; j++)
    5128            0 :             if (pclient->open_record[j].handle == hKey) {
    5129            0 :                count++;
    5130            0 :                line += " \"";
    5131            0 :                line += pclient->name;
    5132            0 :                line += "\"";
    5133              :                //sprintf(line + strlen(line), ", handle %d, mode %d ", pclient->open_record[j].handle, pclient->open_record[j].access_mode);
    5134              :             }
    5135              :       }
    5136              : 
    5137            0 :       if (count < 1) {
    5138            0 :          line += " a deleted client";
    5139              :       }
    5140              : 
    5141            0 :       line += "\n";
    5142              : 
    5143            0 :       std::string *result = (std::string*)xresult;
    5144            0 :       *result += line;
    5145              : 
    5146            0 :       db_unlock_database(hDB);
    5147            0 :    }
    5148            0 :    return DB_SUCCESS;
    5149              : }
    5150              : 
    5151            0 : static int db_fix_open_records(HNDLE hDB, HNDLE hKey, KEY * key, INT level, void *xresult)
    5152              : {
    5153            0 :    std::string *result = (std::string*)xresult;
    5154              :    
    5155              :    /* check if this key has notify count set */
    5156            0 :    if (key->notify_count) {
    5157            0 :       db_lock_database(hDB);
    5158            0 :       DATABASE_HEADER *pheader = _database[hDB - 1].database_header;
    5159            0 :       db_allow_write_locked(&_database[hDB - 1], "db_fix_open_records");
    5160              : 
    5161              :       int i;
    5162            0 :       for (i = 0; i < pheader->max_client_index; i++) {
    5163            0 :          DATABASE_CLIENT *pclient = &pheader->client[i];
    5164              :          int j;
    5165            0 :          for (j = 0; j < pclient->max_index; j++)
    5166            0 :             if (pclient->open_record[j].handle == hKey)
    5167            0 :                break;
    5168            0 :          if (j < pclient->max_index)
    5169            0 :             break;
    5170              :       }
    5171            0 :       if (i == pheader->max_client_index) {
    5172              :          /* check if hKey argument is correct */
    5173            0 :          if (!db_validate_hkey(pheader, hKey)) {
    5174            0 :             db_unlock_database(hDB);
    5175            0 :             return DB_SUCCESS;
    5176              :          }
    5177              : 
    5178              :          /* reset notify count */
    5179            0 :          KEY *pkey = (KEY *) ((char *) pheader + hKey);
    5180            0 :          pkey->notify_count = 0;
    5181              : 
    5182            0 :          std::string path = db_get_path_locked(pheader, hKey);
    5183            0 :          *result += path;
    5184            0 :          *result += " fixed\n";
    5185            0 :       }
    5186              : 
    5187            0 :       db_unlock_database(hDB);
    5188              :    }
    5189            0 :    return DB_SUCCESS;
    5190              : }
    5191              : #endif                          /* LOCAL_ROUTINES */
    5192              : 
    5193            0 : INT db_get_open_records(HNDLE hDB, HNDLE hKey, char *str, INT buf_size, BOOL fix)
    5194              : /********************************************************************\
    5195              : 
    5196              :   Routine: db_get_open_records
    5197              : 
    5198              :   Purpose: Return a string with all open records
    5199              : 
    5200              :   Input:
    5201              :     HNDLE  hDB              Handle to the database
    5202              :     HNDLE  hKey             Key to start search from, 0 for root
    5203              :     INT    buf_size         Size of string
    5204              :     INT    fix              If TRUE, fix records which are open
    5205              :                             but have no client belonging to it.
    5206              : 
    5207              :   Output:
    5208              :     char   *str             Result string. Individual records are
    5209              :                             separated with new lines.
    5210              : 
    5211              :   Function value:
    5212              :     DB_SUCCESS              Successful completion
    5213              : 
    5214              : \********************************************************************/
    5215              : {
    5216            0 :    str[0] = 0;
    5217              : 
    5218            0 :    if (rpc_is_remote())
    5219            0 :       return rpc_call(RPC_DB_GET_OPEN_RECORDS, hDB, hKey, str, buf_size);
    5220              : 
    5221            0 :    std::string result;
    5222              : 
    5223              : #ifdef LOCAL_ROUTINES
    5224              : 
    5225            0 :    if (fix)
    5226            0 :       db_scan_tree(hDB, hKey, 0, db_fix_open_records, &result); // FIXME: should use db_scan_tree_wlocked()
    5227              :    else
    5228            0 :       db_scan_tree(hDB, hKey, 0, db_find_open_records, &result); // FIXME: should use db_scan_tree_locked()
    5229              : 
    5230              : #endif
    5231              : 
    5232            0 :    mstrlcpy(str, result.c_str(), buf_size);
    5233              : 
    5234            0 :    return DB_SUCCESS;
    5235            0 : }
    5236              : 
    5237              : /**dox***************************************************************/
    5238              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    5239              : 
    5240              : 
    5241              : /********************************************************************/
    5242              : /**
    5243              : Set value of a single key.
    5244              : 
    5245              : The function sets a single value or a whole array to a ODB key.
    5246              : Since the data buffer is of type void, no type checking can be performed by the
    5247              : compiler. Therefore the type has to be explicitly supplied, which is checked
    5248              : against the type stored in the ODB. key_name can contain the full path of a key
    5249              : (like: "/Equipment/Trigger/Settings/Level1") while hkey is zero which refers
    5250              : to the root, or hkey can refer to a sub-directory (like /Equipment/Trigger)
    5251              : and key_name is interpreted relative to that directory like "Settings/Level1".
    5252              : \code
    5253              : INT level1;
    5254              :   db_set_value(hDB, 0, "/Equipment/Trigger/Settings/Level1",
    5255              :                           &level1, sizeof(level1), 1, TID_INT32);
    5256              : \endcode
    5257              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    5258              : @param hKeyRoot Handle for key where search starts, zero for root.
    5259              : @param key_name Name of key to search, can contain directories.
    5260              : @param data Address of data.
    5261              : @param data_size Size of data (in bytes).
    5262              : @param num_values Number of data elements.
    5263              : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types)
    5264              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_ACCESS, DB_TYPE_MISMATCH
    5265              : */
    5266          102 : INT db_set_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const void *data,
    5267              :                  INT data_size, INT num_values, DWORD type)
    5268              : {
    5269          102 :    if (rpc_is_remote())
    5270            0 :       return rpc_call(RPC_DB_SET_VALUE, hDB, hKeyRoot, key_name, data, data_size, num_values, type);
    5271              : 
    5272              : #ifdef LOCAL_ROUTINES
    5273              :    {
    5274              :       INT status;
    5275              : 
    5276          102 :       if (num_values == 0)
    5277            0 :          return DB_INVALID_PARAM;
    5278              : 
    5279          102 :       db_lock_database(hDB);
    5280              : 
    5281          102 :       DATABASE_HEADER* pheader = _database[hDB - 1].database_header;
    5282              : 
    5283          102 :       db_allow_write_locked(&_database[hDB-1], "db_set_value");
    5284              : 
    5285          102 :       db_err_msg* msg = NULL;
    5286              : 
    5287          102 :       KEY* pkey_root = (KEY*)db_get_pkey(pheader, hKeyRoot, &status, "db_set_value", &msg);
    5288              : 
    5289          102 :       if (pkey_root) {
    5290          102 :          status = db_set_value_wlocked(pheader, hDB, pkey_root, key_name, data, data_size, num_values, type, &msg);
    5291              :       }
    5292              : 
    5293          102 :       db_unlock_database(hDB);
    5294          102 :       if (msg)
    5295            0 :          db_flush_msg(&msg);
    5296              : 
    5297          102 :       return status;
    5298              :    }
    5299              : #endif                          /* LOCAL_ROUTINES */
    5300              : 
    5301              :    return DB_SUCCESS;
    5302              : }
    5303              : 
    5304              : #ifdef LOCAL_ROUTINES
    5305          119 : static int db_set_value_wlocked(DATABASE_HEADER* pheader, HNDLE hDB, KEY* pkey_root, const char *key_name, const void *data, INT data_size, INT num_values, DWORD type, db_err_msg** msg)
    5306              : {
    5307              :    INT status;
    5308              : 
    5309          119 :    if (num_values == 0)
    5310            0 :       return DB_INVALID_PARAM;
    5311              :    
    5312          119 :    KEY* pkey = (KEY*)db_find_pkey_locked(pheader, pkey_root, key_name, &status, msg);
    5313              : 
    5314          119 :    if (!pkey) {
    5315           70 :       status = db_create_key_wlocked(pheader, pkey_root, key_name, type, &pkey, msg);
    5316           70 :       if (status != DB_SUCCESS && status != DB_CREATED)
    5317            0 :          return status;
    5318              :    }
    5319              : 
    5320          119 :    if (data_size == 0) {
    5321            0 :       db_msg(msg, MERROR, "db_set_value", "zero data size not allowed");
    5322            0 :       return DB_TYPE_MISMATCH;
    5323              :    }
    5324              :    
    5325          119 :    if (type != TID_STRING && type != TID_LINK && data_size != rpc_tid_size(type) * num_values) {
    5326            0 :       db_msg(msg, MERROR, "db_set_value", "\"%s\" data_size %d does not match tid %d size %d times num_values %d", key_name, data_size, type, rpc_tid_size(type), num_values);
    5327            0 :       return DB_TYPE_MISMATCH;
    5328              :    }
    5329              : 
    5330          119 :    status = db_check_set_data_locked(pheader, pkey, data, data_size, num_values, type, "db_set_value", msg);
    5331              : 
    5332          119 :    if (status != DB_SUCCESS)
    5333            0 :       return status;
    5334              : 
    5335          119 :    status = db_set_data_wlocked(pheader, pkey, data, data_size, num_values, type, "db_set_value", msg);
    5336              : 
    5337          119 :    if (status != DB_SUCCESS)
    5338            0 :       return status;
    5339              :    
    5340          119 :    db_notify_clients_locked(pheader, hDB, db_pkey_to_hkey(pheader, pkey), -1, TRUE, msg);
    5341              :    
    5342          119 :    return DB_SUCCESS;
    5343              : }
    5344              : #endif /* LOCAL_ROUTINES */
    5345              : 
    5346              : /********************************************************************/
    5347              : /**
    5348              : Set single value of an array.
    5349              : 
    5350              : The function sets a single value of an ODB key which is an array.
    5351              : key_name can contain the full path of a key
    5352              : (like: "/Equipment/Trigger/Settings/Level1") while hkey is zero which refers
    5353              : to the root, or hkey can refer to a sub-directory (like /Equipment/Trigger)
    5354              : and key_name is interpreted relative to that directory like "Settings/Level1".
    5355              : \code
    5356              : INT level1;
    5357              :   db_set_value_index(hDB, 0, "/Equipment/Trigger/Settings/Level1",
    5358              :                           &level1, sizeof(level1), 15, TID_INT32, FALSE);
    5359              : \endcode
    5360              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    5361              : @param hKeyRoot Handle for key where search starts, zero for root.
    5362              : @param key_name Name of key to search, can contain directories.
    5363              : @param data Address of data.
    5364              : @param data_size Size of data (in bytes).
    5365              : @param index Array index of value.
    5366              : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types)
    5367              : @param truncate Truncate array to current index if TRUE
    5368              : @return \<same as db_set_data_index\>
    5369              : */
    5370            0 : INT db_set_value_index(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const void *data,
    5371              :                        INT data_size, INT idx, DWORD type, BOOL trunc)
    5372              : {
    5373              :    int status;
    5374              :    HNDLE hkey;
    5375              : 
    5376            0 :    status = db_find_key(hDB, hKeyRoot, key_name, &hkey);
    5377            0 :    if (!hkey) {
    5378            0 :       status = db_create_key(hDB, hKeyRoot, key_name, type);
    5379            0 :       status = db_find_key(hDB, hKeyRoot, key_name, &hkey);
    5380            0 :       if (status != DB_SUCCESS)
    5381            0 :          return status;
    5382              :    } else
    5383            0 :       if (trunc) {
    5384            0 :          status = db_set_num_values(hDB, hkey, idx + 1);
    5385            0 :          if (status != DB_SUCCESS)
    5386            0 :             return status;
    5387              :       }
    5388              : 
    5389            0 :    return db_set_data_index(hDB, hkey, data, data_size, idx, type);
    5390              : }
    5391              : 
    5392              : /********************************************************************/
    5393              : /**
    5394              : Get value of a single key.
    5395              : 
    5396              : The function returns single values or whole arrays which are contained
    5397              : in an ODB key. Since the data buffer is of type void, no type checking can be
    5398              : performed by the compiler. Therefore the type has to be explicitly supplied,
    5399              : which is checked against the type stored in the ODB. key_name can contain the
    5400              : full path of a key (like: "/Equipment/Trigger/Settings/Level1") while hkey is
    5401              : zero which refers to the root, or hkey can refer to a sub-directory
    5402              : (like: /Equipment/Trigger) and key_name is interpreted relative to that directory
    5403              : like "Settings/Level1".
    5404              : \code
    5405              : INT level1, size;
    5406              :   size = sizeof(level1);
    5407              :   db_get_value(hDB, 0, "/Equipment/Trigger/Settings/Level1",
    5408              :                                    &level1, &size, TID_INT32, 0);
    5409              : \endcode
    5410              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    5411              : @param hKeyRoot Handle for key where search starts, zero for root.
    5412              : @param key_name Name of key to search, can contain directories.
    5413              : @param data Address of data.
    5414              : @param buf_size Maximum buffer size on input, number of written bytes on return.
    5415              : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types)
    5416              : @param create If TRUE, create key if not existing
    5417              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_ACCESS, DB_TYPE_MISMATCH,
    5418              : DB_TRUNCATED, DB_NO_KEY
    5419              : */
    5420           35 : INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, void *data, INT * buf_size, DWORD type, BOOL create)
    5421              : {
    5422           35 :    if (rpc_is_remote())
    5423            0 :       return rpc_call(RPC_DB_GET_VALUE, hDB, hKeyRoot, key_name, data, buf_size, type, create);
    5424              : 
    5425              : #ifdef LOCAL_ROUTINES
    5426              :    {
    5427              :       INT status;
    5428              : 
    5429           35 :       if (hDB > _database_entries || hDB <= 0) {
    5430            0 :          cm_msg(MERROR, "db_get_value", "invalid database handle %d", hDB);
    5431            0 :          return DB_INVALID_HANDLE;
    5432              :       }
    5433              : 
    5434           35 :       if (!_database[hDB - 1].attached) {
    5435            0 :          cm_msg(MERROR, "db_get_value", "invalid database handle %d", hDB);
    5436            0 :          return DB_INVALID_HANDLE;
    5437              :       }
    5438              : 
    5439              :       /* check if key name contains index */
    5440              :       char keyname[MAX_ODB_PATH];
    5441           35 :       mstrlcpy(keyname, key_name, sizeof(keyname));
    5442           35 :       int idx = -1;
    5443           35 :       if (strchr(keyname, '[') && strchr(keyname, ']')) {
    5444              :          char* p;
    5445            4 :          for (p = strchr(keyname, '[') + 1; *p && *p != ']'; p++)
    5446            2 :             if (!isdigit(*p))
    5447            0 :                break;
    5448              : 
    5449            2 :          if (*p && *p == ']') {
    5450            2 :             idx = atoi(strchr(keyname, '[') + 1);
    5451            2 :             *strchr(keyname, '[') = 0;
    5452              :          }
    5453              :       }
    5454              : 
    5455              :       /* now lock database */
    5456           35 :       db_lock_database(hDB);
    5457              : 
    5458           35 :       DATABASE_HEADER* pheader = _database[hDB - 1].database_header;
    5459           35 :       db_err_msg* msg = NULL;
    5460              : 
    5461           35 :       const KEY* pkey_root = db_get_pkey(pheader, hKeyRoot, &status, "db_get_value", &msg);
    5462              : 
    5463           35 :       if (!pkey_root) {
    5464            0 :          db_unlock_database(hDB);
    5465            0 :          if (msg)
    5466            0 :             db_flush_msg(&msg);
    5467            0 :          return status;
    5468              :       }
    5469              : 
    5470           35 :       const KEY* pkey = db_find_pkey_locked(pheader, pkey_root, keyname, &status, &msg);
    5471              : 
    5472           35 :       if (!pkey) {
    5473           18 :          if (create) {
    5474           14 :             db_allow_write_locked(&_database[hDB-1], "db_get_value");
    5475              :             /* set default value if key was created */
    5476              : 
    5477              :             /* get string size from data size */
    5478              :             int size;
    5479           14 :             if (type == TID_STRING || type == TID_LINK)
    5480            1 :                size = *buf_size;
    5481              :             else
    5482           13 :                size = rpc_tid_size(type);
    5483              : 
    5484           14 :             status = db_set_value_wlocked(pheader, hDB, (KEY*)pkey_root, keyname, data, *buf_size, *buf_size / size, type, &msg);
    5485           14 :             db_unlock_database(hDB);
    5486           14 :             if (msg)
    5487            0 :                db_flush_msg(&msg);
    5488           14 :             return status;
    5489              :          } else {
    5490            4 :             db_unlock_database(hDB);
    5491            4 :             if (msg)
    5492            0 :                db_flush_msg(&msg);
    5493            4 :             return DB_NO_KEY;
    5494              :          }
    5495              :       }
    5496              : 
    5497           17 :       status = db_get_data_locked(pheader, pkey, idx, data, buf_size, type, &msg);
    5498              : 
    5499           17 :       db_unlock_database(hDB);
    5500           17 :       if (msg)
    5501            0 :          db_flush_msg(&msg);
    5502              : 
    5503           17 :       return status;
    5504              :    }
    5505              : #endif                          /* LOCAL_ROUTINES */
    5506              : 
    5507              :    return DB_SUCCESS;
    5508              : }
    5509              : 
    5510              : #ifdef LOCAL_ROUTINES
    5511           60 : static INT db_get_data_locked(DATABASE_HEADER* pheader, const KEY* pkey, int idx, void *data, INT * buf_size, DWORD type, db_err_msg** msg)
    5512              : {
    5513              :    /* check for correct type */
    5514           60 :    if (pkey->type != (type)) {
    5515            0 :       db_msg(msg, MERROR, "db_get_data_locked", "odb entry \"%s\" is of type %s, not %s", db_get_path_locked(pheader, pkey).c_str(), rpc_tid_name(pkey->type), rpc_tid_name(type));
    5516            0 :       return DB_TYPE_MISMATCH;
    5517              :    }
    5518              : 
    5519              :    /* check for correct type */
    5520           60 :    if (pkey->type == TID_KEY) {
    5521            0 :       db_msg(msg, MERROR, "db_get_data_locked", "odb entry \"%s\" is of type %s, cannot contain data", db_get_path_locked(pheader, pkey).c_str(), rpc_tid_name(pkey->type));
    5522            0 :       return DB_TYPE_MISMATCH;
    5523              :    }
    5524              : 
    5525              :    /* check for read access */
    5526           60 :    if (!(pkey->access_mode & MODE_READ)) {
    5527            0 :       db_msg(msg, MERROR, "db_get_data_locked", "odb entry \"%s\" has no read access", db_get_path_locked(pheader, pkey).c_str());
    5528            0 :       return DB_NO_ACCESS;
    5529              :    }
    5530              : 
    5531              :    /* check if buffer is too small */
    5532           60 :    if ((idx == -1 && pkey->num_values * pkey->item_size > *buf_size) || (idx != -1 && pkey->item_size > *buf_size)) {
    5533            0 :       memcpy(data, (char *) pheader + pkey->data, *buf_size);
    5534            0 :       db_msg(msg, MERROR, "db_get_data_locked", "odb entry \"%s\" data truncated, size is %d (%d*%d), buffer size is only %d", db_get_path_locked(pheader, pkey).c_str(), pkey->num_values * pkey->item_size, pkey->num_values, pkey->item_size, *buf_size);
    5535            0 :       return DB_TRUNCATED;
    5536              :    }
    5537              : 
    5538              :    /* check if index in boundaries */
    5539           60 :    if (idx != -1 && idx >= pkey->num_values) {
    5540            0 :       cm_msg(MERROR, "db_get_data_locked", "odb entry \"%s\" index %d is out of valid range 0..%d", db_get_path_locked(pheader, pkey).c_str(), idx, pkey->num_values-1);
    5541            0 :       return DB_INVALID_PARAM;
    5542              :    }
    5543              : 
    5544              :    /* copy key data */
    5545           60 :    if (idx == -1) {
    5546           56 :       memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
    5547           56 :       *buf_size = pkey->num_values * pkey->item_size;
    5548              :    } else {
    5549            4 :       memcpy(data, (char *) pheader + pkey->data + idx * pkey->item_size, pkey->item_size);
    5550            4 :       *buf_size = pkey->item_size;
    5551              :    }
    5552              : 
    5553           60 :    return DB_SUCCESS;
    5554              : }
    5555              : #endif                          /* LOCAL_ROUTINES */
    5556              : 
    5557              : /********************************************************************/
    5558              : /**
    5559              : Enumerate subkeys from a key, follow links.
    5560              : 
    5561              : hkey must correspond to a valid ODB directory. The index is
    5562              : usually incremented in a loop until the last key is reached. Information about the
    5563              : sub-keys can be obtained with db_get_key(). If a returned key is of type
    5564              : TID_KEY, it contains itself sub-keys. To scan a whole ODB sub-tree, the
    5565              : function db_scan_tree() can be used.
    5566              : \code
    5567              : INT   i;
    5568              : HNDLE hkey, hsubkey;
    5569              : KEY   key;
    5570              :   db_find_key(hdb, 0, "/Runinfo", &hkey);
    5571              :   for (i=0 ; ; i++)
    5572              :   {
    5573              :    db_enum_key(hdb, hkey, i, &hsubkey);
    5574              :    if (!hSubkey)
    5575              :     break; // end of list reached
    5576              :    // print key name
    5577              :    db_get_key(hdb, hkey, &key);
    5578              :    printf("%s\n", key.name);
    5579              :   }
    5580              : \endcode
    5581              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    5582              : @param hKey          Handle for key where search starts, zero for root.
    5583              : @param idx          Subkey index, sould be initially 0, then
    5584              :                     incremented in each call until
    5585              :                     *subhKey becomes zero and the function
    5586              :                     returns DB_NO_MORE_SUBKEYS
    5587              : @param subkey_handle Handle of subkey which can be used in
    5588              :                     db_get_key() and db_get_data()
    5589              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_MORE_SUBKEYS
    5590              : */
    5591           44 : INT db_enum_key(HNDLE hDB, HNDLE hKey, INT idx, HNDLE * subkey_handle)
    5592              : {
    5593           44 :    if (rpc_is_remote())
    5594            0 :       return rpc_call(RPC_DB_ENUM_KEY, hDB, hKey, idx, subkey_handle);
    5595              : 
    5596              : #ifdef LOCAL_ROUTINES
    5597              :    {
    5598              :       DATABASE_HEADER *pheader;
    5599              :       INT i;
    5600              :       char str[256];
    5601              :       HNDLE parent;
    5602              : 
    5603           44 :       if (hDB > _database_entries || hDB <= 0) {
    5604            0 :          cm_msg(MERROR, "db_enum_key", "invalid database handle");
    5605            6 :          return DB_INVALID_HANDLE;
    5606              :       }
    5607              : 
    5608           44 :       if (!_database[hDB - 1].attached) {
    5609            0 :          cm_msg(MERROR, "db_enum_key", "invalid database handle");
    5610            0 :          return DB_INVALID_HANDLE;
    5611              :       }
    5612              : 
    5613           44 :       *subkey_handle = 0;
    5614              : 
    5615              :       /* first lock database */
    5616           44 :       db_lock_database(hDB);
    5617              : 
    5618           44 :       pheader = _database[hDB - 1].database_header;
    5619           44 :       if (!hKey)
    5620            3 :          hKey = pheader->root_key;
    5621              : 
    5622              :       /* check if hKey argument is correct */
    5623           44 :       if (!db_validate_hkey(pheader, hKey)) {
    5624            0 :          db_unlock_database(hDB);
    5625            0 :          return DB_INVALID_HANDLE;
    5626              :       }
    5627              : 
    5628           44 :       db_err_msg *msg = NULL;
    5629              :       int status;
    5630              : 
    5631           44 :       const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_enum_key", &msg);
    5632              : 
    5633           44 :       if (!pkey) {
    5634            0 :          db_unlock_database(hDB);
    5635            0 :          db_flush_msg(&msg);
    5636            0 :          return DB_NO_MORE_SUBKEYS;
    5637              :       }
    5638              : 
    5639           44 :       if (pkey->type != TID_KEY) {
    5640            0 :          db_unlock_database(hDB);
    5641            0 :          return DB_NO_MORE_SUBKEYS;
    5642              :       }
    5643              : 
    5644           44 :       const KEYLIST* pkeylist = db_get_pkeylist(pheader, hKey, pkey, "db_enum_key", &msg);
    5645              : 
    5646           44 :       if (!pkeylist) {
    5647            0 :          db_unlock_database(hDB);
    5648            0 :          db_flush_msg(&msg);
    5649            0 :          return DB_NO_MORE_SUBKEYS;
    5650              :       }
    5651              : 
    5652           44 :       if (idx >= pkeylist->num_keys) {
    5653            6 :          db_unlock_database(hDB);
    5654            6 :          return DB_NO_MORE_SUBKEYS;
    5655              :       }
    5656              : 
    5657           38 :       pkey = db_get_pkey(pheader, pkeylist->first_key, &status, "db_enum_key", &msg);
    5658              :       
    5659           38 :       if (!pkey) {
    5660            0 :          std::string path = db_get_path_locked(pheader, hKey);
    5661            0 :          HNDLE xfirst_key = pkeylist->first_key;
    5662            0 :          db_unlock_database(hDB);
    5663            0 :          if (msg)
    5664            0 :             db_flush_msg(&msg);
    5665            0 :          cm_msg(MERROR, "db_enum_key", "hkey %d path \"%s\" invalid first_key %d", hKey, path.c_str(), xfirst_key);
    5666            0 :          return DB_NO_MORE_SUBKEYS;
    5667            0 :       }
    5668              : 
    5669          183 :       for (i = 0; i < idx; i++) {
    5670          145 :          if (pkey->next_key == 0) {
    5671            0 :             std::string path = db_get_path_locked(pheader, hKey);
    5672            0 :             db_unlock_database(hDB);
    5673            0 :             cm_msg(MERROR, "db_enum_key", "hkey %d path \"%s\" unexpected end of key list at index %d", hKey, path.c_str(), i);
    5674            0 :             return DB_NO_MORE_SUBKEYS;
    5675            0 :          }
    5676              : 
    5677          145 :          pkey = db_get_pkey(pheader, pkey->next_key, &status, "db_enum_key", &msg);
    5678              : 
    5679          145 :          if (!pkey) {
    5680            0 :             std::string path = db_get_path_locked(pheader, hKey);
    5681            0 :             db_unlock_database(hDB);
    5682            0 :             db_flush_msg(&msg);
    5683            0 :             cm_msg(MERROR, "db_enum_key", "hkey %d path \"%s\" invalid key list at index %d, next_key %d", hKey, path.c_str(), i, pkey->next_key);
    5684            0 :             return DB_NO_MORE_SUBKEYS;
    5685            0 :          }
    5686              :       }
    5687              : 
    5688              :       /* resolve links */
    5689           38 :       if (pkey->type == TID_LINK) {
    5690            0 :          strcpy(str, (char *) pheader + pkey->data);
    5691              : 
    5692              :          /* no not resolve if link to array index */
    5693            0 :          if (strlen(str) > 0 && str[strlen(str) - 1] == ']') {
    5694            0 :             *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
    5695            0 :             db_unlock_database(hDB);
    5696            0 :             return DB_SUCCESS;
    5697              :          }
    5698              : 
    5699            0 :          if (*str == '/') {
    5700              :             /* absolute path */
    5701            0 :             db_unlock_database(hDB);
    5702            0 :             return db_find_key(hDB, 0, str, subkey_handle);
    5703              :          } else {
    5704              :             /* relative path */
    5705            0 :             if (pkey->parent_keylist) {
    5706            0 :                pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
    5707            0 :                parent = pkeylist->parent;
    5708            0 :                db_unlock_database(hDB);
    5709            0 :                return db_find_key(hDB, parent, str, subkey_handle);
    5710              :             } else {
    5711            0 :                db_unlock_database(hDB);
    5712            0 :                return db_find_key(hDB, 0, str, subkey_handle);
    5713              :             }
    5714              :          }
    5715              :       }
    5716              : 
    5717           38 :       *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
    5718           38 :       db_unlock_database(hDB);
    5719              :    }
    5720              : #endif                          /* LOCAL_ROUTINES */
    5721              : 
    5722           38 :    return DB_SUCCESS;
    5723              : }
    5724              : 
    5725              : /**dox***************************************************************/
    5726              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    5727              : 
    5728              : 
    5729              : /*------------------------------------------------------------------*/
    5730           36 : INT db_enum_link(HNDLE hDB, HNDLE hKey, INT idx, HNDLE * subkey_handle)
    5731              : /********************************************************************\
    5732              : 
    5733              :   Routine: db_enum_link
    5734              : 
    5735              :   Purpose: Enumerate subkeys from a key, don't follow links
    5736              : 
    5737              :   Input:
    5738              :     HNDLE hDB               Handle to the database
    5739              :     HNDLE hKey              Handle of key to enumerate, zero for the
    5740              :                             root key
    5741              :     INT   idx               Subkey index, sould be initially 0, then
    5742              :                             incremented in each call until
    5743              :                             *subhKey becomes zero and the function
    5744              :                             returns DB_NO_MORE_SUBKEYS
    5745              : 
    5746              :   Output:
    5747              :     HNDLE *subkey_handle    Handle of subkey which can be used in
    5748              :                             db_get_key and db_get_data
    5749              : 
    5750              :   Function value:
    5751              :     DB_SUCCESS              Successful completion
    5752              :     DB_INVALID_HANDLE       Database handle is invalid
    5753              :     DB_NO_MORE_SUBKEYS      Last subkey reached
    5754              : 
    5755              : \********************************************************************/
    5756              : {
    5757           36 :    if (rpc_is_remote())
    5758            0 :       return rpc_call(RPC_DB_ENUM_LINK, hDB, hKey, idx, subkey_handle);
    5759              : 
    5760              : #ifdef LOCAL_ROUTINES
    5761              :    {
    5762              :       DATABASE_HEADER *pheader;
    5763              :       KEYLIST *pkeylist;
    5764              :       KEY *pkey;
    5765              :       INT i;
    5766              : 
    5767           36 :       if (hDB > _database_entries || hDB <= 0) {
    5768            0 :          cm_msg(MERROR, "db_enum_link", "invalid database handle");
    5769            0 :          return DB_INVALID_HANDLE;
    5770              :       }
    5771              : 
    5772           36 :       if (!_database[hDB - 1].attached) {
    5773            0 :          cm_msg(MERROR, "db_enum_link", "invalid database handle");
    5774            0 :          return DB_INVALID_HANDLE;
    5775              :       }
    5776              : 
    5777           36 :       *subkey_handle = 0;
    5778              : 
    5779              :       /* first lock database */
    5780           36 :       db_lock_database(hDB);
    5781              : 
    5782           36 :       pheader = _database[hDB - 1].database_header;
    5783           36 :       if (!hKey)
    5784            0 :          hKey = pheader->root_key;
    5785              : 
    5786              :       /* check if hKey argument is correct */
    5787           36 :       if (!db_validate_hkey(pheader, hKey)) {
    5788            0 :          db_unlock_database(hDB);
    5789            0 :          return DB_INVALID_HANDLE;
    5790              :       }
    5791              : 
    5792           36 :       pkey = (KEY *) ((char *) pheader + hKey);
    5793              : 
    5794           36 :       if (pkey->type != TID_KEY) {
    5795            0 :          db_unlock_database(hDB);
    5796            0 :          return DB_NO_MORE_SUBKEYS;
    5797              :       }
    5798           36 :       pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
    5799              : 
    5800           36 :       if (idx >= pkeylist->num_keys) {
    5801           10 :          db_unlock_database(hDB);
    5802           10 :          return DB_NO_MORE_SUBKEYS;
    5803              :       }
    5804              : 
    5805              :       // FIXME: validate pkeylist->first_key
    5806           26 :       pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
    5807           98 :       for (i = 0; i < idx; i++) {
    5808              :          // FIXME: validate pkey->next_key
    5809           72 :          pkey = (KEY *) ((char *) pheader + pkey->next_key);
    5810              :       }
    5811              : 
    5812           26 :       *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
    5813           26 :       db_unlock_database(hDB);
    5814              :    }
    5815              : #endif                          /* LOCAL_ROUTINES */
    5816              : 
    5817           26 :    return DB_SUCCESS;
    5818              : }
    5819              : 
    5820              : /*------------------------------------------------------------------*/
    5821            0 : INT db_get_next_link(HNDLE hDB, HNDLE hKey, HNDLE * subkey_handle)
    5822              : /********************************************************************\
    5823              : 
    5824              :   Routine: db_get_next_link
    5825              : 
    5826              :   Purpose: Get next key in ODB after hKey
    5827              : 
    5828              :   Input:
    5829              :     HNDLE hDB               Handle to the database
    5830              :     HNDLE hKey              Handle of key to enumerate, zero for the
    5831              :                             root key
    5832              : 
    5833              :   Output:
    5834              :     HNDLE *subkey_handle    Handle of subkey which can be used in
    5835              :                             db_get_key and db_get_data
    5836              : 
    5837              :   Function value:
    5838              :     DB_SUCCESS              Successful completion
    5839              :     DB_INVALID_HANDLE       Database handle is invalid
    5840              :     DB_NO_MORE_SUBKEYS      Last subkey reached
    5841              : 
    5842              : \********************************************************************/
    5843              : {
    5844            0 :    if (rpc_is_remote())
    5845            0 :       return rpc_call(RPC_DB_GET_NEXT_LINK, hDB, hKey, subkey_handle);
    5846              : 
    5847              : #ifdef LOCAL_ROUTINES
    5848              :    {
    5849              :       DATABASE_HEADER *pheader;
    5850              :       KEYLIST *pkeylist;
    5851              :       KEY *pkey;
    5852              :       INT descent;
    5853              : 
    5854            0 :       if (hDB > _database_entries || hDB <= 0) {
    5855            0 :          cm_msg(MERROR, "db_enum_link", "invalid database handle");
    5856            0 :          return DB_INVALID_HANDLE;
    5857              :       }
    5858              : 
    5859            0 :       if (!_database[hDB - 1].attached) {
    5860            0 :          cm_msg(MERROR, "db_enum_link", "invalid database handle");
    5861            0 :          return DB_INVALID_HANDLE;
    5862              :       }
    5863              : 
    5864            0 :       *subkey_handle = 0;
    5865              : 
    5866              :       /* first lock database */
    5867            0 :       db_lock_database(hDB);
    5868              : 
    5869            0 :       pheader = _database[hDB - 1].database_header;
    5870            0 :       if (!hKey)
    5871            0 :          hKey = pheader->root_key;
    5872              : 
    5873              :       /* check if hKey argument is correct */
    5874            0 :       if (!db_validate_hkey(pheader, hKey)) {
    5875            0 :          db_unlock_database(hDB);
    5876            0 :          return DB_INVALID_HANDLE;
    5877              :       }
    5878              : 
    5879            0 :       pkey = (KEY *) ((char *) pheader + hKey);
    5880              : 
    5881            0 :       descent = TRUE;
    5882              :       do {
    5883            0 :          if (pkey->type != TID_KEY || !descent) {
    5884            0 :             if (pkey->next_key) {
    5885              :                /* key has next key, return it */
    5886              :                // FIXME: validate pkey->next_key
    5887            0 :                pkey = (KEY *) ((char *) pheader + pkey->next_key);
    5888              : 
    5889            0 :                if (pkey->type != TID_KEY) {
    5890            0 :                   *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
    5891            0 :                   db_unlock_database(hDB);
    5892            0 :                   return DB_SUCCESS;
    5893              :                }
    5894              : 
    5895              :                /* key has subkeys, so descent on the next iteration */
    5896            0 :                descent = TRUE;
    5897              :             } else {
    5898            0 :                if (pkey->parent_keylist == 0) {
    5899              :                   /* return if we are back to the root key */
    5900            0 :                   db_unlock_database(hDB);
    5901            0 :                   return DB_NO_MORE_SUBKEYS;
    5902              :                }
    5903              : 
    5904              :                /* key is last in list, traverse up */
    5905            0 :                pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
    5906              : 
    5907              :                // FIXME: validate pkeylist->parent
    5908            0 :                pkey = (KEY *) ((char *) pheader + pkeylist->parent);
    5909            0 :                descent = FALSE;
    5910              :             }
    5911              :          } else {
    5912            0 :             if (descent) {
    5913              :                /* find first subkey */
    5914            0 :                pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
    5915              : 
    5916            0 :                if (pkeylist->num_keys == 0) {
    5917              :                   /* if key has no subkeys, look for next key on this level */
    5918            0 :                   descent = FALSE;
    5919              :                } else {
    5920              :                   /* get first subkey */
    5921              :                   // FIXME: validate pkeylist->first_key
    5922            0 :                   pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
    5923              : 
    5924            0 :                   if (pkey->type != TID_KEY) {
    5925            0 :                      *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
    5926            0 :                      db_unlock_database(hDB);
    5927            0 :                      return DB_SUCCESS;
    5928              :                   }
    5929              :                }
    5930              :             }
    5931              :          }
    5932              : 
    5933              :       } while (TRUE);
    5934              :    }
    5935              : #endif                          /* LOCAL_ROUTINES */
    5936              : 
    5937              :    return DB_SUCCESS;
    5938              : }
    5939              : 
    5940              : /**dox***************************************************************/
    5941              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    5942              : 
    5943              : /********************************************************************/
    5944              : 
    5945              : #ifdef LOCAL_ROUTINES
    5946              : 
    5947          243 : static INT db_get_key_locked(const DATABASE_HEADER* pheader, HNDLE hKey, KEY * key, db_err_msg** msg)
    5948              : {
    5949          243 :    if (!hKey)
    5950            0 :       hKey = pheader->root_key;
    5951              : 
    5952              :    /* check if hKey argument is correct */
    5953          243 :    if (!db_validate_hkey(pheader, hKey)) {
    5954            0 :       return DB_INVALID_HANDLE;
    5955              :    }
    5956              :    
    5957          243 :    const KEY* pkey = (const KEY *) ((char *) pheader + hKey);
    5958              :    
    5959          243 :    if (pkey->type < 1 || pkey->type >= TID_LAST) {
    5960            0 :       int pkey_type = pkey->type;
    5961            0 :       db_msg(msg, MERROR, "db_get_key", "hkey %d invalid key type %d", hKey, pkey_type);
    5962            0 :       return DB_INVALID_HANDLE;
    5963              :    }
    5964              :    
    5965              :    /* check for link to array index */
    5966          243 :    if (pkey->type == TID_LINK) {
    5967              :       char link_name[MAX_ODB_PATH];
    5968            0 :       mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
    5969            0 :       if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
    5970            0 :          if (strchr(link_name, '[') == NULL)
    5971            0 :             return DB_INVALID_LINK;
    5972              : 
    5973              :          HNDLE hkeylink;
    5974            0 :          if (db_find_key_locked(pheader, 0, link_name, &hkeylink, msg) != DB_SUCCESS)
    5975            0 :             return DB_INVALID_LINK;
    5976            0 :          int status = db_get_key_locked(pheader, hkeylink, key, msg);
    5977            0 :          key->num_values = 1;        // fake number of values
    5978            0 :          return status;
    5979              :       }
    5980              :    }
    5981              :    
    5982          243 :    memcpy(key, pkey, sizeof(KEY));
    5983              : 
    5984          243 :    return DB_SUCCESS;
    5985              : }
    5986              : #endif /* LOCAL_ROUTINES */
    5987              : 
    5988              : /********************************************************************/
    5989              : /**
    5990              : Get key structure from a handle.
    5991              : 
    5992              : KEY structure has following format:
    5993              : \code
    5994              : typedef struct {
    5995              :   DWORD         type;                 // TID_xxx type
    5996              :   INT           num_values;           // number of values
    5997              :   char          name[NAME_LENGTH];    // name of variable
    5998              :   INT           data;                 // Address of variable (offset)
    5999              :   INT           total_size;           // Total size of data block
    6000              :   INT           item_size;            // Size of single data item
    6001              :   WORD          access_mode;          // Access mode
    6002              :   WORD          notify_count;         // Notify counter
    6003              :   INT           next_key;             // Address of next key
    6004              :   INT           parent_keylist;       // keylist to which this key belongs
    6005              :   INT           last_written;         // Time of last write action
    6006              : } KEY;
    6007              : \endcode
    6008              : Most of these values are used for internal purposes, the values which are of
    6009              : public interest are type, name, num_values, item_size and total_size.
    6010              : For keys which contain a single value, num_values equals to one and total_size equals to item_size. For keys which contain an array of strings (TID_STRING),
    6011              : item_size equals to the length of one string.
    6012              : \code
    6013              : KEY   key;
    6014              : HNDLE hkey;
    6015              : db_find_key(hDB, 0, "/Runinfo/Run number", &hkey);
    6016              : db_get_key(hDB, hkey, &key);
    6017              : printf("The run number is of type %s\n", rpc_tid_name(key.type));
    6018              : \endcode
    6019              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    6020              : @param hKey Handle for key where search starts, zero for root. If Key is a link to an array element, this link is resolved. In this case function returns the key of the link destination and num_values is set to 1.
    6021              : @param key Pointer to KEY stucture.
    6022              : @return DB_SUCCESS, DB_INVALID_HANDLE
    6023              : */
    6024          243 : INT db_get_key(HNDLE hDB, HNDLE hKey, KEY * key)
    6025              : {
    6026          243 :    if (rpc_is_remote())
    6027            0 :       return rpc_call(RPC_DB_GET_KEY, hDB, hKey, key);
    6028              : 
    6029              : #ifdef LOCAL_ROUTINES
    6030              :    {
    6031              :       DATABASE_HEADER *pheader;
    6032              :       int status;
    6033              : 
    6034          243 :       if (hDB > _database_entries || hDB <= 0) {
    6035            0 :          cm_msg(MERROR, "db_get_key", "invalid database handle");
    6036            0 :          return DB_INVALID_HANDLE;
    6037              :       }
    6038              : 
    6039          243 :       if (!_database[hDB - 1].attached) {
    6040            0 :          cm_msg(MERROR, "db_get_key", "invalid database handle");
    6041            0 :          return DB_INVALID_HANDLE;
    6042              :       }
    6043              : 
    6044          243 :       if (hKey < (int) sizeof(DATABASE_HEADER) && hKey != 0) {
    6045            0 :          cm_msg(MERROR, "db_get_key", "invalid key handle");
    6046            0 :          return DB_INVALID_HANDLE;
    6047              :       }
    6048              : 
    6049          243 :       db_err_msg *msg = NULL;
    6050              : 
    6051          243 :       db_lock_database(hDB);
    6052              : 
    6053          243 :       pheader = _database[hDB - 1].database_header;
    6054              : 
    6055          243 :       status = db_get_key_locked(pheader, hKey, key, &msg);
    6056              : 
    6057          243 :       db_unlock_database(hDB);
    6058              : 
    6059          243 :       if (msg)
    6060            0 :          db_flush_msg(&msg);
    6061              : 
    6062          243 :       return status;
    6063              :    }
    6064              : #endif                          /* LOCAL_ROUTINES */
    6065              : 
    6066              :    return DB_SUCCESS;
    6067              : }
    6068              : 
    6069              : /********************************************************************/
    6070              : /**
    6071              : Same as db_get_key, but it does not follow links
    6072              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    6073              : @param hKey Handle for key where search starts, zero for root.
    6074              : @param key Pointer to KEY stucture.
    6075              : @return DB_SUCCESS, DB_INVALID_HANDLE
    6076              : */
    6077           20 : INT db_get_link(HNDLE hDB, HNDLE hKey, KEY * key)
    6078              : {
    6079           20 :    if (rpc_is_remote())
    6080            0 :       return rpc_call(RPC_DB_GET_LINK, hDB, hKey, key);
    6081              : 
    6082              : #ifdef LOCAL_ROUTINES
    6083              :    {
    6084              :       DATABASE_HEADER *pheader;
    6085              : 
    6086           20 :       if (hDB > _database_entries || hDB <= 0) {
    6087            0 :          cm_msg(MERROR, "db_get_link", "invalid database handle");
    6088            0 :          return DB_INVALID_HANDLE;
    6089              :       }
    6090              : 
    6091           20 :       if (!_database[hDB - 1].attached) {
    6092            0 :          cm_msg(MERROR, "db_get_link", "invalid database handle");
    6093            0 :          return DB_INVALID_HANDLE;
    6094              :       }
    6095              : 
    6096           20 :       if (hKey < (int) sizeof(DATABASE_HEADER) && hKey != 0) {
    6097            0 :          cm_msg(MERROR, "db_get_link", "invalid key handle");
    6098            0 :          return DB_INVALID_HANDLE;
    6099              :       }
    6100              : 
    6101           20 :       db_err_msg *msg = NULL;
    6102              : 
    6103           20 :       db_lock_database(hDB);
    6104              : 
    6105           20 :       pheader = _database[hDB - 1].database_header;
    6106              : 
    6107           20 :       int status = DB_SUCCESS;
    6108              : 
    6109           20 :       const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_get_link", &msg);
    6110           20 :       if (pkey) {
    6111           20 :          memcpy(key, pkey, sizeof(KEY));
    6112              :       } else {
    6113            0 :          memset(key, 0, sizeof(KEY));
    6114              :          //abort();
    6115              :       }
    6116              : 
    6117           20 :       db_unlock_database(hDB);
    6118              : 
    6119           20 :       if (msg)
    6120            0 :          db_flush_msg(&msg);
    6121              : 
    6122           20 :       return status;
    6123              :    }
    6124              : #endif                          /* LOCAL_ROUTINES */
    6125              : 
    6126              :    return DB_SUCCESS;
    6127              : }
    6128              : 
    6129              : /********************************************************************/
    6130              : /**
    6131              : Get time when key was last modified
    6132              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    6133              : @param hKey              Handle of key to operate on
    6134              : @param delta             Seconds since last update
    6135              : @return DB_SUCCESS, DB_INVALID_HANDLE
    6136              : */
    6137            0 : INT db_get_key_time(HNDLE hDB, HNDLE hKey, DWORD * delta)
    6138              : {
    6139            0 :    if (rpc_is_remote())
    6140            0 :       return rpc_call(RPC_DB_GET_KEY_TIME, hDB, hKey, delta);
    6141              : 
    6142              : #ifdef LOCAL_ROUTINES
    6143              :    {
    6144              :       DATABASE_HEADER *pheader;
    6145              :       KEY *pkey;
    6146              : 
    6147            0 :       if (hDB > _database_entries || hDB <= 0) {
    6148            0 :          cm_msg(MERROR, "db_get_key", "invalid database handle");
    6149            0 :          return DB_INVALID_HANDLE;
    6150              :       }
    6151              : 
    6152            0 :       if (!_database[hDB - 1].attached) {
    6153            0 :          cm_msg(MERROR, "db_get_key", "invalid database handle");
    6154            0 :          return DB_INVALID_HANDLE;
    6155              :       }
    6156              : 
    6157            0 :       if (hKey < (int) sizeof(DATABASE_HEADER)) {
    6158            0 :          cm_msg(MERROR, "db_get_key", "invalid key handle");
    6159            0 :          return DB_INVALID_HANDLE;
    6160              :       }
    6161              : 
    6162            0 :       db_lock_database(hDB);
    6163              : 
    6164            0 :       pheader = _database[hDB - 1].database_header;
    6165              : 
    6166              :       /* check if hKey argument is correct */
    6167            0 :       if (!db_validate_hkey(pheader, hKey)) {
    6168            0 :          db_unlock_database(hDB);
    6169            0 :          return DB_INVALID_HANDLE;
    6170              :       }
    6171              : 
    6172            0 :       pkey = (KEY *) ((char *) pheader + hKey);
    6173              : 
    6174            0 :       *delta = ss_time() - pkey->last_written;
    6175              : 
    6176            0 :       db_unlock_database(hDB);
    6177              : 
    6178              :    }
    6179              : #endif                          /* LOCAL_ROUTINES */
    6180              : 
    6181            0 :    return DB_SUCCESS;
    6182              : }
    6183              : 
    6184              : /********************************************************************/
    6185              : /**
    6186              : Get key info (separate values instead of structure)
    6187              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    6188              : @param hKey              Handle of key to operate on
    6189              : @param name             Key name
    6190              : @param name_size        Size of the give name (done with sizeof())
    6191              : @param type             Key type (see @ref Midas_Data_Types).
    6192              : @param num_values       Number of values in key.
    6193              : @param item_size        Size of individual key value (used for strings)
    6194              : @return DB_SUCCESS, DB_INVALID_HANDLE
    6195              : */
    6196            0 : INT db_get_key_info(HNDLE hDB, HNDLE hKey, char *name, INT name_size, INT * type, INT * num_values, INT * item_size)
    6197              : {
    6198            0 :    if (rpc_is_remote())
    6199            0 :       return rpc_call(RPC_DB_GET_KEY_INFO, hDB, hKey, name, name_size, type, num_values, item_size);
    6200              : 
    6201              : #ifdef LOCAL_ROUTINES
    6202              :    {
    6203              :       DATABASE_HEADER *pheader;
    6204              :       KEY *pkey;
    6205              :       KEYLIST *pkeylist;
    6206              : 
    6207            0 :       if (hDB > _database_entries || hDB <= 0) {
    6208            0 :          cm_msg(MERROR, "db_get_key_info", "invalid database handle");
    6209            0 :          return DB_INVALID_HANDLE;
    6210              :       }
    6211              : 
    6212            0 :       if (!_database[hDB - 1].attached) {
    6213            0 :          cm_msg(MERROR, "db_get_key_info", "invalid database handle");
    6214            0 :          return DB_INVALID_HANDLE;
    6215              :       }
    6216              : 
    6217            0 :       if (hKey < (int) sizeof(DATABASE_HEADER)) {
    6218            0 :          cm_msg(MERROR, "db_get_key_info", "invalid key handle");
    6219            0 :          return DB_INVALID_HANDLE;
    6220              :       }
    6221              : 
    6222            0 :       db_lock_database(hDB);
    6223              : 
    6224            0 :       pheader = _database[hDB - 1].database_header;
    6225              : 
    6226              :       /* check if hKey argument is correct */
    6227            0 :       if (!db_validate_hkey(pheader, hKey)) {
    6228            0 :          db_unlock_database(hDB);
    6229            0 :          return DB_INVALID_HANDLE;
    6230              :       }
    6231              : 
    6232            0 :       pkey = (KEY *) ((char *) pheader + hKey);
    6233              : 
    6234            0 :       if ((INT) strlen(pkey->name) + 1 > name_size) {
    6235              :          /* truncate name */
    6236            0 :          memcpy(name, pkey->name, name_size - 1);
    6237            0 :          name[name_size] = 0;
    6238              :       } else
    6239            0 :          strcpy(name, pkey->name);
    6240              : 
    6241              :       /* convert "root" to "/" */
    6242            0 :       if (strcmp(name, "root") == 0)
    6243            0 :          strcpy(name, "/");
    6244              : 
    6245            0 :       *type = pkey->type;
    6246            0 :       *num_values = pkey->num_values;
    6247            0 :       *item_size = pkey->item_size;
    6248              : 
    6249            0 :       if (pkey->type == TID_KEY) {
    6250            0 :          pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
    6251            0 :          *num_values = pkeylist->num_keys;
    6252              :       }
    6253              : 
    6254            0 :       db_unlock_database(hDB);
    6255              :    }
    6256              : #endif                          /* LOCAL_ROUTINES */
    6257              : 
    6258            0 :    return DB_SUCCESS;
    6259              : }
    6260              : 
    6261              : /**dox***************************************************************/
    6262              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    6263              : 
    6264              : 
    6265              : /*------------------------------------------------------------------*/
    6266            0 : INT db_rename_key(HNDLE hDB, HNDLE hKey, const char *name)
    6267              : /********************************************************************\
    6268              : 
    6269              :   Routine: db_get_key
    6270              : 
    6271              :   Purpose: Rename a key
    6272              : 
    6273              :   Input:
    6274              :     HNDLE hDB               Handle to the database
    6275              :     HNDLE hKey              Handle of key
    6276              :     char  *name             New key name
    6277              : 
    6278              :   Output:
    6279              :     <none>
    6280              : 
    6281              :   Function value:
    6282              :     DB_SUCCESS              Successful completion
    6283              :     DB_INVALID_HANDLE       Database handle is invalid
    6284              :     DB_INVALID_NAME         Key name contains '/'
    6285              : 
    6286              : \********************************************************************/
    6287              : {
    6288            0 :    if (rpc_is_remote())
    6289            0 :       return rpc_call(RPC_DB_RENAME_KEY, hDB, hKey, name);
    6290              : 
    6291              : #ifdef LOCAL_ROUTINES
    6292              :    {
    6293              :       DATABASE_HEADER *pheader;
    6294              :       KEY *pkey;
    6295              :       int status;
    6296              : 
    6297            0 :       if (hDB > _database_entries || hDB <= 0) {
    6298            0 :          cm_msg(MERROR, "db_rename_key", "invalid database handle");
    6299            0 :          return DB_INVALID_HANDLE;
    6300              :       }
    6301              : 
    6302            0 :       if (!_database[hDB - 1].attached) {
    6303            0 :          cm_msg(MERROR, "db_rename_key", "invalid database handle");
    6304            0 :          return DB_INVALID_HANDLE;
    6305              :       }
    6306              : 
    6307            0 :       if (hKey < (int) sizeof(DATABASE_HEADER)) {
    6308            0 :          cm_msg(MERROR, "db_rename_key", "invalid key handle");
    6309            0 :          return DB_INVALID_HANDLE;
    6310              :       }
    6311              : 
    6312            0 :       db_err_msg* msg = NULL;
    6313            0 :       status = db_validate_name(name, FALSE, "db_rename_key", &msg);
    6314            0 :       if (msg)
    6315            0 :          db_flush_msg(&msg);
    6316            0 :       if (status != DB_SUCCESS)
    6317            0 :          return status;
    6318              : 
    6319            0 :       if (name == NULL) {
    6320            0 :          cm_msg(MERROR, "db_rename_key", "key name is NULL");
    6321            0 :          return DB_INVALID_NAME;
    6322              :       }
    6323              : 
    6324            0 :       if (strlen(name) < 1) {
    6325            0 :          cm_msg(MERROR, "db_rename_key", "key name is too short");
    6326            0 :          return DB_INVALID_NAME;
    6327              :       }
    6328              : 
    6329            0 :       if (strchr(name, '/')) {
    6330            0 :          cm_msg(MERROR, "db_rename_key", "key name may not contain \"/\"");
    6331            0 :          return DB_INVALID_NAME;
    6332              :       }
    6333              : 
    6334            0 :       db_lock_database(hDB);
    6335              : 
    6336            0 :       pheader = _database[hDB - 1].database_header;
    6337              : 
    6338              :       /* check if hKey argument is correct */
    6339            0 :       if (!db_validate_hkey(pheader, hKey)) {
    6340            0 :          db_unlock_database(hDB);
    6341            0 :          return DB_INVALID_HANDLE;
    6342              :       }
    6343              : 
    6344            0 :       pkey = (KEY *) ((char *) pheader + hKey);
    6345              : 
    6346            0 :       if (!pkey->type) {
    6347            0 :          int pkey_type = pkey->type;
    6348            0 :          db_unlock_database(hDB);
    6349            0 :          cm_msg(MERROR, "db_rename_key", "hkey %d invalid key type %d", hKey, pkey_type);
    6350            0 :          return DB_INVALID_HANDLE;
    6351              :       }
    6352              : 
    6353            0 :       db_allow_write_locked(&_database[hDB - 1], "db_rename_key");
    6354              : 
    6355            0 :       mstrlcpy(pkey->name, name, NAME_LENGTH);
    6356              : 
    6357            0 :       db_unlock_database(hDB);
    6358              : 
    6359              :    }
    6360              : #endif                          /* LOCAL_ROUTINES */
    6361              : 
    6362            0 :    return DB_SUCCESS;
    6363              : }
    6364              : 
    6365              : /*------------------------------------------------------------------*/
    6366            0 : INT db_reorder_key(HNDLE hDB, HNDLE hKey, INT idx)
    6367              : /********************************************************************\
    6368              : 
    6369              :   Routine: db_reorder_key
    6370              : 
    6371              :   Purpose: Reorder key so that key hKey apprears at position 'index'
    6372              :            in keylist (or at bottom if index<0)
    6373              : 
    6374              :   Input:
    6375              :     HNDLE hDB               Handle to the database
    6376              :     HNDLE hKey              Handle of key
    6377              :     INT   idx               New positio of key in keylist
    6378              : 
    6379              :   Output:
    6380              :     <none>
    6381              : 
    6382              :   Function value:
    6383              :     DB_SUCCESS              Successful completion
    6384              :     DB_INVALID_HANDLE       Database handle is invalid
    6385              :     DB_NO_ACCESS            Key is locked for write
    6386              :     DB_OPEN_RECORD          Key, subkey or parent key is open
    6387              : 
    6388              : \********************************************************************/
    6389              : {
    6390            0 :    if (rpc_is_remote())
    6391            0 :       return rpc_call(RPC_DB_REORDER_KEY, hDB, hKey, idx);
    6392              : 
    6393              : #ifdef LOCAL_ROUTINES
    6394              :    {
    6395              :       DATABASE_HEADER *pheader;
    6396              :       KEY *pkey, *pnext_key, *pkey_tmp;
    6397              :       KEYLIST *pkeylist;
    6398              :       INT i;
    6399              : 
    6400            0 :       if (hDB > _database_entries || hDB <= 0) {
    6401            0 :          cm_msg(MERROR, "db_rename_key", "invalid database handle");
    6402            0 :          return DB_INVALID_HANDLE;
    6403              :       }
    6404              : 
    6405            0 :       if (!_database[hDB - 1].attached) {
    6406            0 :          cm_msg(MERROR, "db_rename_key", "invalid database handle");
    6407            0 :          return DB_INVALID_HANDLE;
    6408              :       }
    6409              : 
    6410            0 :       if (hKey < (int) sizeof(DATABASE_HEADER)) {
    6411            0 :          cm_msg(MERROR, "db_rename_key", "invalid key handle");
    6412            0 :          return DB_INVALID_HANDLE;
    6413              :       }
    6414              : 
    6415            0 :       db_lock_database(hDB);
    6416              : 
    6417            0 :       pheader = _database[hDB - 1].database_header;
    6418              : 
    6419              :       /* check if hKey argument is correct */
    6420            0 :       if (!db_validate_hkey(pheader, hKey)) {
    6421            0 :          db_unlock_database(hDB);
    6422            0 :          return DB_INVALID_HANDLE;
    6423              :       }
    6424              : 
    6425            0 :       pkey = (KEY *) ((char *) pheader + hKey);
    6426              : 
    6427            0 :       if (!pkey->type) {
    6428            0 :          int pkey_type = pkey->type;
    6429            0 :          db_unlock_database(hDB);
    6430            0 :          cm_msg(MERROR, "db_reorder_key", "hkey %d invalid key type %d", hKey, pkey_type);
    6431            0 :          return DB_INVALID_HANDLE;
    6432              :       }
    6433              : 
    6434            0 :       if (!(pkey->access_mode & MODE_WRITE)) {
    6435            0 :          db_unlock_database(hDB);
    6436            0 :          return DB_NO_ACCESS;
    6437              :       }
    6438              : 
    6439              :       /* check if someone has opened key or parent */
    6440              :       do {
    6441              : #ifdef CHECK_OPEN_RECORD
    6442            0 :          if (pkey->notify_count) {
    6443            0 :             db_unlock_database(hDB);
    6444            0 :             return DB_OPEN_RECORD;
    6445              :          }
    6446              : #endif
    6447            0 :          if (pkey->parent_keylist == 0)
    6448            0 :             break;
    6449              : 
    6450            0 :          pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
    6451              :          // FIXME: validate pkeylist->parent
    6452            0 :          pkey = (KEY *) ((char *) pheader + pkeylist->parent);
    6453              :       } while (TRUE);
    6454              :       
    6455            0 :       db_allow_write_locked(&_database[hDB - 1], "db_reorder_key");
    6456              :       
    6457            0 :       pkey = (KEY *) ((char *) pheader + hKey); // NB: hKey is already validated
    6458            0 :       pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
    6459              : 
    6460              :       /* first remove key from list */
    6461            0 :       pnext_key = (KEY *) (POINTER_T) pkey->next_key; // FIXME: what is this pointer cast?
    6462              : 
    6463            0 :       if ((KEY *) ((char *) pheader + pkeylist->first_key) == pkey) {
    6464              :          /* key is first in list */
    6465            0 :          pkeylist->first_key = (POINTER_T) pnext_key;
    6466              :       } else {
    6467              :          /* find predecessor */
    6468              :          // FIXME: validate pkeylist->first_key
    6469            0 :          pkey_tmp = (KEY *) ((char *) pheader + pkeylist->first_key);
    6470            0 :          while ((KEY *) ((char *) pheader + pkey_tmp->next_key) != pkey) {
    6471              :             // FIXME: validate pkey_tmp->next_key
    6472            0 :             pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
    6473              :          }
    6474            0 :          pkey_tmp->next_key = (POINTER_T) pnext_key;
    6475              :       }
    6476              : 
    6477              :       /* add key to list at proper index */
    6478              :       // FIXME: validate pkeylist->first_key
    6479            0 :       pkey_tmp = (KEY *) ((char *) pheader + pkeylist->first_key);
    6480            0 :       if (idx < 0 || idx >= pkeylist->num_keys - 1) {
    6481              :          /* add at bottom */
    6482              : 
    6483              :          /* find last key */
    6484            0 :          for (i = 0; i < pkeylist->num_keys - 2; i++) {
    6485              :             // FIXME: validate pkey_tmp->next_key
    6486            0 :             pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
    6487              :          }
    6488              : 
    6489            0 :          pkey_tmp->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
    6490            0 :          pkey->next_key = 0;
    6491              :       } else {
    6492            0 :          if (idx == 0) {
    6493              :             /* add at top */
    6494            0 :             pkey->next_key = pkeylist->first_key;
    6495            0 :             pkeylist->first_key = (POINTER_T) pkey - (POINTER_T) pheader;
    6496              :          } else {
    6497              :             /* add at position index */
    6498            0 :             for (i = 0; i < idx - 1; i++) {
    6499              :                // FIXME: validate pkey_tmp->next_key
    6500            0 :                pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
    6501              :             }
    6502              : 
    6503            0 :             pkey->next_key = pkey_tmp->next_key;
    6504            0 :             pkey_tmp->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
    6505              :          }
    6506              :       }
    6507              : 
    6508            0 :       db_unlock_database(hDB);
    6509              : 
    6510              :    }
    6511              : #endif                          /* LOCAL_ROUTINES */
    6512              : 
    6513            0 :    return DB_SUCCESS;
    6514              : }
    6515              : 
    6516              : /**dox***************************************************************/
    6517              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    6518              : 
    6519              : 
    6520              : /********************************************************************/
    6521              : /**
    6522              : Get key data from a handle
    6523              : 
    6524              : The function returns single values or whole arrays which are contained
    6525              : in an ODB key. Since the data buffer is of type void, no type checking can be
    6526              : performed by the compiler. Therefore the type has to be explicitly supplied,
    6527              : which is checked against the type stored in the ODB.
    6528              : \code
    6529              :   HNLDE hkey;
    6530              :   INT   run_number, size;
    6531              :   // get key handle for run number
    6532              :   db_find_key(hDB, 0, "/Runinfo/Run number", &hkey);
    6533              :   // return run number
    6534              :   size = sizeof(run_number);
    6535              :   db_get_data(hDB, hkey, &run_number, &size,TID_INT32);
    6536              : \endcode
    6537              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    6538              : @param hKey         Handle for key where search starts, zero for root.
    6539              : @param data         Pointer to the return data.
    6540              : @param buf_size     Size of data buffer.
    6541              : @param type         Type of key, one of TID_xxx (see @ref Midas_Data_Types).
    6542              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TRUNCATED, DB_TYPE_MISMATCH
    6543              : */
    6544           40 : INT db_get_data(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, DWORD type)
    6545              : {
    6546           40 :    if (rpc_is_remote())
    6547            0 :       return rpc_call(RPC_DB_GET_DATA, hDB, hKey, data, buf_size, type);
    6548              : 
    6549              : #ifdef LOCAL_ROUTINES
    6550              :    {
    6551              :       int status;
    6552              : 
    6553           40 :       if (hDB > _database_entries || hDB <= 0) {
    6554            0 :          cm_msg(MERROR, "db_get_data", "Invalid database handle");
    6555            0 :          return DB_INVALID_HANDLE;
    6556              :       }
    6557              : 
    6558           40 :       if (!_database[hDB - 1].attached) {
    6559            0 :          cm_msg(MERROR, "db_get_data", "invalid database handle");
    6560            0 :          return DB_INVALID_HANDLE;
    6561              :       }
    6562              : 
    6563           40 :       if (hKey < (int) sizeof(DATABASE_HEADER)) {
    6564            0 :          cm_msg(MERROR, "db_get_data", "invalid key handle");
    6565            0 :          return DB_INVALID_HANDLE;
    6566              :       }
    6567              : 
    6568           40 :       db_lock_database(hDB);
    6569              : 
    6570           40 :       DATABASE_HEADER* pheader = _database[hDB - 1].database_header;
    6571           40 :       db_err_msg* msg = NULL;
    6572              : 
    6573           40 :       const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_get_data", &msg);
    6574              : 
    6575           40 :       if (!pkey) {
    6576            0 :          db_unlock_database(hDB);
    6577            0 :          if (msg)
    6578            0 :             db_flush_msg(&msg);
    6579            0 :          return status;
    6580              :       }
    6581              : 
    6582              :       /* check for read access */
    6583           40 :       if (!(pkey->access_mode & MODE_READ)) {
    6584            0 :          db_unlock_database(hDB);
    6585            0 :          if (msg)
    6586            0 :             db_flush_msg(&msg);
    6587            0 :          return DB_NO_ACCESS;
    6588              :       }
    6589              : 
    6590              :       /* follow links to array index */
    6591           40 :       if (pkey->type == TID_LINK) {
    6592            0 :          std::string link_name = (char *) pheader + pkey->data;
    6593            0 :          if (link_name.length() > 0 && link_name.back() == ']') {
    6594            0 :             size_t pos = link_name.rfind("[");
    6595            0 :             if (pos == std::string::npos) {
    6596            0 :                db_msg(&msg, MERROR, "db_get_data", "missing \"[\" in symlink to array element \"%s\" in \"%s\"", link_name.c_str(), db_get_path_locked(pheader, pkey).c_str());
    6597            0 :                db_unlock_database(hDB);
    6598            0 :                if (msg)
    6599            0 :                   db_flush_msg(&msg);
    6600            0 :                return DB_INVALID_LINK;
    6601              :             }
    6602            0 :             int idx = atoi(link_name.c_str()+pos+1);
    6603            0 :             link_name.resize(pos);
    6604              :             //printf("link name [%s] idx %d\n", link_name.c_str(), idx);
    6605              : 
    6606              :             // relative symlinks did not work in the old db_get_data(), make sure they do not work now. K.O.
    6607            0 :             if (link_name[0] != '/') {
    6608            0 :                db_msg(&msg, MERROR, "db_get_data", "symlink \"%s\" should start with \"/\" in \"%s\"", link_name.c_str(), db_get_path_locked(pheader, pkey).c_str());
    6609            0 :                db_unlock_database(hDB);
    6610            0 :                if (msg)
    6611            0 :                   db_flush_msg(&msg);
    6612            0 :                return DB_INVALID_LINK;
    6613              :             }
    6614              : 
    6615            0 :             const KEY* pkey = db_find_pkey_locked(pheader, NULL, link_name.c_str(), &status, &msg);
    6616              : 
    6617            0 :             if (!pkey) {
    6618            0 :                db_unlock_database(hDB);
    6619            0 :                if (msg)
    6620            0 :                   db_flush_msg(&msg);
    6621            0 :                return status;
    6622              :             }
    6623              : 
    6624              :             //printf("db_get_data [%s] type [%s] idx %d\n", db_get_path_locked(pheader, pkey).c_str(), rpc_tid_name(type), idx);
    6625              : 
    6626            0 :             status = db_get_data_locked(pheader, pkey, idx, data, buf_size, type, &msg);
    6627              :             
    6628            0 :             db_unlock_database(hDB);
    6629            0 :             if (msg)
    6630            0 :                db_flush_msg(&msg);
    6631            0 :             return status;
    6632              :          }
    6633            0 :       }
    6634              : 
    6635              :       //printf("db_get_data [%s] type [%s]\n", db_get_path_locked(pheader, pkey).c_str(), rpc_tid_name(type));
    6636              : 
    6637           40 :       status = db_get_data_locked(pheader, pkey, -1, data, buf_size, type, &msg);
    6638              : 
    6639           40 :       db_unlock_database(hDB);
    6640           40 :       if (msg)
    6641            0 :          db_flush_msg(&msg);
    6642              : 
    6643           40 :       return status;
    6644              :    }
    6645              : #endif                          /* LOCAL_ROUTINES */
    6646              : 
    6647              :    return DB_SUCCESS;
    6648              : }
    6649              : 
    6650              : /********************************************************************/
    6651              : /**
    6652              : Same as db_get_data, but do not follow a link to an array index
    6653              : 
    6654              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    6655              : @param hKey         Handle for key where search starts, zero for root.
    6656              : @param data         Pointer to the return data.
    6657              : @param buf_size     Size of data buffer.
    6658              : @param type         Type of key, one of TID_xxx (see @ref Midas_Data_Types).
    6659              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TRUNCATED, DB_TYPE_MISMATCH
    6660              : */
    6661           20 : INT db_get_link_data(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, DWORD type)
    6662              : {
    6663           20 :    if (rpc_is_remote())
    6664            0 :       return rpc_call(RPC_DB_GET_LINK_DATA, hDB, hKey, data, buf_size, type);
    6665              : 
    6666              : #ifdef LOCAL_ROUTINES
    6667              :    {
    6668              :       DATABASE_HEADER *pheader;
    6669              :       KEY *pkey;
    6670              : 
    6671           20 :       if (hDB > _database_entries || hDB <= 0) {
    6672            0 :          cm_msg(MERROR, "db_get_data", "Invalid database handle");
    6673            0 :          return DB_INVALID_HANDLE;
    6674              :       }
    6675              : 
    6676           20 :       if (!_database[hDB - 1].attached) {
    6677            0 :          cm_msg(MERROR, "db_get_data", "invalid database handle");
    6678            0 :          return DB_INVALID_HANDLE;
    6679              :       }
    6680              : 
    6681           20 :       if (hKey < (int) sizeof(DATABASE_HEADER)) {
    6682            0 :          cm_msg(MERROR, "db_get_data", "invalid key handle");
    6683            0 :          return DB_INVALID_HANDLE;
    6684              :       }
    6685              : 
    6686           20 :       db_lock_database(hDB);
    6687              : 
    6688           20 :       pheader = _database[hDB - 1].database_header;
    6689              : 
    6690              :       /* check if hKey argument is correct */
    6691           20 :       if (!db_validate_hkey(pheader, hKey)) {
    6692            0 :          db_unlock_database(hDB);
    6693            0 :          return DB_INVALID_HANDLE;
    6694              :       }
    6695              : 
    6696           20 :       pkey = (KEY *) ((char *) pheader + hKey);
    6697              : 
    6698              :       /* check for read access */
    6699           20 :       if (!(pkey->access_mode & MODE_READ)) {
    6700            0 :          db_unlock_database(hDB);
    6701            0 :          return DB_NO_ACCESS;
    6702              :       }
    6703              : 
    6704           20 :       if (!pkey->type) {
    6705            0 :          int pkey_type = pkey->type;
    6706            0 :          db_unlock_database(hDB);
    6707            0 :          cm_msg(MERROR, "db_get_data", "hkey %d invalid key type %d", hKey, pkey_type);
    6708            0 :          return DB_INVALID_HANDLE;
    6709              :       }
    6710              : 
    6711           20 :       if (pkey->type != type) {
    6712            0 :          int pkey_type = pkey->type;
    6713              :          char pkey_name[NAME_LENGTH];
    6714            0 :          mstrlcpy(pkey_name, pkey->name, sizeof(pkey_name));
    6715            0 :          db_unlock_database(hDB);
    6716            0 :          cm_msg(MERROR, "db_get_data", "\"%s\" is of type %s, not %s", pkey_name, rpc_tid_name(pkey_type), rpc_tid_name(type));
    6717            0 :          return DB_TYPE_MISMATCH;
    6718              :       }
    6719              : 
    6720              :       /* keys cannot contain data */
    6721           20 :       if (pkey->type == TID_KEY) {
    6722            0 :          db_unlock_database(hDB);
    6723            0 :          cm_msg(MERROR, "db_get_data", "Key cannot contain data");
    6724            0 :          return DB_TYPE_MISMATCH;
    6725              :       }
    6726              : 
    6727              :       /* check if key has data */
    6728           20 :       if (pkey->data == 0) {
    6729            0 :          memset(data, 0, *buf_size);
    6730            0 :          *buf_size = 0;
    6731            0 :          db_unlock_database(hDB);
    6732            0 :          return DB_SUCCESS;
    6733              :       }
    6734              : 
    6735              :       /* check if buffer is too small */
    6736           20 :       if (pkey->num_values * pkey->item_size > *buf_size) {
    6737            0 :          int pkey_size = pkey->num_values * pkey->item_size;
    6738            0 :          memcpy(data, (char *) pheader + pkey->data, *buf_size);
    6739            0 :          db_unlock_database(hDB);
    6740            0 :          std::string path = db_get_path(hDB, hKey);
    6741            0 :          cm_msg(MERROR, "db_get_data", "data for key \"%s\" truncated from %d to %d bytes", path.c_str(), pkey_size, *buf_size);
    6742            0 :          return DB_TRUNCATED;
    6743            0 :       }
    6744              : 
    6745              :       /* copy key data */
    6746           20 :       memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
    6747           20 :       *buf_size = pkey->num_values * pkey->item_size;
    6748              : 
    6749           20 :       db_unlock_database(hDB);
    6750              : 
    6751              :    }
    6752              : #endif                          /* LOCAL_ROUTINES */
    6753              : 
    6754           20 :    return DB_SUCCESS;
    6755              : }
    6756              : 
    6757              : /**dox***************************************************************/
    6758              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    6759              : 
    6760              : /*------------------------------------------------------------------*/
    6761            0 : INT db_get_data1(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, DWORD type, INT * num_values)
    6762              : /********************************************************************\
    6763              : 
    6764              :   Routine: db_get_data1
    6765              : 
    6766              :   Purpose: Get key data from a handle, return number of values
    6767              : 
    6768              :   Input:
    6769              :     HNDLE  hDB              Handle to the database
    6770              :     HNDLE  hKey             Handle of key
    6771              :     INT    *buf_size        Size of data buffer
    6772              :     DWORD  type             Type of data
    6773              : 
    6774              :   Output:
    6775              :     void   *data            Key data
    6776              :     INT    *buf_size        Size of key data
    6777              :     INT    *num_values      Number of values
    6778              : 
    6779              :   Function value:
    6780              :     DB_SUCCESS              Successful completion
    6781              :     DB_INVALID_HANDLE       Database handle is invalid
    6782              :     DB_TRUNCATED            Return buffer is smaller than key data
    6783              :     DB_TYPE_MISMATCH        Type mismatch
    6784              : 
    6785              : \********************************************************************/
    6786              : {
    6787            0 :    if (rpc_is_remote())
    6788            0 :       return rpc_call(RPC_DB_GET_DATA1, hDB, hKey, data, buf_size, type, num_values);
    6789              : 
    6790              : #ifdef LOCAL_ROUTINES
    6791              :    {
    6792              :       DATABASE_HEADER *pheader;
    6793              :       KEY *pkey;
    6794              : 
    6795            0 :       if (hDB > _database_entries || hDB <= 0) {
    6796            0 :          cm_msg(MERROR, "db_get_data", "Invalid database handle");
    6797            0 :          return DB_INVALID_HANDLE;
    6798              :       }
    6799              : 
    6800            0 :       if (!_database[hDB - 1].attached) {
    6801            0 :          cm_msg(MERROR, "db_get_data", "invalid database handle");
    6802            0 :          return DB_INVALID_HANDLE;
    6803              :       }
    6804              : 
    6805            0 :       if (hKey < (int) sizeof(DATABASE_HEADER)) {
    6806            0 :          cm_msg(MERROR, "db_get_data", "invalid key handle");
    6807            0 :          return DB_INVALID_HANDLE;
    6808              :       }
    6809              : 
    6810            0 :       db_lock_database(hDB);
    6811              : 
    6812            0 :       pheader = _database[hDB - 1].database_header;
    6813              : 
    6814              :       /* check if hKey argument is correct */
    6815            0 :       if (!db_validate_hkey(pheader, hKey)) {
    6816            0 :          db_unlock_database(hDB);
    6817            0 :          return DB_INVALID_HANDLE;
    6818              :       }
    6819              : 
    6820            0 :       pkey = (KEY *) ((char *) pheader + hKey);
    6821              : 
    6822              :       /* check for read access */
    6823            0 :       if (!(pkey->access_mode & MODE_READ)) {
    6824            0 :          db_unlock_database(hDB);
    6825            0 :          return DB_NO_ACCESS;
    6826              :       }
    6827              : 
    6828            0 :       if (!pkey->type) {
    6829            0 :          int pkey_type = pkey->type;
    6830            0 :          db_unlock_database(hDB);
    6831            0 :          cm_msg(MERROR, "db_get_data", "hkey %d invalid key type %d", hKey, pkey_type);
    6832            0 :          return DB_INVALID_HANDLE;
    6833              :       }
    6834              : 
    6835            0 :       if (pkey->type != type) {
    6836            0 :          int pkey_type = pkey->type;
    6837              :          char pkey_name[NAME_LENGTH];
    6838            0 :          mstrlcpy(pkey_name, pkey->name, sizeof(pkey_name));
    6839            0 :          db_unlock_database(hDB);
    6840            0 :          cm_msg(MERROR, "db_get_data", "\"%s\" is of type %s, not %s", pkey_name, rpc_tid_name(pkey_type), rpc_tid_name(type));
    6841            0 :          return DB_TYPE_MISMATCH;
    6842              :       }
    6843              : 
    6844              :       /* keys cannot contain data */
    6845            0 :       if (pkey->type == TID_KEY) {
    6846            0 :          db_unlock_database(hDB);
    6847            0 :          cm_msg(MERROR, "db_get_data", "Key cannot contain data");
    6848            0 :          return DB_TYPE_MISMATCH;
    6849              :       }
    6850              : 
    6851              :       /* check if key has data */
    6852            0 :       if (pkey->data == 0) {
    6853            0 :          memset(data, 0, *buf_size);
    6854            0 :          *buf_size = 0;
    6855            0 :          db_unlock_database(hDB);
    6856            0 :          return DB_SUCCESS;
    6857              :       }
    6858              : 
    6859              :       /* check if buffer is too small */
    6860            0 :       if (pkey->num_values * pkey->item_size > *buf_size) {
    6861            0 :          int pkey_size = pkey->num_values * pkey->item_size;
    6862            0 :          memcpy(data, (char *) pheader + pkey->data, *buf_size);
    6863            0 :          db_unlock_database(hDB);
    6864            0 :          std::string path = db_get_path(hDB, hKey);
    6865            0 :          cm_msg(MERROR, "db_get_data", "data for key \"%s\" truncated from %d to %d bytes", path.c_str(), pkey_size, *buf_size);
    6866            0 :          return DB_TRUNCATED;
    6867            0 :       }
    6868              : 
    6869              :       /* copy key data */
    6870            0 :       memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
    6871            0 :       *buf_size = pkey->num_values * pkey->item_size;
    6872            0 :       *num_values = pkey->num_values;
    6873              : 
    6874            0 :       db_unlock_database(hDB);
    6875              : 
    6876              :    }
    6877              : #endif                          /* LOCAL_ROUTINES */
    6878              : 
    6879            0 :    return DB_SUCCESS;
    6880              : }
    6881              : 
    6882              : /**dox***************************************************************/
    6883              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    6884              : 
    6885              : /********************************************************************/
    6886              : /**
    6887              : returns a single value of keys containing arrays of values.
    6888              : 
    6889              : The function returns a single value of keys containing arrays of values.
    6890              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    6891              : @param hKey         Handle for key where search starts, zero for root.
    6892              : @param data         Size of data buffer.
    6893              : @param buf_size     Return size of the record.
    6894              : @param idx          Index of array [0..n-1].
    6895              : @param type         Type of key, one of TID_xxx (see @ref Midas_Data_Types).
    6896              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TRUNCATED, DB_OUT_OF_RANGE
    6897              : */
    6898           58 : INT db_get_data_index(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, INT idx, DWORD type)
    6899              : {
    6900           58 :    if (rpc_is_remote())
    6901            0 :       return rpc_call(RPC_DB_GET_DATA_INDEX, hDB, hKey, data, buf_size, idx, type);
    6902              : 
    6903              : #ifdef LOCAL_ROUTINES
    6904              :    {
    6905              :       DATABASE_HEADER *pheader;
    6906              :       KEY *pkey;
    6907              : 
    6908           58 :       if (hDB > _database_entries || hDB <= 0) {
    6909            0 :          cm_msg(MERROR, "db_get_data", "Invalid database handle");
    6910            0 :          return DB_INVALID_HANDLE;
    6911              :       }
    6912              : 
    6913           58 :       if (!_database[hDB - 1].attached) {
    6914            0 :          cm_msg(MERROR, "db_get_data", "invalid database handle");
    6915            0 :          return DB_INVALID_HANDLE;
    6916              :       }
    6917              : 
    6918           58 :       if (hKey < (int) sizeof(DATABASE_HEADER)) {
    6919            0 :          cm_msg(MERROR, "db_get_data", "invalid key handle");
    6920            0 :          return DB_INVALID_HANDLE;
    6921              :       }
    6922              : 
    6923           58 :       db_lock_database(hDB);
    6924              : 
    6925           58 :       pheader = _database[hDB - 1].database_header;
    6926              : 
    6927              :       /* check if hKey argument is correct */
    6928           58 :       if (!db_validate_hkey(pheader, hKey)) {
    6929            0 :          db_unlock_database(hDB);
    6930            0 :          return DB_INVALID_HANDLE;
    6931              :       }
    6932              : 
    6933           58 :       pkey = (KEY *) ((char *) pheader + hKey);
    6934              : 
    6935              :       /* check for read access */
    6936           58 :       if (!(pkey->access_mode & MODE_READ)) {
    6937            0 :          db_unlock_database(hDB);
    6938            0 :          return DB_NO_ACCESS;
    6939              :       }
    6940              : 
    6941           58 :       if (!pkey->type) {
    6942            0 :          int pkey_type = pkey->type;
    6943            0 :          db_unlock_database(hDB);
    6944            0 :          cm_msg(MERROR, "db_get_data_index", "hkey %d invalid key type %d", hKey, pkey_type);
    6945            0 :          return DB_INVALID_HANDLE;
    6946              :       }
    6947              : 
    6948           58 :       if (pkey->type != type) {
    6949            0 :          int pkey_type = pkey->type;
    6950              :          char pkey_name[NAME_LENGTH];
    6951            0 :          mstrlcpy(pkey_name, pkey->name, sizeof(pkey_name));
    6952            0 :          db_unlock_database(hDB);
    6953            0 :          cm_msg(MERROR, "db_get_data_index", "\"%s\" is of type %s, not %s", pkey_name, rpc_tid_name(pkey_type), rpc_tid_name(type));
    6954            0 :          return DB_TYPE_MISMATCH;
    6955              :       }
    6956              : 
    6957              :       /* keys cannot contain data */
    6958           58 :       if (pkey->type == TID_KEY) {
    6959            0 :          db_unlock_database(hDB);
    6960            0 :          cm_msg(MERROR, "db_get_data_index", "Key cannot contain data");
    6961            0 :          return DB_TYPE_MISMATCH;
    6962              :       }
    6963              : 
    6964              :       /* check if key has data */
    6965           58 :       if (pkey->data == 0) {
    6966            0 :          memset(data, 0, *buf_size);
    6967            0 :          *buf_size = 0;
    6968            0 :          db_unlock_database(hDB);
    6969            0 :          return DB_SUCCESS;
    6970              :       }
    6971              : 
    6972              :       /* check if index in range */
    6973           58 :       if (idx < 0 || idx >= pkey->num_values) {
    6974            0 :          int pkey_num_values = pkey->num_values;
    6975            0 :          memset(data, 0, *buf_size);
    6976            0 :          db_unlock_database(hDB);
    6977              : 
    6978            0 :          std::string path = db_get_path(hDB, hKey);
    6979            0 :          cm_msg(MERROR, "db_get_data_index", "index (%d) exceeds array length (%d) for key \"%s\"", idx, pkey_num_values, path.c_str());
    6980            0 :          return DB_OUT_OF_RANGE;
    6981            0 :       }
    6982              : 
    6983              :       /* check if buffer is too small */
    6984           58 :       if (pkey->item_size > *buf_size) {
    6985            0 :          int pkey_size = pkey->item_size;
    6986              :          /* copy data */
    6987            0 :          memcpy(data, (char *) pheader + pkey->data + idx * pkey->item_size, *buf_size);
    6988            0 :          db_unlock_database(hDB);
    6989            0 :          std::string path = db_get_path(hDB, hKey);
    6990            0 :          cm_msg(MERROR, "db_get_data_index", "data for key \"%s\" truncated from %d to %d bytes", path.c_str(), pkey_size, *buf_size);
    6991            0 :          return DB_TRUNCATED;
    6992            0 :       }
    6993              : 
    6994              :       /* copy key data */
    6995           58 :       memcpy(data, (char *) pheader + pkey->data + idx * pkey->item_size, pkey->item_size);
    6996           58 :       *buf_size = pkey->item_size;
    6997              : 
    6998           58 :       db_unlock_database(hDB);
    6999              : 
    7000              :    }
    7001              : #endif                          /* LOCAL_ROUTINES */
    7002              : 
    7003           58 :    return DB_SUCCESS;
    7004              : }
    7005              : 
    7006              : #ifdef LOCAL_ROUTINES
    7007              : /********************************************************************/
    7008              : /**
    7009              : Set key data, adjust number of values if previous data has different size.
    7010              : @param pkey Key to change
    7011              : @param idx  Data index to change, "-1" means the whole array of data
    7012              : @param data Buffer from which data gets copied to.
    7013              : @param data_size Size of data buffer.
    7014              : @param num_values Number of data values (for arrays).
    7015              : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
    7016              : @return DB_SUCCESS, DB_FULL
    7017              : */
    7018              : 
    7019          163 : static INT db_set_data_wlocked(DATABASE_HEADER* pheader, KEY* pkey, const void *data, INT data_size, INT num_values, DWORD type, const char* caller, db_err_msg** msg)
    7020              : {
    7021              :    /* if no buf_size given (Java!), calculate it */
    7022          163 :    if (data_size == 0)
    7023            0 :       data_size = pkey->item_size * num_values;
    7024              : 
    7025              :    /* resize data size if necessary */
    7026          163 :    if (pkey->total_size != data_size) {
    7027              :       // FIXME: validate pkey->data!
    7028           22 :       pkey->data = (POINTER_T) realloc_data(pheader, (char *) pheader + pkey->data, pkey->total_size, data_size, caller);
    7029              :       
    7030           22 :       if (pkey->data == 0) {
    7031            0 :          pkey->total_size = 0;
    7032            0 :          db_msg(msg, MERROR, caller, "Cannot reallocate \"%s\" with new size %d bytes, online database full", db_get_path_locked(pheader, pkey).c_str(), data_size);
    7033            0 :          return DB_FULL;
    7034              :       }
    7035              :       
    7036           22 :       pkey->data -= (POINTER_T) pheader;
    7037           22 :       pkey->total_size = data_size;
    7038              :    }
    7039              :    
    7040              :    /* set number of values */
    7041          163 :    pkey->num_values = num_values;
    7042              : 
    7043          163 :    if (type == TID_STRING || type == TID_LINK)
    7044           24 :       pkey->item_size = data_size / num_values;
    7045              :    else
    7046          139 :       pkey->item_size = rpc_tid_size(type);
    7047              :    
    7048          163 :    if ((type == TID_STRING || type == TID_LINK) && pkey->num_values == 1) {
    7049              :       /* copy string up to NUL termination */
    7050           24 :       mstrlcpy((char *) pheader + pkey->data, (const char*)data, data_size);
    7051              :    } else {
    7052              :       /* copy data */
    7053          139 :       memcpy((char *) pheader + pkey->data, data, data_size);
    7054              :    }
    7055              :    
    7056              :    /* update time */
    7057          163 :    pkey->last_written = ss_time();
    7058              : 
    7059          163 :    return DB_SUCCESS;
    7060              : }
    7061              : 
    7062            4 : static INT db_set_data_index_wlocked(DATABASE_HEADER* pheader, KEY* pkey, int idx, const void *data, INT data_size, DWORD type, const char* caller, db_err_msg** msg)
    7063              : {
    7064              :    /* increase key size if necessary */
    7065            4 :    if (idx >= pkey->num_values || pkey->item_size == 0) {
    7066              :       // FIXME: validate pkey->data
    7067            4 :       pkey->data = (POINTER_T) realloc_data(pheader, (char *) pheader + pkey->data, pkey->total_size, data_size * (idx + 1), caller);
    7068              :       
    7069            4 :       if (pkey->data == 0) {
    7070            0 :          pkey->total_size = 0;
    7071            0 :          pkey->num_values = 0;
    7072            0 :          db_msg(msg, MERROR, caller, "Cannot reallocate \"%s\" with new num_values %d and new size %d bytes, online database full", db_get_path_locked(pheader, pkey).c_str(), idx + 1, data_size * (idx + 1));
    7073            0 :          return DB_FULL;
    7074              :       }
    7075              :       
    7076            4 :       pkey->data -= (POINTER_T) pheader;
    7077            4 :       if (!pkey->item_size)
    7078            4 :          pkey->item_size = data_size;
    7079            4 :       pkey->total_size = data_size * (idx + 1);
    7080            4 :       pkey->num_values = idx + 1;
    7081              :    }
    7082              : 
    7083              : #if 0
    7084              :    /* cut strings which are too long */
    7085              :    if ((type == TID_STRING || type == TID_LINK) && (int) strlen((char *) data) + 1 > pkey->item_size)
    7086              :       *((char *) data + pkey->item_size - 1) = 0;
    7087              :    
    7088              :    /* copy data */
    7089              :    memcpy((char *) pheader + pkey->data + idx * pkey->item_size, data, pkey->item_size);
    7090              : #endif
    7091              : 
    7092            4 :    if ((type == TID_STRING || type == TID_LINK)) {
    7093              :       /* cut strings which are too long */
    7094            4 :       mstrlcpy((char *) pheader + pkey->data + idx * pkey->item_size, (char*)data, pkey->item_size);
    7095              :    } else {
    7096              :       /* copy data */
    7097            0 :       memcpy((char *) pheader + pkey->data + idx * pkey->item_size, data, pkey->item_size);
    7098              :    }
    7099              : 
    7100              :    /* update time */
    7101            4 :    pkey->last_written = ss_time();
    7102              : 
    7103            4 :    return DB_SUCCESS;
    7104              : }
    7105              : 
    7106          163 : static INT db_check_set_data_locked(DATABASE_HEADER* pheader, const KEY* pkey, const void *data, INT data_size, INT num_values, DWORD type, const char* caller, db_err_msg** msg)
    7107              : {
    7108              :    /* check for write access */
    7109          163 :    if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
    7110            0 :       return DB_NO_ACCESS;
    7111              :    }
    7112              :    
    7113          163 :    if (pkey->type != type) {
    7114            0 :       db_msg(msg, MERROR, caller, "\"%s\" is of type %s, not %s", db_get_path_locked(pheader, pkey).c_str(), rpc_tid_name(pkey->type), rpc_tid_name(type));
    7115            0 :       return DB_TYPE_MISMATCH;
    7116              :    }
    7117              :    
    7118              :    /* keys cannot contain data */
    7119          163 :    if (pkey->type == TID_KEY) {
    7120            0 :       db_msg(msg, MERROR, caller, "\"%s\" of type TID_KEY cannot contain data", db_get_path_locked(pheader, pkey).c_str());
    7121            0 :       return DB_TYPE_MISMATCH;
    7122              :    }
    7123              :    
    7124          163 :    if (type == TID_STRING || type == TID_LINK) {
    7125           24 :       if (num_values > 1) {
    7126            0 :          int item_size = pkey->item_size;
    7127            0 :          if (data_size > 0 && num_values > 0)
    7128            0 :             item_size = data_size/num_values;
    7129              :          //printf("db_check_set_data for %s: utf8 check for odb \"%s\" string array size %d, item size %d\n", caller, db_get_path_locked(pheader, pkey).c_str(), num_values, item_size);
    7130            0 :          for (int i=0; i<num_values; i++) {
    7131            0 :             const char* value = ((const char*)data) + i * item_size;
    7132              :             //printf("db_check_set_data for %s: utf8 check for odb \"%s\" string array size %d item_size %d, index %d, value \"%s\"\n", caller, db_get_path_locked(pheader, pkey).c_str(), num_values, item_size, i, value);
    7133            0 :             if (!is_utf8(value)) {
    7134            0 :                db_msg(msg, MERROR, caller, "\"%s\" index %d set to invalid UTF-8 Unicode string value \"%s\"", db_get_path_locked(pheader, pkey).c_str(), i, value);
    7135              :                // just a warning for now. K.O.
    7136              :                //return DB_TYPE_MISMATCH;
    7137              :             }
    7138              :          }
    7139              :       } else {
    7140           24 :          const char* value = (const char*)data;
    7141              :          //printf("db_check_set_data for %s: utf8 check for odb \"%s\" value \"%s\" size %d\n", caller, db_get_path_locked(pheader, pkey).c_str(), value, data_size);
    7142           24 :          if (!is_utf8(value)) {
    7143            0 :             db_msg(msg, MERROR, caller, "\"%s\" set to invalid UTF-8 Unicode string value \"%s\"", db_get_path_locked(pheader, pkey).c_str(), value);
    7144              :             // just a warning for now. K.O.
    7145              :             //return DB_TYPE_MISMATCH;
    7146              :          }
    7147              :       }
    7148              :    }
    7149              : 
    7150          163 :    return DB_SUCCESS;
    7151              : }
    7152              : 
    7153            4 : static INT db_check_set_data_index_locked(DATABASE_HEADER* pheader, const KEY* pkey, int idx, const void *data, INT data_size, DWORD type, const char* caller, db_err_msg** msg)
    7154              : {
    7155              :    /* check for write access */
    7156            4 :    if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
    7157            0 :       return DB_NO_ACCESS;
    7158              :    }
    7159              :    
    7160            4 :    if (pkey->type != type) {
    7161            0 :       db_msg(msg, MERROR, caller, "\"%s\" is of type %s, not %s", db_get_path_locked(pheader, pkey).c_str(), rpc_tid_name(pkey->type), rpc_tid_name(type));
    7162            0 :       return DB_TYPE_MISMATCH;
    7163              :    }
    7164              :    
    7165              :    /* keys cannot contain data */
    7166            4 :    if (pkey->type == TID_KEY) {
    7167            0 :       db_msg(msg, MERROR, "db_set_data_index", "\"%s\" of type TID_KEY cannot contain data", db_get_path_locked(pheader, pkey).c_str());
    7168            0 :       return DB_TYPE_MISMATCH;
    7169              :    }
    7170              :    
    7171              :    /* check utf-8 encoding */
    7172            4 :    if (pkey->type == TID_STRING || pkey->type == TID_LINK) {
    7173              :       //printf("db_check_set_data_index for %s: utf8 check for odb \"%s\" value \"%s\"\n", caller, db_get_path_locked(pheader, pkey).c_str(), data);
    7174            4 :       const char* value = (const char*)data;
    7175            4 :       if (!is_utf8(value)) {
    7176            0 :          db_msg(msg, MERROR, "db_set_data_index", "\"%s\" index %d set to invalid UTF-8 Unicode string value \"%s\"", db_get_path_locked(pheader, pkey).c_str(), idx, value);
    7177              :          // just a warning for now. K.O.
    7178              :          //return DB_TYPE_MISMATCH;
    7179              :       }
    7180              :    }
    7181              : 
    7182              :    /* check for valid idx */
    7183            4 :    if (idx < 0) {
    7184            0 :       db_msg(msg, MERROR, caller, "\%s\" given invalid index %d",  db_get_path_locked(pheader, pkey).c_str(), idx);
    7185            0 :       return DB_INVALID_PARAM;
    7186              :    }
    7187              :    
    7188              :    /* check for valid array element size: if new element size
    7189              :       is different from existing size, ODB becomes corrupted */
    7190            4 :    if (pkey->item_size != 0 && data_size != pkey->item_size) {
    7191            0 :       db_msg(msg, MERROR, caller, "\"%s\" invalid element data size %d, expected %d", db_get_path_locked(pheader, pkey).c_str(), data_size, pkey->item_size);
    7192            0 :       return DB_TYPE_MISMATCH;
    7193              :    }
    7194              : 
    7195            4 :    return DB_SUCCESS;
    7196              : }
    7197              : 
    7198              : #endif // LOCAL_ROUTINES
    7199              : 
    7200              : /********************************************************************/
    7201              : /**
    7202              : Set key data from a handle. Adjust number of values if
    7203              : previous data has different size.
    7204              : \code
    7205              : HNLDE hkey;
    7206              :  INT   run_number;
    7207              :  // get key handle for run number
    7208              :  db_find_key(hDB, 0, "/Runinfo/Run number", &hkey);
    7209              :  // set run number
    7210              :  db_set_data(hDB, hkey, &run_number, sizeof(run_number),TID_INT32);
    7211              : \endcode
    7212              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    7213              : @param hKey Handle for key where search starts, zero for root.
    7214              : @param data Buffer from which data gets copied to.
    7215              : @param buf_size Size of data buffer.
    7216              : @param num_values Number of data values (for arrays).
    7217              : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
    7218              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TRUNCATED
    7219              : */
    7220            6 : INT db_set_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
    7221              : {
    7222            6 :    if (rpc_is_remote())
    7223            0 :       return rpc_call(RPC_DB_SET_DATA, hDB, hKey, data, buf_size, num_values, type);
    7224              : 
    7225              : #ifdef LOCAL_ROUTINES
    7226              :    {
    7227              :       DATABASE_HEADER *pheader;
    7228              :       KEY *pkey;
    7229              :       HNDLE hkeylink;
    7230              :       int link_idx;
    7231              :       char link_name[256];
    7232              :       int status;
    7233              : 
    7234            6 :       if (hDB > _database_entries || hDB <= 0) {
    7235            0 :          cm_msg(MERROR, "db_set_data", "invalid database handle");
    7236            0 :          return DB_INVALID_HANDLE;
    7237              :       }
    7238              : 
    7239            6 :       if (!_database[hDB - 1].attached) {
    7240            0 :          cm_msg(MERROR, "db_set_data", "invalid database handle");
    7241            0 :          return DB_INVALID_HANDLE;
    7242              :       }
    7243              : 
    7244            6 :       if (hKey < (int) sizeof(DATABASE_HEADER)) {
    7245            0 :          cm_msg(MERROR, "db_set_data", "invalid key handle");
    7246            0 :          return DB_INVALID_HANDLE;
    7247              :       }
    7248              : 
    7249            6 :       if (num_values == 0)
    7250            0 :          return DB_INVALID_PARAM;
    7251              : 
    7252            6 :       db_lock_database(hDB);
    7253            6 :       db_err_msg* msg = NULL;
    7254              : 
    7255            6 :       pheader = _database[hDB - 1].database_header;
    7256              : 
    7257              :       /* check if hKey argument is correct */
    7258            6 :       if (!db_validate_hkey(pheader, hKey)) {
    7259            0 :          db_unlock_database(hDB);
    7260            0 :          return DB_INVALID_HANDLE;
    7261              :       }
    7262              : 
    7263            6 :       pkey = (KEY *) ((char *) pheader + hKey);
    7264              : 
    7265              :       /* check for write access */
    7266            6 :       if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
    7267            0 :          db_unlock_database(hDB);
    7268            0 :          return DB_NO_ACCESS;
    7269              :       }
    7270              : 
    7271              :       /* check for link to array index */
    7272            6 :       if (pkey->type == TID_LINK) {
    7273            0 :          mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
    7274            0 :          if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
    7275            0 :             db_unlock_database(hDB);
    7276            0 :             if (strchr(link_name, '[') == NULL)
    7277            0 :                return DB_INVALID_LINK;
    7278            0 :             link_idx = atoi(strchr(link_name, '[') + 1);
    7279            0 :             *strchr(link_name, '[') = 0;
    7280            0 :             if (db_find_key(hDB, 0, link_name, &hkeylink) != DB_SUCCESS)
    7281            0 :                return DB_INVALID_LINK;
    7282            0 :             return db_set_data_index(hDB, hkeylink, data, buf_size, link_idx, type);
    7283              :          }
    7284              :       }
    7285              : 
    7286            6 :       status = db_check_set_data_locked(pheader, pkey, data, buf_size, num_values, type, "db_set_data", &msg);
    7287              : 
    7288            6 :       if (status != DB_SUCCESS) {
    7289            0 :          db_unlock_database(hDB);
    7290            0 :          if (msg)
    7291            0 :             db_flush_msg(&msg);
    7292            0 :          return status;
    7293              :       }
    7294              : 
    7295            6 :       db_allow_write_locked(&_database[hDB-1], "db_set_data");
    7296              : 
    7297            6 :       status = db_set_data_wlocked(pheader, pkey, data, buf_size, num_values, type, "db_set_data", &msg);
    7298              : 
    7299            6 :       if (status != DB_SUCCESS) {
    7300            0 :          db_unlock_database(hDB);
    7301            0 :          if (msg)
    7302            0 :             db_flush_msg(&msg);
    7303            0 :          return status;
    7304              :       }
    7305              : 
    7306            6 :       db_notify_clients_locked(pheader, hDB, hKey, -1, TRUE, &msg);
    7307            6 :       db_unlock_database(hDB);
    7308            6 :       if (msg)
    7309            0 :          db_flush_msg(&msg);
    7310              : 
    7311              : 
    7312              :    }
    7313              : #endif                          /* LOCAL_ROUTINES */
    7314              : 
    7315            6 :    return DB_SUCCESS;
    7316              : }
    7317              : 
    7318            0 : INT db_set_data1(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
    7319              : /*
    7320              :  
    7321              :  Same as db_set_data(), but do not notify hot-linked clients
    7322              :  
    7323              :  */
    7324              : {
    7325            0 :    if (rpc_is_remote())
    7326            0 :       return rpc_call(RPC_DB_SET_DATA1, hDB, hKey, data, buf_size, num_values, type);
    7327              :    
    7328              : #ifdef LOCAL_ROUTINES
    7329              :    {
    7330              :    DATABASE_HEADER *pheader;
    7331              :    KEY *pkey;
    7332              :    HNDLE hkeylink;
    7333              :    int link_idx;
    7334              :    char link_name[256];
    7335              :    int status;
    7336              :    
    7337            0 :    if (hDB > _database_entries || hDB <= 0) {
    7338            0 :       cm_msg(MERROR, "db_set_data1", "invalid database handle");
    7339            0 :       return DB_INVALID_HANDLE;
    7340              :    }
    7341              :    
    7342            0 :    if (!_database[hDB - 1].attached) {
    7343            0 :       cm_msg(MERROR, "db_set_data1", "invalid database handle");
    7344            0 :       return DB_INVALID_HANDLE;
    7345              :    }
    7346              :    
    7347            0 :    if (hKey < (int) sizeof(DATABASE_HEADER)) {
    7348            0 :       cm_msg(MERROR, "db_set_data1", "invalid key handle");
    7349            0 :       return DB_INVALID_HANDLE;
    7350              :    }
    7351              :    
    7352            0 :    if (num_values == 0)
    7353            0 :       return DB_INVALID_PARAM;
    7354              :    
    7355            0 :    db_lock_database(hDB);
    7356            0 :    db_err_msg* msg = NULL;
    7357              :    
    7358            0 :    pheader = _database[hDB - 1].database_header;
    7359              :    
    7360              :    /* check if hKey argument is correct */
    7361            0 :    if (!db_validate_hkey(pheader, hKey)) {
    7362            0 :       db_unlock_database(hDB);
    7363            0 :       return DB_INVALID_HANDLE;
    7364              :    }
    7365              :    
    7366            0 :    pkey = (KEY *) ((char *) pheader + hKey);
    7367              : 
    7368              :    /* check for write access */
    7369            0 :    if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
    7370            0 :       db_unlock_database(hDB);
    7371            0 :       return DB_NO_ACCESS;
    7372              :    }
    7373              :    
    7374              :    /* check for link to array index */
    7375            0 :    if (pkey->type == TID_LINK) {
    7376            0 :       mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
    7377            0 :       if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
    7378            0 :          db_unlock_database(hDB);
    7379            0 :          if (strchr(link_name, '[') == NULL)
    7380            0 :             return DB_INVALID_LINK;
    7381            0 :          link_idx = atoi(strchr(link_name, '[') + 1);
    7382            0 :          *strchr(link_name, '[') = 0;
    7383            0 :          if (db_find_key(hDB, 0, link_name, &hkeylink) != DB_SUCCESS)
    7384            0 :             return DB_INVALID_LINK;
    7385            0 :          return db_set_data_index1(hDB, hkeylink, data, buf_size, link_idx, type, FALSE);
    7386              :       }
    7387              :    }
    7388              : 
    7389            0 :    status = db_check_set_data_locked(pheader, pkey, data, buf_size, num_values, type, "db_set_data1", &msg);
    7390              : 
    7391            0 :    if (status != DB_SUCCESS) {
    7392            0 :       db_unlock_database(hDB);
    7393            0 :       if (msg)
    7394            0 :          db_flush_msg(&msg);
    7395            0 :       return status;
    7396              :    }
    7397              :    
    7398            0 :    db_allow_write_locked(&_database[hDB - 1], "db_set_data1");
    7399              : 
    7400            0 :    status = db_set_data_wlocked(pheader, pkey, data, buf_size, num_values, type, "db_set_data1", &msg);
    7401              :    
    7402            0 :    if (status != DB_SUCCESS) {
    7403            0 :       db_unlock_database(hDB);
    7404            0 :       if (msg)
    7405            0 :          db_flush_msg(&msg);
    7406            0 :       return status;
    7407              :    }
    7408              :    
    7409            0 :    db_unlock_database(hDB);
    7410            0 :    if (msg)
    7411            0 :       db_flush_msg(&msg);
    7412              :    
    7413              :    }
    7414              : #endif                          /* LOCAL_ROUTINES */
    7415              :    
    7416            0 :    return DB_SUCCESS;
    7417              : }
    7418              : 
    7419              : /********************************************************************/
    7420              : /**
    7421              : Same as db_set_data, but it does not follow a link to an array index
    7422              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    7423              : @param hKey Handle for key where search starts, zero for root.
    7424              : @param data Buffer from which data gets copied to.
    7425              : @param buf_size Size of data buffer.
    7426              : @param num_values Number of data values (for arrays).
    7427              : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
    7428              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TRUNCATED
    7429              : */
    7430           38 : INT db_set_link_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
    7431              : {
    7432           38 :    if (rpc_is_remote())
    7433            0 :       return rpc_call(RPC_DB_SET_LINK_DATA, hDB, hKey, data, buf_size, num_values, type);
    7434              : 
    7435              : #ifdef LOCAL_ROUTINES
    7436              :    {
    7437              :       DATABASE_HEADER *pheader;
    7438              :       KEY *pkey;
    7439              :       int status;
    7440              : 
    7441           38 :       if (hDB > _database_entries || hDB <= 0) {
    7442            0 :          cm_msg(MERROR, "db_set_data", "invalid database handle");
    7443            0 :          return DB_INVALID_HANDLE;
    7444              :       }
    7445              : 
    7446           38 :       if (!_database[hDB - 1].attached) {
    7447            0 :          cm_msg(MERROR, "db_set_data", "invalid database handle");
    7448            0 :          return DB_INVALID_HANDLE;
    7449              :       }
    7450              : 
    7451           38 :       if (hKey < (int) sizeof(DATABASE_HEADER)) {
    7452            0 :          cm_msg(MERROR, "db_set_data", "invalid key handle");
    7453            0 :          return DB_INVALID_HANDLE;
    7454              :       }
    7455              : 
    7456           38 :       if (num_values == 0)
    7457            0 :          return DB_INVALID_PARAM;
    7458              : 
    7459           38 :       db_lock_database(hDB);
    7460           38 :       db_err_msg* msg = NULL;
    7461              : 
    7462           38 :       pheader = _database[hDB - 1].database_header;
    7463              : 
    7464              :       /* check if hKey argument is correct */
    7465           38 :       if (!db_validate_hkey(pheader, hKey)) {
    7466            0 :          db_unlock_database(hDB);
    7467            0 :          return DB_INVALID_HANDLE;
    7468              :       }
    7469              : 
    7470           38 :       pkey = (KEY *) ((char *) pheader + hKey);
    7471              : 
    7472           38 :       status = db_check_set_data_locked(pheader, pkey, data, buf_size, num_values, type, "db_set_link_data", &msg);
    7473              :       
    7474           38 :       if (status != DB_SUCCESS) {
    7475            0 :          db_unlock_database(hDB);
    7476            0 :          if (msg)
    7477            0 :             db_flush_msg(&msg);
    7478            0 :          return status;
    7479              :       }
    7480              : 
    7481           38 :       db_allow_write_locked(&_database[hDB - 1], "db_set_link_data");
    7482              : 
    7483           38 :       status = db_set_data_wlocked(pheader, pkey, data, buf_size, num_values, type, "db_set_link_data", &msg);
    7484              :       
    7485           38 :       if (status != DB_SUCCESS) {
    7486            0 :          db_unlock_database(hDB);
    7487            0 :          if (msg)
    7488            0 :             db_flush_msg(&msg);
    7489            0 :          return status;
    7490              :       }
    7491              : 
    7492           38 :       db_notify_clients_locked(pheader, hDB, hKey, -1, TRUE, &msg);
    7493           38 :       db_unlock_database(hDB);
    7494           38 :       if (msg)
    7495            0 :          db_flush_msg(&msg);
    7496              : 
    7497              :    }
    7498              : #endif                          /* LOCAL_ROUTINES */
    7499              : 
    7500           38 :    return DB_SUCCESS;
    7501              : }
    7502              : 
    7503              : /**dox***************************************************************/
    7504              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    7505              : 
    7506              : /*------------------------------------------------------------------*/
    7507            1 : INT db_set_num_values(HNDLE hDB, HNDLE hKey, INT num_values)
    7508              : /********************************************************************\
    7509              : 
    7510              :   Routine: db_set_num_values
    7511              : 
    7512              :   Purpose: Set number of values in a key. Extend with zeros or truncate.
    7513              : 
    7514              :   Input:
    7515              :     HNDLE  hDB              Handle to the database
    7516              :     HNDLE  hKey             Handle of key
    7517              :     INT    num_values       Number of data values
    7518              : 
    7519              :   Output:
    7520              :     none
    7521              : 
    7522              :   Function value:
    7523              :     DB_SUCCESS              Successful completion
    7524              :     DB_INVALID_HANDLE       Database handle is invalid
    7525              : 
    7526              : \********************************************************************/
    7527              : {
    7528            1 :    if (rpc_is_remote())
    7529            0 :       return rpc_call(RPC_DB_SET_NUM_VALUES, hDB, hKey, num_values);
    7530              : 
    7531              : #ifdef LOCAL_ROUTINES
    7532              :    {
    7533              :       DATABASE_HEADER *pheader;
    7534              :       KEY *pkey;
    7535              :       INT new_size;
    7536              : 
    7537            1 :       if (hDB > _database_entries || hDB <= 0) {
    7538            0 :          cm_msg(MERROR, "db_set_num_values", "invalid database handle");
    7539            0 :          return DB_INVALID_HANDLE;
    7540              :       }
    7541              : 
    7542            1 :       if (!_database[hDB - 1].attached) {
    7543            0 :          cm_msg(MERROR, "db_set_num_values", "invalid database handle");
    7544            0 :          return DB_INVALID_HANDLE;
    7545              :       }
    7546              : 
    7547            1 :       if (hKey < (int) sizeof(DATABASE_HEADER)) {
    7548            0 :          cm_msg(MERROR, "db_set_num_values", "invalid key handle");
    7549            0 :          return DB_INVALID_HANDLE;
    7550              :       }
    7551              : 
    7552            1 :       if (num_values <= 0) {
    7553            0 :          cm_msg(MERROR, "db_set_num_values", "invalid num_values %d", num_values);
    7554            0 :          return DB_INVALID_PARAM;
    7555              :       }
    7556              : 
    7557            1 :       if (num_values == 0)
    7558            0 :          return DB_INVALID_PARAM;
    7559              : 
    7560            1 :       db_lock_database(hDB);
    7561            1 :       db_err_msg* msg = NULL;
    7562              : 
    7563            1 :       pheader = _database[hDB - 1].database_header;
    7564              : 
    7565              :       /* check if hKey argument is correct */
    7566            1 :       if (!db_validate_hkey(pheader, hKey)) {
    7567            0 :          db_unlock_database(hDB);
    7568            0 :          return DB_INVALID_HANDLE;
    7569              :       }
    7570              : 
    7571            1 :       pkey = (KEY *) ((char *) pheader + hKey);
    7572              : 
    7573              :       /* check for write access */
    7574            1 :       if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
    7575            0 :          db_unlock_database(hDB);
    7576            0 :          return DB_NO_ACCESS;
    7577              :       }
    7578              : 
    7579              :       /* keys cannot contain data */
    7580            1 :       if (pkey->type == TID_KEY) {
    7581            0 :          db_unlock_database(hDB);
    7582            0 :          cm_msg(MERROR, "db_set_num_values", "Key cannot contain data");
    7583            0 :          return DB_TYPE_MISMATCH;
    7584              :       }
    7585              : 
    7586            1 :       if (pkey->total_size != pkey->item_size * pkey->num_values) {
    7587            0 :          db_unlock_database(hDB);
    7588            0 :          cm_msg(MERROR, "db_set_num_values", "Corrupted key");
    7589            0 :          return DB_CORRUPTED;
    7590              :       }
    7591              : 
    7592            1 :       if (pkey->item_size == 0) {
    7593            0 :          db_unlock_database(hDB);
    7594            0 :          cm_msg(MERROR, "db_set_num_values", "Cannot resize array with item_size equal to zero");
    7595            0 :          return DB_INVALID_PARAM;
    7596              :       }
    7597              : 
    7598            1 :       db_allow_write_locked(&_database[hDB - 1], "db_set_num_values");
    7599              : 
    7600              :       /* resize data size if necessary */
    7601            1 :       if (pkey->num_values != num_values) {
    7602            1 :          new_size = pkey->item_size * num_values;
    7603              : 
    7604            1 :          pkey->data = (POINTER_T) realloc_data(pheader, (char *) pheader + pkey->data, pkey->total_size, new_size, "db_set_num_values");
    7605              : 
    7606            1 :          if (pkey->data == 0) {
    7607            0 :             pkey->total_size = 0;
    7608            0 :             pkey->num_values = 0;
    7609            0 :             db_msg(&msg, MERROR, "db_set_num_values", "Cannot resize \"%s\" with num_values %d and new size %d bytes, online database full", db_get_path_locked(pheader, pkey).c_str(), num_values, new_size);
    7610            0 :             db_unlock_database(hDB);
    7611            0 :             if (msg)
    7612            0 :                db_flush_msg(&msg);
    7613            0 :             return DB_FULL;
    7614              :          }
    7615              : 
    7616            1 :          pkey->data -= (POINTER_T) pheader;
    7617            1 :          pkey->total_size = new_size;
    7618            1 :          pkey->num_values = num_values;
    7619              :       }
    7620              : 
    7621              :       /* update time */
    7622            1 :       pkey->last_written = ss_time();
    7623              : 
    7624            1 :       db_notify_clients_locked(pheader, hDB, hKey, -1, TRUE, &msg);
    7625            1 :       db_unlock_database(hDB);
    7626            1 :       if (msg)
    7627            0 :          db_flush_msg(&msg);
    7628              : 
    7629              :    }
    7630              : #endif                          /* LOCAL_ROUTINES */
    7631              : 
    7632            1 :    return DB_SUCCESS;
    7633              : }
    7634              : 
    7635              : /**dox***************************************************************/
    7636              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    7637              : 
    7638              : /********************************************************************/
    7639              : /**
    7640              : Set key data for a key which contains an array of values.
    7641              : 
    7642              : This function sets individual values of a key containing an array.
    7643              : If the index is larger than the array size, the array is extended and the intermediate
    7644              : values are set to zero.
    7645              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    7646              : @param hKey Handle for key where search starts, zero for root.
    7647              : @param data Pointer to single value of data.
    7648              : @param data_size
    7649              : @param idx Size of single data element.
    7650              : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
    7651              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_ACCESS, DB_TYPE_MISMATCH
    7652              : */
    7653            4 : INT db_set_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
    7654              : {
    7655            4 :    if (rpc_is_remote())
    7656            0 :       return rpc_call(RPC_DB_SET_DATA_INDEX, hDB, hKey, data, data_size, idx, type);
    7657              : 
    7658              : #ifdef LOCAL_ROUTINES
    7659              :    {
    7660              :       DATABASE_HEADER *pheader;
    7661              :       KEY *pkey;
    7662              :       char link_name[256];
    7663              :       int link_idx;
    7664              :       HNDLE hkeylink;
    7665              :       int status;
    7666              : 
    7667            4 :       if (hDB > _database_entries || hDB <= 0) {
    7668            0 :          cm_msg(MERROR, "db_set_data_index", "invalid database handle");
    7669            0 :          return DB_INVALID_HANDLE;
    7670              :       }
    7671              : 
    7672            4 :       if (!_database[hDB - 1].attached) {
    7673            0 :          cm_msg(MERROR, "db_set_data_index", "invalid database handle");
    7674            0 :          return DB_INVALID_HANDLE;
    7675              :       }
    7676              : 
    7677            4 :       if (hKey < (int) sizeof(DATABASE_HEADER)) {
    7678            0 :          cm_msg(MERROR, "db_set_data_index", "invalid key handle");
    7679            0 :          return DB_INVALID_HANDLE;
    7680              :       }
    7681              : 
    7682            4 :       db_lock_database(hDB);
    7683            4 :       db_err_msg* msg = NULL;
    7684              : 
    7685            4 :       pheader = _database[hDB - 1].database_header;
    7686              : 
    7687              :       /* check if hKey argument is correct */
    7688            4 :       if (!db_validate_hkey(pheader, hKey)) {
    7689            0 :          db_unlock_database(hDB);
    7690            0 :          return DB_INVALID_HANDLE;
    7691              :       }
    7692              : 
    7693            4 :       pkey = (KEY *) ((char *) pheader + hKey);
    7694              : 
    7695              :       /* check for write access */
    7696            4 :       if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
    7697            0 :          db_unlock_database(hDB);
    7698            0 :          return DB_NO_ACCESS;
    7699              :       }
    7700              : 
    7701              :       /* check for link to array index */
    7702            4 :       if (pkey->type == TID_LINK) {
    7703            0 :          mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
    7704            0 :          if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
    7705            0 :             db_unlock_database(hDB);
    7706            0 :             if (strchr(link_name, '[') == NULL)
    7707            0 :                return DB_INVALID_LINK;
    7708            0 :             link_idx = atoi(strchr(link_name, '[') + 1);
    7709            0 :             *strchr(link_name, '[') = 0;
    7710            0 :             if (db_find_key(hDB, 0, link_name, &hkeylink) != DB_SUCCESS)
    7711            0 :                return DB_INVALID_LINK;
    7712            0 :             return db_set_data_index(hDB, hkeylink, data, data_size, link_idx, type);
    7713              :          }
    7714              :       }
    7715              : 
    7716            4 :       status = db_check_set_data_index_locked(pheader, pkey, idx, data, data_size, type, "db_set_data_index", &msg);
    7717              : 
    7718            4 :       if (status != DB_SUCCESS) {
    7719            0 :          db_unlock_database(hDB);
    7720            0 :          if (msg)
    7721            0 :             db_flush_msg(&msg);
    7722            0 :          return status;
    7723              :       }
    7724              : 
    7725            4 :       db_allow_write_locked(&_database[hDB-1], "db_set_data_index");
    7726              : 
    7727            4 :       status = db_set_data_index_wlocked(pheader, pkey, idx, data, data_size, type, "db_set_data_index", &msg);
    7728              :       
    7729            4 :       if (status != DB_SUCCESS) {
    7730            0 :          db_unlock_database(hDB);
    7731            0 :          if (msg)
    7732            0 :             db_flush_msg(&msg);
    7733            0 :          return status;
    7734              :       }
    7735              : 
    7736            4 :       db_notify_clients_locked(pheader, hDB, hKey, idx, TRUE, &msg);
    7737            4 :       db_unlock_database(hDB);
    7738            4 :       if (msg)
    7739            0 :          db_flush_msg(&msg);
    7740              : 
    7741              :    }
    7742              : #endif                          /* LOCAL_ROUTINES */
    7743              : 
    7744            4 :    return DB_SUCCESS;
    7745              : }
    7746              : 
    7747              : /********************************************************************/
    7748              : /**
    7749              : Same as db_set_data_index, but does not follow links.
    7750              : 
    7751              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    7752              : @param hKey Handle for key where search starts, zero for root.
    7753              : @param data Pointer to single value of data.
    7754              : @param data_size
    7755              : @param idx Size of single data element.
    7756              : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
    7757              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_ACCESS, DB_TYPE_MISMATCH
    7758              : */
    7759            0 : INT db_set_link_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
    7760              : {
    7761            0 :    if (rpc_is_remote())
    7762            0 :       return rpc_call(RPC_DB_SET_LINK_DATA_INDEX, hDB, hKey, data, data_size, idx, type);
    7763              : 
    7764              : #ifdef LOCAL_ROUTINES
    7765              :    {
    7766              :       DATABASE_HEADER *pheader;
    7767              :       KEY *pkey;
    7768              :       int status;
    7769              : 
    7770            0 :       if (hDB > _database_entries || hDB <= 0) {
    7771            0 :          cm_msg(MERROR, "db_set_link_data_index", "invalid database handle");
    7772            0 :          return DB_INVALID_HANDLE;
    7773              :       }
    7774              : 
    7775            0 :       if (!_database[hDB - 1].attached) {
    7776            0 :          cm_msg(MERROR, "db_set_link_data_index", "invalid database handle");
    7777            0 :          return DB_INVALID_HANDLE;
    7778              :       }
    7779              : 
    7780            0 :       if (hKey < (int) sizeof(DATABASE_HEADER)) {
    7781            0 :          cm_msg(MERROR, "db_set_link_data_index", "invalid key handle");
    7782            0 :          return DB_INVALID_HANDLE;
    7783              :       }
    7784              : 
    7785            0 :       db_lock_database(hDB);
    7786            0 :       db_err_msg* msg = NULL;
    7787              : 
    7788            0 :       pheader = _database[hDB - 1].database_header;
    7789              : 
    7790              :       /* check if hKey argument is correct */
    7791            0 :       if (!db_validate_hkey(pheader, hKey)) {
    7792            0 :          db_unlock_database(hDB);
    7793            0 :          return DB_INVALID_HANDLE;
    7794              :       }
    7795              : 
    7796            0 :       pkey = (KEY *) ((char *) pheader + hKey);
    7797              : 
    7798            0 :       status = db_check_set_data_index_locked(pheader, pkey, idx, data, data_size, type, "db_set_link_data_index", &msg);
    7799              :       
    7800            0 :       if (status != DB_SUCCESS) {
    7801            0 :          db_unlock_database(hDB);
    7802            0 :          if (msg)
    7803            0 :             db_flush_msg(&msg);
    7804            0 :          return status;
    7805              :       }
    7806              : 
    7807            0 :       db_allow_write_locked(&_database[hDB - 1], "db_set_link_data_index");
    7808              : 
    7809            0 :       status = db_set_data_index_wlocked(pheader, pkey, idx, data, data_size, type, "db_set_link_data_index", &msg);
    7810              :       
    7811            0 :       if (status != DB_SUCCESS) {
    7812            0 :          db_unlock_database(hDB);
    7813            0 :          if (msg)
    7814            0 :             db_flush_msg(&msg);
    7815            0 :          return status;
    7816              :       }
    7817              : 
    7818            0 :       db_notify_clients_locked(pheader, hDB, hKey, idx, TRUE, &msg);
    7819            0 :       db_unlock_database(hDB);
    7820            0 :       if (msg)
    7821            0 :          db_flush_msg(&msg);
    7822              : 
    7823              :    }
    7824              : #endif                          /* LOCAL_ROUTINES */
    7825              : 
    7826            0 :    return DB_SUCCESS;
    7827              : }
    7828              : 
    7829              : /**dox***************************************************************/
    7830              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    7831              : 
    7832              : /*------------------------------------------------------------------*/
    7833            0 : INT db_set_data_index1(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type, BOOL bNotify)
    7834              : /********************************************************************\
    7835              : 
    7836              :   Routine: db_set_data_index1
    7837              : 
    7838              :   Purpose: Set key data for a key which contains an array of values.
    7839              :            Optionally notify clients which have key open.
    7840              : 
    7841              :   Input:
    7842              :     HNDLE  hDB              Handle to the database
    7843              :     HNDLE  hKey             Handle of key to enumerate
    7844              :     void   *data            Pointer to single value of data
    7845              :     INT    data_size        Size of single data element
    7846              :     INT    idx              Index of array to change [0..n-1]
    7847              :     DWORD  type             Type of data
    7848              :     BOOL   bNotify          If TRUE, notify clients
    7849              : 
    7850              :   Output:
    7851              :     none
    7852              : 
    7853              :   Function value:
    7854              :     DB_SUCCESS              Successful completion
    7855              :     DB_INVALID_HANDLE       Database handle is invalid
    7856              :     DB_TYPE_MISMATCH        Key was created with different type
    7857              :     DB_NO_ACCESS            No write access
    7858              : 
    7859              : \********************************************************************/
    7860              : {
    7861            0 :    if (rpc_is_remote())
    7862            0 :       return rpc_call(RPC_DB_SET_DATA_INDEX1, hDB, hKey, data, data_size, idx, type, bNotify);
    7863              : 
    7864              : #ifdef LOCAL_ROUTINES
    7865              :    {
    7866              :       DATABASE_HEADER *pheader;
    7867              :       KEY *pkey;
    7868              :       int status;
    7869              : 
    7870            0 :       if (hDB > _database_entries || hDB <= 0) {
    7871            0 :          cm_msg(MERROR, "db_set_data_index1", "invalid database handle");
    7872            0 :          return DB_INVALID_HANDLE;
    7873              :       }
    7874              : 
    7875            0 :       if (!_database[hDB - 1].attached) {
    7876            0 :          cm_msg(MERROR, "db_set_data_index1", "invalid database handle");
    7877            0 :          return DB_INVALID_HANDLE;
    7878              :       }
    7879              : 
    7880            0 :       if (hKey < (int) sizeof(DATABASE_HEADER)) {
    7881            0 :          cm_msg(MERROR, "db_set_data_index1", "invalid key handle");
    7882            0 :          return DB_INVALID_HANDLE;
    7883              :       }
    7884              : 
    7885            0 :       db_lock_database(hDB);
    7886            0 :       db_err_msg* msg = NULL;
    7887              : 
    7888            0 :       pheader = _database[hDB - 1].database_header;
    7889              : 
    7890              :       /* check if hKey argument is correct */
    7891            0 :       if (!db_validate_hkey(pheader, hKey)) {
    7892            0 :          db_unlock_database(hDB);
    7893            0 :          return DB_INVALID_HANDLE;
    7894              :       }
    7895              : 
    7896            0 :       pkey = (KEY *) ((char *) pheader + hKey);
    7897              : 
    7898            0 :       status = db_check_set_data_index_locked(pheader, pkey, idx, data, data_size, type, "db_set_data_index1", &msg);
    7899              : 
    7900            0 :       if (status != DB_SUCCESS) {
    7901            0 :          db_unlock_database(hDB);
    7902            0 :          if (msg)
    7903            0 :             db_flush_msg(&msg);
    7904            0 :          return status;
    7905              :       }
    7906              : 
    7907            0 :       db_allow_write_locked(&_database[hDB - 1], "db_set_data_index1");
    7908              : 
    7909            0 :       status = db_set_data_index_wlocked(pheader, pkey, idx, data, data_size, type, "db_set_data_index1", &msg);
    7910              : 
    7911            0 :       if (status != DB_SUCCESS) {
    7912            0 :          db_unlock_database(hDB);
    7913            0 :          if (msg)
    7914            0 :             db_flush_msg(&msg);
    7915            0 :          return status;
    7916              :       }
    7917              : 
    7918            0 :       if (bNotify)
    7919            0 :          db_notify_clients_locked(pheader, hDB, hKey, idx, TRUE, &msg);
    7920              :       
    7921            0 :       db_unlock_database(hDB);
    7922            0 :       if (msg)
    7923            0 :          db_flush_msg(&msg);
    7924              : 
    7925              :    }
    7926              : #endif                          /* LOCAL_ROUTINES */
    7927              : 
    7928            0 :    return DB_SUCCESS;
    7929              : }
    7930              : 
    7931              : /*----------------------------------------------------------------------------*/
    7932              : 
    7933            0 : INT db_merge_data(HNDLE hDB, HNDLE hKeyRoot, const char *name, void *data, INT data_size, INT num_values, INT type)
    7934              : /********************************************************************\
    7935              : 
    7936              :   Routine: db_merge_data
    7937              : 
    7938              :   Purpose: Merge an array with an ODB array. If the ODB array doesn't
    7939              :            exist, create it and fill it with the array. If it exists,
    7940              :            load it in the array. Adjust ODB array size if necessary.
    7941              : 
    7942              :   Input:
    7943              :     HNDLE  hDB              Handle to the database
    7944              :     HNDLE  hKeyRoot         Key handle to start with, 0 for root
    7945              :     cha    *name            Key name relative to hKeyRoot
    7946              :     void   *data            Pointer to data array
    7947              :     INT    data_size        Size of data array
    7948              :     INT    num_values       Number of values in array
    7949              :     DWORD  type             Type of data
    7950              : 
    7951              :   Output:
    7952              :     none
    7953              : 
    7954              :   Function value:
    7955              :     <same as db_set_data>
    7956              : 
    7957              : \********************************************************************/
    7958              : {
    7959              :    HNDLE hKey;
    7960              :    INT status, old_size;
    7961              : 
    7962            0 :    if (num_values == 0)
    7963            0 :       return DB_INVALID_PARAM;
    7964              : 
    7965            0 :    status = db_find_key(hDB, hKeyRoot, name, &hKey);
    7966            0 :    if (status != DB_SUCCESS) {
    7967            0 :       db_create_key(hDB, hKeyRoot, name, type);
    7968            0 :       status = db_find_key(hDB, hKeyRoot, name, &hKey);
    7969            0 :       if (status != DB_SUCCESS)
    7970            0 :          return status;
    7971            0 :       status = db_set_data(hDB, hKey, data, data_size, num_values, type);
    7972              :    } else {
    7973            0 :       old_size = data_size;
    7974            0 :       db_get_data(hDB, hKey, data, &old_size, type);
    7975            0 :       status = db_set_data(hDB, hKey, data, data_size, num_values, type);
    7976              :    }
    7977              : 
    7978            0 :    return status;
    7979              : }
    7980              : 
    7981              : #ifdef LOCAL_ROUTINES
    7982              : 
    7983              : /*------------------------------------------------------------------*/
    7984          164 : static int db_set_mode_wlocked(DATABASE_HEADER *pheader, KEY *pkey, WORD mode, int recurse, db_err_msg** msg)
    7985              : /********************************************************************\
    7986              : 
    7987              :   Routine: db_set_mode_wlocked()
    7988              : 
    7989              :   Purpose: Set access mode of key
    7990              : 
    7991              :   Input:
    7992              :     pheader                 Database
    7993              :     pkey                    Key
    7994              :     DWORD  mode             Access mode, any or'ed combination of
    7995              :                             MODE_READ, MODE_WRITE, MODE_EXCLUSIVE
    7996              :                             and MODE_DELETE
    7997              :     recurse                 Value of 0: do not recurse subtree,
    7998              :                             value of 1: recurse subtree, becomes recurse level
    7999              : 
    8000              :   Function value:
    8001              :     DB_SUCCESS              Successful completion
    8002              : 
    8003              : \********************************************************************/
    8004              : {
    8005              :    /* resolve links */
    8006          164 :    if (pkey->type == TID_LINK) {
    8007              :       int status;
    8008            0 :       pkey = (KEY*)db_resolve_link_locked(pheader, pkey, &status, msg);
    8009            0 :       if (!pkey) {
    8010            0 :          return status;
    8011              :       }
    8012              :    }
    8013              : 
    8014          164 :    if (pkey->type == TID_KEY && recurse) {
    8015              :       // drop "const" from KEY* we are permitted to write to ODB!
    8016           32 :       KEY* psubkey = (KEY*)db_enum_first_locked(pheader, pkey, msg);
    8017          174 :       while (psubkey) {
    8018          142 :          db_set_mode_wlocked(pheader, psubkey, mode, recurse+1, msg);
    8019          142 :          psubkey = (KEY*)db_enum_next_locked(pheader, pkey, psubkey, msg);
    8020              :       }
    8021              :    }
    8022              : 
    8023              :    /* now set mode */
    8024          164 :    pkey->access_mode = mode;
    8025              : 
    8026          164 :    return DB_SUCCESS;
    8027              : }
    8028              : 
    8029              : #endif
    8030              : 
    8031              : /*------------------------------------------------------------------*/
    8032           20 : INT db_set_mode(HNDLE hDB, HNDLE hKey, WORD mode, BOOL recurse)
    8033              : /********************************************************************\
    8034              : 
    8035              :   Routine: db_set_mode
    8036              : 
    8037              :   Purpose: Set access mode of key
    8038              : 
    8039              :   Input:
    8040              :     HNDLE  hDB              Handle to the database
    8041              :     HNDLE  hKey             Key handle
    8042              :     DWORD  mode             Access mode, any or'ed combination of
    8043              :                             MODE_READ, MODE_WRITE, MODE_EXCLUSIVE
    8044              :                             and MODE_DELETE
    8045              :     BOOL   recurse          Value of 0 (FALSE): do not recurse subtree,
    8046              :                             value of 1 (TRUE): recurse subtree,
    8047              :                             value of 2: recurse subtree, assume database is locked by caller.
    8048              : 
    8049              :   Output:
    8050              :     none
    8051              : 
    8052              :   Function value:
    8053              :     DB_SUCCESS              Successful completion
    8054              :     DB_INVALID_HANDLE       Database handle is invalid
    8055              : 
    8056              : \********************************************************************/
    8057              : {
    8058           20 :    if (rpc_is_remote())
    8059            0 :       return rpc_call(RPC_DB_SET_MODE, hDB, hKey, mode, recurse);
    8060              : 
    8061              : #ifdef LOCAL_ROUTINES
    8062              :    {
    8063              :       DATABASE_HEADER *pheader;
    8064           20 :       BOOL locked = FALSE;
    8065              : 
    8066           20 :       if (hDB > _database_entries || hDB <= 0) {
    8067            0 :          cm_msg(MERROR, "db_set_mode", "invalid database handle");
    8068            0 :          return DB_INVALID_HANDLE;
    8069              :       }
    8070              : 
    8071           20 :       if (!_database[hDB - 1].attached) {
    8072            0 :          cm_msg(MERROR, "db_set_mode", "invalid database handle");
    8073            0 :          return DB_INVALID_HANDLE;
    8074              :       }
    8075              : 
    8076           20 :       if (recurse < 2) {
    8077           20 :          db_lock_database(hDB);
    8078           20 :          locked = TRUE;
    8079              :       }
    8080              : 
    8081           20 :       pheader = _database[hDB - 1].database_header;
    8082              : 
    8083           20 :       db_err_msg* msg = NULL;
    8084           20 :       int status = 0;
    8085              : 
    8086           20 :       KEY *pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_set_mode", &msg);
    8087              : 
    8088           20 :       if (!pkey) {
    8089            0 :          if (locked) {
    8090            0 :             db_unlock_database(hDB);
    8091            0 :             if (msg)
    8092            0 :                db_flush_msg(&msg);
    8093            0 :             return status;
    8094              :          }
    8095              :       }
    8096              : 
    8097           20 :       db_allow_write_locked(&_database[hDB-1], "db_set_mode");
    8098              : 
    8099           20 :       status = db_set_mode_wlocked(pheader, pkey, mode, recurse, &msg);
    8100              : 
    8101           20 :       if (locked) {
    8102           20 :          db_unlock_database(hDB);
    8103           20 :          if (msg)
    8104            0 :             db_flush_msg(&msg);
    8105              :       }
    8106              : 
    8107           20 :       return status;
    8108              :    }
    8109              : #endif                          /* LOCAL_ROUTINES */
    8110              : 
    8111              :    return DB_SUCCESS;
    8112              : }
    8113              : 
    8114              : /**dox***************************************************************/
    8115              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    8116              : 
    8117              : /********************************************************************/
    8118              : /**
    8119              : Load a branch of a database from an .ODB file.
    8120              : 
    8121              : This function is used by the ODBEdit command load. For a
    8122              : description of the ASCII format, see db_copy(). Data can be loaded relative to
    8123              : the root of the ODB (hkey equal zero) or relative to a certain key.
    8124              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    8125              : @param hKeyRoot Handle for key where search starts, zero for root.
    8126              : @param filename Filename of .ODB file.
    8127              : @param bRemote If TRUE, the file is loaded by the server process on the
    8128              : back-end, if FALSE, it is loaded from the current process
    8129              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_FILE_ERROR
    8130              : */
    8131            0 : INT db_load(HNDLE hDB, HNDLE hKeyRoot, const char *filename, BOOL bRemote)
    8132              : {
    8133              :    struct stat stat_buf;
    8134              :    INT hfile, size, n, i, status;
    8135              :    char *buffer;
    8136              : 
    8137            0 :    if (rpc_is_remote() && bRemote)
    8138            0 :       return rpc_call(RPC_DB_LOAD, hDB, hKeyRoot, filename);
    8139              : 
    8140              :    /* open file */
    8141            0 :    hfile = open(filename, O_RDONLY | O_TEXT, 0644);
    8142            0 :    if (hfile == -1) {
    8143            0 :       cm_msg(MERROR, "db_load", "file \"%s\" not found", filename);
    8144            0 :       return DB_FILE_ERROR;
    8145              :    }
    8146              : 
    8147              :    /* allocate buffer with file size */
    8148            0 :    fstat(hfile, &stat_buf);
    8149            0 :    size = stat_buf.st_size;
    8150            0 :    buffer = (char *) malloc(size + 1);
    8151              : 
    8152            0 :    if (buffer == NULL) {
    8153            0 :       cm_msg(MERROR, "db_load", "cannot allocate ODB load buffer");
    8154            0 :       close(hfile);
    8155            0 :       return DB_NO_MEMORY;
    8156              :    }
    8157              : 
    8158            0 :    n = 0;
    8159              : 
    8160              :    do {
    8161            0 :       i = read(hfile, buffer + n, size - n);
    8162            0 :       if (i <= 0)
    8163            0 :          break;
    8164            0 :       n += i;
    8165              :    } while (TRUE);
    8166              : 
    8167            0 :    buffer[n] = 0;
    8168              : 
    8169            0 :    if (strncmp(buffer, "<?xml version=\"1.0\"", 19) == 0) {
    8170            0 :       status = db_paste_xml(hDB, hKeyRoot, buffer);
    8171            0 :       if (status != DB_SUCCESS)
    8172            0 :          printf("Error in file \"%s\"\n", filename);
    8173            0 :    } else if( buffer[0] == '{'){
    8174            0 :       if(strrchr(buffer, '}')){
    8175            0 :          status = db_paste_json(hDB, hKeyRoot, buffer);
    8176              :       } else {
    8177            0 :          status = DB_FILE_ERROR;
    8178              :       }
    8179              :    } else
    8180            0 :       status = db_paste(hDB, hKeyRoot, buffer);
    8181              : 
    8182            0 :    close(hfile);
    8183            0 :    free(buffer);
    8184              : 
    8185            0 :    return status;
    8186              : }
    8187              : 
    8188              : /********************************************************************/
    8189              : /**
    8190              : Copy an ODB subtree in ASCII format to a buffer
    8191              : 
    8192              : This function converts the binary ODB contents to an ASCII.
    8193              : The function db_paste() can be used to convert the ASCII representation back
    8194              : to binary ODB contents. The functions db_load() and db_save() internally
    8195              : use db_copy() and db_paste(). This function converts the binary ODB
    8196              : contents to an ASCII representation of the form:
    8197              : - For single value:
    8198              : \code
    8199              : [ODB path]
    8200              :  key name = type : value
    8201              : \endcode
    8202              : - For strings:
    8203              : \code
    8204              : key name = STRING : [size] string contents
    8205              : \endcode
    8206              : - For arrayes (type can be BYTE, SBYTE, CHAR, WORD, SHORT, DWORD,
    8207              : INT, BOOL, FLOAT, DOUBLE, STRING or LINK):
    8208              : \code
    8209              : key name = type[size] :
    8210              :  [0] value0
    8211              :  [1] value1
    8212              :  [2] value2
    8213              :  ...
    8214              : \endcode
    8215              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    8216              : @param hKey Handle for key where search starts, zero for root.
    8217              : @param buffer ASCII buffer which receives ODB contents.
    8218              : @param buffer_size Size of buffer, returns remaining space in buffer.
    8219              : @param path Internal use only, must be empty ("").
    8220              : @return DB_SUCCESS, DB_TRUNCATED, DB_NO_MEMORY
    8221              : */
    8222            4 : INT db_copy(HNDLE hDB, HNDLE hKey, char *buffer, INT * buffer_size, const char *path)
    8223              : {
    8224              :    INT i, j, size, status;
    8225              :    KEY key;
    8226              :    HNDLE hSubkey;
    8227              :    char full_path[MAX_ODB_PATH];
    8228              :    char *data;
    8229              :    char line[MAX_STRING_LENGTH * 2];
    8230              :    BOOL bWritten;
    8231              : 
    8232            4 :    mstrlcpy(full_path, path, sizeof(full_path));
    8233              : 
    8234            4 :    bWritten = FALSE;
    8235              : 
    8236              :    /* first enumerate this level */
    8237            4 :    for (i = 0;; i++) {
    8238           24 :       db_enum_link(hDB, hKey, i, &hSubkey);
    8239              : 
    8240           24 :       if (i == 0 && !hSubkey) {
    8241              :          /* If key has no subkeys, just write this key */
    8242            0 :          status = db_get_link(hDB, hKey, &key);
    8243            0 :          if (status != DB_SUCCESS)
    8244            0 :             continue;
    8245            0 :          size = key.total_size;
    8246            0 :          data = (char *) malloc(size);
    8247            0 :          if (data == NULL) {
    8248            0 :             cm_msg(MERROR, "db_copy", "cannot allocate data buffer");
    8249            0 :             return DB_NO_MEMORY;
    8250              :          }
    8251            0 :          line[0] = 0;
    8252              : 
    8253            0 :          if (key.type != TID_KEY) {
    8254            0 :             status = db_get_link_data(hDB, hKey, data, &size, key.type);
    8255            0 :             if (status != DB_SUCCESS)
    8256            0 :                continue;
    8257            0 :             if (key.num_values == 1) {
    8258            0 :                sprintf(line, "%s = %s : ", key.name, rpc_tid_name(key.type));
    8259              : 
    8260            0 :                if (key.type == TID_STRING && strchr(data, '\n') != NULL) {
    8261              :                   /* multiline string */
    8262            0 :                   sprintf(line + strlen(line), "[====#$@$#====]\n");
    8263              : 
    8264              :                   /* copy line to buffer */
    8265            0 :                   if ((INT) (strlen(line) + 1) > *buffer_size) {
    8266            0 :                      free(data);
    8267            0 :                      return DB_TRUNCATED;
    8268              :                   }
    8269              : 
    8270            0 :                   strcpy(buffer, line);
    8271            0 :                   buffer += strlen(line);
    8272            0 :                   *buffer_size -= strlen(line);
    8273              : 
    8274              :                   /* copy multiple lines to buffer */
    8275            0 :                   if (key.item_size > *buffer_size) {
    8276            0 :                      free(data);
    8277            0 :                      return DB_TRUNCATED;
    8278              :                   }
    8279              : 
    8280            0 :                   strcpy(buffer, data);
    8281            0 :                   buffer += strlen(data);
    8282            0 :                   *buffer_size -= strlen(data);
    8283              : 
    8284            0 :                   strcpy(line, "\n====#$@$#====\n");
    8285              :                } else {
    8286            0 :                   std::string str = db_sprintf(data, key.item_size, 0, key.type);
    8287              : 
    8288            0 :                   if (key.type == TID_STRING || key.type == TID_LINK)
    8289            0 :                      sprintf(line + strlen(line), "[%d] ", key.item_size);
    8290              : 
    8291            0 :                   sprintf(line + strlen(line), "%s\n", str.c_str()); // FIXME: buffer overflow. K.O. Aug2024
    8292            0 :                }
    8293              :             } else {
    8294            0 :                sprintf(line, "%s = %s[%d] :\n", key.name, rpc_tid_name(key.type), key.num_values);
    8295              : 
    8296            0 :                for (j = 0; j < key.num_values; j++) {
    8297            0 :                   if (key.type == TID_STRING || key.type == TID_LINK)
    8298            0 :                      sprintf(line + strlen(line), "[%d] ", key.item_size);
    8299              :                   else
    8300            0 :                      sprintf(line + strlen(line), "[%d] ", j);
    8301              : 
    8302            0 :                   std::string str = db_sprintf(data, key.item_size, j, key.type);
    8303            0 :                   sprintf(line + strlen(line), "%s\n", str.c_str()); // FIXME: buffer overflow. K.O. Aug2024
    8304              : 
    8305              :                   /* copy line to buffer */
    8306            0 :                   if ((INT) (strlen(line) + 1) > *buffer_size) {
    8307            0 :                      free(data);
    8308            0 :                      return DB_TRUNCATED;
    8309              :                   }
    8310              : 
    8311            0 :                   strcpy(buffer, line);
    8312            0 :                   buffer += strlen(line);
    8313            0 :                   *buffer_size -= strlen(line);
    8314            0 :                   line[0] = 0;
    8315            0 :                }
    8316              :             }
    8317              :          }
    8318              : 
    8319              :          /* copy line to buffer */
    8320            0 :          if ((INT) (strlen(line) + 1) > *buffer_size) {
    8321            0 :             free(data);
    8322            0 :             return DB_TRUNCATED;
    8323              :          }
    8324              : 
    8325            0 :          strcpy(buffer, line);
    8326            0 :          buffer += strlen(line);
    8327            0 :          *buffer_size -= strlen(line);
    8328              : 
    8329            0 :          free(data);
    8330            0 :          data = NULL;
    8331              :       }
    8332              : 
    8333           24 :       if (!hSubkey)
    8334            4 :          break;
    8335              : 
    8336           20 :       status = db_get_link(hDB, hSubkey, &key);
    8337           20 :       if (status != DB_SUCCESS)
    8338            0 :          continue;
    8339              : 
    8340           20 :       if (strcmp(key.name, "arr2") == 0)
    8341            0 :          printf("\narr2\n");
    8342           20 :       size = key.total_size;
    8343           20 :       data = (char *) malloc(size);
    8344           20 :       if (data == NULL) {
    8345            0 :          cm_msg(MERROR, "db_copy", "cannot allocate data buffer");
    8346            0 :          return DB_NO_MEMORY;
    8347              :       }
    8348              : 
    8349           20 :       line[0] = 0;
    8350              : 
    8351           20 :       if (key.type == TID_KEY) {
    8352              :          char str[MAX_ODB_PATH];
    8353              : 
    8354              :          /* new line */
    8355            0 :          if (bWritten) {
    8356            0 :             if (*buffer_size < 2) {
    8357            0 :                free(data);
    8358            0 :                return DB_TRUNCATED;
    8359              :             }
    8360              : 
    8361            0 :             strcpy(buffer, "\n");
    8362            0 :             buffer += 1;
    8363            0 :             *buffer_size -= 1;
    8364              :          }
    8365              : 
    8366            0 :          strcpy(str, full_path);
    8367            0 :          if (str[0] && str[strlen(str) - 1] != '/')
    8368            0 :             strcat(str, "/");
    8369            0 :          strcat(str, key.name);
    8370              : 
    8371              :          /* recurse */
    8372            0 :          status = db_copy(hDB, hSubkey, buffer, buffer_size, str);
    8373            0 :          if (status != DB_SUCCESS) {
    8374            0 :             free(data);
    8375            0 :             return status;
    8376              :          }
    8377              : 
    8378            0 :          buffer += strlen(buffer);
    8379            0 :          bWritten = FALSE;
    8380              :       } else {
    8381           20 :          status = db_get_link_data(hDB, hSubkey, data, &size, key.type);
    8382           20 :          if (status != DB_SUCCESS)
    8383            0 :             continue;
    8384              : 
    8385           20 :          if (!bWritten) {
    8386            4 :             if (path[0] == 0)
    8387            4 :                sprintf(line, "[.]\n");
    8388              :             else
    8389            0 :                sprintf(line, "[%s]\n", path);
    8390            4 :             bWritten = TRUE;
    8391              :          }
    8392              : 
    8393           20 :          if (key.num_values == 1) {
    8394           20 :             sprintf(line + strlen(line), "%s = %s : ", key.name, rpc_tid_name(key.type));
    8395              : 
    8396           20 :             if (key.type == TID_STRING && strchr(data, '\n') != NULL) {
    8397              :                /* multiline string */
    8398            0 :                sprintf(line + strlen(line), "[====#$@$#====]\n");
    8399              : 
    8400              :                /* ensure string limiter */
    8401            0 :                data[size - 1] = 0;
    8402              : 
    8403              :                /* copy line to buffer */
    8404            0 :                if ((INT) (strlen(line) + 1) > *buffer_size) {
    8405            0 :                   free(data);
    8406            0 :                   return DB_TRUNCATED;
    8407              :                }
    8408              : 
    8409            0 :                strcpy(buffer, line);
    8410            0 :                buffer += strlen(line);
    8411            0 :                *buffer_size -= strlen(line);
    8412              : 
    8413              :                /* copy multiple lines to buffer */
    8414            0 :                if (key.item_size > *buffer_size) {
    8415            0 :                   free(data);
    8416            0 :                   return DB_TRUNCATED;
    8417              :                }
    8418              : 
    8419            0 :                strcpy(buffer, data);
    8420            0 :                buffer += strlen(data);
    8421            0 :                *buffer_size -= strlen(data);
    8422              : 
    8423            0 :                strcpy(line, "\n====#$@$#====\n");
    8424              :             } else {
    8425           20 :                std::string str = db_sprintf(data, key.item_size, 0, key.type);
    8426              : 
    8427           20 :                if (key.type == TID_STRING || key.type == TID_LINK)
    8428            4 :                   sprintf(line + strlen(line), "[%d] ", key.item_size);
    8429              : 
    8430           20 :                sprintf(line + strlen(line), "%s\n", str.c_str()); // FIXME: buffer overflow. K.O. Aug2024
    8431           20 :             }
    8432              :          } else {
    8433            0 :             sprintf(line + strlen(line), "%s = %s[%d] :\n", key.name, rpc_tid_name(key.type), key.num_values);
    8434              : 
    8435            0 :             for (j = 0; j < key.num_values; j++) {
    8436            0 :                if (key.type == TID_STRING || key.type == TID_LINK)
    8437            0 :                   sprintf(line + strlen(line), "[%d] ", key.item_size);
    8438              :                else
    8439            0 :                   sprintf(line + strlen(line), "[%d] ", j);
    8440              : 
    8441            0 :                std::string str = db_sprintf(data, key.item_size, j, key.type);
    8442            0 :                sprintf(line + strlen(line), "%s\n", str.c_str()); // FIXME: buffer overflow. K.O. Aug2024
    8443              : 
    8444              :                /* copy line to buffer */
    8445            0 :                if ((INT) (strlen(line) + 1) > *buffer_size) {
    8446            0 :                   free(data);
    8447            0 :                   return DB_TRUNCATED;
    8448              :                }
    8449              : 
    8450            0 :                strcpy(buffer, line);
    8451            0 :                buffer += strlen(line);
    8452            0 :                *buffer_size -= strlen(line);
    8453            0 :                line[0] = 0;
    8454            0 :             }
    8455              :          }
    8456              : 
    8457              :          /* copy line to buffer */
    8458           20 :          if ((INT) (strlen(line) + 1) > *buffer_size) {
    8459            0 :             free(data);
    8460            0 :             return DB_TRUNCATED;
    8461              :          }
    8462              : 
    8463           20 :          strcpy(buffer, line);
    8464           20 :          buffer += strlen(line);
    8465           20 :          *buffer_size -= strlen(line);
    8466              :       }
    8467              : 
    8468           20 :       free(data);
    8469           20 :       data = NULL;
    8470           20 :    }
    8471              : 
    8472            4 :    if (bWritten) {
    8473            4 :       if (*buffer_size < 2)
    8474            0 :          return DB_TRUNCATED;
    8475              : 
    8476            4 :       strcpy(buffer, "\n");
    8477            4 :       buffer += 1;
    8478            4 :       *buffer_size -= 1;
    8479              :    }
    8480              : 
    8481            4 :    return DB_SUCCESS;
    8482              : }
    8483              : 
    8484              : /********************************************************************/
    8485              : /**
    8486              : Copy an ODB subtree in ASCII format from a buffer
    8487              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    8488              : @param hKeyRoot Handle for key where search starts, zero for root.
    8489              : @param buffer NULL-terminated buffer
    8490              : @return DB_SUCCESS, DB_TRUNCATED, DB_NO_MEMORY
    8491              : */
    8492            6 : INT db_paste(HNDLE hDB, HNDLE hKeyRoot, const char *buffer)
    8493              : {
    8494              :    char title[MAX_STRING_LENGTH];
    8495              :    char *data;
    8496              :    const char *pold;
    8497              :    INT data_size, index;
    8498              :    INT tid, i, j, n_data, string_length, status, size;
    8499              :    HNDLE hKey;
    8500              :    KEY root_key;
    8501              : 
    8502            6 :    title[0] = 0;
    8503              : 
    8504            6 :    if (hKeyRoot == 0)
    8505            0 :       db_find_key(hDB, hKeyRoot, "", &hKeyRoot);
    8506              : 
    8507            6 :    db_get_key(hDB, hKeyRoot, &root_key);
    8508              : 
    8509              :    /* initial data size */
    8510            6 :    data_size = 1000;
    8511            6 :    data = (char *) malloc(data_size);
    8512            6 :    if (data == NULL) {
    8513            0 :       cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
    8514            0 :       return DB_NO_MEMORY;
    8515              :    }
    8516              : 
    8517              :    do {
    8518              :       char line[10*MAX_STRING_LENGTH];
    8519              : 
    8520           56 :       if (*buffer == 0)
    8521            6 :          break;
    8522              : 
    8523         1048 :       for (i = 0; *buffer != '\n' && *buffer && i < 10*MAX_STRING_LENGTH; i++)
    8524          998 :          line[i] = *buffer++;
    8525              : 
    8526           50 :       if (i == 10*MAX_STRING_LENGTH) {
    8527            0 :          line[10*MAX_STRING_LENGTH-1] = 0;
    8528            0 :          cm_msg(MERROR, "db_paste", "line too long: %s...", line);
    8529            0 :          free(data);
    8530            0 :          return DB_TRUNCATED;
    8531              :       }
    8532              : 
    8533           50 :       line[i] = 0;
    8534           50 :       if (*buffer == '\n')
    8535           50 :          buffer++;
    8536              : 
    8537              :       /* check if it is a section title */
    8538           50 :       if (line[0] == '[') {
    8539              :          /* extract title and append '/' */
    8540            6 :          mstrlcpy(title, line + 1, sizeof(title));
    8541            6 :          if (strchr(title, ']'))
    8542            6 :             *strchr(title, ']') = 0;
    8543            6 :          if (title[0] && title[strlen(title) - 1] != '/')
    8544            6 :             mstrlcat(title, "/", sizeof(title));
    8545              :       } else {
    8546              :          /* valid data line if it includes '=' and no ';' */
    8547           44 :          if (strchr(line, '=') && line[0] != ';') {
    8548              :             char key_name[MAX_ODB_PATH];
    8549              :             char test_str[MAX_ODB_PATH];
    8550              :             char data_str[10*MAX_STRING_LENGTH];
    8551              : 
    8552              :             /* copy type info and data */
    8553           38 :             char* pline = strrchr(line, '=') + 1;
    8554           38 :             while (strstr(line, ": [") != NULL && strstr(line, ": [") < pline) {
    8555            0 :                pline -= 2;
    8556            0 :                while (*pline != '=' && pline > line)
    8557            0 :                   pline--;
    8558            0 :                pline++;
    8559              :             }
    8560           76 :             while (*pline == ' ')
    8561           38 :                pline++;
    8562           38 :             mstrlcpy(data_str, pline, sizeof(data_str));
    8563              : 
    8564              :             /* extract key name */
    8565           38 :             *strrchr(line, '=') = 0;
    8566           38 :             while (strstr(line, ": [") && strchr(line, '='))
    8567            0 :                *strrchr(line, '=') = 0;
    8568              : 
    8569           38 :             pline = &line[strlen(line) - 1];
    8570           76 :             while (*pline == ' ')
    8571           38 :                *pline-- = 0;
    8572              : 
    8573           38 :             key_name[0] = 0;
    8574           38 :             if (title[0] != '.')
    8575            0 :                mstrlcpy(key_name, title, sizeof(key_name));
    8576              : 
    8577           38 :             mstrlcat(key_name, line, sizeof(key_name));
    8578              : 
    8579              :             /* evaluate type info */
    8580           38 :             mstrlcpy(line, data_str, sizeof(line));
    8581           38 :             if (strchr(line, ' '))
    8582           38 :                *strchr(line, ' ') = 0;
    8583              : 
    8584           38 :             n_data = 1;
    8585           38 :             if (strchr(line, '[')) {
    8586            0 :                n_data = atol(strchr(line, '[') + 1);
    8587            0 :                *strchr(line, '[') = 0;
    8588              :             }
    8589              : 
    8590          428 :             for (tid = 0; tid < TID_LAST; tid++)
    8591          422 :                if (strcmp(rpc_tid_name(tid), line) == 0)
    8592           32 :                   break;
    8593           38 :             if (tid == TID_LAST) {
    8594           44 :                for (tid = 0; tid < TID_LAST; tid++)
    8595           44 :                   if (strcmp(rpc_tid_name_old(tid), line) == 0)
    8596            6 :                      break;
    8597              :             }
    8598              : 
    8599           38 :             string_length = 0;
    8600              : 
    8601           38 :             if (tid == TID_LAST)
    8602            0 :                cm_msg(MERROR, "db_paste", "found unknown data type \"%s\" in ODB file", line);
    8603              :             else {
    8604              :                /* skip type info */
    8605           38 :                char* pc = data_str;
    8606          220 :                while (*pc != ' ' && *pc)
    8607          182 :                   pc++;
    8608          152 :                while ((*pc == ' ' || *pc == ':') && *pc)
    8609          114 :                   pc++;
    8610              : 
    8611              :                //mstrlcpy(data_str, pc, sizeof(data_str)); // MacOS 10.9 does not permit mstrlcpy() of overlapping strings
    8612           38 :                assert(strlen(pc) < sizeof(data_str)); // "pc" points at a substring inside "data_str"
    8613           38 :                memmove(data_str, pc, strlen(pc)+1);
    8614              : 
    8615           38 :                if (n_data > 1) {
    8616            0 :                   data_str[0] = 0;
    8617            0 :                   if (!*buffer)
    8618            0 :                      break;
    8619              : 
    8620            0 :                   for (j = 0; *buffer != '\n' && *buffer; j++)
    8621            0 :                      data_str[j] = *buffer++;
    8622            0 :                   data_str[j] = 0;
    8623            0 :                   if (*buffer == '\n')
    8624            0 :                      buffer++;
    8625              :                }
    8626              : 
    8627           76 :                for (i = 0; i < n_data; i++) {
    8628              :                   /* strip trailing \n */
    8629           38 :                   char* pc = &data_str[strlen(data_str) - 1];
    8630           38 :                   while (*pc == '\n' || *pc == '\r')
    8631            0 :                      *pc-- = 0;
    8632              : 
    8633           38 :                   if (tid == TID_STRING || tid == TID_LINK) {
    8634            8 :                      if (!string_length) {
    8635            8 :                         if (data_str[1] == '=')
    8636            0 :                            string_length = -1;
    8637              :                         else
    8638            8 :                            string_length = atoi(data_str + 1);
    8639            8 :                         if (string_length > MAX_STRING_LENGTH) {
    8640            0 :                            string_length = MAX_STRING_LENGTH;
    8641            0 :                            cm_msg(MERROR, "db_paste", "found string exceeding MAX_STRING_LENGTH, odb path \"%s\"", key_name);
    8642              :                         }
    8643            8 :                         if (string_length == 0) {
    8644            0 :                            string_length = 32;
    8645            0 :                            cm_msg(MERROR, "db_paste", "found string length of zero, set to 32, odb path \"%s\"", key_name);
    8646              :                         }
    8647              :                      }
    8648              : 
    8649            8 :                      if (string_length == -1) {
    8650              :                         /* multi-line string */
    8651            0 :                         if (strstr(buffer, "\n====#$@$#====\n") != NULL) {
    8652            0 :                            string_length = (POINTER_T) strstr(buffer, "\n====#$@$#====\n") - (POINTER_T) buffer + 1;
    8653              : 
    8654            0 :                            if (string_length >= data_size) {
    8655            0 :                               data_size += string_length + 100;
    8656            0 :                               data = (char *) realloc(data, data_size);
    8657            0 :                               if (data == NULL) {
    8658            0 :                                  cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
    8659            0 :                                  return DB_NO_MEMORY;
    8660              :                               }
    8661              :                            }
    8662              : 
    8663            0 :                            memset(data, 0, data_size);
    8664            0 :                            strncpy(data, buffer, string_length);
    8665            0 :                            data[string_length - 1] = 0;
    8666            0 :                            buffer = strstr(buffer, "\n====#$@$#====\n") + strlen("\n====#$@$#====\n");
    8667              :                         } else
    8668            0 :                            cm_msg(MERROR, "db_paste", "found multi-line string without termination sequence");
    8669              :                      } else {
    8670            8 :                         char* pc = data_str + 2;
    8671           28 :                         while (*pc && *pc != ' ')
    8672           20 :                            pc++;
    8673              : 
    8674              :                         // skip one space (needed for strings starting with spaces)
    8675            8 :                         if (*pc)
    8676            8 :                            pc++;
    8677              : 
    8678              :                         /* limit string size */
    8679            8 :                         *(pc + string_length - 1) = 0;
    8680              : 
    8681              :                         /* increase data buffer if necessary */
    8682            8 :                         if (string_length * (i + 1) >= data_size) {
    8683            0 :                            data_size += 1000;
    8684            0 :                            data = (char *) realloc(data, data_size);
    8685            0 :                            if (data == NULL) {
    8686            0 :                               cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
    8687            0 :                               return DB_NO_MEMORY;
    8688              :                            }
    8689              :                         }
    8690              : 
    8691            8 :                         mstrlcpy(data + string_length * i, pc, string_length);
    8692              :                      }
    8693            8 :                   } else {
    8694           30 :                      char* pc = data_str;
    8695              : 
    8696           30 :                      if (n_data > 1 && data_str[0] == '[') {
    8697            0 :                         index = atoi(data_str+1);
    8698            0 :                         pc = strchr(data_str, ']') + 1;
    8699            0 :                         while (*pc && *pc == ' ')
    8700            0 :                            pc++;
    8701              :                      } else
    8702           30 :                         index = 0;
    8703              : 
    8704              :                      /* increase data buffer if necessary */
    8705           30 :                      if (rpc_tid_size(tid) * (index + 1) >= data_size) {
    8706            0 :                         data_size += 1000;
    8707            0 :                         data = (char *) realloc(data, data_size);
    8708            0 :                         if (data == NULL) {
    8709            0 :                            cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
    8710            0 :                            return DB_NO_MEMORY;
    8711              :                         }
    8712              :                      }
    8713              : 
    8714           30 :                      db_sscanf(pc, data, &size, index, tid);
    8715              :                   }
    8716              : 
    8717           38 :                   if (i < n_data - 1) {
    8718            0 :                      data_str[0] = 0;
    8719            0 :                      if (!*buffer)
    8720            0 :                         break;
    8721              : 
    8722            0 :                      pold = buffer;
    8723              : 
    8724            0 :                      for (j = 0; *buffer != '\n' && *buffer; j++)
    8725            0 :                         data_str[j] = *buffer++;
    8726            0 :                      data_str[j] = 0;
    8727            0 :                      if (*buffer == '\n')
    8728            0 :                         buffer++;
    8729              : 
    8730              :                      /* test if valid data */
    8731            0 :                      if (tid != TID_STRING && tid != TID_LINK) {
    8732            0 :                         if (data_str[0] == 0 || (strchr(data_str, '=')
    8733            0 :                                                  && strchr(data_str, ':')))
    8734            0 :                            buffer = pold;
    8735              :                      }
    8736              :                   }
    8737              :                }
    8738              : 
    8739              :                /* skip system client entries */
    8740           38 :                mstrlcpy(test_str, key_name, sizeof(test_str));
    8741           38 :                test_str[15] = 0;
    8742              : 
    8743           38 :                if (!equal_ustring(test_str, "/System/Clients")) {
    8744           38 :                   if (root_key.type != TID_KEY) {
    8745              :                      /* root key is destination key */
    8746            0 :                      hKey = hKeyRoot;
    8747              :                   } else {
    8748              :                      /* create key and set value */
    8749           38 :                      if (key_name[0] == '/') {
    8750            0 :                         status = db_find_link(hDB, 0, key_name, &hKey);
    8751            0 :                         if (status == DB_NO_KEY) {
    8752            0 :                            db_create_key(hDB, 0, key_name, tid);
    8753            0 :                            status = db_find_link(hDB, 0, key_name, &hKey);
    8754              :                         }
    8755              :                      } else {
    8756           38 :                         status = db_find_link(hDB, hKeyRoot, key_name, &hKey);
    8757           38 :                         if (status == DB_NO_KEY) {
    8758           38 :                            db_create_key(hDB, hKeyRoot, key_name, tid);
    8759           38 :                            status = db_find_link(hDB, hKeyRoot, key_name, &hKey);
    8760              :                         }
    8761              :                      }
    8762              :                   }
    8763              : 
    8764              :                   /* set key data if created successfully */
    8765           38 :                   if (hKey) {
    8766           38 :                      if (tid == TID_STRING || tid == TID_LINK)
    8767            8 :                         db_set_link_data(hDB, hKey, data, string_length * n_data, n_data, tid);
    8768              :                      else
    8769           30 :                         db_set_link_data(hDB, hKey, data, rpc_tid_size(tid) * n_data, n_data, tid);
    8770              :                   }
    8771              :                }
    8772              :             }
    8773              :          }
    8774              :       }
    8775           50 :    } while (TRUE);
    8776              : 
    8777            6 :    free(data);
    8778            6 :    return DB_SUCCESS;
    8779              : }
    8780              : 
    8781              : /********************************************************************/
    8782              : /*
    8783              :   Only internally used by db_paste_xml
    8784              : */
    8785            0 : static int db_paste_node(HNDLE hDB, HNDLE hKeyRoot, PMXML_NODE node)
    8786              : {
    8787              :    int status;
    8788              : 
    8789            0 :    if (strcmp(mxml_get_name(node), "odb") == 0) {
    8790            0 :       for (int i = 0; i < mxml_get_number_of_children(node); i++) {
    8791            0 :          status = db_paste_node(hDB, hKeyRoot, mxml_subnode(node, i));
    8792            0 :          if (status != DB_SUCCESS)
    8793            0 :             return status;
    8794              :       }
    8795            0 :    } else if (strcmp(mxml_get_name(node), "dir") == 0) {
    8796            0 :       const char* name = mxml_get_attribute(node, "name");
    8797              : 
    8798            0 :       if (name == NULL) {
    8799            0 :          cm_msg(MERROR, "db_paste_node", "found key \"%s\" with no name in XML data", mxml_get_name(node));
    8800            0 :          return DB_TYPE_MISMATCH;
    8801              :       }
    8802              : 
    8803              :       HNDLE hKey;
    8804            0 :       status = db_find_link(hDB, hKeyRoot, name, &hKey);
    8805              : 
    8806            0 :       if (status == DB_NO_KEY) {
    8807            0 :          status = db_create_key(hDB, hKeyRoot, name, TID_KEY);
    8808            0 :          if (status == DB_NO_ACCESS) {
    8809            0 :             cm_msg(MINFO, "db_paste_node", "cannot load key \"%s\": write protected", name);
    8810            0 :             return DB_SUCCESS;  /* key or tree is locked, just skip it */
    8811              :          }
    8812              : 
    8813            0 :          if (status != DB_SUCCESS && status != DB_KEY_EXIST) {
    8814            0 :             cm_msg(MERROR, "db_paste_node", "cannot create key \"%s\" in ODB, status = %d", name, status);
    8815            0 :             return status;
    8816              :          }
    8817            0 :          status = db_find_link(hDB, hKeyRoot, name, &hKey);
    8818            0 :          if (status != DB_SUCCESS) {
    8819            0 :             cm_msg(MERROR, "db_paste_node", "cannot find key \"%s\" in ODB", name);
    8820            0 :             return status;
    8821              :          }
    8822              :       }
    8823              : 
    8824            0 :       std::string path = db_get_path(hDB, hKey);
    8825            0 :       if (!equal_ustring(path.c_str(), "/System/Clients")) {
    8826            0 :          for (int i = 0; i < mxml_get_number_of_children(node); i++) {
    8827            0 :             status = db_paste_node(hDB, hKey, mxml_subnode(node, i));
    8828            0 :             if (status != DB_SUCCESS)
    8829            0 :                return status;
    8830              :          }
    8831              :       }
    8832            0 :    } else if (strcmp(mxml_get_name(node), "key") == 0 || strcmp(mxml_get_name(node), "keyarray") == 0) {
    8833              : 
    8834            0 :       const char* name = mxml_get_attribute(node, "name");
    8835              : 
    8836            0 :       if (name == NULL) {
    8837            0 :          cm_msg(MERROR, "db_paste_node", "found key \"%s\" with no name in XML data", mxml_get_name(node));
    8838            0 :          return DB_TYPE_MISMATCH;
    8839              :       }
    8840              : 
    8841              :       int num_values;
    8842            0 :       if (strcmp(mxml_get_name(node), "keyarray") == 0)
    8843            0 :          num_values = atoi(mxml_get_attribute(node, "num_values"));
    8844              :       else
    8845            0 :          num_values = 0;
    8846              : 
    8847            0 :       const char* type = mxml_get_attribute(node, "type");
    8848              : 
    8849            0 :       if (type == NULL) {
    8850            0 :          cm_msg(MERROR, "db_paste_node", "found key \"%s\" with no type in XML data", mxml_get_name(node));
    8851            0 :          return DB_TYPE_MISMATCH;
    8852              :       }
    8853              : 
    8854            0 :       int tid = rpc_name_tid(type);
    8855            0 :       if (tid == 0) {
    8856            0 :          cm_msg(MERROR, "db_paste_node", "found unknown data type \"%s\" in XML data", type);
    8857            0 :          return DB_TYPE_MISMATCH;
    8858              :       }
    8859              : 
    8860              :       HNDLE hKey;
    8861            0 :       status = db_find_link(hDB, hKeyRoot, name, &hKey);
    8862            0 :       if (status == DB_NO_KEY) {
    8863            0 :          status = db_create_key(hDB, hKeyRoot, name, tid);
    8864            0 :          if (status == DB_NO_ACCESS) {
    8865            0 :             cm_msg(MINFO, "db_paste_node", "cannot load key \"%s\": write protected", name);
    8866            0 :             return DB_SUCCESS;  /* key or tree is locked, just skip it */
    8867              :          }
    8868              : 
    8869            0 :          if (status != DB_SUCCESS) {
    8870            0 :             cm_msg(MERROR, "db_paste_node", "cannot create key \"%s\" in ODB, status = %d", name, status);
    8871            0 :             return status;
    8872              :          }
    8873            0 :          status = db_find_link(hDB, hKeyRoot, name, &hKey);
    8874            0 :          if (status != DB_SUCCESS) {
    8875            0 :             cm_msg(MERROR, "db_paste_node", "cannot find key \"%s\" in ODB, status = %d", name, status);
    8876            0 :             return status;
    8877              :          }
    8878              :       } else {
    8879              :          KEY key;
    8880            0 :          status = db_get_key(hDB, hKey, &key);
    8881            0 :          if (status != DB_SUCCESS) {
    8882            0 :             cm_msg(MERROR, "db_paste_node", "cannot get key \"%s\" in ODB, status = %d", name, status);
    8883            0 :             return status;
    8884              :          }
    8885              : 
    8886            0 :          if (num_values > 0 && num_values != key.num_values && key.item_size > 0) {
    8887            0 :             status = db_set_num_values(hDB, hKey, num_values);
    8888            0 :             if (status != DB_SUCCESS) {
    8889            0 :                cm_msg(MERROR, "db_paste_node", "cannot resize key \"%s\" in ODB, status = %d", name, status);
    8890            0 :                return status;
    8891              :             }
    8892              :          }
    8893              :       }
    8894              : 
    8895            0 :       int size = 0;
    8896            0 :       char *buf = NULL;
    8897              : 
    8898            0 :       if (tid == TID_STRING || tid == TID_LINK) {
    8899            0 :          size = atoi(mxml_get_attribute(node, "size"));
    8900            0 :          buf = (char *)malloc(size);
    8901            0 :          assert(buf);
    8902            0 :          buf[0] = 0;
    8903              :       }
    8904              : 
    8905            0 :       if (num_values) {
    8906              :          /* evaluate array */
    8907            0 :          for (int i = 0; i < mxml_get_number_of_children(node); i++) {
    8908            0 :             PMXML_NODE child = mxml_subnode(node, i);
    8909              :             int idx;
    8910            0 :             if (mxml_get_attribute(child, "index"))
    8911            0 :                idx = atoi(mxml_get_attribute(child, "index"));
    8912              :             else
    8913            0 :                idx = i;
    8914            0 :             if (tid == TID_STRING || tid == TID_LINK) {
    8915            0 :                if (mxml_get_value(child) == NULL) {
    8916            0 :                   status = db_set_data_index(hDB, hKey, "", size, i, tid);
    8917            0 :                   if (status == DB_NO_ACCESS) {
    8918            0 :                      cm_msg(MINFO, "db_paste_node", "cannot load string or link \"%s\": write protected", mxml_get_attribute(node, "name"));
    8919            0 :                      return DB_SUCCESS;  /* key or tree is locked, just skip it */
    8920            0 :                   } else if (status != DB_SUCCESS) {
    8921            0 :                      cm_msg(MERROR, "db_paste_node", "cannot load string or link \"%s\": db_set_data_index() status %d", mxml_get_attribute(node, "name"), status);
    8922            0 :                      return status;
    8923              :                   }
    8924              :                } else {
    8925            0 :                   mstrlcpy(buf, mxml_get_value(child), size);
    8926            0 :                   status = db_set_data_index(hDB, hKey, buf, size, idx, tid);
    8927            0 :                   if (status == DB_NO_ACCESS) {
    8928            0 :                      cm_msg(MINFO, "db_paste_node", "cannot load array element \"%s\": write protected", mxml_get_attribute(node, "name"));
    8929            0 :                      return DB_SUCCESS;  /* key or tree is locked, just skip it */
    8930            0 :                   } else if (status != DB_SUCCESS) {
    8931            0 :                      cm_msg(MERROR, "db_paste_node", "cannot load array element \"%s\": db_set_data_index() status %d", mxml_get_attribute(node, "name"), status);
    8932            0 :                      return status;
    8933              :                   }
    8934              :                }
    8935              :             } else {
    8936              :                char data[256];
    8937            0 :                db_sscanf(mxml_get_value(child), data, &size, 0, tid);
    8938            0 :                status = db_set_data_index(hDB, hKey, data, rpc_tid_size(tid), idx, tid);
    8939            0 :                if (status == DB_NO_ACCESS) {
    8940            0 :                   cm_msg(MINFO, "db_paste_node", "cannot load array element \"%s\": write protected", mxml_get_attribute(node, "name"));
    8941            0 :                   return DB_SUCCESS;  /* key or tree is locked, just skip it */
    8942            0 :                } else if (status != DB_SUCCESS) {
    8943            0 :                   cm_msg(MERROR, "db_paste_node", "cannot load array element \"%s\": db_set_data_index() status %d", mxml_get_attribute(node, "name"), status);
    8944            0 :                   return status;
    8945              :                }
    8946              :             }
    8947              :          }
    8948              : 
    8949              :       } else {                  /* single value */
    8950            0 :          if (tid == TID_STRING || tid == TID_LINK) {
    8951            0 :             size = atoi(mxml_get_attribute(node, "size"));
    8952            0 :             if (mxml_get_value(node) == NULL) {
    8953            0 :                status = db_set_data(hDB, hKey, "", size, 1, tid);
    8954            0 :                if (status == DB_NO_ACCESS) {
    8955            0 :                   cm_msg(MINFO, "db_paste_node", "cannot load string or link \"%s\": write protected", mxml_get_attribute(node, "name"));
    8956            0 :                   return DB_SUCCESS;  /* key or tree is locked, just skip it */
    8957            0 :                } else if (status != DB_SUCCESS) {
    8958            0 :                   cm_msg(MERROR, "db_paste_node", "cannot load string or link \"%s\": db_set_data() status %d", mxml_get_attribute(node, "name"), status);
    8959            0 :                   return status;
    8960              :                }
    8961              :             } else {
    8962            0 :                mstrlcpy(buf, mxml_get_value(node), size);
    8963            0 :                status = db_set_data(hDB, hKey, buf, size, 1, tid);
    8964            0 :                if (status == DB_NO_ACCESS) {
    8965            0 :                   cm_msg(MINFO, "db_paste_node", "cannot load value \"%s\": write protected", mxml_get_attribute(node, "name"));
    8966            0 :                   return DB_SUCCESS;  /* key or tree is locked, just skip it */
    8967            0 :                } else if (status != DB_SUCCESS) {
    8968            0 :                   cm_msg(MERROR, "db_paste_node", "cannot load value \"%s\": db_set_data() status %d", mxml_get_attribute(node, "name"), status);
    8969            0 :                   return status;
    8970              :                }
    8971              :             }
    8972              :          } else {
    8973              :             char data[256];
    8974            0 :             db_sscanf(mxml_get_value(node), data, &size, 0, tid);
    8975            0 :             status = db_set_data(hDB, hKey, data, rpc_tid_size(tid), 1, tid);
    8976            0 :             if (status == DB_NO_ACCESS) {
    8977            0 :                cm_msg(MINFO, "db_paste_node", "cannot load value \"%s\": write protected", mxml_get_attribute(node, "name"));
    8978            0 :                return DB_SUCCESS;  /* key or tree is locked, just skip it */
    8979            0 :             } else if (status != DB_SUCCESS) {
    8980            0 :                cm_msg(MERROR, "db_paste_node", "cannot load value \"%s\": db_set_data() status %d", mxml_get_attribute(node, "name"), status);
    8981            0 :                return status;
    8982              :             }
    8983              :          }
    8984              :       }
    8985              : 
    8986            0 :       if (buf) {
    8987            0 :          free(buf);
    8988            0 :          buf = NULL;
    8989              :       }
    8990              :    }
    8991              : 
    8992            0 :    return DB_SUCCESS;
    8993              : }
    8994              : 
    8995              : /********************************************************************/
    8996              : /**
    8997              : Paste an ODB subtree in XML format from a buffer
    8998              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    8999              : @param hKeyRoot Handle for key where search starts, zero for root.
    9000              : @param buffer NULL-terminated buffer
    9001              : @return DB_SUCCESS, DB_INVALID_PARAM, DB_NO_MEMORY, DB_TYPE_MISMATCH
    9002              : */
    9003            0 : INT db_paste_xml(HNDLE hDB, HNDLE hKeyRoot, const char *buffer)
    9004              : {
    9005              :    char error[256];
    9006              :    INT status;
    9007              :    PMXML_NODE tree, node;
    9008              : 
    9009            0 :    if (hKeyRoot == 0)
    9010            0 :       db_find_key(hDB, hKeyRoot, "", &hKeyRoot);
    9011              : 
    9012              :    /* parse XML buffer */
    9013            0 :    tree = mxml_parse_buffer(buffer, error, sizeof(error), NULL);
    9014            0 :    if (tree == NULL) {
    9015            0 :       puts(error);
    9016            0 :       return DB_TYPE_MISMATCH;
    9017              :    }
    9018              : 
    9019            0 :    node = mxml_find_node(tree, "odb");
    9020            0 :    if (node == NULL) {
    9021            0 :       puts("Cannot find element \"odb\" in XML data");
    9022            0 :       return DB_TYPE_MISMATCH;
    9023              :    }
    9024              : 
    9025            0 :    status = db_paste_node(hDB, hKeyRoot, node);
    9026              : 
    9027            0 :    mxml_free_tree(tree);
    9028              : 
    9029            0 :    return status;
    9030              : }
    9031              : 
    9032              : /********************************************************************/
    9033              : /**
    9034              : Copy an ODB subtree in XML format to a buffer
    9035              : 
    9036              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    9037              : @param hKey Handle for key where search starts, zero for root.
    9038              : @param buffer ASCII buffer which receives ODB contents.
    9039              : @param buffer_size Size of buffer, returns remaining space in buffer.
    9040              : @return DB_SUCCESS, DB_TRUNCATED, DB_NO_MEMORY
    9041              : */
    9042            0 : INT db_copy_xml(HNDLE hDB, HNDLE hKey, char *buffer, int *buffer_size, bool header)
    9043              : {
    9044              : 
    9045            0 :    if (rpc_is_remote())
    9046            0 :       return rpc_call(RPC_DB_COPY_XML, hDB, hKey, buffer, buffer_size, header);
    9047              : 
    9048              : #ifdef LOCAL_ROUTINES
    9049              :    {
    9050              :       INT len;
    9051              :       char *p;
    9052              :       MXML_WRITER *writer;
    9053              : 
    9054              :       /* open file */
    9055            0 :       writer = mxml_open_buffer();
    9056            0 :       if (writer == NULL) {
    9057            0 :          cm_msg(MERROR, "db_copy_xml", "Cannot allocate buffer");
    9058            0 :          return DB_NO_MEMORY;
    9059              :       }
    9060              : 
    9061            0 :       if (header) {
    9062            0 :          std::string path = db_get_path(hDB, hKey);
    9063              : 
    9064              :          /* write XML header */
    9065            0 :          mxml_start_element(writer, "odb");
    9066            0 :          mxml_write_attribute(writer, "root", path.c_str());
    9067            0 :          mxml_write_attribute(writer, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
    9068            0 :          mxml_write_attribute(writer, "xsi:noNamespaceSchemaLocation", "http://midas.psi.ch/odb.xsd");
    9069              : 
    9070            0 :          db_save_xml_key(hDB, hKey, 0, writer);
    9071            0 :          mxml_end_element(writer); // "odb"
    9072            0 :       } else {
    9073              :          KEY key;
    9074            0 :          int status = db_get_key(hDB, hKey, &key);
    9075            0 :          if (status != DB_SUCCESS)
    9076            0 :             return status;
    9077              : 
    9078            0 :          db_save_xml_key(hDB, hKey, 1, writer);
    9079              :       }
    9080              : 
    9081            0 :       p = mxml_close_buffer(writer);
    9082            0 :       len = strlen(p) + 1;
    9083            0 :       if (len > *buffer_size) {
    9084            0 :          free(p);
    9085            0 :          *buffer_size = 0;
    9086            0 :          return DB_TRUNCATED;
    9087              :       }
    9088              : 
    9089            0 :       mstrlcpy(buffer, p, len);
    9090            0 :       free(p);
    9091            0 :       *buffer_size = len;
    9092              :    }
    9093              : #endif                          /* LOCAL_ROUTINES */
    9094              : 
    9095            0 :    return DB_SUCCESS;
    9096              : }
    9097              : 
    9098              : /**dox***************************************************************/
    9099              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    9100              : 
    9101              : /*------------------------------------------------------------------*/
    9102            0 : void name2c(char *str)
    9103              : /********************************************************************\
    9104              : 
    9105              :   Routine: name2c
    9106              : 
    9107              :   Purpose: Convert key name to C name. Internal use only.
    9108              : 
    9109              : \********************************************************************/
    9110              : {
    9111            0 :    if (*str >= '0' && *str <= '9')
    9112            0 :       *str = '_';
    9113              : 
    9114            0 :    while (*str) {
    9115            0 :       if (!(*str >= 'a' && *str <= 'z') && !(*str >= 'A' && *str <= 'Z') && !(*str >= '0' && *str <= '9'))
    9116            0 :          *str = '_';
    9117            0 :       *str = (char) tolower(*str);
    9118            0 :       str++;
    9119              :    }
    9120            0 : }
    9121              : 
    9122              : /*------------------------------------------------------------------*/
    9123            0 : static void db_save_tree_struct(HNDLE hDB, HNDLE hKey, int hfile, INT level)
    9124              : /********************************************************************\
    9125              : 
    9126              :   Routine: db_save_tree_struct
    9127              : 
    9128              :   Purpose: Save database tree as a C structure. Gets called by
    9129              :            db_save_struct(). Internal use only.
    9130              : 
    9131              : \********************************************************************/
    9132              : {
    9133              :    INT i, idx;
    9134              :    KEY key;
    9135              :    HNDLE hSubkey;
    9136              :    int wr;
    9137              : 
    9138              :    /* first enumerate this level */
    9139            0 :    for (idx = 0;; idx++) {
    9140              :       char name[MAX_ODB_PATH];
    9141              : 
    9142            0 :       db_enum_link(hDB, hKey, idx, &hSubkey);
    9143            0 :       if (!hSubkey)
    9144            0 :          break;
    9145              : 
    9146              :       /* first get the name of the link, than the type of the link target */
    9147            0 :       db_get_key(hDB, hSubkey, &key);
    9148            0 :       mstrlcpy(name, key.name, sizeof(name));
    9149            0 :       db_enum_key(hDB, hKey, idx, &hSubkey);
    9150              : 
    9151            0 :       db_get_key(hDB, hSubkey, &key);
    9152              : 
    9153            0 :       if (key.type != TID_KEY) {
    9154              :          char line[MAX_ODB_PATH];
    9155              :          char str[MAX_ODB_PATH];
    9156              : 
    9157            0 :          for (i = 0; i <= level; i++) {
    9158            0 :             wr = write(hfile, "  ", 2);
    9159            0 :             assert(wr == 2);
    9160              :          }
    9161              : 
    9162            0 :          switch (key.type) {
    9163            0 :          case TID_INT8:
    9164              :          case TID_CHAR:
    9165            0 :             strcpy(line, "char");
    9166            0 :             break;
    9167            0 :          case TID_INT16:
    9168            0 :             strcpy(line, "short");
    9169            0 :             break;
    9170            0 :          case TID_FLOAT:
    9171            0 :             strcpy(line, "float");
    9172            0 :             break;
    9173            0 :          case TID_DOUBLE:
    9174            0 :             strcpy(line, "double");
    9175            0 :             break;
    9176            0 :          case TID_BITFIELD:
    9177            0 :             strcpy(line, "unsigned char");
    9178            0 :             break;
    9179            0 :          case TID_STRING:
    9180            0 :             strcpy(line, "char");
    9181            0 :             break;
    9182            0 :          case TID_LINK:
    9183            0 :             strcpy(line, "char");
    9184            0 :             break;
    9185            0 :          default:
    9186            0 :             strcpy(line, rpc_tid_name(key.type));
    9187            0 :             break;
    9188              :          }
    9189              : 
    9190            0 :          mstrlcat(line, "                    ", sizeof(line));
    9191            0 :          mstrlcpy(str, name, sizeof(str));
    9192            0 :          name2c(str);
    9193              : 
    9194            0 :          if (key.num_values > 1)
    9195            0 :             sprintf(str + strlen(str), "[%d]", key.num_values);
    9196            0 :          if (key.type == TID_STRING || key.type == TID_LINK)
    9197            0 :             sprintf(str + strlen(str), "[%d]", key.item_size);
    9198              : 
    9199            0 :          mstrlcpy(line + 10, str, sizeof(line) - 10);
    9200            0 :          mstrlcat(line, ";\n", sizeof(line));
    9201              : 
    9202            0 :          wr = write(hfile, line, strlen(line));
    9203            0 :          assert(wr > 0);
    9204              :       } else {
    9205              :          char line[10+MAX_ODB_PATH];
    9206              :          char str[MAX_ODB_PATH];
    9207              : 
    9208              :          /* recurse subtree */
    9209            0 :          for (i = 0; i <= level; i++) {
    9210            0 :             wr = write(hfile, "  ", 2);
    9211            0 :             assert(wr == 2);
    9212              :          }
    9213              : 
    9214            0 :          sprintf(line, "struct {\n");
    9215            0 :          wr = write(hfile, line, strlen(line));
    9216            0 :          assert(wr > 0);
    9217            0 :          db_save_tree_struct(hDB, hSubkey, hfile, level + 1);
    9218              : 
    9219            0 :          for (i = 0; i <= level; i++) {
    9220            0 :             wr = write(hfile, "  ", 2);
    9221            0 :             assert(wr == 2);
    9222              :          }
    9223              : 
    9224            0 :          strcpy(str, name);
    9225            0 :          name2c(str);
    9226              : 
    9227            0 :          sprintf(line, "} %s;\n", str);
    9228            0 :          wr = write(hfile, line, strlen(line));
    9229            0 :          assert(wr > 0);
    9230              :       }
    9231            0 :    }
    9232            0 : }
    9233              : 
    9234              : /**dox***************************************************************/
    9235              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    9236              : 
    9237              : /********************************************************************/
    9238              : /**
    9239              : Save a branch of a database to an .ODB file
    9240              : 
    9241              : This function is used by the ODBEdit command save. For a
    9242              : description of the ASCII format, see db_copy(). Data of the whole ODB can
    9243              : be saved (hkey equal zero) or only a sub-tree.
    9244              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    9245              : @param hKey Handle for key where search starts, zero for root.
    9246              : @param filename Filename of .ODB file.
    9247              : @param bRemote Flag for saving database on remote server.
    9248              : @return DB_SUCCESS, DB_FILE_ERROR
    9249              : */
    9250            0 : INT db_save(HNDLE hDB, HNDLE hKey, const char *filename, BOOL bRemote)
    9251              : {
    9252            0 :    if (rpc_is_remote() && bRemote)
    9253            0 :       return rpc_call(RPC_DB_SAVE, hDB, hKey, filename, bRemote);
    9254              : 
    9255              : #ifdef LOCAL_ROUTINES
    9256              :    {
    9257              :       INT hfile, size, buffer_size, n, status;
    9258              :       char *buffer;
    9259              : 
    9260              :       /* open file */
    9261            0 :       hfile = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT, 0644);
    9262            0 :       if (hfile == -1) {
    9263            0 :          cm_msg(MERROR, "db_save", "Cannot open file \"%s\"", filename);
    9264            0 :          return DB_FILE_ERROR;
    9265              :       }
    9266              : 
    9267            0 :       std::string path = db_get_path(hDB, hKey);
    9268              : 
    9269            0 :       buffer_size = 10000;
    9270              :       do {
    9271            0 :          buffer = (char *) malloc(buffer_size);
    9272            0 :          if (buffer == NULL) {
    9273            0 :             cm_msg(MERROR, "db_save", "cannot allocate ODB dump buffer");
    9274            0 :             break;
    9275              :          }
    9276              : 
    9277            0 :          size = buffer_size;
    9278            0 :          status = db_copy(hDB, hKey, buffer, &size, path.c_str());
    9279            0 :          if (status != DB_TRUNCATED) {
    9280            0 :             n = write(hfile, buffer, buffer_size - size);
    9281            0 :             free(buffer);
    9282            0 :             buffer = NULL;
    9283              : 
    9284            0 :             if (n != buffer_size - size) {
    9285            0 :                cm_msg(MERROR, "db_save", "cannot save .ODB file");
    9286            0 :                close(hfile);
    9287            0 :                return DB_FILE_ERROR;
    9288              :             }
    9289            0 :             break;
    9290              :          }
    9291              : 
    9292              :          /* increase buffer size if truncated */
    9293            0 :          free(buffer);
    9294            0 :          buffer = NULL;
    9295            0 :          buffer_size *= 2;
    9296              :       } while (1);
    9297              : 
    9298            0 :       close(hfile);
    9299              : 
    9300            0 :    }
    9301              : #endif                          /* LOCAL_ROUTINES */
    9302              : 
    9303            0 :    return DB_SUCCESS;
    9304              : }
    9305              : 
    9306              : /*------------------------------------------------------------------*/
    9307              : 
    9308            0 : void xml_encode(char *src, int size)
    9309              : {
    9310              :    int i;
    9311              :    char *dst, *p;
    9312              : 
    9313            0 :    dst = (char *) malloc(size);
    9314            0 :    if (dst == NULL)
    9315            0 :       return;
    9316              : 
    9317            0 :    *dst = 0;
    9318            0 :    for (i = 0; i < (int) strlen(src); i++) {
    9319            0 :       switch (src[i]) {
    9320            0 :       case '<':
    9321            0 :          mstrlcat(dst, "&lt;", size);
    9322            0 :          break;
    9323            0 :       case '>':
    9324            0 :          mstrlcat(dst, "&gt;", size);
    9325            0 :          break;
    9326            0 :       case '&':
    9327            0 :          mstrlcat(dst, "&amp;", size);
    9328            0 :          break;
    9329            0 :       case '\"':
    9330            0 :          mstrlcat(dst, "&quot;", size);
    9331            0 :          break;
    9332            0 :       case '\'':
    9333            0 :          mstrlcat(dst, "&apos;", size);
    9334            0 :          break;
    9335            0 :       default:
    9336            0 :          if ((int) strlen(dst) >= size) {
    9337            0 :             free(dst);
    9338            0 :             return;
    9339              :          }
    9340            0 :          p = dst + strlen(dst);
    9341            0 :          *p = src[i];
    9342            0 :          *(p + 1) = 0;
    9343              :       }
    9344              :    }
    9345              : 
    9346            0 :    mstrlcpy(src, dst, size);
    9347              : }
    9348              : 
    9349              : /*------------------------------------------------------------------*/
    9350              : 
    9351            0 : INT db_save_xml_key(HNDLE hDB, HNDLE hKey, INT level, MXML_WRITER * writer)
    9352              : {
    9353              :    INT i, idx, size, status;
    9354              :    char *data;
    9355              :    HNDLE hSubkey;
    9356              :    KEY key;
    9357              : 
    9358            0 :    status = db_get_link(hDB, hKey, &key);
    9359            0 :    if (status != DB_SUCCESS)
    9360            0 :       return status;
    9361              : 
    9362            0 :    if (key.type == TID_KEY) {
    9363              : 
    9364              :       /* save opening tag for subtree */
    9365              : 
    9366            0 :       if (level > 0) {
    9367            0 :          mxml_start_element(writer, "dir");
    9368            0 :          mxml_write_attribute(writer, "name", key.name);
    9369            0 :          mxml_write_attribute(writer, "handle", std::to_string(hKey).c_str());
    9370              :       }
    9371              : 
    9372            0 :       for (idx = 0;; idx++) {
    9373            0 :          db_enum_link(hDB, hKey, idx, &hSubkey);
    9374              : 
    9375            0 :          if (!hSubkey)
    9376            0 :             break;
    9377              : 
    9378              :          /* save subtree */
    9379            0 :          status = db_save_xml_key(hDB, hSubkey, level + 1, writer);
    9380            0 :          if (status != DB_SUCCESS)
    9381            0 :             return status;
    9382              :       }
    9383              : 
    9384              :       /* save closing tag for subtree */
    9385            0 :       if (level > 0)
    9386            0 :          mxml_end_element(writer);
    9387              : 
    9388              :    } else {
    9389              :       /* save key value */
    9390              : 
    9391            0 :       if (key.num_values > 1)
    9392            0 :          mxml_start_element(writer, "keyarray");
    9393              :       else
    9394            0 :          mxml_start_element(writer, "key");
    9395            0 :       mxml_write_attribute(writer, "name", key.name);
    9396            0 :       mxml_write_attribute(writer, "type", rpc_tid_name(key.type));
    9397            0 :       mxml_write_attribute(writer, "handle", std::to_string(hKey).c_str());
    9398              : 
    9399            0 :       if (key.type == TID_STRING || key.type == TID_LINK) {
    9400              :          char str[256];
    9401            0 :          sprintf(str, "%d", key.item_size);
    9402            0 :          mxml_write_attribute(writer, "size", str);
    9403              :       }
    9404              : 
    9405            0 :       if (key.num_values > 1) {
    9406              :          char str[256];
    9407            0 :          sprintf(str, "%d", key.num_values);
    9408            0 :          mxml_write_attribute(writer, "num_values", str);
    9409              :       }
    9410              : 
    9411            0 :       size = key.total_size;
    9412            0 :       data = (char *) malloc(size+1); // an extra byte to zero-terminate strings
    9413            0 :       if (data == NULL) {
    9414            0 :          cm_msg(MERROR, "db_save_xml_key", "cannot allocate data buffer");
    9415            0 :          return DB_NO_MEMORY;
    9416              :       }
    9417              : 
    9418            0 :       db_get_link_data(hDB, hKey, data, &size, key.type);
    9419              : 
    9420            0 :       if (key.num_values == 1) {
    9421            0 :          if (key.type == TID_STRING) {
    9422            0 :             data[size] = 0; // make sure strings are NUL-terminated
    9423            0 :             mxml_write_value(writer, data);
    9424              :          } else {
    9425            0 :             std::string str = db_sprintf(data, key.item_size, 0, key.type);
    9426            0 :             if (key.type == TID_STRING && strlen(data) >= MAX_STRING_LENGTH) {
    9427            0 :                std::string path = db_get_path(hDB, hKey);
    9428            0 :                cm_msg(MERROR, "db_save_xml_key", "Long odb string probably truncated, odb path \"%s\", string length %d truncated to %d", path.c_str(), (int)strlen(data), (int)str.length());
    9429            0 :             }
    9430            0 :             mxml_write_value(writer, str.c_str());
    9431            0 :          }
    9432            0 :          mxml_end_element(writer);
    9433              : 
    9434              :       } else {                  /* array of values */
    9435              : 
    9436            0 :          for (i = 0; i < key.num_values; i++) {
    9437              : 
    9438            0 :             mxml_start_element(writer, "value");
    9439              : 
    9440              :             {
    9441              :                char str[256];
    9442            0 :                sprintf(str, "%d", i);
    9443            0 :                mxml_write_attribute(writer, "index", str);
    9444              :             }
    9445              : 
    9446            0 :             if (key.type == TID_STRING) {
    9447            0 :                char* p = data + i * key.item_size;
    9448            0 :                p[key.item_size - 1] = 0; // make sure string is NUL-terminated
    9449              :                //cm_msg(MINFO, "db_save_xml_key", "odb string array item_size %d, index %d length %d", key.item_size, i, (int)strlen(p));
    9450            0 :                mxml_write_value(writer, p);
    9451              :             } else {
    9452            0 :                std::string str = db_sprintf(data, key.item_size, i, key.type);
    9453            0 :                if ((key.type == TID_STRING) && (str.length() >= MAX_STRING_LENGTH-1)) {
    9454            0 :                   std::string path = db_get_path(hDB, hKey);
    9455            0 :                   cm_msg(MERROR, "db_save_xml_key", "Long odb string array probably truncated, odb path \"%s\"[%d]", path.c_str(), i);
    9456            0 :                }
    9457            0 :                mxml_write_value(writer, str.c_str());
    9458            0 :             }
    9459              : 
    9460            0 :             mxml_end_element(writer);
    9461              :          }
    9462              : 
    9463            0 :          mxml_end_element(writer);      /* keyarray */
    9464              :       }
    9465              : 
    9466            0 :       free(data);
    9467            0 :       data = NULL;
    9468              :    }
    9469              : 
    9470            0 :    return DB_SUCCESS;
    9471              : }
    9472              : 
    9473              : /********************************************************************/
    9474              : /**
    9475              : Save a branch of a database to an .xml file
    9476              : 
    9477              : This function is used by the ODBEdit command save to write the contents
    9478              : of the ODB into a XML file. Data of the whole ODB can
    9479              : be saved (hkey equal zero) or only a sub-tree.
    9480              : @param hDB          ODB handle obtained via cm_get_experiment_database().
    9481              : @param hKey Handle for key where search starts, zero for root.
    9482              : @param filename Filename of .XML file.
    9483              : @return DB_SUCCESS, DB_FILE_ERROR
    9484              : */
    9485            0 : INT db_save_xml(HNDLE hDB, HNDLE hKey, const char *filename)
    9486              : {
    9487              : #ifdef LOCAL_ROUTINES
    9488              :    {
    9489              :       INT status;
    9490              :       MXML_WRITER *writer;
    9491              : 
    9492              :       /* open file */
    9493            0 :       writer = mxml_open_file(filename);
    9494            0 :       if (writer == NULL) {
    9495            0 :          cm_msg(MERROR, "db_save_xml", "Cannot open file \"%s\"", filename);
    9496            0 :          return DB_FILE_ERROR;
    9497              :       }
    9498              : 
    9499            0 :       std::string path = db_get_path(hDB, hKey);
    9500              : 
    9501              :       /* write XML header */
    9502            0 :       mxml_start_element(writer, "odb");
    9503            0 :       mxml_write_attribute(writer, "root", path.c_str());
    9504            0 :       mxml_write_attribute(writer, "filename", filename);
    9505            0 :       mxml_write_attribute(writer, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
    9506              : 
    9507            0 :       std::string xsd_path;
    9508              : 
    9509            0 :       if (getenv("MIDASSYS"))
    9510            0 :          xsd_path = getenv("MIDASSYS");
    9511              :       else
    9512            0 :          xsd_path = ".";
    9513              : 
    9514            0 :       xsd_path += DIR_SEPARATOR_STR;
    9515            0 :       xsd_path += "odb.xsd";
    9516            0 :       mxml_write_attribute(writer, "xsi:noNamespaceSchemaLocation", xsd_path.c_str());
    9517              : 
    9518            0 :       status = db_save_xml_key(hDB, hKey, 0, writer);
    9519              : 
    9520            0 :       mxml_end_element(writer); // "odb"
    9521            0 :       mxml_close_file(writer);
    9522              : 
    9523            0 :       return status;
    9524            0 :    }
    9525              : #endif                          /* LOCAL_ROUTINES */
    9526              : 
    9527              :    return DB_SUCCESS;
    9528              : }
    9529              : 
    9530              : /*------------------------------------------------------------------*/
    9531              : 
    9532            0 : void json_write(char **buffer, int* buffer_size, int* buffer_end, int level, const char* s, int quoted)
    9533              : {
    9534              :    int len, remain, xlevel;
    9535              : 
    9536            0 :    len = strlen(s);
    9537            0 :    remain = *buffer_size - *buffer_end;
    9538            0 :    assert(remain >= 0);
    9539              : 
    9540            0 :    xlevel = 2*level;
    9541              : 
    9542            0 :    while (10 + xlevel + 3*len > remain) {
    9543              :       // reallocate the buffer
    9544            0 :       int new_buffer_size = 2*(*buffer_size);
    9545            0 :       if (new_buffer_size < 4*1024)
    9546            0 :          new_buffer_size = 4*1024;
    9547              :       //printf("reallocate: len %d, size %d, remain %d, allocate %d\n", len, *buffer_size, remain, new_buffer_size);
    9548            0 :       assert(new_buffer_size > *buffer_size);
    9549            0 :       *buffer = (char *)realloc(*buffer, new_buffer_size);
    9550            0 :       assert(*buffer);
    9551            0 :       *buffer_size = new_buffer_size;
    9552            0 :       remain = *buffer_size - *buffer_end;
    9553            0 :       assert(remain >= 0);
    9554              :    }
    9555              : 
    9556            0 :    if (xlevel) {
    9557              :       int i;
    9558            0 :       for (i=0; i<xlevel; i++)
    9559            0 :          (*buffer)[(*buffer_end)++] = ' ';
    9560              :    }
    9561              : 
    9562            0 :    if (!quoted) {
    9563            0 :       memcpy(*buffer + *buffer_end, s, len);
    9564            0 :       *buffer_end += len;
    9565            0 :       (*buffer)[*buffer_end] = 0; // NUL-terminate the buffer
    9566            0 :       return;
    9567              :    }
    9568              : 
    9569            0 :    char *bufptr = *buffer;
    9570            0 :    int bufend = *buffer_end;
    9571              :    
    9572            0 :    bufptr[bufend++] = '"';
    9573              : 
    9574            0 :    while (*s) {
    9575            0 :       switch (*s) {
    9576            0 :       case '\"':
    9577            0 :          bufptr[bufend++] = '\\';
    9578            0 :          bufptr[bufend++] = '\"';
    9579            0 :          s++;
    9580            0 :          break;
    9581            0 :       case '\\':
    9582            0 :          bufptr[bufend++] = '\\';
    9583            0 :          bufptr[bufend++] = '\\';
    9584            0 :          s++;
    9585            0 :          break;
    9586              : #if 0
    9587              :       case '/':
    9588              :          bufptr[bufend++] = '\\';
    9589              :          bufptr[bufend++] = '/';
    9590              :          s++;
    9591              :          break;
    9592              : #endif
    9593            0 :       case '\b':
    9594            0 :          bufptr[bufend++] = '\\';
    9595            0 :          bufptr[bufend++] = 'b';
    9596            0 :          s++;
    9597            0 :          break;
    9598            0 :       case '\f':
    9599            0 :          bufptr[bufend++] = '\\';
    9600            0 :          bufptr[bufend++] = 'f';
    9601            0 :          s++;
    9602            0 :          break;
    9603            0 :       case '\n':
    9604            0 :          bufptr[bufend++] = '\\';
    9605            0 :          bufptr[bufend++] = 'n';
    9606            0 :          s++;
    9607            0 :          break;
    9608            0 :       case '\r':
    9609            0 :          bufptr[bufend++] = '\\';
    9610            0 :          bufptr[bufend++] = 'r';
    9611            0 :          s++;
    9612            0 :          break;
    9613            0 :       case '\t':
    9614            0 :          bufptr[bufend++] = '\\';
    9615            0 :          bufptr[bufend++] = 't';
    9616            0 :          s++;
    9617            0 :          break;
    9618            0 :       default:
    9619            0 :          bufptr[bufend++] = *s++;
    9620              :       }
    9621              :    }
    9622              : 
    9623            0 :    bufptr[bufend++] = '"';
    9624            0 :    bufptr[bufend] = 0; // NUL-terminate the buffer
    9625              : 
    9626            0 :    *buffer_end = bufend;
    9627              : 
    9628            0 :    remain = *buffer_size - *buffer_end;
    9629            0 :    assert(remain > 0);
    9630              : }
    9631              : 
    9632            0 : static void json_ensure_decimal_dot(char *str)
    9633              : {
    9634              :    // sprintf "%.2e" can result in strings like "1,23e6" if
    9635              :    // the user's locale has a comma for the decimal spearator.
    9636              :    // However the JSON RFC requires a dot for the decimal.
    9637              :    // This approach of "sprintf-then-replace" is much safer than 
    9638              :    // changing the user's locale, and much faster than the C++
    9639              :    // approach of using a stringstream that we "imbue" with a C locale.
    9640            0 :    char* comma = strchr(str, ',');
    9641              : 
    9642            0 :    if (comma != nullptr) {
    9643            0 :       *comma = '.';
    9644              :    }
    9645            0 : }
    9646              : 
    9647            0 : static void json_write_data(char **buffer, int* buffer_size, int* buffer_end, int level, const KEY* key, const char* p)
    9648              : {
    9649              :    char str[256];
    9650            0 :    switch (key->type) {
    9651            0 :    case TID_UINT8:
    9652            0 :       sprintf(str, "%u", *(unsigned char*)p);
    9653            0 :       json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9654            0 :       break;
    9655            0 :    case TID_INT8:
    9656            0 :       sprintf(str, "%d", *(char*)p);
    9657            0 :       json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9658            0 :       break;
    9659            0 :    case TID_CHAR:
    9660            0 :       sprintf(str, "%c", *(char*)p);
    9661            0 :       json_write(buffer, buffer_size, buffer_end, 0, str, 1);
    9662            0 :       break;
    9663            0 :    case TID_UINT16:
    9664            0 :       sprintf(str, "\"0x%04x\"", *(WORD*)p);
    9665            0 :       json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9666            0 :       break;
    9667            0 :    case TID_INT16:
    9668            0 :       sprintf(str, "%d", *(short*)p);
    9669            0 :       json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9670            0 :       break;
    9671            0 :    case TID_UINT32:
    9672            0 :       sprintf(str, "\"0x%08x\"", *(DWORD*)p);
    9673            0 :       json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9674            0 :       break;
    9675            0 :    case TID_INT32:
    9676            0 :       sprintf(str, "%d", *(int*)p);
    9677            0 :       json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9678            0 :       break;
    9679            0 :    case TID_UINT64:
    9680              :       // encode as hex string
    9681            0 :       sprintf(str, "\"0x%016llx\"", *(UINT64*)p);
    9682            0 :       json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9683            0 :       break;
    9684            0 :    case TID_INT64:
    9685              :       // encode as decimal string
    9686            0 :       sprintf(str, "\"%lld\"", *(INT64*)p);
    9687            0 :       json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9688            0 :       break;
    9689            0 :    case TID_BOOL:
    9690            0 :       if (*(int*)p)
    9691            0 :          json_write(buffer, buffer_size, buffer_end, 0, "true", 0);
    9692              :       else
    9693            0 :          json_write(buffer, buffer_size, buffer_end, 0, "false", 0);
    9694            0 :       break;
    9695            0 :    case TID_FLOAT: {
    9696            0 :       float flt = (*(float*)p);
    9697            0 :       if (isnan(flt))
    9698            0 :          json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
    9699            0 :       else if (isinf(flt)) {
    9700            0 :          if (flt > 0)
    9701            0 :             json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
    9702              :          else
    9703            0 :             json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
    9704            0 :       } else if (flt == 0)
    9705            0 :          json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
    9706            0 :       else if (flt == (int)flt) {
    9707            0 :          sprintf(str, "%.0f", flt);
    9708            0 :          json_ensure_decimal_dot(str);
    9709            0 :          json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9710              :       } else {
    9711            0 :          sprintf(str, "%.7e", flt);
    9712            0 :          json_ensure_decimal_dot(str);
    9713            0 :          json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9714              :       }
    9715            0 :       break;
    9716              :    }
    9717            0 :    case TID_DOUBLE: {
    9718            0 :       double dbl = (*(double*)p);
    9719            0 :       if (isnan(dbl))
    9720            0 :          json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
    9721            0 :       else if (isinf(dbl)) {
    9722            0 :          if (dbl > 0)
    9723            0 :             json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
    9724              :          else
    9725            0 :             json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
    9726            0 :       } else if (dbl == 0)
    9727            0 :          json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
    9728            0 :       else if (dbl == (int)dbl) {
    9729            0 :          sprintf(str, "%.0f", dbl);
    9730            0 :          json_ensure_decimal_dot(str);
    9731            0 :          json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9732              :       } else {
    9733            0 :          sprintf(str, "%.16e", dbl);
    9734            0 :          json_ensure_decimal_dot(str);
    9735            0 :          json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9736              :       }
    9737            0 :       break;
    9738              :    }
    9739            0 :    case TID_BITFIELD:
    9740            0 :       json_write(buffer, buffer_size, buffer_end, 0, "(TID_BITFIELD value)", 1);
    9741            0 :       break;
    9742            0 :    case TID_STRING:
    9743              :       // data is already NUL terminated // p[key.item_size-1] = 0;  // make sure string is NUL terminated!
    9744            0 :       json_write(buffer, buffer_size, buffer_end, 0, p, 1);
    9745            0 :       break;
    9746            0 :    case TID_ARRAY:
    9747            0 :       json_write(buffer, buffer_size, buffer_end, 0, "(TID_ARRAY value)", 1);
    9748            0 :       break;
    9749            0 :    case TID_STRUCT:
    9750            0 :       json_write(buffer, buffer_size, buffer_end, 0, "(TID_STRUCT value)", 1);
    9751            0 :       break;
    9752            0 :    case TID_KEY:
    9753            0 :       json_write(buffer, buffer_size, buffer_end, 0, "{ }", 0);
    9754            0 :       break;
    9755            0 :    case TID_LINK:
    9756              :       // data is already NUL terminated // p[key.item_size-1] = 0;  // make sure string is NUL terminated!
    9757            0 :       json_write(buffer, buffer_size, buffer_end, 0, p, 1);
    9758            0 :       break;
    9759            0 :    default:
    9760            0 :       json_write(buffer, buffer_size, buffer_end, 0, "(TID_UNKNOWN value)", 1);
    9761              :    }
    9762            0 : }
    9763              : 
    9764            0 : static void json_write_key(HNDLE hDB, HNDLE hKey, const KEY* key, const char* link_path, char **buffer, int* buffer_size, int* buffer_end)
    9765              : {
    9766              :    char str[256]; // not used to store anything long, only numeric values like: "item_size: 100"
    9767              : 
    9768            0 :    json_write(buffer, buffer_size, buffer_end, 0, "{ ", 0);
    9769              : 
    9770            0 :    sprintf(str, "\"type\" : %d", key->type);
    9771            0 :    json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9772              : 
    9773            0 :    if (link_path) {
    9774            0 :       json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
    9775            0 :       json_write(buffer, buffer_size, buffer_end, 0, "link", 1);
    9776            0 :       json_write(buffer, buffer_size, buffer_end, 0, ": ", 0);
    9777            0 :       json_write(buffer, buffer_size, buffer_end, 0, link_path, 1);
    9778              :    }
    9779              : 
    9780            0 :    if (key->num_values > 1) {
    9781            0 :       json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
    9782              : 
    9783            0 :       sprintf(str, "\"num_values\" : %d", key->num_values);
    9784            0 :       json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9785              :    }
    9786              : 
    9787            0 :    if (key->type == TID_STRING) {
    9788            0 :       json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
    9789              : 
    9790            0 :       sprintf(str, "\"item_size\" : %d", key->item_size);
    9791            0 :       json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9792              :    }
    9793              : 
    9794            0 :    if (key->notify_count > 0) {
    9795            0 :       json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
    9796              : 
    9797            0 :       sprintf(str, "\"notify_count\" : %d", key->notify_count);
    9798            0 :       json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9799              :    }
    9800              : 
    9801            0 :    json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
    9802              : 
    9803            0 :    sprintf(str, "\"access_mode\" : %d", key->access_mode);
    9804            0 :    json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9805              : 
    9806            0 :    json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
    9807              : 
    9808            0 :    sprintf(str, "\"last_written\" : %d", key->last_written);
    9809            0 :    json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9810              : 
    9811            0 :    json_write(buffer, buffer_size, buffer_end, 0, " ", 0);
    9812              : 
    9813            0 :    json_write(buffer, buffer_size, buffer_end, 0, "}", 0);
    9814            0 : }
    9815              : 
    9816            0 : static int db_save_json_key_obsolete(HNDLE hDB, HNDLE hKey, INT level, char **buffer, int* buffer_size, int* buffer_end, int save_keys, int follow_links, int recurse)
    9817              : {
    9818              :    INT i, size, status;
    9819              :    char *data;
    9820              :    KEY key;
    9821              :    KEY link_key;
    9822              :    char link_path[MAX_ODB_PATH];
    9823            0 :    int omit_top_level_braces = 0;
    9824              : 
    9825              :    //printf("db_save_json_key: key %d, level %d, save_keys %d, follow_links %d, recurse %d\n", hKey, level, save_keys, follow_links, recurse);
    9826              : 
    9827            0 :    if (level < 0) {
    9828            0 :       level = 0;
    9829            0 :       omit_top_level_braces = 1;
    9830              :    }
    9831              : 
    9832            0 :    status = db_get_link(hDB, hKey, &key);
    9833              : 
    9834            0 :    if (status != DB_SUCCESS)
    9835            0 :       return status;
    9836              : 
    9837            0 :    link_key = key;
    9838              : 
    9839            0 :    if (key.type == TID_LINK) {
    9840            0 :       size = sizeof(link_path);
    9841            0 :       status = db_get_data(hDB, hKey, link_path, &size, TID_LINK);
    9842              : 
    9843            0 :       if (status != DB_SUCCESS)
    9844            0 :          return status;
    9845              : 
    9846            0 :       if (follow_links) {
    9847            0 :          status = db_find_key(hDB, 0, link_path, &hKey);
    9848              : 
    9849            0 :          if (status != DB_SUCCESS)
    9850            0 :             return status;
    9851              : 
    9852            0 :          status = db_get_key(hDB, hKey, &key);
    9853              : 
    9854            0 :          if (status != DB_SUCCESS)
    9855            0 :             return status;
    9856              :       }
    9857              :    }
    9858              : 
    9859              :    //printf("key [%s] link [%s], type %d, link %d\n", key.name, link_key.name, key.type, link_key.type);
    9860              : 
    9861            0 :    if (key.type == TID_KEY && (recurse || level<=0)) {
    9862            0 :       int idx = 0;
    9863            0 :       int do_close_curly_bracket = 0;
    9864              : 
    9865            0 :       if (level == 0 && !omit_top_level_braces) {
    9866            0 :          json_write(buffer, buffer_size, buffer_end, 0, "{\n", 0);
    9867            0 :          do_close_curly_bracket = 1;
    9868              :       }
    9869            0 :       else if (level > 0) {
    9870            0 :          json_write(buffer, buffer_size, buffer_end, level, link_key.name, 1);
    9871            0 :          json_write(buffer, buffer_size, buffer_end, 0, " : {\n", 0);
    9872            0 :          do_close_curly_bracket = 1;
    9873              :       }
    9874              : 
    9875            0 :       if (level > 100) {
    9876            0 :          std::string path = db_get_path(hDB, hKey);
    9877              : 
    9878            0 :          json_write(buffer, buffer_size, buffer_end, 0, "/error", 1);
    9879            0 :          json_write(buffer, buffer_size, buffer_end, 0, " : ", 0);
    9880            0 :          json_write(buffer, buffer_size, buffer_end, 0, "max nesting level exceed", 1);
    9881              : 
    9882            0 :          cm_msg(MERROR, "db_save_json_key", "max nesting level exceeded at \"%s\", check for symlink loops in this subtree", path.c_str());
    9883              : 
    9884            0 :       } else {
    9885              :          HNDLE hSubkey;
    9886              : 
    9887            0 :          for (;; idx++) {
    9888            0 :             db_enum_link(hDB, hKey, idx, &hSubkey);
    9889              : 
    9890            0 :             if (!hSubkey)
    9891            0 :                break;
    9892              : 
    9893            0 :             if (idx != 0) {
    9894            0 :                json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
    9895              :             }
    9896              : 
    9897              :             /* save subtree */
    9898            0 :             status = db_save_json_key_obsolete(hDB, hSubkey, level + 1, buffer, buffer_size, buffer_end, save_keys, follow_links, recurse);
    9899            0 :             if (status != DB_SUCCESS)
    9900            0 :                return status;
    9901              :          }
    9902              :       }
    9903              : 
    9904            0 :       if (do_close_curly_bracket) {
    9905            0 :          if (idx > 0)
    9906            0 :             json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
    9907            0 :          json_write(buffer, buffer_size, buffer_end, level, "}", 0);
    9908              :       }
    9909              : 
    9910            0 :    } else {
    9911              : 
    9912            0 :       if (save_keys && level == 0) {
    9913            0 :          json_write(buffer, buffer_size, buffer_end, 0, "{\n", 0);
    9914              :       }
    9915              : 
    9916              :       /* save key value */
    9917              : 
    9918            0 :       if (save_keys == 1) {
    9919              :          char str[NAME_LENGTH+15];
    9920            0 :          sprintf(str, "%s/key", link_key.name);
    9921              : 
    9922            0 :          json_write(buffer, buffer_size, buffer_end, level, str, 1);
    9923            0 :          json_write(buffer, buffer_size, buffer_end, 0, " : { ", 0);
    9924              : 
    9925            0 :          sprintf(str, "\"type\" : %d", key.type);
    9926            0 :          json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9927              : 
    9928            0 :          if (link_key.type == TID_LINK && follow_links) {
    9929            0 :             json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
    9930            0 :             json_write(buffer, buffer_size, buffer_end, 0, "link", 1);
    9931            0 :             json_write(buffer, buffer_size, buffer_end, 0, ": ", 0);
    9932            0 :             json_write(buffer, buffer_size, buffer_end, 0, link_path, 1);
    9933              :          }
    9934              : 
    9935            0 :          if (key.num_values > 1) {
    9936            0 :             json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
    9937              : 
    9938            0 :             sprintf(str, "\"num_values\" : %d", key.num_values);
    9939            0 :             json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9940              :          }
    9941              : 
    9942            0 :          if (key.type == TID_STRING || key.type == TID_LINK) {
    9943            0 :             json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
    9944              : 
    9945            0 :             sprintf(str, "\"item_size\" : %d", key.item_size);
    9946            0 :             json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9947              :          }
    9948              : 
    9949            0 :          if (key.notify_count > 0) {
    9950            0 :             json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
    9951              : 
    9952            0 :             sprintf(str, "\"notify_count\" : %d", key.notify_count);
    9953            0 :             json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9954              :          }
    9955              : 
    9956            0 :          json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
    9957              : 
    9958            0 :          sprintf(str, "\"access_mode\" : %d", key.access_mode);
    9959            0 :          json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9960              : 
    9961            0 :          json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
    9962              : 
    9963            0 :          sprintf(str, "\"last_written\" : %d", key.last_written);
    9964            0 :          json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9965              : 
    9966            0 :          json_write(buffer, buffer_size, buffer_end, 0, " ", 0);
    9967              : 
    9968            0 :          json_write(buffer, buffer_size, buffer_end, 0, "}", 0);
    9969              : 
    9970            0 :          json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
    9971              :       }
    9972              : 
    9973            0 :       if (save_keys == 2) {
    9974              :          char str[NAME_LENGTH+15];
    9975            0 :          sprintf(str, "%s/last_written", link_key.name);
    9976              : 
    9977            0 :          json_write(buffer, buffer_size, buffer_end, level, str, 1);
    9978              : 
    9979            0 :          sprintf(str, " : %d", key.last_written);
    9980            0 :          json_write(buffer, buffer_size, buffer_end, 0, str, 0);
    9981              : 
    9982            0 :          json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
    9983              :       }
    9984              : 
    9985            0 :       if (save_keys) {
    9986            0 :          json_write(buffer, buffer_size, buffer_end, level, link_key.name, 1);
    9987            0 :          json_write(buffer, buffer_size, buffer_end, 0, " : ", 0);
    9988              :       }
    9989              : 
    9990            0 :       if (key.num_values > 1) {
    9991            0 :          json_write(buffer, buffer_size, buffer_end, 0, "[ ", 0);
    9992              :       }
    9993              : 
    9994            0 :       size = key.total_size;
    9995            0 :       data = (char *) malloc(size);
    9996            0 :       if (data == NULL) {
    9997            0 :          cm_msg(MERROR, "db_save_json_key", "cannot allocate data buffer for %d bytes", size);
    9998            0 :          return DB_NO_MEMORY;
    9999              :       }
   10000              : 
   10001            0 :       if (key.type != TID_KEY) {
   10002            0 :          if (follow_links)
   10003            0 :             status = db_get_data(hDB, hKey, data, &size, key.type);
   10004              :          else
   10005            0 :             status = db_get_link_data(hDB, hKey, data, &size, key.type);
   10006              : 
   10007            0 :          if (status != DB_SUCCESS)
   10008            0 :             return status;
   10009              :       }
   10010              : 
   10011            0 :       for (i = 0; i < key.num_values; i++) {
   10012              :          char str[256];
   10013            0 :          char *p = data + key.item_size*i;
   10014              : 
   10015            0 :          if (i != 0)
   10016            0 :             json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
   10017              : 
   10018            0 :          switch (key.type) {
   10019            0 :          case TID_UINT8:
   10020            0 :             sprintf(str, "%u", *(unsigned char*)p);
   10021            0 :             json_write(buffer, buffer_size, buffer_end, 0, str, 0);
   10022            0 :             break;
   10023            0 :          case TID_INT8:
   10024            0 :             sprintf(str, "%d", *(char*)p);
   10025            0 :             json_write(buffer, buffer_size, buffer_end, 0, str, 0);
   10026            0 :             break;
   10027            0 :          case TID_CHAR:
   10028            0 :             sprintf(str, "%c", *(char*)p);
   10029            0 :             json_write(buffer, buffer_size, buffer_end, 0, str, 1);
   10030            0 :             break;
   10031            0 :          case TID_UINT16:
   10032            0 :             sprintf(str, "\"0x%04x\"", *(WORD*)p);
   10033            0 :             json_write(buffer, buffer_size, buffer_end, 0, str, 0);
   10034            0 :             break;
   10035            0 :          case TID_INT16:
   10036            0 :             sprintf(str, "%d", *(short*)p);
   10037            0 :             json_write(buffer, buffer_size, buffer_end, 0, str, 0);
   10038            0 :             break;
   10039            0 :          case TID_UINT32:
   10040            0 :             sprintf(str, "\"0x%08x\"", *(DWORD*)p);
   10041            0 :             json_write(buffer, buffer_size, buffer_end, 0, str, 0);
   10042            0 :             break;
   10043            0 :          case TID_INT32:
   10044            0 :             sprintf(str, "%d", *(int*)p);
   10045            0 :             json_write(buffer, buffer_size, buffer_end, 0, str, 0);
   10046            0 :             break;
   10047            0 :          case TID_UINT64:
   10048            0 :             sprintf(str, "\"0x%08llx\"", *(UINT64*)p);
   10049            0 :             json_write(buffer, buffer_size, buffer_end, 0, str, 0);
   10050            0 :             break;
   10051            0 :          case TID_INT64:
   10052            0 :             sprintf(str, "%lld", *(INT64*)p);
   10053            0 :             json_write(buffer, buffer_size, buffer_end, 0, str, 0);
   10054            0 :             break;
   10055            0 :          case TID_BOOL:
   10056            0 :             if (*(int*)p)
   10057            0 :                json_write(buffer, buffer_size, buffer_end, 0, "true", 0);
   10058              :             else
   10059            0 :                json_write(buffer, buffer_size, buffer_end, 0, "false", 0);
   10060            0 :             break;
   10061            0 :          case TID_FLOAT: {
   10062            0 :             float flt = (*(float*)p);
   10063            0 :             if (isnan(flt))
   10064            0 :                json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
   10065            0 :             else if (isinf(flt)) {
   10066            0 :                if (flt > 0)
   10067            0 :                   json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
   10068              :                else
   10069            0 :                   json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
   10070            0 :             } else if (flt == 0)
   10071            0 :                json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
   10072            0 :             else if (flt == (int)flt) {
   10073            0 :                sprintf(str, "%.0f", flt);
   10074            0 :                json_write(buffer, buffer_size, buffer_end, 0, str, 0);
   10075              :             } else {
   10076            0 :                sprintf(str, "%.7e", flt);
   10077            0 :                json_write(buffer, buffer_size, buffer_end, 0, str, 0);
   10078              :             }
   10079            0 :             break;
   10080              :          }
   10081            0 :          case TID_DOUBLE: {
   10082            0 :             double dbl = (*(double*)p);
   10083            0 :             if (isnan(dbl))
   10084            0 :                json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
   10085            0 :             else if (isinf(dbl)) {
   10086            0 :                if (dbl > 0)
   10087            0 :                   json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
   10088              :                else
   10089            0 :                   json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
   10090            0 :             } else if (dbl == 0)
   10091            0 :                json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
   10092            0 :             else if (dbl == (int)dbl) {
   10093            0 :                sprintf(str, "%.0f", dbl);
   10094            0 :                json_write(buffer, buffer_size, buffer_end, 0, str, 0);
   10095              :             } else {
   10096            0 :                sprintf(str, "%.16e", dbl);
   10097            0 :                json_write(buffer, buffer_size, buffer_end, 0, str, 0);
   10098              :             }
   10099            0 :             break;
   10100              :          }
   10101            0 :          case TID_BITFIELD:
   10102            0 :             json_write(buffer, buffer_size, buffer_end, 0, "(TID_BITFIELD value)", 1);
   10103            0 :             break;
   10104            0 :          case TID_STRING:
   10105            0 :             p[key.item_size-1] = 0;  // make sure string is NUL terminated!
   10106            0 :             json_write(buffer, buffer_size, buffer_end, 0, p, 1);
   10107            0 :             break;
   10108            0 :          case TID_ARRAY:
   10109            0 :             json_write(buffer, buffer_size, buffer_end, 0, "(TID_ARRAY value)", 1);
   10110            0 :             break;
   10111            0 :          case TID_STRUCT:
   10112            0 :             json_write(buffer, buffer_size, buffer_end, 0, "(TID_STRUCT value)", 1);
   10113            0 :             break;
   10114            0 :          case TID_KEY:
   10115            0 :             json_write(buffer, buffer_size, buffer_end, 0, "{ }", 0);
   10116            0 :             break;
   10117            0 :          case TID_LINK:
   10118            0 :             p[key.item_size-1] = 0;  // make sure string is NUL terminated!
   10119            0 :             json_write(buffer, buffer_size, buffer_end, 0, p, 1);
   10120            0 :             break;
   10121            0 :          default:
   10122            0 :             json_write(buffer, buffer_size, buffer_end, 0, "(TID_UNKNOWN value)", 1);
   10123              :          }
   10124              : 
   10125              :       }
   10126              : 
   10127            0 :       if (key.num_values > 1) {
   10128            0 :          json_write(buffer, buffer_size, buffer_end, 0, " ]", 0);
   10129              :       } else {
   10130            0 :          json_write(buffer, buffer_size, buffer_end, 0, "", 0);
   10131              :       }
   10132              : 
   10133            0 :       free(data);
   10134            0 :       data = NULL;
   10135              : 
   10136            0 :       if (save_keys && level == 0) {
   10137            0 :          json_write(buffer, buffer_size, buffer_end, 0, "\n}", 0);
   10138              :       }
   10139              :    }
   10140              : 
   10141            0 :    return DB_SUCCESS;
   10142              : }
   10143              : 
   10144              : /********************************************************************/
   10145              : /**
   10146              : Copy an ODB array in JSON format to a buffer
   10147              : 
   10148              : @param hDB          ODB handle obtained via cm_get_experiment_database().
   10149              : @param hKey Handle for key
   10150              : @param buffer returns pointer to ASCII buffer with ODB contents
   10151              : @param buffer_size returns size of ASCII buffer
   10152              : @param buffer_end returns number of bytes contained in buffer
   10153              : @return DB_SUCCESS, DB_NO_MEMORY
   10154              : */
   10155            0 : INT db_copy_json_array(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end)
   10156              : {
   10157              :    int size, asize;
   10158              :    int status;
   10159              :    char* data;
   10160              :    int i;
   10161              :    KEY key;
   10162              : 
   10163            0 :    status = db_get_key(hDB, hKey, &key);
   10164            0 :    if (status != DB_SUCCESS)
   10165            0 :       return status;
   10166              : 
   10167            0 :    assert(key.type != TID_KEY);
   10168              : 
   10169            0 :    if (key.num_values > 1) {
   10170            0 :       json_write(buffer, buffer_size, buffer_end, 0, "[ ", 0);
   10171              :    }
   10172              : 
   10173            0 :    size = key.total_size;
   10174              : 
   10175            0 :    asize = size;
   10176            0 :    if (asize < 1024)
   10177            0 :       asize = 1024;
   10178              :    
   10179            0 :    data = (char *) malloc(asize);
   10180            0 :    if (data == NULL) {
   10181            0 :       cm_msg(MERROR, "db_save_json_key_data", "cannot allocate data buffer for %d bytes", asize);
   10182            0 :       return DB_NO_MEMORY;
   10183              :    }
   10184              : 
   10185            0 :    data[0] = 0; // protect against TID_STRING that has key.total_size == 0.
   10186              : 
   10187            0 :    status = db_get_data(hDB, hKey, data, &size, key.type);
   10188            0 :    if (status != DB_SUCCESS) {
   10189            0 :       free(data);
   10190            0 :       return status;
   10191              :    }
   10192              : 
   10193            0 :    for (i = 0; i < key.num_values; i++) {
   10194            0 :       char *p = data + key.item_size*i;
   10195              : 
   10196            0 :       if (i != 0)
   10197            0 :          json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
   10198              :       
   10199            0 :       json_write_data(buffer, buffer_size, buffer_end, 0, &key, p);
   10200              :    }
   10201              :    
   10202            0 :    if (key.num_values > 1) {
   10203            0 :       json_write(buffer, buffer_size, buffer_end, 0, " ]", 0);
   10204              :    }
   10205              :    
   10206            0 :    free(data);
   10207            0 :    data = NULL;
   10208              : 
   10209            0 :    return DB_SUCCESS;
   10210              : }
   10211              : 
   10212              : /********************************************************************/
   10213              : /**
   10214              : Copy an ODB array element in JSON format to a buffer
   10215              : 
   10216              : @param hDB          ODB handle obtained via cm_get_experiment_database().
   10217              : @param hKey Handle for key
   10218              : @param index Array index
   10219              : @param buffer returns pointer to ASCII buffer with ODB contents
   10220              : @param buffer_size returns size of ASCII buffer
   10221              : @param buffer_end returns number of bytes contained in buffer
   10222              : @return DB_SUCCESS, DB_NO_MEMORY
   10223              : */
   10224            0 : INT db_copy_json_index(HNDLE hDB, HNDLE hKey, int index, char **buffer, int* buffer_size, int* buffer_end)
   10225              : {
   10226              :    int status;
   10227              :    KEY key;
   10228              : 
   10229            0 :    status = db_get_key(hDB, hKey, &key);
   10230              : 
   10231            0 :    if (status != DB_SUCCESS)
   10232            0 :       return status;
   10233              : 
   10234            0 :    int size = key.item_size;
   10235            0 :    char* data = (char*)malloc(size + 1); // extra byte for string NUL termination
   10236            0 :    assert(data != NULL);
   10237              : 
   10238            0 :    status = db_get_data_index(hDB, hKey, data, &size, index, key.type);
   10239              : 
   10240            0 :    if (status != DB_SUCCESS) {
   10241            0 :       free(data);
   10242            0 :       return status;
   10243              :    }
   10244              : 
   10245            0 :    assert(size <= key.item_size);
   10246            0 :    data[key.item_size] = 0; // make sure data is NUL terminated, in case of strings.
   10247              : 
   10248            0 :    json_write_data(buffer, buffer_size, buffer_end, 0, &key, data);
   10249              : 
   10250            0 :    free(data);
   10251              : 
   10252            0 :    return DB_SUCCESS;
   10253              : }
   10254              : 
   10255            0 : static int json_write_bare_key(HNDLE hDB, HNDLE hLink, const KEY& link, char **buffer, int *buffer_size, int *buffer_end, int level, int flags, time_t timestamp, bool need_comma)
   10256              : {
   10257              :    int status;
   10258              :    char link_buf[MAX_ODB_PATH]; // link_path points to link_buf
   10259            0 :    char* link_path = NULL;
   10260              : 
   10261            0 :    HNDLE hLinkTarget = hLink;
   10262              : 
   10263            0 :    if (link.type == TID_LINK) {
   10264            0 :       int size = sizeof(link_buf);
   10265            0 :       status = db_get_link_data(hDB, hLink, link_buf, &size, TID_LINK);
   10266            0 :       if (status != DB_SUCCESS)
   10267            0 :          return status;
   10268              :       
   10269              :       //printf("link size %d, len %d, data [%s]\n", size, (int)strlen(link_buf), link_buf);
   10270              :       
   10271            0 :       if ((size > 0) && (strlen(link_buf) > 0)) {
   10272            0 :          link_path = link_buf;
   10273              :          
   10274              :          // resolve the link, unless it is a link to an array element
   10275            0 :          if ((flags & JSFLAG_FOLLOW_LINKS) && strchr(link_path, '[') == NULL) {
   10276            0 :             status = db_find_key(hDB, 0, link_path, &hLinkTarget);
   10277            0 :             if (status != DB_SUCCESS) {
   10278              :                // dangling link to nowhere
   10279            0 :                   hLinkTarget = hLink;
   10280              :             }
   10281              :          }
   10282              :       }
   10283              :    }
   10284              :    
   10285              :    KEY link_target;
   10286            0 :    status = db_get_key(hDB, hLinkTarget, &link_target);
   10287            0 :    if (status != DB_SUCCESS)
   10288            0 :       return status;
   10289              :    
   10290            0 :    if (flags & JSFLAG_OMIT_OLD) {
   10291            0 :       if (link_target.last_written) {
   10292            0 :          if (link_target.last_written < timestamp) {
   10293              :             // omit old data
   10294            0 :             return DB_SUCCESS;
   10295              :          }
   10296              :       }
   10297              :    }
   10298              :    
   10299            0 :    if (need_comma) {
   10300            0 :       json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
   10301              :    } else {
   10302            0 :       json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
   10303              :    }
   10304              :    
   10305              :    char link_name[MAX_ODB_PATH];
   10306              : 
   10307            0 :    mstrlcpy(link_name, link.name, sizeof(link_name));
   10308              :    
   10309            0 :    if (flags & JSFLAG_LOWERCASE) {
   10310            0 :       for (int i=0; (i<(int)sizeof(link.name)) && link.name[i]; i++)
   10311            0 :          link_name[i] = tolower(link.name[i]);
   10312              :    }
   10313              :    
   10314            0 :    if ((flags & JSFLAG_LOWERCASE) && !(flags & JSFLAG_OMIT_NAMES)) {
   10315              :       char buf[MAX_ODB_PATH];
   10316            0 :       mstrlcpy(buf, link_name, sizeof(buf));
   10317            0 :       mstrlcat(buf, "/name", sizeof(buf));
   10318            0 :       json_write(buffer, buffer_size, buffer_end, level, buf, 1);
   10319            0 :       json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
   10320            0 :       json_write(buffer, buffer_size, buffer_end, 0, link.name, 1);
   10321            0 :       json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
   10322              :    }
   10323              :    
   10324            0 :    if (link.type != TID_KEY && (flags & JSFLAG_SAVE_KEYS)) {
   10325              :       char buf[MAX_ODB_PATH];
   10326            0 :       mstrlcpy(buf, link_name, sizeof(buf));
   10327            0 :       mstrlcat(buf, "/key", sizeof(buf));
   10328            0 :       json_write(buffer, buffer_size, buffer_end, level, buf, 1);
   10329            0 :       json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
   10330            0 :       json_write_key(hDB, hLink, &link_target, link_path, buffer, buffer_size, buffer_end);
   10331            0 :       json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
   10332            0 :    } else if ((link_target.type != TID_KEY) && !(flags & JSFLAG_OMIT_LAST_WRITTEN)) {
   10333              :       char buf[MAX_ODB_PATH];
   10334            0 :       mstrlcpy(buf, link_name, sizeof(buf));
   10335            0 :       mstrlcat(buf, "/last_written", sizeof(buf));
   10336            0 :       json_write(buffer, buffer_size, buffer_end, level, buf, 1);
   10337            0 :       json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
   10338            0 :       sprintf(buf, "%d", link_target.last_written);
   10339            0 :       json_write(buffer, buffer_size, buffer_end, 0, buf, 0);
   10340            0 :       json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
   10341              :    }
   10342              :    
   10343            0 :    json_write(buffer, buffer_size, buffer_end, level, link_name, 1);
   10344            0 :    json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
   10345              :    
   10346            0 :    if (link_target.type == TID_KEY && !(flags & JSFLAG_RECURSE)) {
   10347            0 :       json_write(buffer, buffer_size, buffer_end, 0, "{ }" , 0);
   10348              :    } else {
   10349            0 :       status = json_write_anything(hDB, hLinkTarget, buffer, buffer_size, buffer_end, level, 0, flags, timestamp);
   10350            0 :       if (status != DB_SUCCESS)
   10351            0 :          return status;
   10352              :    }
   10353              : 
   10354            0 :    return DB_SUCCESS;
   10355              : }
   10356              :    
   10357            0 : int json_write_bare_subdir(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end, int level, int flags, time_t timestamp)
   10358              : {
   10359            0 :    if (level > MAX_ODB_PATH/2) {
   10360              :       // max nesting level is limited by max odb path, where each subdirectory takes
   10361              :       // at least 2 bytes - 1 byte directory name and 1 byte for "/"
   10362            0 :       cm_msg(MERROR, "json_write_bare_subdir", "Max ODB subdirectory nesting level exceeded %d", level);
   10363            0 :       return DB_TRUNCATED;
   10364              :    }
   10365              : 
   10366            0 :    for (int i=0; ; i++) {
   10367              :       int status;
   10368              : 
   10369              :       HNDLE hLink;
   10370            0 :       status = db_enum_link(hDB, hKey, i, &hLink);
   10371            0 :       if (status != DB_SUCCESS && !hLink)
   10372            0 :          break;
   10373              : 
   10374              :       KEY link;
   10375            0 :       status = db_get_link(hDB, hLink, &link);
   10376            0 :       if (status != DB_SUCCESS)
   10377            0 :          return status;
   10378              : 
   10379            0 :       bool need_comma = (i!=0);
   10380              : 
   10381            0 :       status = json_write_bare_key(hDB, hLink, link, buffer, buffer_size, buffer_end, level, flags, timestamp, need_comma);
   10382            0 :       if (status != DB_SUCCESS)
   10383            0 :          return status;
   10384            0 :    }
   10385              : 
   10386            0 :    return DB_SUCCESS;
   10387              : }
   10388              : 
   10389            0 : int EXPRT json_write_anything(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end, int level, int must_be_subdir, int flags, time_t timestamp)
   10390              : {
   10391              :    int status;
   10392              :    KEY key;
   10393              : 
   10394            0 :    status = db_get_key(hDB, hKey, &key);
   10395            0 :    if (status != DB_SUCCESS)
   10396            0 :       return status;
   10397              : 
   10398            0 :    if (key.type == TID_KEY) {
   10399              : 
   10400            0 :       json_write(buffer, buffer_size, buffer_end, 0, "{", 0);
   10401              : 
   10402            0 :       status = json_write_bare_subdir(hDB, hKey, buffer, buffer_size, buffer_end, level+1, flags, timestamp);
   10403            0 :       if (status != DB_SUCCESS)
   10404            0 :          return status;
   10405              : 
   10406            0 :       json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
   10407            0 :       json_write(buffer, buffer_size, buffer_end, level, "}", 0);
   10408              : 
   10409            0 :    } else if (must_be_subdir) {
   10410            0 :       json_write(buffer, buffer_size, buffer_end, 0, "{", 0);
   10411            0 :       status = json_write_bare_key(hDB, hKey, key, buffer, buffer_size, buffer_end, level+1, flags, timestamp, false);
   10412            0 :       if (status != DB_SUCCESS)
   10413            0 :          return status;
   10414            0 :       json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
   10415            0 :       json_write(buffer, buffer_size, buffer_end, level, "}", 0);
   10416              :    } else {
   10417            0 :       status = db_copy_json_array(hDB, hKey, buffer, buffer_size, buffer_end);
   10418              : 
   10419            0 :       if (status != DB_SUCCESS)
   10420            0 :          return status;
   10421              :    }
   10422              : 
   10423            0 :    return DB_SUCCESS;
   10424              : }
   10425              : 
   10426            0 : INT db_copy_json_ls(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end)
   10427              : {
   10428              :    int status;
   10429            0 :    bool unlock = false;
   10430              : 
   10431            0 :    if (!rpc_is_remote()) {
   10432            0 :       status = db_lock_database(hDB);
   10433            0 :       if (status != DB_SUCCESS) {
   10434            0 :          return status;
   10435              :       }
   10436            0 :       unlock = true;
   10437              :    }
   10438              : 
   10439            0 :    status = json_write_anything(hDB, hKey, buffer, buffer_size, buffer_end, JS_LEVEL_0, JS_MUST_BE_SUBDIR, JSFLAG_SAVE_KEYS|JSFLAG_FOLLOW_LINKS, 0);
   10440              : 
   10441            0 :    if (unlock) {
   10442            0 :       db_unlock_database(hDB);
   10443              :    }
   10444              : 
   10445            0 :    return status;
   10446              : }
   10447              : 
   10448            0 : INT db_copy_json_values(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end, int omit_names, int omit_last_written, time_t omit_old_timestamp, int preserve_case)
   10449              : {
   10450              :    int status;
   10451            0 :    int flags = JSFLAG_FOLLOW_LINKS|JSFLAG_RECURSE;
   10452            0 :    if (omit_names)
   10453            0 :       flags |= JSFLAG_OMIT_NAMES;
   10454            0 :    if (omit_last_written)
   10455            0 :       flags |= JSFLAG_OMIT_LAST_WRITTEN;
   10456            0 :    if (omit_old_timestamp)
   10457            0 :       flags |= JSFLAG_OMIT_OLD;
   10458            0 :    if (!preserve_case)
   10459            0 :       flags |= JSFLAG_LOWERCASE;
   10460              : 
   10461            0 :    bool unlock = false;
   10462              : 
   10463            0 :    if (!rpc_is_remote()) {
   10464            0 :       status = db_lock_database(hDB);
   10465            0 :       if (status != DB_SUCCESS) {
   10466            0 :          return status;
   10467              :       }
   10468            0 :       unlock = true;
   10469              :    }
   10470              : 
   10471            0 :    status = json_write_anything(hDB, hKey, buffer, buffer_size, buffer_end, JS_LEVEL_0, 0, flags, omit_old_timestamp);
   10472              : 
   10473            0 :    if (unlock) {
   10474            0 :       db_unlock_database(hDB);
   10475              :    }
   10476              :    
   10477            0 :    return status;
   10478              : }
   10479              : 
   10480            0 : INT db_copy_json_save(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end)
   10481              : {
   10482              :    int status;
   10483            0 :    bool unlock = false;
   10484              : 
   10485            0 :    if (!rpc_is_remote()) {
   10486            0 :       status = db_lock_database(hDB);
   10487            0 :       if (status != DB_SUCCESS) {
   10488            0 :          return status;
   10489              :       }
   10490            0 :       unlock = true;
   10491              :    }
   10492              : 
   10493            0 :    status = json_write_anything(hDB, hKey, buffer, buffer_size, buffer_end, JS_LEVEL_0, JS_MUST_BE_SUBDIR, JSFLAG_SAVE_KEYS|JSFLAG_RECURSE, 0);
   10494              : 
   10495            0 :    if (unlock) {
   10496            0 :       db_unlock_database(hDB);
   10497              :    }
   10498              :    
   10499            0 :    return status;
   10500              : }
   10501              : 
   10502              : /********************************************************************/
   10503              : /**
   10504              : Copy an ODB subtree in JSON format to a buffer
   10505              : 
   10506              : @param hDB          ODB handle obtained via cm_get_experiment_database().
   10507              : @param hKey Handle for key where search starts, zero for root.
   10508              : @param buffer returns pointer to ASCII buffer with ODB contents
   10509              : @param buffer_size returns size of ASCII buffer
   10510              : @param buffer_end returns number of bytes contained in buffer
   10511              : @return DB_SUCCESS, DB_NO_MEMORY
   10512              : */
   10513            0 : INT db_copy_json_obsolete(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end, int save_keys, int follow_links, int recurse)
   10514              : {
   10515            0 :    db_save_json_key_obsolete(hDB, hKey, 0, buffer, buffer_size, buffer_end, save_keys, follow_links, recurse);
   10516            0 :    json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
   10517            0 :    return DB_SUCCESS;
   10518              : }
   10519              : 
   10520              : /********************************************************************/
   10521              : /**
   10522              : Save a branch of a database to an .json file
   10523              : 
   10524              : This function is used by the ODBEdit command save to write the contents
   10525              : of the ODB into a JSON file. Data of the whole ODB can
   10526              : be saved (hkey equal zero) or only a sub-tree.
   10527              : @param hDB          ODB handle obtained via cm_get_experiment_database().
   10528              : @param hKey Handle for key where search starts, zero for root.
   10529              : @param filename Filename of .json file.
   10530              : @return DB_SUCCESS, DB_FILE_ERROR
   10531              : */
   10532            0 : INT db_save_json(HNDLE hDB, HNDLE hKey, const char *filename, int flags)
   10533              : {
   10534              :    INT status;
   10535              :    
   10536              :    /* open file */
   10537            0 :    FILE *fp = fopen(filename, "w");
   10538            0 :    if (fp == NULL) {
   10539            0 :       cm_msg(MERROR, "db_save_json", "Cannot open file \"%s\", fopen() errno %d (%s)", filename, errno, strerror(errno));
   10540            0 :       return DB_FILE_ERROR;
   10541              :    }
   10542              :    
   10543            0 :    bool unlock = false;
   10544              :    
   10545            0 :    if (!rpc_is_remote()) {
   10546            0 :       status = db_lock_database(hDB);
   10547            0 :       if (status != DB_SUCCESS) {
   10548            0 :          fclose(fp);
   10549            0 :          return status;
   10550              :       }
   10551            0 :       unlock = true;
   10552              :    }
   10553              :    
   10554            0 :    std::string path = db_get_path(hDB, hKey);
   10555              : 
   10556            0 :    bool emptySubdir = false;
   10557              :    HNDLE hSubKey;
   10558            0 :    status = db_enum_link(hDB, hKey, 0, &hSubKey);
   10559            0 :    if (status == DB_NO_MORE_SUBKEYS)
   10560            0 :       emptySubdir = true;
   10561              : 
   10562            0 :    char* buffer = NULL;
   10563            0 :    int buffer_size = 0;
   10564            0 :    int buffer_end = 0;
   10565              :    
   10566            0 :    json_write(&buffer, &buffer_size, &buffer_end, 0, "{\n", 0);
   10567              :    
   10568            0 :    json_write(&buffer, &buffer_size, &buffer_end, 1, "/MIDAS version", 1);
   10569            0 :    json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
   10570            0 :    json_write(&buffer, &buffer_size, &buffer_end, 0, MIDAS_VERSION, 1);
   10571            0 :    json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
   10572              :    
   10573            0 :    json_write(&buffer, &buffer_size, &buffer_end, 1, "/MIDAS git revision", 1);
   10574            0 :    json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
   10575            0 :    json_write(&buffer, &buffer_size, &buffer_end, 0, GIT_REVISION, 1);
   10576            0 :    json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
   10577              :    
   10578            0 :    json_write(&buffer, &buffer_size, &buffer_end, 1, "/filename", 1);
   10579            0 :    json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
   10580            0 :    json_write(&buffer, &buffer_size, &buffer_end, 0, filename, 1);
   10581            0 :    json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
   10582              :    
   10583            0 :    json_write(&buffer, &buffer_size, &buffer_end, 1, "/ODB path", 1);
   10584            0 :    json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
   10585            0 :    json_write(&buffer, &buffer_size, &buffer_end, 0, path.c_str(), 1);
   10586              : 
   10587            0 :    if (emptySubdir)
   10588            0 :       json_write(&buffer, &buffer_size, &buffer_end, 0, "", 0);
   10589              :    else
   10590            0 :       json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
   10591              : 
   10592              :    //status = db_save_json_key_obsolete(hDB, hKey, -1, &buffer, &buffer_size, &buffer_end, 1, 0, 1);
   10593            0 :    status = json_write_bare_subdir(hDB, hKey, &buffer, &buffer_size, &buffer_end, JS_LEVEL_1, flags, 0);
   10594              :    
   10595            0 :    json_write(&buffer, &buffer_size, &buffer_end, 0, "\n}\n", 0);
   10596              : 
   10597            0 :    if (unlock) {
   10598            0 :       db_unlock_database(hDB);
   10599              :    }
   10600              :    
   10601            0 :    if (status == DB_SUCCESS) {
   10602            0 :       if (buffer) {
   10603            0 :          size_t wr = fwrite(buffer, 1, buffer_end, fp);
   10604            0 :          if (wr != (size_t)buffer_end) {
   10605            0 :             cm_msg(MERROR, "db_save_json", "Cannot write to file \"%s\", fwrite() errno %d (%s)", filename, errno, strerror(errno));
   10606            0 :             free(buffer);
   10607            0 :             fclose(fp);
   10608            0 :             return DB_FILE_ERROR;
   10609              :          }
   10610              :       }
   10611              :    }
   10612              :    
   10613            0 :    if (buffer)
   10614            0 :       free(buffer);
   10615              :    
   10616            0 :    fclose(fp);
   10617              : 
   10618            0 :    return DB_SUCCESS;
   10619            0 : }
   10620              : 
   10621              : /********************************************************************/
   10622              : /**
   10623              : Save a branch of a database to a C structure .H file
   10624              : @param hDB          ODB handle obtained via cm_get_experiment_database().
   10625              : @param hKey Handle for key where search starts, zero for root.
   10626              : @param file_name Filename of .ODB file.
   10627              : @param struct_name Name of structure. If struct_name == NULL,
   10628              :                    the name of the key is used.
   10629              : @param append      If TRUE, append to end of existing file
   10630              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_FILE_ERROR
   10631              : */
   10632            0 : INT db_save_struct(HNDLE hDB, HNDLE hKey, const char *file_name, const char *struct_name, BOOL append)
   10633              : {
   10634              :    KEY key;
   10635              :    char str[100], line[10+100];
   10636              :    INT status, i, fh;
   10637              :    int wr, size;
   10638              : 
   10639              :    /* open file */
   10640            0 :    fh = open(file_name, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0644);
   10641              : 
   10642            0 :    if (fh == -1) {
   10643            0 :       cm_msg(MERROR, "db_save_struct", "Cannot open file\"%s\"", file_name);
   10644            0 :       return DB_FILE_ERROR;
   10645              :    }
   10646              : 
   10647            0 :    status = db_get_key(hDB, hKey, &key);
   10648            0 :    if (status != DB_SUCCESS) {
   10649            0 :       cm_msg(MERROR, "db_save_struct", "cannot find key");
   10650            0 :       return DB_INVALID_HANDLE;
   10651              :    }
   10652              : 
   10653            0 :    sprintf(line, "typedef struct {\n");
   10654              : 
   10655            0 :    size = strlen(line);
   10656            0 :    wr = write(fh, line, size);
   10657            0 :    if (wr != size) {
   10658            0 :       cm_msg(MERROR, "db_save_struct", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
   10659            0 :       close(fh);
   10660            0 :       return DB_FILE_ERROR;
   10661              :    }
   10662              : 
   10663            0 :    db_save_tree_struct(hDB, hKey, fh, 0);
   10664              : 
   10665            0 :    if (struct_name && struct_name[0])
   10666            0 :       mstrlcpy(str, struct_name, sizeof(str));
   10667              :    else
   10668            0 :       mstrlcpy(str, key.name, sizeof(str));
   10669              : 
   10670            0 :    name2c(str);
   10671            0 :    for (i = 0; i < (int) strlen(str); i++)
   10672            0 :       str[i] = (char) toupper(str[i]);
   10673              : 
   10674            0 :    sprintf(line, "} %s;\n\n", str);
   10675              : 
   10676            0 :    size = strlen(line);
   10677            0 :    wr = write(fh, line, size);
   10678            0 :    if (wr != size) {
   10679            0 :       cm_msg(MERROR, "db_save_struct", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
   10680            0 :       close(fh);
   10681            0 :       return DB_FILE_ERROR;
   10682              :    }
   10683              : 
   10684            0 :    close(fh);
   10685              : 
   10686            0 :    return DB_SUCCESS;
   10687              : }
   10688              : 
   10689              : /**dox***************************************************************/
   10690              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
   10691              : 
   10692              : /*------------------------------------------------------------------*/
   10693            0 : INT db_save_string(HNDLE hDB, HNDLE hKey, const char *file_name, const char *string_name, BOOL append)
   10694              : /********************************************************************\
   10695              : 
   10696              :   Routine: db_save_string
   10697              : 
   10698              :   Purpose: Save a branch of a database as a string which can be used
   10699              :            by db_create_record.
   10700              : 
   10701              :   Input:
   10702              :     HNDLE hDB               Handle to the database
   10703              :     HNDLE hKey              Handle of key to start, 0 for root
   10704              :     int   fh                File handle to write to
   10705              :     char  string_name       Name of string. If struct_name == NULL,
   10706              :                             the name of the key is used.
   10707              : 
   10708              :   Output:
   10709              :     none
   10710              : 
   10711              :   Function value:
   10712              :     DB_SUCCESS              Successful completion
   10713              :     DB_INVALID_HANDLE       Database handle is invalid
   10714              : 
   10715              : \********************************************************************/
   10716              : {
   10717              :    KEY key;
   10718              :    char str[256], line[50+256];
   10719              :    INT status, i, size, fh, buffer_size;
   10720            0 :    char *buffer = NULL, *pc;
   10721              :    int wr;
   10722              : 
   10723              : 
   10724              :    /* open file */
   10725            0 :    fh = open(file_name, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0644);
   10726              : 
   10727            0 :    if (fh == -1) {
   10728            0 :       cm_msg(MERROR, "db_save_string", "Cannot open file\"%s\"", file_name);
   10729            0 :       return DB_FILE_ERROR;
   10730              :    }
   10731              : 
   10732            0 :    status = db_get_key(hDB, hKey, &key);
   10733            0 :    if (status != DB_SUCCESS) {
   10734            0 :       cm_msg(MERROR, "db_save_string", "cannot find key");
   10735            0 :       return DB_INVALID_HANDLE;
   10736              :    }
   10737              : 
   10738            0 :    if (string_name && string_name[0])
   10739            0 :       strcpy(str, string_name);
   10740              :    else
   10741            0 :       strcpy(str, key.name);
   10742              : 
   10743            0 :    name2c(str);
   10744            0 :    for (i = 0; i < (int) strlen(str); i++)
   10745            0 :       str[i] = (char) toupper(str[i]);
   10746              : 
   10747            0 :    sprintf(line, "#define %s(_name) const char *_name[] = {\\\n", str);
   10748            0 :    size = strlen(line);
   10749            0 :    wr = write(fh, line, size);
   10750            0 :    if (wr != size) {
   10751            0 :       cm_msg(MERROR, "db_save", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
   10752            0 :       close(fh);
   10753            0 :       if (buffer)
   10754            0 :          free(buffer);
   10755            0 :       return DB_FILE_ERROR;
   10756              :    }
   10757              : 
   10758            0 :    buffer_size = 10000;
   10759              :    do {
   10760            0 :       buffer = (char *) malloc(buffer_size);
   10761            0 :       if (buffer == NULL) {
   10762            0 :          cm_msg(MERROR, "db_save", "cannot allocate ODB dump buffer");
   10763            0 :          break;
   10764              :       }
   10765              : 
   10766            0 :       size = buffer_size;
   10767            0 :       status = db_copy(hDB, hKey, buffer, &size, "");
   10768            0 :       if (status != DB_TRUNCATED)
   10769            0 :          break;
   10770              : 
   10771              :       /* increase buffer size if truncated */
   10772            0 :       free(buffer);
   10773            0 :       buffer = NULL;
   10774            0 :       buffer_size *= 2;
   10775              :    } while (1);
   10776              : 
   10777              : 
   10778            0 :    pc = buffer;
   10779              : 
   10780              :    do {
   10781            0 :       i = 0;
   10782            0 :       line[i++] = '"';
   10783            0 :       while (*pc != '\n' && *pc != 0) {
   10784            0 :          if (*pc == '\"' || *pc == '\'')
   10785            0 :             line[i++] = '\\';
   10786            0 :          line[i++] = *pc++;
   10787              :       }
   10788            0 :       strcpy(&line[i], "\",\\\n");
   10789            0 :       if (i > 0) {
   10790            0 :          size = strlen(line);
   10791            0 :          wr = write(fh, line, size);
   10792            0 :          if (wr != size) {
   10793            0 :             cm_msg(MERROR, "db_save", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
   10794            0 :             close(fh);
   10795            0 :             if (buffer)
   10796            0 :                free(buffer);
   10797            0 :             return DB_FILE_ERROR;
   10798              :          }
   10799              :       }
   10800              : 
   10801            0 :       if (*pc == '\n')
   10802            0 :          pc++;
   10803              : 
   10804            0 :    } while (*pc);
   10805              : 
   10806            0 :    sprintf(line, "NULL }\n\n");
   10807            0 :    size = strlen(line);
   10808            0 :    wr = write(fh, line, size);
   10809            0 :    if (wr != size) {
   10810            0 :       cm_msg(MERROR, "db_save", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
   10811            0 :       close(fh);
   10812            0 :       if (buffer)
   10813            0 :          free(buffer);
   10814            0 :       return DB_FILE_ERROR;
   10815              :    }
   10816              : 
   10817            0 :    close(fh);
   10818            0 :    free(buffer);
   10819              : 
   10820            0 :    return DB_SUCCESS;
   10821              : }
   10822              : 
   10823              : /**dox***************************************************************/
   10824              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
   10825              : 
   10826              : /********************************************************************/
   10827              : /**
   10828              : Convert a database value to a string according to its type.
   10829              : 
   10830              : This function is a convenient way to convert a binary ODB value into a
   10831              : string depending on its type if is not known at compile time. If it is known, the
   10832              : normal sprintf() function can be used.
   10833              : \code
   10834              : ...
   10835              :   for (j=0 ; j<key.num_values ; j++)
   10836              :   {
   10837              :     db_sprintf(pbuf, pdata, key.item_size, j, key.type);
   10838              :     strcat(pbuf, "\n");
   10839              :   }
   10840              :   ...
   10841              : \endcode
   10842              : @param string output ASCII string of data. must be at least MAX_STRING_LENGTH bytes long.
   10843              : @param data Value data.
   10844              : @param data_size Size of single data element.
   10845              : @param idx Index for array data.
   10846              : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
   10847              : @return DB_SUCCESS
   10848              : */
   10849            0 : INT db_sprintf(char *string, const void *data, INT data_size, INT idx, DWORD type)
   10850              : {
   10851            0 :    if (data_size == 0)
   10852            0 :       sprintf(string, "<NULL>");
   10853              :    else
   10854            0 :       switch (type) {
   10855            0 :       case TID_UINT8:
   10856            0 :          sprintf(string, "%d", *(((BYTE *) data) + idx));
   10857            0 :          break;
   10858            0 :       case TID_INT8:
   10859            0 :          sprintf(string, "%d", *(((char *) data) + idx));
   10860            0 :          break;
   10861            0 :       case TID_CHAR:
   10862            0 :          sprintf(string, "%c", *(((char *) data) + idx));
   10863            0 :          break;
   10864            0 :       case TID_UINT16:
   10865            0 :          sprintf(string, "%u", *(((WORD *) data) + idx));
   10866            0 :          break;
   10867            0 :       case TID_INT16:
   10868            0 :          sprintf(string, "%d", *(((short *) data) + idx));
   10869            0 :          break;
   10870            0 :       case TID_UINT32:
   10871            0 :          sprintf(string, "%u", *(((DWORD *) data) + idx));
   10872            0 :          break;
   10873            0 :       case TID_INT32:
   10874            0 :          sprintf(string, "%d", *(((INT *) data) + idx));
   10875            0 :          break;
   10876            0 :       case TID_UINT64:
   10877            0 :          sprintf(string, "%llu", *(((UINT64 *) data) + idx));
   10878            0 :          break;
   10879            0 :       case TID_INT64:
   10880            0 :          sprintf(string, "%lld", *(((INT64 *) data) + idx));
   10881            0 :          break;
   10882            0 :       case TID_BOOL:
   10883            0 :          sprintf(string, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
   10884            0 :          break;
   10885            0 :       case TID_FLOAT:
   10886            0 :          if (ss_isnan(*(((float *) data) + idx)))
   10887            0 :             sprintf(string, "NAN");
   10888              :          else
   10889            0 :             sprintf(string, "%.7g", *(((float *) data) + idx));
   10890            0 :          break;
   10891            0 :       case TID_DOUBLE:
   10892            0 :          if (ss_isnan(*(((double *) data) + idx)))
   10893            0 :             sprintf(string, "NAN");
   10894              :          else
   10895            0 :             sprintf(string, "%.16lg", *(((double *) data) + idx));
   10896            0 :          break;
   10897            0 :       case TID_BITFIELD:
   10898            0 :          sprintf(string, "%u", *(((DWORD *) data) + idx));
   10899            0 :          break;
   10900            0 :       case TID_STRING:
   10901              :       case TID_LINK:
   10902            0 :          mstrlcpy(string, ((char *) data) + data_size * idx, MAX_STRING_LENGTH);
   10903            0 :          break;
   10904            0 :       default:
   10905            0 :          sprintf(string, "<unknown>");
   10906            0 :          break;
   10907              :       }
   10908              : 
   10909            0 :    return DB_SUCCESS;
   10910              : }
   10911              : 
   10912              : /********************************************************************/
   10913              : /**
   10914              : Same as db_sprintf, but with additional format parameter
   10915              : 
   10916              : @param string output ASCII string of data.
   10917              : @param format Format specifier passed to sprintf()
   10918              : @param data Value data.
   10919              : @param data_size Size of single data element.
   10920              : @param idx Index for array data.
   10921              : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
   10922              : @return DB_SUCCESS
   10923              : */
   10924              : 
   10925            0 : INT db_sprintff(char *string, const char *format, const void *data, INT data_size, INT idx, DWORD type)
   10926              : {
   10927            0 :    if (data_size == 0)
   10928            0 :       sprintf(string, "<NULL>");
   10929              :    else
   10930            0 :       switch (type) {
   10931            0 :       case TID_UINT8:
   10932            0 :          sprintf(string, format, *(((BYTE *) data) + idx));
   10933            0 :          break;
   10934            0 :       case TID_INT8:
   10935            0 :          sprintf(string, format, *(((char *) data) + idx));
   10936            0 :          break;
   10937            0 :       case TID_CHAR:
   10938            0 :          sprintf(string, format, *(((char *) data) + idx));
   10939            0 :          break;
   10940            0 :       case TID_UINT16:
   10941            0 :          sprintf(string, format, *(((WORD *) data) + idx));
   10942            0 :          break;
   10943            0 :       case TID_INT16:
   10944            0 :          sprintf(string, format, *(((short *) data) + idx));
   10945            0 :          break;
   10946            0 :       case TID_UINT32:
   10947            0 :          sprintf(string, format, *(((DWORD *) data) + idx));
   10948            0 :          break;
   10949            0 :       case TID_INT32:
   10950            0 :          sprintf(string, format, *(((INT *) data) + idx));
   10951            0 :          break;
   10952            0 :       case TID_UINT64:
   10953            0 :          sprintf(string, format, *(((UINT64 *) data) + idx));
   10954            0 :          break;
   10955            0 :       case TID_INT64:
   10956            0 :          sprintf(string, format, *(((INT64 *) data) + idx));
   10957            0 :          break;
   10958            0 :       case TID_BOOL:
   10959            0 :          sprintf(string, format, *(((BOOL *) data) + idx) ? 'y' : 'n');
   10960            0 :          break;
   10961            0 :       case TID_FLOAT:
   10962            0 :          if (ss_isnan(*(((float *) data) + idx)))
   10963            0 :             sprintf(string, "NAN");
   10964              :          else
   10965            0 :             sprintf(string, format, *(((float *) data) + idx));
   10966            0 :          break;
   10967            0 :       case TID_DOUBLE:
   10968            0 :          if (ss_isnan(*(((double *) data) + idx)))
   10969            0 :             sprintf(string, "NAN");
   10970              :          else
   10971            0 :             sprintf(string, format, *(((double *) data) + idx));
   10972            0 :          break;
   10973            0 :       case TID_BITFIELD:
   10974            0 :          sprintf(string, format, *(((DWORD *) data) + idx));
   10975            0 :          break;
   10976            0 :       case TID_STRING:
   10977              :       case TID_LINK:
   10978            0 :          mstrlcpy(string, ((char *) data) + data_size * idx, MAX_STRING_LENGTH);
   10979            0 :          break;
   10980            0 :       default:
   10981            0 :          sprintf(string, "<unknown>");
   10982            0 :          break;
   10983              :       }
   10984              : 
   10985            0 :    return DB_SUCCESS;
   10986              : }
   10987              : 
   10988              : /*------------------------------------------------------------------*/
   10989            0 : INT db_sprintfh(char *string, const void *data, INT data_size, INT idx, DWORD type)
   10990              : /********************************************************************\
   10991              : 
   10992              :   Routine: db_sprintfh
   10993              : 
   10994              :   Purpose: Convert a database value to a string according to its type
   10995              :            in hex format
   10996              : 
   10997              :   Input:
   10998              :     void  *data             Value data
   10999              :     INT   idx               Index for array data
   11000              :     INT   data_size         Size of single data element
   11001              :     DWORD type              Valye type, one of TID_xxx
   11002              : 
   11003              :   Output:
   11004              :     char  *string           ASCII string of data
   11005              : 
   11006              :   Function value:
   11007              :     DB_SUCCESS              Successful completion
   11008              : 
   11009              : \********************************************************************/
   11010              : {
   11011            0 :    if (data_size == 0)
   11012            0 :       sprintf(string, "<NULL>");
   11013              :    else
   11014            0 :       switch (type) {
   11015            0 :       case TID_UINT8:
   11016            0 :          sprintf(string, "0x%X", *(((BYTE *) data) + idx));
   11017            0 :          break;
   11018            0 :       case TID_INT8:
   11019            0 :          sprintf(string, "0x%X", *(((char *) data) + idx));
   11020            0 :          break;
   11021            0 :       case TID_CHAR:
   11022            0 :          sprintf(string, "%c", *(((char *) data) + idx));
   11023            0 :          break;
   11024            0 :       case TID_UINT16:
   11025            0 :          sprintf(string, "0x%X", *(((WORD *) data) + idx));
   11026            0 :          break;
   11027            0 :       case TID_INT16:
   11028            0 :          sprintf(string, "0x%hX", *(((short *) data) + idx));
   11029            0 :          break;
   11030            0 :       case TID_UINT32:
   11031            0 :          sprintf(string, "0x%X", *(((DWORD *) data) + idx));
   11032            0 :          break;
   11033            0 :       case TID_INT32:
   11034            0 :          sprintf(string, "0x%X", *(((INT *) data) + idx));
   11035            0 :          break;
   11036            0 :       case TID_UINT64:
   11037            0 :          sprintf(string, "0x%llX", *(((UINT64 *) data) + idx));
   11038            0 :          break;
   11039            0 :       case TID_INT64:
   11040            0 :          sprintf(string, "0x%llX", *(((INT64 *) data) + idx));
   11041            0 :          break;
   11042            0 :       case TID_BOOL:
   11043            0 :          sprintf(string, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
   11044            0 :          break;
   11045            0 :       case TID_FLOAT:
   11046            0 :          if (ss_isnan(*(((float *) data) + idx)))
   11047            0 :             sprintf(string, "NAN");
   11048              :          else
   11049            0 :             sprintf(string, "%.7g", *(((float *) data) + idx));
   11050            0 :          break;
   11051            0 :       case TID_DOUBLE:
   11052            0 :          if (ss_isnan(*(((double *) data) + idx)))
   11053            0 :             sprintf(string, "NAN");
   11054              :          else
   11055            0 :             sprintf(string, "%.16lg", *(((double *) data) + idx));
   11056            0 :          break;
   11057            0 :       case TID_BITFIELD:
   11058            0 :          sprintf(string, "0x%X", *(((DWORD *) data) + idx));
   11059            0 :          break;
   11060            0 :       case TID_STRING:
   11061              :       case TID_LINK:
   11062            0 :          sprintf(string, "%s", ((char *) data) + data_size * idx);
   11063            0 :          break;
   11064            0 :       default:
   11065            0 :          sprintf(string, "<unknown>");
   11066            0 :          break;
   11067              :       }
   11068              : 
   11069            0 :    return DB_SUCCESS;
   11070              : }
   11071              : 
   11072              : /********************************************************************/
   11073              : /**
   11074              : Convert a database value to a string according to its type.
   11075              : 
   11076              : This function is a convenient way to convert a binary ODB value into a
   11077              : string depending on its type if is not known at compile time. If it is known, the
   11078              : normal sprintf() function can be used.
   11079              : \code
   11080              : ...
   11081              :   for (j=0 ; j<key.num_values ; j++)
   11082              :   {
   11083              :     std::string s = db_sprintf(pdata, key.item_size, j, key.type);
   11084              :   }
   11085              :   ...
   11086              : \endcode
   11087              : @param data Value data.
   11088              : @param data_size Size of single data element.
   11089              : @param idx Index for array data.
   11090              : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
   11091              : @return string output ASCII string of data.
   11092              : */
   11093           20 : std::string db_sprintf(const void *data, INT data_size, INT idx, DWORD type)
   11094              : {
   11095           20 :    if (data_size == 0) {
   11096            0 :       return "<NULL>";
   11097              :    } else {
   11098              :       char buf[256];
   11099           20 :       switch (type) {
   11100            0 :       case TID_UINT8:
   11101            0 :          sprintf(buf, "%d", *(((BYTE *) data) + idx));
   11102            0 :          return buf;
   11103            0 :       case TID_INT8:
   11104            0 :          sprintf(buf, "%d", *(((char *) data) + idx));
   11105            0 :          return buf;
   11106            0 :       case TID_CHAR:
   11107            0 :          sprintf(buf, "%c", *(((char *) data) + idx));
   11108            0 :          return buf;
   11109            0 :       case TID_UINT16:
   11110            0 :          sprintf(buf, "%u", *(((WORD *) data) + idx));
   11111            0 :          return buf;
   11112            0 :       case TID_INT16:
   11113            0 :          sprintf(buf, "%d", *(((short *) data) + idx));
   11114            0 :          return buf;
   11115            4 :       case TID_UINT32:
   11116            4 :          sprintf(buf, "%u", *(((DWORD *) data) + idx));
   11117            8 :          return buf;
   11118            4 :       case TID_INT32:
   11119            4 :          sprintf(buf, "%d", *(((INT *) data) + idx));
   11120            8 :          return buf;
   11121            0 :       case TID_UINT64:
   11122            0 :          sprintf(buf, "%llu", *(((UINT64 *) data) + idx));
   11123            0 :          return buf;
   11124            0 :       case TID_INT64:
   11125            0 :          sprintf(buf, "%lld", *(((INT64 *) data) + idx));
   11126            0 :          return buf;
   11127            8 :       case TID_BOOL:
   11128            8 :          sprintf(buf, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
   11129           16 :          return buf;
   11130            0 :       case TID_FLOAT:
   11131            0 :          if (ss_isnan(*(((float *) data) + idx))) {
   11132            0 :             return "NAN";
   11133              :          } else {
   11134            0 :             sprintf(buf, "%.7g", *(((float *) data) + idx));
   11135            0 :             return buf;
   11136              :          }
   11137            0 :       case TID_DOUBLE:
   11138            0 :          if (ss_isnan(*(((double *) data) + idx))) {
   11139            0 :             return "NAN";
   11140              :          } else {
   11141            0 :             sprintf(buf, "%.16lg", *(((double *) data) + idx));
   11142            0 :             return buf;
   11143              :          }
   11144            0 :       case TID_BITFIELD:
   11145            0 :          sprintf(buf, "%u", *(((DWORD *) data) + idx));
   11146            0 :          return buf;
   11147            4 :       case TID_STRING:
   11148              :       case TID_LINK:
   11149            8 :          return (((char *) data) + data_size * idx);
   11150            0 :       default:
   11151            0 :          return "<unknown>";
   11152              :       }
   11153              :    }
   11154              : }
   11155              : 
   11156              : /********************************************************************/
   11157              : /**
   11158              : Same as db_sprintf, but with additional format parameter
   11159              : 
   11160              : @param string output ASCII string of data.
   11161              : @param format Format specifier passed to sprintf()
   11162              : @param data Value data.
   11163              : @param data_size Size of single data element.
   11164              : @param idx Index for array data.
   11165              : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
   11166              : @return DB_SUCCESS
   11167              : */
   11168              : 
   11169            0 : std::string db_sprintff(const char *format, const void *data, INT data_size, INT idx, DWORD type)
   11170              : {
   11171            0 :    if (data_size == 0) {
   11172            0 :       return "<NULL>";
   11173              :    } else {
   11174              :       char buf[256];
   11175            0 :       switch (type) {
   11176            0 :       case TID_UINT8:
   11177            0 :          sprintf(buf, format, *(((BYTE *) data) + idx));
   11178            0 :          return buf;
   11179            0 :       case TID_INT8:
   11180            0 :          sprintf(buf, format, *(((char *) data) + idx));
   11181            0 :          return buf;
   11182            0 :       case TID_CHAR:
   11183            0 :          sprintf(buf, format, *(((char *) data) + idx));
   11184            0 :          return buf;
   11185            0 :       case TID_UINT16:
   11186            0 :          sprintf(buf, format, *(((WORD *) data) + idx));
   11187            0 :          return buf;
   11188            0 :       case TID_INT16:
   11189            0 :          sprintf(buf, format, *(((short *) data) + idx));
   11190            0 :          return buf;
   11191            0 :       case TID_UINT32:
   11192            0 :          sprintf(buf, format, *(((DWORD *) data) + idx));
   11193            0 :          return buf;
   11194            0 :       case TID_INT32:
   11195            0 :          sprintf(buf, format, *(((INT *) data) + idx));
   11196            0 :          return buf;
   11197            0 :       case TID_UINT64:
   11198            0 :          sprintf(buf, format, *(((UINT64 *) data) + idx));
   11199            0 :          return buf;
   11200            0 :       case TID_INT64:
   11201            0 :          sprintf(buf, format, *(((INT64 *) data) + idx));
   11202            0 :          return buf;
   11203            0 :       case TID_BOOL:
   11204            0 :          sprintf(buf, format, *(((BOOL *) data) + idx) ? 'y' : 'n');
   11205            0 :          return buf;
   11206            0 :       case TID_FLOAT:
   11207            0 :          if (ss_isnan(*(((float *) data) + idx))) {
   11208            0 :             return "NAN";
   11209              :          } else {
   11210            0 :             sprintf(buf, format, *(((float *) data) + idx));
   11211            0 :             return buf;
   11212              :          }
   11213            0 :       case TID_DOUBLE:
   11214            0 :          if (ss_isnan(*(((double *) data) + idx))) {
   11215            0 :             return "NAN";
   11216              :          } else {
   11217            0 :             sprintf(buf, format, *(((double *) data) + idx));
   11218            0 :             return buf;
   11219              :          }
   11220            0 :       case TID_BITFIELD:
   11221            0 :          sprintf(buf, format, *(((DWORD *) data) + idx));
   11222            0 :          return buf;
   11223            0 :       case TID_STRING:
   11224              :       case TID_LINK:
   11225            0 :          return (((char *) data) + data_size * idx);
   11226            0 :       default:
   11227            0 :          return "<unknown>";
   11228              :       }
   11229              :    }
   11230              : }
   11231              : 
   11232              : /**dox***************************************************************/
   11233              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
   11234              : 
   11235              : /*------------------------------------------------------------------*/
   11236            0 : std::string db_sprintfh(const void *data, INT data_size, INT idx, DWORD type)
   11237              : /********************************************************************\
   11238              : 
   11239              :   Routine: db_sprintfh
   11240              : 
   11241              :   Purpose: Convert a database value to a string according to its type
   11242              :            in hex format
   11243              : 
   11244              :   Input:
   11245              :     void  *data             Value data
   11246              :     INT   idx               Index for array data
   11247              :     INT   data_size         Size of single data element
   11248              :     DWORD type              Valye type, one of TID_xxx
   11249              : 
   11250              :   Output:
   11251              :     char  *string           ASCII string of data
   11252              : 
   11253              :   Function value:
   11254              :     DB_SUCCESS              Successful completion
   11255              : 
   11256              : \********************************************************************/
   11257              : {
   11258            0 :    if (data_size == 0) {
   11259            0 :       return "<NULL>";
   11260              :    } else {
   11261              :       char buf[256];
   11262            0 :       switch (type) {
   11263            0 :       case TID_UINT8:
   11264            0 :          sprintf(buf, "0x%X", *(((BYTE *) data) + idx));
   11265            0 :          return buf;
   11266            0 :       case TID_INT8:
   11267            0 :          sprintf(buf, "0x%X", *(((char *) data) + idx));
   11268            0 :          return buf;
   11269            0 :       case TID_CHAR:
   11270            0 :          sprintf(buf, "%c", *(((char *) data) + idx));
   11271            0 :          return buf;
   11272            0 :       case TID_UINT16:
   11273            0 :          sprintf(buf, "0x%X", *(((WORD *) data) + idx));
   11274            0 :          return buf;
   11275            0 :       case TID_INT16:
   11276            0 :          sprintf(buf, "0x%hX", *(((short *) data) + idx));
   11277            0 :          return buf;
   11278            0 :       case TID_UINT32:
   11279            0 :          sprintf(buf, "0x%X", *(((DWORD *) data) + idx));
   11280            0 :          return buf;
   11281            0 :       case TID_INT32:
   11282            0 :          sprintf(buf, "0x%X", *(((INT *) data) + idx));
   11283            0 :          return buf;
   11284            0 :       case TID_UINT64:
   11285            0 :          sprintf(buf, "0x%llX", *(((UINT64 *) data) + idx));
   11286            0 :          return buf;
   11287            0 :       case TID_INT64:
   11288            0 :          sprintf(buf, "0x%llX", *(((INT64 *) data) + idx));
   11289            0 :          return buf;
   11290            0 :       case TID_BOOL:
   11291            0 :          sprintf(buf, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
   11292            0 :          return buf;
   11293            0 :       case TID_FLOAT:
   11294            0 :          if (ss_isnan(*(((float *) data) + idx))) {
   11295            0 :             return "NAN";
   11296              :          } else {
   11297            0 :             sprintf(buf, "%.7g", *(((float *) data) + idx));
   11298            0 :             return buf;
   11299              :          }
   11300            0 :       case TID_DOUBLE:
   11301            0 :          if (ss_isnan(*(((double *) data) + idx))) {
   11302            0 :             return "NAN";
   11303              :          } else {
   11304            0 :             sprintf(buf, "%.16lg", *(((double *) data) + idx));
   11305            0 :             return buf;
   11306              :          }
   11307            0 :       case TID_BITFIELD:
   11308            0 :          sprintf(buf, "0x%X", *(((DWORD *) data) + idx));
   11309            0 :          return buf;
   11310            0 :       case TID_STRING:
   11311              :       case TID_LINK:
   11312            0 :          return (((char *) data) + data_size * idx);
   11313            0 :       default:
   11314            0 :          return "<unknown>";
   11315              :       }
   11316              :    }
   11317              : }
   11318              : 
   11319              : /*------------------------------------------------------------------*/
   11320           30 : INT db_sscanf(const char *data_str, void *data, INT * data_size, INT i, DWORD tid)
   11321              : /********************************************************************\
   11322              : 
   11323              :   Routine: db_sscanf
   11324              : 
   11325              :   Purpose: Convert a string to a database value according to its type
   11326              : 
   11327              :   Input:
   11328              :     char  *data_str         ASCII string of data
   11329              :     INT   i                 Index for array data
   11330              :     DWORD tid               Value type, one of TID_xxx
   11331              : 
   11332              :   Output:
   11333              :     void  *data             Value data
   11334              :     INT   *data_size        Size of single data element
   11335              : 
   11336              :   Function value:
   11337              :     DB_SUCCESS              Successful completion
   11338              : 
   11339              : \********************************************************************/
   11340              : {
   11341           30 :    DWORD value = 0;
   11342           30 :    BOOL hex = FALSE;
   11343              : 
   11344           30 :    if (data_str == NULL)
   11345            0 :       return 0;
   11346              : 
   11347           30 :    *data_size = rpc_tid_size(tid);
   11348           30 :    if (strncmp(data_str, "0x", 2) == 0) {
   11349            0 :       hex = TRUE;
   11350            0 :       sscanf(data_str + 2, "%x", &value);
   11351              :    }
   11352              : 
   11353           30 :    switch (tid) {
   11354            0 :    case TID_UINT8:
   11355              :    case TID_INT8:
   11356            0 :       if (hex)
   11357            0 :          *((char *) data + i) = (char) value;
   11358              :       else
   11359            0 :          *((char *) data + i) = (char) atoi(data_str);
   11360            0 :       break;
   11361            0 :    case TID_CHAR:
   11362            0 :       *((char *) data + i) = data_str[0];
   11363            0 :       break;
   11364            0 :    case TID_UINT16:
   11365            0 :       if (hex)
   11366            0 :          *((WORD *) data + i) = (WORD) value;
   11367              :       else
   11368            0 :          *((WORD *) data + i) = (WORD) atoi(data_str);
   11369            0 :       break;
   11370            0 :    case TID_INT16:
   11371            0 :       if (hex)
   11372            0 :          *((short int *) data + i) = (short int) value;
   11373              :       else
   11374            0 :          *((short int *) data + i) = (short int) atoi(data_str);
   11375            0 :       break;
   11376            8 :    case TID_UINT32:
   11377            8 :       if (hex)
   11378            0 :          value = strtoul(data_str, nullptr, 16);
   11379              :       else
   11380            8 :          value = strtoul(data_str, nullptr, 10);
   11381              : 
   11382            8 :       *((DWORD *) data + i) = value;
   11383            8 :       break;
   11384            6 :    case TID_INT32:
   11385            6 :       if (hex)
   11386            0 :          value = strtol(data_str, nullptr, 16);
   11387              :       else
   11388            6 :          value = strtol(data_str, nullptr, 10);
   11389              : 
   11390            6 :       *((INT *) data + i) = value;
   11391            6 :       break;
   11392            0 :    case TID_UINT64:
   11393            0 :       if (hex)
   11394            0 :          *((UINT64 *) data + i) = strtoull(data_str, nullptr, 16);
   11395              :       else
   11396            0 :          *((UINT64 *) data + i) = strtoull(data_str, nullptr, 10);
   11397            0 :       break;
   11398            0 :    case TID_INT64:
   11399            0 :       if (hex)
   11400            0 :          *((UINT64 *) data + i) = strtoll(data_str, nullptr, 16);
   11401              :       else
   11402            0 :          *((UINT64 *) data + i) = strtoll(data_str, nullptr, 10);
   11403            0 :       break;
   11404           16 :    case TID_BOOL:
   11405           16 :       if (data_str[0] == 'y' || data_str[0] == 'Y' ||
   11406           16 :           data_str[0] == 't' || data_str[0] == 'T' || atoi(data_str) > 0)
   11407            0 :          *((BOOL *) data + i) = 1;
   11408              :       else
   11409           16 :          *((BOOL *) data + i) = 0;
   11410           16 :       break;
   11411            0 :    case TID_FLOAT:
   11412            0 :       if (data_str[0] == 'n' || data_str[0] == 'N')
   11413            0 :          *((float *) data + i) = (float) ss_nan();
   11414              :       else
   11415            0 :          *((float *) data + i) = (float) atof(data_str);
   11416            0 :       break;
   11417            0 :    case TID_DOUBLE:
   11418            0 :       if (data_str[0] == 'n' || data_str[0] == 'N')
   11419            0 :          *((double *) data + i) = ss_nan();
   11420              :       else
   11421            0 :          *((double *) data + i) = atof(data_str);
   11422            0 :       break;
   11423            0 :    case TID_BITFIELD:
   11424            0 :       if (hex)
   11425            0 :          value = strtoul(data_str, nullptr, 16);
   11426              :       else
   11427            0 :          value = strtoul(data_str, nullptr, 10);
   11428              : 
   11429            0 :       *((DWORD *) data + i) = value;
   11430            0 :       break;
   11431            0 :    case TID_STRING:
   11432              :    case TID_LINK:
   11433            0 :       strcpy((char *) data, data_str);
   11434            0 :       *data_size = strlen(data_str) + 1;
   11435            0 :       break;
   11436              :    }
   11437              : 
   11438           30 :    return DB_SUCCESS;
   11439              : }
   11440              : 
   11441              : /*------------------------------------------------------------------*/
   11442              : 
   11443              : #ifdef LOCAL_ROUTINES
   11444              : 
   11445            0 : static void db_recurse_record_tree_locked(HNDLE hDB, const DATABASE_HEADER* pheader, const KEY* pkey, void **data, INT * total_size, INT base_align, INT * max_align, BOOL bSet, INT convert_flags, db_err_msg** msg)
   11446              : /********************************************************************\
   11447              : 
   11448              :   Routine: db_recurse_record_tree_locked
   11449              : 
   11450              :   Purpose: Recurse a database tree and calculate its size or copy
   11451              :            data. Internal use only.
   11452              : 
   11453              : \********************************************************************/
   11454              : {
   11455              :    const KEY *pold;
   11456              :    INT size, align, corr, total_size_tmp;
   11457              : 
   11458            0 :    KEYLIST *pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
   11459            0 :    if (!pkeylist->first_key)
   11460            0 :       return;
   11461              :    // FIXME: validate pkeylist->first_key
   11462            0 :    pkey = (const KEY *) ((char *) pheader + pkeylist->first_key);
   11463              : 
   11464              :    /* first browse through this level */
   11465              :    do {
   11466            0 :       pold = NULL;
   11467              :       
   11468            0 :       if (pkey->type == TID_LINK) {
   11469            0 :          const KEY *plink = db_resolve_link_locked(pheader, pkey, NULL, msg);
   11470              :          
   11471            0 :          if (!plink)
   11472            0 :             return;
   11473              : 
   11474            0 :          if (plink->type == TID_KEY) {
   11475            0 :             db_recurse_record_tree_locked(hDB, pheader, plink, data, total_size, base_align, NULL, bSet, convert_flags, msg);
   11476              :          } else {
   11477            0 :             pold = pkey;
   11478            0 :             pkey = plink;
   11479              :          }
   11480              :       }
   11481              :       
   11482            0 :       if (pkey->type != TID_KEY) {
   11483              :          /* correct for alignment */
   11484            0 :          align = 1;
   11485              : 
   11486            0 :          if (rpc_tid_size(pkey->type))
   11487            0 :             align = rpc_tid_size(pkey->type) < base_align ? rpc_tid_size(pkey->type) : base_align;
   11488              : 
   11489            0 :          if (max_align && align > *max_align)
   11490            0 :             *max_align = align;
   11491              : 
   11492            0 :          corr = VALIGN(*total_size, align) - *total_size;
   11493            0 :          *total_size += corr;
   11494            0 :          if (data)
   11495            0 :             *data = (void *) ((char *) (*data) + corr);
   11496              : 
   11497              :          /* calculate data size */
   11498            0 :          size = pkey->item_size * pkey->num_values;
   11499              : 
   11500            0 :          if (data) {
   11501            0 :             if (bSet) {
   11502            0 :                KEY* wpkey = (KEY*)pkey;
   11503              :                /* copy data if there is write access */
   11504            0 :                if (pkey->access_mode & MODE_WRITE) {
   11505            0 :                   memcpy((char *) pheader + pkey->data, *data, pkey->item_size * pkey->num_values);
   11506              : 
   11507              :                   /* convert data */
   11508            0 :                   if (convert_flags) {
   11509            0 :                      if (pkey->num_values > 1)
   11510            0 :                         rpc_convert_data((char *) pheader + pkey->data,
   11511            0 :                                          pkey->type, RPC_FIXARRAY, pkey->item_size * pkey->num_values, convert_flags);
   11512              :                      else
   11513            0 :                         rpc_convert_single((char *) pheader + pkey->data, pkey->type, 0, convert_flags);
   11514              :                   }
   11515              : 
   11516              :                   /* update time */
   11517            0 :                   wpkey->last_written = ss_time();
   11518              : 
   11519              :                   /* notify clients which have key open */
   11520            0 :                   db_notify_clients_locked(pheader, hDB, db_pkey_to_hkey(pheader, pkey), -1, TRUE, msg);
   11521              :                }
   11522              :             } else {
   11523              :                /* copy key data if there is read access */
   11524            0 :                if (pkey->access_mode & MODE_READ) {
   11525            0 :                   memcpy(*data, (char *) pheader + pkey->data, pkey->item_size * pkey->num_values);
   11526              : 
   11527              :                   /* convert data */
   11528            0 :                   if (convert_flags) {
   11529            0 :                      if (pkey->num_values > 1)
   11530            0 :                         rpc_convert_data(*data, pkey->type,
   11531              :                                          RPC_FIXARRAY | RPC_OUTGOING,
   11532            0 :                                          pkey->item_size * pkey->num_values, convert_flags);
   11533              :                      else
   11534            0 :                         rpc_convert_single(*data, pkey->type, RPC_OUTGOING, convert_flags);
   11535              :                   }
   11536              :                }
   11537              :             }
   11538              : 
   11539            0 :             *data = (char *) (*data) + size;
   11540              :          }
   11541              : 
   11542            0 :          *total_size += size;
   11543              :       } else {
   11544              :          /* align new substructure according to the maximum
   11545              :             align value in this structure */
   11546            0 :          align = 1;
   11547              : 
   11548            0 :          total_size_tmp = *total_size;
   11549            0 :          db_recurse_record_tree_locked(hDB, pheader, pkey, NULL, &total_size_tmp, base_align, &align, bSet, convert_flags, msg);
   11550              : 
   11551            0 :          if (max_align && align > *max_align)
   11552            0 :             *max_align = align;
   11553              : 
   11554            0 :          corr = VALIGN(*total_size, align) - *total_size;
   11555            0 :          *total_size += corr;
   11556            0 :          if (data)
   11557            0 :             *data = (void *) ((char *) (*data) + corr);
   11558              : 
   11559              :          /* now recurse subtree */
   11560            0 :          db_recurse_record_tree_locked(hDB, pheader, pkey, data, total_size, base_align, NULL, bSet, convert_flags, msg);
   11561              : 
   11562            0 :          corr = VALIGN(*total_size, align) - *total_size;
   11563            0 :          *total_size += corr;
   11564            0 :          if (data)
   11565            0 :             *data = (void *) ((char *) (*data) + corr);
   11566              :       }
   11567              :          
   11568            0 :       if (pold) {
   11569            0 :          pkey = pold;
   11570            0 :          pold = NULL;
   11571              :       }
   11572              : 
   11573            0 :       if (!pkey->next_key)
   11574            0 :          break;
   11575              : 
   11576              :       // FIXME: validate pkey->next_key
   11577            0 :       pkey = (KEY *) ((char *) pheader + pkey->next_key);
   11578            0 :    } while (TRUE);
   11579              : }
   11580              : 
   11581            0 : static void db_recurse_record_tree_locked(HNDLE hDB, HNDLE hKey, void **data, INT * total_size, INT base_align, INT * max_align, BOOL bSet, INT convert_flags, db_err_msg** msg)
   11582              : /********************************************************************\
   11583              : 
   11584              :   Routine: db_recurse_record_tree_locked
   11585              : 
   11586              :   Purpose: Recurse a database tree and calculate its size or copy
   11587              :            data. Internal use only.
   11588              : 
   11589              : \********************************************************************/
   11590              : {
   11591              :    /* get first subkey of hKey */
   11592            0 :    DATABASE_HEADER *pheader = _database[hDB - 1].database_header;
   11593              : 
   11594            0 :    const KEY* pkey = db_get_pkey(pheader, hKey, NULL, "db_recurse_record_tree", msg);
   11595              : 
   11596            0 :    if (!pkey) {
   11597            0 :       return;
   11598              :    }
   11599              : 
   11600            0 :    db_recurse_record_tree_locked(hDB, pheader, pkey, data, total_size, base_align, max_align, bSet, convert_flags, msg);
   11601              : }   
   11602              : 
   11603              : #endif                          /* LOCAL_ROUTINES */
   11604              : 
   11605              : /**dox***************************************************************/
   11606              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
   11607              : 
   11608              : /********************************************************************/
   11609              : /**
   11610              : Calculates the size of a record.
   11611              : @param hDB          ODB handle obtained via cm_get_experiment_database().
   11612              : @param hKey Handle for key where search starts, zero for root.
   11613              : @param align Byte alignment calculated by the stub and
   11614              :               passed to the rpc side to align data
   11615              :               according to local machine. Must be zero
   11616              :               when called from user level
   11617              : @param buf_size Size of record structure
   11618              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TYPE_MISMATCH,
   11619              : DB_STRUCT_SIZE_MISMATCH, DB_NO_KEY
   11620              : */
   11621            0 : INT db_get_record_size(HNDLE hDB, HNDLE hKey, INT align, INT * buf_size)
   11622              : {
   11623            0 :    if (rpc_is_remote()) {
   11624            0 :       align = ss_get_struct_align();
   11625            0 :       return rpc_call(RPC_DB_GET_RECORD_SIZE, hDB, hKey, align, buf_size);
   11626              :    }
   11627              : #ifdef LOCAL_ROUTINES
   11628              :    {
   11629              :       KEY key;
   11630              :       INT status, max_align;
   11631              : 
   11632            0 :       if (!align)
   11633            0 :          align = ss_get_struct_align();
   11634              : 
   11635              :       /* check if key has subkeys */
   11636            0 :       status = db_get_key(hDB, hKey, &key);
   11637            0 :       if (status != DB_SUCCESS)
   11638            0 :          return status;
   11639              : 
   11640            0 :       if (key.type != TID_KEY) {
   11641              :          /* just a single key */
   11642            0 :          *buf_size = key.item_size * key.num_values;
   11643            0 :          return DB_SUCCESS;
   11644              :       }
   11645              : 
   11646            0 :       db_err_msg* msg = NULL;
   11647            0 :       db_lock_database(hDB);
   11648              : 
   11649              :       /* determine record size */
   11650            0 :       *buf_size = max_align = 0;
   11651            0 :       db_recurse_record_tree_locked(hDB, hKey, NULL, buf_size, align, &max_align, 0, 0, &msg);
   11652              : 
   11653              :       //int tmp = *buf_size;
   11654              :       /* correct for byte padding */
   11655            0 :       *buf_size = VALIGN(*buf_size, max_align);
   11656              : 
   11657              :       //if (tmp != *buf_size) {
   11658              :       //   cm_msg(MERROR, "db_get_record_size", "ODB record \"%s\" has unexpected padding from %d to %d bytes", db_get_path(hDB, hKey).c_str(), tmp, *buf_size);
   11659              :       //}
   11660              : 
   11661            0 :       db_unlock_database(hDB);
   11662            0 :       if (msg)
   11663            0 :          db_flush_msg(&msg);
   11664              :    }
   11665              : #endif                          /* LOCAL_ROUTINES */
   11666              : 
   11667            0 :    return DB_SUCCESS;
   11668              : }
   11669              : 
   11670              : /********************************************************************/
   11671              : /**
   11672              : Copy a set of keys to local memory.
   11673              : 
   11674              : An ODB sub-tree can be mapped to a C structure automatically via a
   11675              : hot-link using the function db_open_record() or manually with this function.
   11676              : Problems might occur if the ODB sub-tree contains values which don't match the
   11677              : C structure. Although the structure size is checked against the sub-tree size, no
   11678              : checking can be done if the type and order of the values in the structure are the
   11679              : same than those in the ODB sub-tree. Therefore it is recommended to use the
   11680              : function db_create_record() before db_get_record() is used which
   11681              : ensures that both are equivalent.
   11682              : \code
   11683              : struct {
   11684              :   INT level1;
   11685              :   INT level2;
   11686              : } trigger_settings;
   11687              : char *trigger_settings_str =
   11688              : "[Settings]\n\
   11689              : level1 = INT : 0\n\
   11690              : level2 = INT : 0";
   11691              : 
   11692              : main()
   11693              : {
   11694              :   HNDLE hDB, hkey;
   11695              :   INT   size;
   11696              :   ...
   11697              :   cm_get_experiment_database(&hDB, NULL);
   11698              :   db_create_record(hDB, 0, "/Equipment/Trigger", trigger_settings_str);
   11699              :   db_find_key(hDB, 0, "/Equipment/Trigger/Settings", &hkey);
   11700              :   size = sizeof(trigger_settings);
   11701              :   db_get_record(hDB, hkey, &trigger_settings, &size, 0);
   11702              :   ...
   11703              : }
   11704              : \endcode
   11705              : @param hDB          ODB handle obtained via cm_get_experiment_database().
   11706              : @param hKey         Handle for key where search starts, zero for root.
   11707              : @param data         Pointer to the retrieved data.
   11708              : @param buf_size     Size of data structure, must be obtained via sizeof(RECORD-NAME).
   11709              : @param align        Byte alignment calculated by the stub and
   11710              :                     passed to the rpc side to align data
   11711              :                     according to local machine. Must be zero
   11712              :                     when called from user level.
   11713              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_STRUCT_SIZE_MISMATCH
   11714              : */
   11715            0 : INT db_get_record(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, INT align)
   11716              : {
   11717            0 :    if (rpc_is_remote()) {
   11718            0 :       align = ss_get_struct_align();
   11719            0 :       return rpc_call(RPC_DB_GET_RECORD, hDB, hKey, data, buf_size, align);
   11720              :    }
   11721              : #ifdef LOCAL_ROUTINES
   11722              :    {
   11723              :       KEY key;
   11724              :       INT convert_flags, status;
   11725              :       INT total_size;
   11726              :       void *pdata;
   11727              : 
   11728            0 :       if (data && buf_size) {
   11729            0 :          memset(data, 0x00, *buf_size);
   11730              :       }
   11731              : 
   11732            0 :       convert_flags = 0;
   11733              : 
   11734            0 :       if (!align)
   11735            0 :          align = ss_get_struct_align();
   11736              :       else {
   11737              :          /* only convert data if called remotely, as indicated by align != 0 */
   11738            0 :          if (rpc_is_mserver()) {
   11739            0 :             convert_flags = rpc_get_convert_flags();
   11740              :          }
   11741              :       }
   11742              : 
   11743              :       /* check if key has subkeys */
   11744            0 :       status = db_get_key(hDB, hKey, &key);
   11745            0 :       if (status != DB_SUCCESS)
   11746            0 :          return status;
   11747              : 
   11748            0 :       if (key.type != TID_KEY) {
   11749              :          /* copy single key */
   11750            0 :          if (key.item_size * key.num_values != *buf_size) {
   11751            0 :             cm_msg(MERROR, "db_get_record", "struct size mismatch for \"%s\" (expected size: %d, size in ODB: %d * %d = %d)",
   11752            0 :                    db_get_path(hDB, hKey).c_str(), *buf_size, key.num_values, key.item_size, key.item_size * key.num_values);
   11753            0 :             return DB_STRUCT_SIZE_MISMATCH;
   11754              :          }
   11755              : 
   11756            0 :          db_get_data(hDB, hKey, data, buf_size, key.type);
   11757              : 
   11758            0 :          if (convert_flags) {
   11759            0 :             if (key.num_values > 1)
   11760            0 :                rpc_convert_data(data, key.type, RPC_OUTGOING | RPC_FIXARRAY, key.item_size * key.num_values, convert_flags);
   11761              :             else
   11762            0 :                rpc_convert_single(data, key.type, RPC_OUTGOING, convert_flags);
   11763              :          }
   11764              : 
   11765            0 :          return DB_SUCCESS;
   11766              :       }
   11767              : 
   11768              :       /* check record size */
   11769            0 :       db_get_record_size(hDB, hKey, align, &total_size);
   11770            0 :       if (total_size != *buf_size) {
   11771            0 :          cm_msg(MERROR, "db_get_record", "struct size mismatch for \"%s\" (expected size: %d, size in ODB: %d)", db_get_path(hDB, hKey).c_str(), *buf_size, total_size);
   11772            0 :          return DB_STRUCT_SIZE_MISMATCH;
   11773              :       }
   11774              : 
   11775              :       /* get subkey data */
   11776            0 :       pdata = data;
   11777            0 :       total_size = 0;
   11778              : 
   11779            0 :       db_err_msg* msg = NULL;
   11780            0 :       db_lock_database(hDB);
   11781            0 :       db_recurse_record_tree_locked(hDB, hKey, &pdata, &total_size, align, NULL, FALSE, convert_flags, &msg);
   11782            0 :       db_unlock_database(hDB);
   11783            0 :       if (msg)
   11784            0 :          db_flush_msg(&msg);
   11785              : 
   11786              :       //if (total_size != *buf_size) {
   11787              :       //   cm_msg(MERROR, "db_get_record", "struct size mismatch for \"%s\", expected size: %d, read from ODB: %d bytes", db_get_path(hDB, hKey).c_str(), *buf_size, total_size);
   11788              :       //   //return DB_STRUCT_SIZE_MISMATCH;
   11789              :       //}
   11790              :    }
   11791              : #endif                          /* LOCAL_ROUTINES */
   11792              : 
   11793            0 :    return DB_SUCCESS;
   11794              : }
   11795              : 
   11796              : /********************************************************************/
   11797              : /**
   11798              : Same as db_get_record() but if there is a record mismatch between ODB structure
   11799              : and C record, it is automatically corrected by calling db_check_record()
   11800              : 
   11801              : @param hDB          ODB handle obtained via cm_get_experiment_database().
   11802              : @param hKey         Handle for key where search starts, zero for root.
   11803              : @param data         Pointer to the retrieved data.
   11804              : @param buf_size     Size of data structure, must be obtained via sizeof(RECORD-NAME).
   11805              : @param align        Byte alignment calculated by the stub and
   11806              :                     passed to the rpc side to align data
   11807              :                     according to local machine. Must be zero
   11808              :                     when called from user level.
   11809              : @param rec_str      ASCII representation of ODB record in the format
   11810              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_STRUCT_SIZE_MISMATCH
   11811              : */
   11812            0 : INT db_get_record1(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, INT align, const char *rec_str)
   11813              : {
   11814            0 :    int size = *buf_size;
   11815            0 :    int odb_size = 0;
   11816              :    int status;
   11817              :    //char path[MAX_ODB_PATH];
   11818              : 
   11819              :    /* check record size first */
   11820              : 
   11821            0 :    status = db_get_record_size(hDB, hKey, align, &odb_size);
   11822            0 :    if (status != DB_SUCCESS)
   11823            0 :       return status;
   11824              : 
   11825              :    /* if size mismatch, call repair function */
   11826              : 
   11827            0 :    if (odb_size != size) {
   11828            0 :       std::string path = db_get_path(hDB, hKey);
   11829            0 :       cm_msg(MINFO, "db_get_record1", "Fixing ODB \"%s\" struct size mismatch (expected %d, odb size %d)", path.c_str(), size, odb_size);
   11830            0 :       status = db_create_record(hDB, hKey, "", rec_str);
   11831            0 :       if (status != DB_SUCCESS)
   11832            0 :          return status;
   11833            0 :    }
   11834              : 
   11835              :    /* run db_get_record(), if success, we are done */
   11836              : 
   11837            0 :    status = db_get_record(hDB, hKey, data, buf_size, align);
   11838            0 :    if (status == DB_SUCCESS)
   11839            0 :       return status;
   11840              : 
   11841              :    /* try repair with db_check_record() */
   11842              : 
   11843            0 :    status = db_check_record(hDB, hKey, "", rec_str, TRUE);
   11844            0 :    if (status != DB_SUCCESS)
   11845            0 :       return status;
   11846              : 
   11847              :    /* verify struct size again, because there can still be a mismatch if there
   11848              :     * are extra odb entries at the end of the record as db_check_record()
   11849              :     * seems to ignore all odb entries past the end of "rec_str". K.O.
   11850              :     */
   11851              : 
   11852            0 :    status = db_get_record_size(hDB, hKey, align, &odb_size);
   11853            0 :    if (status != DB_SUCCESS)
   11854            0 :       return status;
   11855              : 
   11856            0 :    std::string path = db_get_path(hDB, hKey);
   11857              : 
   11858            0 :    if (odb_size != size) {
   11859            0 :       cm_msg(MERROR, "db_get_record1", "after db_check_record() still struct size mismatch (expected %d, odb size %d) of \"%s\", calling db_create_record()", size, odb_size, path.c_str());
   11860            0 :       status = db_create_record(hDB, hKey, "", rec_str);
   11861            0 :       if (status != DB_SUCCESS)
   11862            0 :          return status;
   11863              :    }
   11864              : 
   11865            0 :    cm_msg(MERROR, "db_get_record1", "repaired struct size mismatch of \"%s\"", path.c_str());
   11866              : 
   11867            0 :    *buf_size = size;
   11868            0 :    status = db_get_record(hDB, hKey, data, buf_size, align);
   11869              : 
   11870            0 :    return status;
   11871            0 : }
   11872              : 
   11873            0 : static int db_parse_record(const char* rec_str, const char** out_rec_str, char* title, int title_size, char* key_name, int key_name_size, int* tid, int* n_data, int* string_length)
   11874              : {
   11875            0 :    title[0] = 0;
   11876            0 :    key_name[0] = 0;
   11877            0 :    *tid = 0;
   11878            0 :    *n_data = 0;
   11879            0 :    *string_length = 0;
   11880            0 :    *out_rec_str = NULL;
   11881              : 
   11882              :    //
   11883              :    // expected format of rec_str:
   11884              :    //
   11885              :    // title: "[.]",
   11886              :    // numeric value: "example_int = INT : 3",
   11887              :    // string value: "example_string = STRING : [20] /Runinfo/Run number",
   11888              :    // array: "aaa = INT[10] : ...",
   11889              :    // string array: "sarr = STRING[10] : [32] ",
   11890              :    //
   11891              : 
   11892              :    //printf("parse_rec_str: [%s]\n", rec_str);
   11893              : 
   11894            0 :    while (*rec_str == '\n')
   11895            0 :       rec_str++;
   11896              :    
   11897              :    /* check if it is a section title */
   11898            0 :    if (rec_str[0] == '[') {
   11899            0 :       rec_str++;
   11900              : 
   11901            0 :       title[0] = 0;
   11902              :       
   11903              :       /* extract title and append '/' */
   11904            0 :       mstrlcpy(title, rec_str, title_size);
   11905            0 :       char* p = strchr(title, ']');
   11906            0 :       if (p)
   11907            0 :          *p = 0;
   11908              : 
   11909            0 :       int len = strlen(title);
   11910            0 :       if (len > 0) {
   11911            0 :          if (title[len - 1] != '/')
   11912            0 :             mstrlcat(title, "/", title_size);
   11913              :       }
   11914              : 
   11915              :       // skip to the next end-of-line
   11916            0 :       const char* pend = strchr(rec_str, '\n');
   11917            0 :       if (pend)
   11918            0 :          rec_str = pend;
   11919              :       else
   11920            0 :          rec_str = rec_str+strlen(rec_str);
   11921              : 
   11922            0 :       while (*rec_str == '\n')
   11923            0 :          rec_str++;
   11924              : 
   11925            0 :       *out_rec_str = rec_str;
   11926            0 :       return DB_SUCCESS;
   11927              :    }
   11928              : 
   11929            0 :    if (rec_str[0] == ';') {
   11930              :       // skip to the next end-of-line
   11931            0 :       const char* pend = strchr(rec_str, '\n');
   11932            0 :       if (pend)
   11933            0 :          rec_str = pend;
   11934              :       else
   11935            0 :          rec_str = rec_str+strlen(rec_str);
   11936              : 
   11937            0 :       while (*rec_str == '\n')
   11938            0 :          rec_str++;
   11939              : 
   11940            0 :       *out_rec_str = rec_str;
   11941            0 :       return DB_SUCCESS;
   11942              :    }
   11943              : 
   11944            0 :    const char* peq = strchr(rec_str, '=');
   11945            0 :    if (!peq) {
   11946            0 :       cm_msg(MERROR, "db_parse_record", "do not see \'=\'");
   11947            0 :       return DB_INVALID_PARAM;
   11948              :    }
   11949              : 
   11950            0 :    int key_name_len = peq - rec_str;
   11951              : 
   11952              :    // remove trailing equals sign and trailing spaces
   11953            0 :    while (key_name_len > 1) {
   11954            0 :       if (rec_str[key_name_len-1] == '=') {
   11955            0 :          key_name_len--;
   11956            0 :          continue;
   11957              :       }
   11958            0 :       if (rec_str[key_name_len-1] == ' ') {
   11959            0 :          key_name_len--;
   11960            0 :          continue;
   11961              :       }
   11962            0 :       break;
   11963              :    }
   11964              : 
   11965            0 :    memcpy(key_name, rec_str, key_name_len);
   11966            0 :    key_name[key_name_len] = 0;
   11967              : 
   11968            0 :    rec_str = peq + 1; // consume the "=" sign
   11969              : 
   11970            0 :    while (*rec_str == ' ') // consume spaces
   11971            0 :       rec_str++;
   11972              : 
   11973              :    // extract type id
   11974              :    char stid[256];
   11975              :    int i;
   11976            0 :    for (i=0; i<(int)sizeof(stid)-1; i++) {
   11977            0 :       char s = *rec_str;
   11978            0 :       if (s == 0)    break;
   11979            0 :       if (s == ' ')  break;
   11980            0 :       if (s == '\n') break;
   11981            0 :       if (s == '[')  break;
   11982            0 :       stid[i] = s;
   11983            0 :       rec_str++;
   11984              :    }
   11985            0 :    stid[i] = 0;
   11986              : 
   11987            0 :    DWORD xtid = 0;
   11988            0 :    for (xtid = 0; xtid < TID_LAST; xtid++) {
   11989            0 :       if (strcmp(rpc_tid_name(xtid), stid) == 0) {
   11990            0 :          *tid = xtid;
   11991            0 :          break;
   11992              :       }
   11993              :    }
   11994              : 
   11995              :    //printf("tid [%s], tid %d\n", stid, *tid);
   11996              : 
   11997            0 :    if (xtid == TID_LAST) {
   11998            0 :       cm_msg(MERROR, "db_parse_record", "do not see \':\'");
   11999            0 :       return DB_INVALID_PARAM;
   12000              :    }
   12001              :       
   12002            0 :    while (*rec_str == ' ') // consume spaces
   12003            0 :       rec_str++;
   12004              : 
   12005            0 :    *n_data = 1;
   12006              : 
   12007            0 :    if (*rec_str == '[') {
   12008              :       // decode array size
   12009            0 :       rec_str++; // cosume the '['
   12010            0 :       *n_data = atoi(rec_str);
   12011            0 :       const char *pbr = strchr(rec_str, ']');
   12012            0 :       if (!pbr) {
   12013            0 :          cm_msg(MERROR, "db_parse_record", "do not see closing bracket \']\'");
   12014            0 :          return DB_INVALID_PARAM;
   12015              :       }
   12016            0 :       rec_str = pbr + 1; // skip the closing bracket
   12017              :    }
   12018              :    
   12019            0 :    while (*rec_str == ' ') // consume spaces
   12020            0 :       rec_str++;
   12021              : 
   12022            0 :    const char* pcol = strchr(rec_str, ':');
   12023            0 :    if (!pcol) {
   12024            0 :       cm_msg(MERROR, "db_parse_record", "do not see \':\'");
   12025            0 :       return DB_INVALID_PARAM;
   12026              :    }
   12027              : 
   12028            0 :    rec_str = pcol + 1; // skip the ":"
   12029              : 
   12030            0 :    while (*rec_str == ' ') // consume spaces
   12031            0 :       rec_str++;
   12032              : 
   12033            0 :    *string_length = 0;
   12034            0 :    if (xtid == TID_LINK || xtid == TID_STRING) {
   12035              :       // extract string length
   12036            0 :       const char* pbr = strchr(rec_str, '[');
   12037            0 :       if (pbr) {
   12038            0 :          *string_length = atoi(pbr+1);
   12039              :       }
   12040              :    }
   12041              : 
   12042              :    // skip to the next end-of-line
   12043            0 :    const char* pend = strchr(rec_str, '\n');
   12044            0 :    if (pend)
   12045            0 :       rec_str = pend;
   12046              :    else
   12047            0 :       rec_str = rec_str+strlen(rec_str);
   12048              :    
   12049            0 :    while (*rec_str == '\n')
   12050            0 :       rec_str++;
   12051              :    
   12052            0 :    *out_rec_str = rec_str;
   12053            0 :    return DB_SUCCESS;
   12054              : }
   12055              : 
   12056            0 : static int db_get_record2_read_element(HNDLE hDB, HNDLE hKey, const char* key_name, int tid, int n_data, int string_length, char* buf_start, char** buf_ptr, int* buf_remain, BOOL correct)
   12057              : {
   12058            0 :    assert(tid > 0);
   12059            0 :    assert(n_data > 0);
   12060            0 :    int tsize = rpc_tid_size(tid);
   12061            0 :    int offset = *buf_ptr - buf_start;
   12062            0 :    int align = 0;
   12063            0 :    if (tsize && (offset%tsize != 0)) {
   12064            0 :       while (offset%tsize != 0) {
   12065            0 :          align++;
   12066            0 :          *(*buf_ptr) = 0xFF; // pad bytes for correct data alignement
   12067            0 :          (*buf_ptr)++;
   12068            0 :          (*buf_remain)--;
   12069            0 :          offset++;
   12070              :       }
   12071              :    }
   12072            0 :    printf("read element [%s] tid %d, n_data %d, string_length %d, tid_size %d, align %d, offset %d, buf_remain %d\n", key_name, tid, n_data, string_length, tsize, align, offset, *buf_remain);
   12073            0 :    if (tsize > 0) {
   12074            0 :       int xsize = tsize*n_data;
   12075            0 :       if (xsize > *buf_remain) {
   12076            0 :          cm_msg(MERROR, "db_get_record2", "buffer overrun at key \"%s\", size %d, buffer remaining %d", key_name, xsize, *buf_remain);
   12077            0 :          return DB_INVALID_PARAM;
   12078              :       }
   12079            0 :       int ysize = xsize;
   12080            0 :       int status = db_get_value(hDB, hKey, key_name, *buf_ptr, &ysize, tid, FALSE);
   12081              :       //printf("status %d, xsize %d\n", status, xsize);
   12082            0 :       if (status != DB_SUCCESS) {
   12083            0 :          cm_msg(MERROR, "db_get_record2", "cannot read \"%s\", db_get_value() status %d", key_name, status);
   12084            0 :          memset(*buf_ptr, 0, xsize);
   12085            0 :          *buf_ptr += xsize;
   12086            0 :          *buf_remain -= xsize;
   12087            0 :          return status;
   12088              :       }
   12089            0 :       *buf_ptr += xsize;
   12090            0 :       *buf_remain -= xsize;
   12091            0 :    } else if (tid == TID_STRING) {
   12092            0 :       int xstatus = 0;
   12093              :       int i;
   12094            0 :       for (i=0; i<n_data; i++) {
   12095            0 :          int xsize = string_length;
   12096            0 :          if (xsize > *buf_remain) {
   12097            0 :             cm_msg(MERROR, "db_get_record2", "string buffer overrun at key \"%s\" index %d, size %d, buffer remaining %d", key_name, i, xsize, *buf_remain);
   12098            0 :             return DB_INVALID_PARAM;
   12099              :          }
   12100              :          char xkey_name[MAX_ODB_PATH+100];
   12101            0 :          sprintf(xkey_name, "%s[%d]", key_name, i);
   12102            0 :          int status = db_get_value(hDB, hKey, xkey_name, *buf_ptr, &xsize, tid, FALSE);
   12103              :          //printf("status %d, string length %d, xsize %d, actual len %d\n", status, string_length, xsize, (int)strlen(*buf_ptr));
   12104            0 :          if (status == DB_TRUNCATED) {
   12105              :             // make sure string is NUL terminated
   12106            0 :             (*buf_ptr)[string_length-1] = 0;
   12107            0 :             cm_msg(MERROR, "db_get_record2", "string key \"%s\" index %d, string value was truncated", key_name, i);
   12108            0 :          } else if (status != DB_SUCCESS) {
   12109            0 :             cm_msg(MERROR, "db_get_record2", "cannot read string \"%s\"[%d], db_get_value() status %d", key_name, i, status);
   12110            0 :             memset(*buf_ptr, 0, string_length);
   12111            0 :             xstatus = status;
   12112              :          }
   12113            0 :          *buf_ptr += string_length;
   12114            0 :          *buf_remain -= string_length;
   12115              :       }
   12116            0 :       if (xstatus != 0) {
   12117            0 :          return xstatus;
   12118              :       }
   12119              :    } else {
   12120            0 :       cm_msg(MERROR, "db_get_record2", "cannot read key \"%s\" of unsupported type %d", key_name, tid);
   12121            0 :       return DB_INVALID_PARAM;
   12122              :    }
   12123            0 :    return DB_SUCCESS;
   12124              : }
   12125              : 
   12126              : /********************************************************************/
   12127              : /**
   12128              : Copy a set of keys to local memory.
   12129              : 
   12130              : An ODB sub-tree can be mapped to a C structure automatically via a
   12131              : hot-link using the function db_open_record1() or manually with this function.
   12132              : For correct operation, the description string *must* match the C data
   12133              : structure. If the contents of ODB sub-tree does not exactly match
   12134              : the description string, db_get_record2() will try to read as much as it can
   12135              : and return DB_TRUNCATED to inform the user that there was a mismatch somewhere.
   12136              : To ensure that the ODB sub-tree matches the desciption string, call db_create_record()
   12137              : or db_check_record() before calling db_get_record2(). Unlike db_get_record()
   12138              : and db_get_record1(), this function will not complain about data strucure mismatches.
   12139              : It will ignore all extra entries in the ODB sub-tree and it will set to zero the C-structure
   12140              : data fields that do not have corresponding ODB entries.
   12141              : \code
   12142              : struct {
   12143              :   INT level1;
   12144              :   INT level2;
   12145              : } trigger_settings;
   12146              : const char *trigger_settings_str =
   12147              : "[Settings]\n\
   12148              : level1 = INT : 0\n\
   12149              : level2 = INT : 0";
   12150              : 
   12151              : main()
   12152              : {
   12153              :   HNDLE hDB, hkey;
   12154              :   INT   size;
   12155              :   ...
   12156              :   cm_get_experiment_database(&hDB, NULL);
   12157              :   db_create_record(hDB, 0, "/Equipment/Trigger", trigger_settings_str);
   12158              :   db_find_key(hDB, 0, "/Equipment/Trigger/Settings", &hkey);
   12159              :   size = sizeof(trigger_settings);
   12160              :   db_get_record2(hDB, hkey, &trigger_settings, &size, 0, trigger_settings_str);
   12161              :   ...
   12162              : }
   12163              : \endcode
   12164              : @param hDB          ODB handle obtained via cm_get_experiment_database().
   12165              : @param hKey         Handle for key where search starts, zero for root.
   12166              : @param data         Pointer to the retrieved data.
   12167              : @param buf_size     Size of data structure, must be obtained via sizeof(data).
   12168              : @param align        Byte alignment calculated by the stub and
   12169              :                     passed to the rpc side to align data
   12170              :                     according to local machine. Must be zero
   12171              :                     when called from user level.
   12172              : @param rec_str      Description of the data structure, see db_create_record()
   12173              : @param correct      Must be set to zero
   12174              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_STRUCT_SIZE_MISMATCH
   12175              : */
   12176            0 : INT db_get_record2(HNDLE hDB, HNDLE hKey, void *data, INT * xbuf_size, INT align, const char *rec_str, BOOL correct)
   12177              : {
   12178            0 :    int status = DB_SUCCESS;
   12179              : 
   12180            0 :    printf("db_get_record2!\n");
   12181              : 
   12182            0 :    assert(data != NULL);
   12183            0 :    assert(xbuf_size != NULL);
   12184            0 :    assert(*xbuf_size > 0);
   12185            0 :    assert(correct == 0);
   12186              : 
   12187            0 :    int truncated = 0;
   12188              : #if 1
   12189            0 :    char* r1 = NULL;
   12190            0 :    int rs = *xbuf_size;
   12191              :    if (1) {
   12192            0 :       r1 = (char*)malloc(rs);
   12193            0 :       assert(r1 != NULL);
   12194            0 :       memset(data, 0xFF, *xbuf_size);
   12195            0 :       memset(r1, 0xFF, rs);
   12196              :       //status = db_get_record1(hDB, hKey, r1, &rs, 0, rec_str);
   12197            0 :       status = db_get_record(hDB, hKey, r1, &rs, 0);
   12198            0 :       printf("db_get_record status %d\n", status);
   12199              :    }
   12200              : #endif
   12201              :       
   12202            0 :    char* buf_start = (char*)data;
   12203            0 :    int buf_size = *xbuf_size;
   12204              : 
   12205            0 :    char* buf_ptr = buf_start;
   12206            0 :    int buf_remain = buf_size;
   12207              : 
   12208            0 :    while (rec_str && *rec_str != 0) {
   12209              :       char title[256];
   12210              :       char key_name[MAX_ODB_PATH];
   12211            0 :       int tid = 0;
   12212            0 :       int n_data = 0;
   12213            0 :       int string_length = 0;
   12214            0 :       const char* rec_str_next = NULL;
   12215              :       
   12216            0 :       status = db_parse_record(rec_str, &rec_str_next, title, sizeof(title), key_name, sizeof(key_name), &tid, &n_data, &string_length);
   12217              : 
   12218              :       //printf("parse [%s], status %d, title [%s], key_name [%s], tid %d, n_data %d, string_length %d, next [%s]\n", rec_str, status, title, key_name, tid, n_data, string_length, rec_str_next);
   12219              : 
   12220            0 :       rec_str = rec_str_next;
   12221              : 
   12222            0 :       if (status != DB_SUCCESS) {
   12223            0 :          return status;
   12224              :       }
   12225              : 
   12226            0 :       if (key_name[0] == 0) {
   12227              :          // skip title strings, comments, etc
   12228            0 :          continue;
   12229              :       }
   12230              :       
   12231            0 :       status = db_get_record2_read_element(hDB, hKey, key_name, tid, n_data, string_length, buf_start, &buf_ptr, &buf_remain, correct);
   12232            0 :       if (status == DB_INVALID_PARAM) {
   12233            0 :          cm_msg(MERROR, "db_get_record2", "error: cannot continue reading odb record because of previous fatal error, status %d", status);
   12234            0 :          return DB_INVALID_PARAM;
   12235            0 :       } if (status != DB_SUCCESS) {
   12236            0 :          truncated = 1;
   12237              :       }
   12238              : 
   12239            0 :       rec_str = rec_str_next;
   12240              :    }
   12241              : 
   12242            0 :    if (r1) {
   12243            0 :       int ok = -1;
   12244              :       int i;
   12245            0 :       for (i=0; i<rs; i++) {
   12246            0 :          if (r1[i] != buf_start[i]) {
   12247            0 :             ok = i;
   12248            0 :             break;
   12249              :          }
   12250              :       }
   12251            0 :       if (ok>=0 || buf_remain>0) {
   12252            0 :          printf("db_get_record2: miscompare at %d out of %d, buf_remain %d\n", ok, rs, buf_remain);
   12253              :       } else {
   12254            0 :          printf("db_get_record2: check ok\n");
   12255              :       }
   12256              :    }
   12257              :    
   12258            0 :    if (buf_remain > 0) {
   12259              :       // FIXME: we finished processing the data definition string, but unused space remains in the buffer
   12260            0 :       return DB_TRUNCATED;
   12261              :    }
   12262              : 
   12263            0 :    if (truncated)
   12264            0 :       return DB_TRUNCATED;
   12265              :    else
   12266            0 :       return DB_SUCCESS;
   12267              : }
   12268              : 
   12269              : /********************************************************************/
   12270              : /**
   12271              : Copy a set of keys from local memory to the database.
   12272              : 
   12273              : An ODB sub-tree can be mapped to a C structure automatically via a
   12274              : hot-link using the function db_open_record() or manually with this function.
   12275              : Problems might occur if the ODB sub-tree contains values which don't match the
   12276              : C structure. Although the structure size is checked against the sub-tree size, no
   12277              : checking can be done if the type and order of the values in the structure are the
   12278              : same than those in the ODB sub-tree. Therefore it is recommended to use the
   12279              : function db_create_record() before using this function.
   12280              : \code
   12281              : ...
   12282              :   memset(&lazyst,0,size);
   12283              :   if (db_find_key(hDB, pLch->hKey, "Statistics",&hKeyst) == DB_SUCCESS)
   12284              :     status = db_set_record(hDB, hKeyst, &lazyst, size, 0);
   12285              :   else
   12286              :     cm_msg(MERROR,"task","record %s/statistics not found", pLch->name)
   12287              : ...
   12288              : \endcode
   12289              : @param hDB          ODB handle obtained via cm_get_experiment_database().
   12290              : @param hKey Handle for key where search starts, zero for root.
   12291              : @param data Pointer where data is stored.
   12292              : @param buf_size Size of data structure, must be obtained via sizeof(RECORD-NAME).
   12293              : @param align  Byte alignment calculated by the stub and
   12294              :               passed to the rpc side to align data
   12295              :               according to local machine. Must be zero
   12296              :               when called from user level.
   12297              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TYPE_MISMATCH, DB_STRUCT_SIZE_MISMATCH
   12298              : */
   12299            0 : INT db_set_record(HNDLE hDB, HNDLE hKey, void *data, INT buf_size, INT align)
   12300              : {
   12301            0 :    if (rpc_is_remote()) {
   12302            0 :       align = ss_get_struct_align();
   12303            0 :       return rpc_call(RPC_DB_SET_RECORD, hDB, hKey, data, buf_size, align);
   12304              :    }
   12305              : #ifdef LOCAL_ROUTINES
   12306              :    {
   12307              :       KEY key;
   12308              :       INT convert_flags;
   12309              :       INT total_size;
   12310              :       void *pdata;
   12311              : 
   12312            0 :       convert_flags = 0;
   12313              : 
   12314            0 :       if (!align)
   12315            0 :          align = ss_get_struct_align();
   12316              :       else {
   12317              :          /* only convert data if called remotely, as indicated by align != 0 */
   12318            0 :          if (rpc_is_mserver()) {
   12319            0 :             convert_flags = rpc_get_convert_flags();
   12320              :          }
   12321              :       }
   12322              : 
   12323              :       /* check if key has subkeys */
   12324            0 :       db_get_key(hDB, hKey, &key);
   12325            0 :       if (key.type != TID_KEY) {
   12326              :          /* copy single key */
   12327            0 :          if (key.item_size * key.num_values != buf_size) {
   12328            0 :             cm_msg(MERROR, "db_set_record", "struct size mismatch for \"%s\"", key.name);
   12329            0 :             return DB_STRUCT_SIZE_MISMATCH;
   12330              :          }
   12331              : 
   12332            0 :          if (convert_flags) {
   12333            0 :             if (key.num_values > 1)
   12334            0 :                rpc_convert_data(data, key.type, RPC_FIXARRAY, key.item_size * key.num_values, convert_flags);
   12335              :             else
   12336            0 :                rpc_convert_single(data, key.type, 0, convert_flags);
   12337              :          }
   12338              : 
   12339            0 :          db_set_data(hDB, hKey, data, key.total_size, key.num_values, key.type);
   12340            0 :          return DB_SUCCESS;
   12341              :       }
   12342              : 
   12343              :       /* check record size */
   12344            0 :       db_get_record_size(hDB, hKey, align, &total_size);
   12345            0 :       if (total_size != buf_size) {
   12346            0 :          cm_msg(MERROR, "db_set_record", "struct size mismatch for \"%s\"", key.name);
   12347            0 :          return DB_STRUCT_SIZE_MISMATCH;
   12348              :       }
   12349              : 
   12350              :       /* set subkey data */
   12351            0 :       pdata = data;
   12352            0 :       total_size = 0;
   12353              : 
   12354            0 :       db_lock_database(hDB);
   12355            0 :       db_err_msg* msg = NULL;
   12356            0 :       db_allow_write_locked(&_database[hDB-1], "db_set_record");
   12357            0 :       db_recurse_record_tree_locked(hDB, hKey, &pdata, &total_size, align, NULL, TRUE, convert_flags, &msg);
   12358            0 :       db_unlock_database(hDB);
   12359            0 :       if (msg)
   12360            0 :          db_flush_msg(&msg);
   12361              :    }
   12362              : #endif                          /* LOCAL_ROUTINES */
   12363              : 
   12364            0 :    return DB_SUCCESS;
   12365              : }
   12366              : 
   12367              : /**dox***************************************************************/
   12368              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
   12369              : 
   12370              : /*------------------------------------------------------------------*/
   12371            2 : INT db_add_open_record(HNDLE hDB, HNDLE hKey, WORD access_mode)
   12372              : /********************************************************************\
   12373              : 
   12374              :   Routine: db_add_open_record
   12375              : 
   12376              :   Purpose: Server part of db_open_record. Internal use only.
   12377              : 
   12378              : \********************************************************************/
   12379              : {
   12380            2 :    if (rpc_is_remote())
   12381            0 :       return rpc_call(RPC_DB_ADD_OPEN_RECORD, hDB, hKey, access_mode);
   12382              : 
   12383              : #ifdef LOCAL_ROUTINES
   12384              :    {
   12385              :       INT i;
   12386              :       int status;
   12387              : 
   12388            2 :       if (hDB > _database_entries || hDB <= 0) {
   12389            0 :          cm_msg(MERROR, "db_add_open_record", "invalid database handle");
   12390            0 :          return DB_INVALID_HANDLE;
   12391              :       }
   12392              : 
   12393            2 :       db_err_msg* msg = NULL;
   12394              : 
   12395              :       /* lock database */
   12396            2 :       db_lock_database(hDB);
   12397              : 
   12398            2 :       DATABASE *pdb = &_database[hDB - 1];
   12399            2 :       DATABASE_HEADER *pheader = pdb->database_header;
   12400            2 :       DATABASE_CLIENT *pclient = db_get_my_client_locked(pdb);
   12401              : 
   12402              :       /* check if key is already open */
   12403            2 :       for (i = 0; i < pclient->max_index; i++) {
   12404            0 :          if (pclient->open_record[i].handle == hKey)
   12405            0 :             break;
   12406              :       }
   12407              : 
   12408            2 :       if (i < pclient->max_index) {
   12409            0 :          db_unlock_database(hDB);
   12410            0 :          return DB_SUCCESS;
   12411              :       }
   12412              : 
   12413              :       /* not found, search free entry */
   12414            2 :       for (i = 0; i < pclient->max_index; i++) {
   12415            0 :          if (pclient->open_record[i].handle == 0)
   12416            0 :             break;
   12417              :       }
   12418              : 
   12419              :       /* check if maximum number reached */
   12420            2 :       if (i == MAX_OPEN_RECORDS) {
   12421            0 :          db_unlock_database(hDB);
   12422            0 :          return DB_NO_MEMORY;
   12423              :       }
   12424              : 
   12425            2 :       db_allow_write_locked(pdb, "db_add_open_record");
   12426              : 
   12427            2 :       KEY *pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_add_open_record", &msg);
   12428              : 
   12429            2 :       if (!pkey) {
   12430            0 :          db_unlock_database(hDB);
   12431            0 :          if (msg)
   12432            0 :             db_flush_msg(&msg);
   12433            0 :          return status;
   12434              :       }
   12435              : 
   12436            2 :       if (i == pclient->max_index)
   12437            2 :          pclient->max_index++;
   12438              : 
   12439            2 :       pclient->open_record[i].handle = hKey;
   12440            2 :       pclient->open_record[i].access_mode = access_mode;
   12441              : 
   12442              :       /* increment notify_count */
   12443            2 :       pkey->notify_count++;
   12444              : 
   12445            2 :       pclient->num_open_records++;
   12446              : 
   12447              :       /* set exclusive bit if requested */
   12448            2 :       if (access_mode & MODE_WRITE)
   12449            0 :          db_set_mode(hDB, hKey, (WORD) (pkey->access_mode | MODE_EXCLUSIVE), 2);
   12450              : 
   12451            2 :       db_unlock_database(hDB);
   12452              :    }
   12453              : #endif                          /* LOCAL_ROUTINES */
   12454              : 
   12455            2 :    return DB_SUCCESS;
   12456              : }
   12457              : 
   12458              : #ifdef LOCAL_ROUTINES
   12459              : 
   12460            2 : static int db_remove_open_record_wlocked(DATABASE* pdb, DATABASE_HEADER* pheader, HNDLE hKey)
   12461              : {
   12462            2 :    int status = DB_SUCCESS;
   12463              : 
   12464            2 :    DATABASE_CLIENT *pclient = db_get_my_client_locked(pdb);
   12465              : 
   12466              :    /* search key */
   12467              :    int idx;
   12468            2 :    for (idx = 0; idx < pclient->max_index; idx++)
   12469            2 :       if (pclient->open_record[idx].handle == hKey)
   12470            2 :          break;
   12471              :    
   12472            2 :    if (idx == pclient->max_index) {
   12473            0 :       return DB_INVALID_HANDLE;
   12474              :    }
   12475              : 
   12476            2 :    KEY* pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_remove_open_record_wlocked", NULL);
   12477              : 
   12478            2 :    if (!pkey)
   12479            0 :       return status;
   12480              : 
   12481              :    /* decrement notify_count */
   12482              : 
   12483            2 :    if (pkey->notify_count > 0)
   12484            2 :       pkey->notify_count--;
   12485              :    
   12486            2 :    pclient->num_open_records--;
   12487              :    
   12488              :    /* remove exclusive flag */
   12489            2 :    if (pclient->open_record[idx].access_mode & MODE_WRITE)
   12490            0 :       db_set_mode_wlocked(pheader, pkey, (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2, NULL);
   12491              :    
   12492            2 :    memset(&pclient->open_record[idx], 0, sizeof(OPEN_RECORD));
   12493              :    
   12494              :    /* calculate new max_index entry */
   12495              :    int i;
   12496            4 :    for (i = pclient->max_index - 1; i >= 0; i--)
   12497            2 :       if (pclient->open_record[i].handle != 0)
   12498            0 :          break;
   12499            2 :    pclient->max_index = i + 1;
   12500              : 
   12501            2 :    return DB_SUCCESS;
   12502              : }
   12503              : #endif // LOCAL_ROUTINES
   12504              : 
   12505              : 
   12506            2 : INT db_remove_open_record(HNDLE hDB, HNDLE hKey, BOOL lock)
   12507              : /********************************************************************\
   12508              : 
   12509              :   Routine: db_remove_open_record
   12510              : 
   12511              :   Purpose: Gets called by db_close_record. Internal use only.
   12512              : 
   12513              : \********************************************************************/
   12514              : {
   12515            2 :    if (rpc_is_remote())
   12516            0 :       return rpc_call(RPC_DB_REMOVE_OPEN_RECORD, hDB, hKey, lock);
   12517              : 
   12518            2 :    int status = DB_SUCCESS;
   12519              :    
   12520              : #ifdef LOCAL_ROUTINES
   12521              :    {
   12522            2 :       if (hDB > _database_entries || hDB <= 0) {
   12523            0 :          cm_msg(MERROR, "db_remove_open_record", "invalid database handle %d", hDB);
   12524            0 :          return DB_INVALID_HANDLE;
   12525              :       }
   12526              : 
   12527            2 :       db_lock_database(hDB);
   12528              : 
   12529            2 :       DATABASE *pdb = &_database[hDB - 1];
   12530            2 :       DATABASE_HEADER *pheader = pdb->database_header;
   12531              : 
   12532            2 :       db_allow_write_locked(pdb, "db_remove_open_record");
   12533              : 
   12534            2 :       status = db_remove_open_record_wlocked(pdb, pheader, hKey);
   12535              : 
   12536            2 :       db_unlock_database(hDB);
   12537              :    }
   12538              : #endif                          /* LOCAL_ROUTINES */
   12539              : 
   12540            2 :    return status;
   12541              : }
   12542              : 
   12543              : /*------------------------------------------------------------------*/
   12544              : 
   12545              : #ifdef LOCAL_ROUTINES
   12546              : 
   12547          168 : static INT db_notify_clients_locked(const DATABASE_HEADER* pheader, HNDLE hDB, HNDLE hKeyMod, int index, BOOL bWalk, db_err_msg** msg)
   12548              : /********************************************************************\
   12549              : 
   12550              :   Routine: db_notify_clients
   12551              : 
   12552              :   Purpose: Gets called by db_set_xxx functions. Internal use only.
   12553              : 
   12554              : \********************************************************************/
   12555              : {
   12556              :    HNDLE hKey;
   12557              :    KEYLIST *pkeylist;
   12558              :    INT i, j;
   12559              :    char str[80];
   12560              :    int status;
   12561              : 
   12562          168 :    hKey = hKeyMod;
   12563              : 
   12564          168 :    const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_notify_clients", msg);
   12565              :    
   12566          168 :    if (!pkey) {
   12567            0 :       return status;
   12568              :    }
   12569              :    
   12570              :    do {
   12571              : 
   12572              :       /* check which client has record open */
   12573          878 :       if (pkey->notify_count)
   12574            0 :          for (i = 0; i < pheader->max_client_index; i++) {
   12575            0 :             const DATABASE_CLIENT* pclient = &pheader->client[i];
   12576            0 :             for (j = 0; j < pclient->max_index; j++)
   12577            0 :                if (pclient->open_record[j].handle == hKey) {
   12578              :                   /* send notification to remote process */
   12579            0 :                   sprintf(str, "O %d %d %d %d", hDB, hKey, hKeyMod, index);
   12580            0 :                   ss_resume(pclient->port, str);
   12581              :                }
   12582              :          }
   12583              : 
   12584          878 :       if (pkey->parent_keylist == 0 || !bWalk)
   12585          168 :          return DB_SUCCESS;
   12586              : 
   12587              :       // FIXME: validate pkey->parent_keylist
   12588          710 :       pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
   12589          710 :       pkey = db_get_pkey(pheader, pkeylist->parent, &status, "db_notify_clients", msg);
   12590          710 :       if (!pkey) {
   12591            0 :          return status;
   12592              :       }
   12593          710 :       hKey = db_pkey_to_hkey(pheader, pkey);
   12594          710 :    } while (TRUE);
   12595              : 
   12596              : }
   12597              : 
   12598              : #endif                          /* LOCAL_ROUTINES */
   12599              : /*------------------------------------------------------------------*/
   12600              : 
   12601              : 
   12602            0 : INT db_notify_clients(HNDLE hDB, HNDLE hKeyMod, int index, BOOL bWalk)
   12603              : /********************************************************************\
   12604              : 
   12605              :   Routine: db_notify_clients
   12606              : 
   12607              :   Purpose: Gets called by db_set_xxx functions. Internal use only.
   12608              :  \********************************************************************/
   12609              : {
   12610            0 :    if (rpc_is_remote()) {
   12611            0 :       cm_msg(MERROR, "db_notify_clients", "db_notify_clients() does not work in remotely connected MIDAS clients");
   12612            0 :       return DB_INVALID_HANDLE;
   12613              :    }
   12614              :    
   12615              : #ifdef LOCAL_ROUTINES
   12616              :    {
   12617            0 :       db_err_msg* msg = NULL;
   12618            0 :       int status = db_lock_database(hDB);
   12619            0 :       if (status != DB_SUCCESS)
   12620            0 :          return status;
   12621            0 :       DATABASE_HEADER* pheader = _database[hDB - 1].database_header;
   12622            0 :       db_notify_clients_locked(pheader, hDB, hKeyMod, index, bWalk, &msg);
   12623            0 :       db_unlock_database(hDB);
   12624            0 :       if (msg)
   12625            0 :          db_flush_msg(&msg);
   12626              :    }
   12627              : #endif
   12628            0 :    return DB_SUCCESS;
   12629              : }
   12630              : 
   12631            0 : INT db_notify_clients_array(HNDLE hDB, HNDLE hKeys[], INT size)
   12632              : /********************************************************************\
   12633              :  
   12634              :  Routine: db_notify_clients_array
   12635              :  
   12636              :  Purpose: This function is typically called after a set of calls
   12637              :           to db_set_data1 which omits hot-link notification to 
   12638              :           programs. After several ODB values are modified in a set,
   12639              :           this function has to be called to trigger the hot-links
   12640              :           of the whole set.
   12641              :  
   12642              :  \********************************************************************/
   12643              : {
   12644            0 :    if (rpc_is_remote())
   12645            0 :       return rpc_call(RPC_DB_NOTIFY_CLIENTS_ARRAY, hDB, hKeys, size);
   12646              :    
   12647              : #ifdef LOCAL_ROUTINES
   12648              :    {
   12649            0 :       int status = db_lock_database(hDB);
   12650            0 :       if (status != DB_SUCCESS)
   12651            0 :          return status;
   12652            0 :       db_err_msg* msg = NULL;
   12653            0 :       DATABASE_HEADER* pheader = _database[hDB - 1].database_header;
   12654            0 :       int count = size/sizeof(INT);
   12655            0 :       for (int i=0 ; i<count; i++) {
   12656            0 :          db_notify_clients_locked(pheader, hDB, hKeys[i], -1, TRUE, &msg);
   12657              :       }
   12658            0 :       db_unlock_database(hDB);
   12659            0 :       if (msg)
   12660            0 :          db_flush_msg(&msg);
   12661              :    }
   12662              : #endif
   12663            0 :    return DB_SUCCESS;
   12664              : }
   12665              : 
   12666              : /*------------------------------------------------------------------*/
   12667            4 : static void merge_records(HNDLE hDB, HNDLE hKey, KEY * pkey, INT level, void *info)
   12668              : {
   12669              :    char full_name[MAX_ODB_PATH];
   12670              :    INT status, size;
   12671              :    HNDLE hKeyInit;
   12672              :    KEY initkey, key;
   12673              : 
   12674              :    /* avoid compiler warnings */
   12675            4 :    status = level;
   12676              : 
   12677              :    /* compose name of init key */
   12678            4 :    std::string s = db_get_path(hDB, hKey);
   12679            4 :    mstrlcpy(full_name, s.c_str(), sizeof(full_name));
   12680            4 :    *strchr(full_name, 'O') = 'I';
   12681              : 
   12682              :    /* if key in init record found, copy original data to init data */
   12683            4 :    status = db_find_key(hDB, 0, full_name, &hKeyInit);
   12684            4 :    if (status == DB_SUCCESS) {
   12685            4 :       status = db_get_key(hDB, hKeyInit, &initkey);
   12686            4 :       if (status != DB_SUCCESS) {
   12687            0 :          cm_msg(MERROR, "merge_records", "merge_record error at \'%s\', db_get_key() status %d", full_name, status);
   12688            0 :          return;
   12689              :       }
   12690            4 :       status = db_get_key(hDB, hKey, &key);
   12691            4 :       if (status != DB_SUCCESS) {
   12692            0 :          cm_msg(MERROR, "merge_records", "merge_record error at \'%s\', second db_get_key() status %d", full_name, status);
   12693            0 :          return;
   12694              :       }
   12695              : 
   12696            4 :       if (initkey.type != TID_KEY && initkey.type == key.type) {
   12697            2 :          char* allocbuffer = NULL;
   12698              :          char  stackbuffer[10000];
   12699            2 :          char* buffer = stackbuffer;
   12700            2 :          size = sizeof(stackbuffer);
   12701              :          while (1) {
   12702              :             /* copy data from original key to new key */
   12703            2 :             status = db_get_data(hDB, hKey, buffer, &size, initkey.type);
   12704            2 :             if (status == DB_SUCCESS) {
   12705            2 :                status = db_set_data(hDB, hKeyInit, buffer, initkey.total_size, initkey.num_values, initkey.type);
   12706            2 :                if (status != DB_SUCCESS) {
   12707            0 :                   cm_msg(MERROR, "merge_records", "merge_record error at \'%s\', db_set_data() status %d", full_name, status);
   12708            0 :                   return;
   12709              :                }
   12710            2 :                break;
   12711              :             }
   12712            0 :             if (status == DB_TRUNCATED) {
   12713            0 :                size *= 2;
   12714            0 :                allocbuffer = (char *)realloc(allocbuffer, size);
   12715            0 :                assert(allocbuffer != NULL);
   12716            0 :                buffer = allocbuffer;
   12717            0 :                continue;
   12718              :             }
   12719            0 :             cm_msg(MERROR, "merge_records", "aborting on unexpected failure of db_get_data(%s), status %d", full_name, status);
   12720            0 :             abort();
   12721              :          }
   12722            2 :          if (allocbuffer)
   12723            0 :             free(allocbuffer);
   12724              :       }
   12725            0 :    } else if (status == DB_NO_KEY) {
   12726              :       /* do nothing */
   12727            0 :    } else if (status == DB_INVALID_LINK) {
   12728            0 :       status = db_find_link(hDB, 0, full_name, &hKeyInit);
   12729            0 :       if (status == DB_SUCCESS) {
   12730            0 :          size = sizeof(full_name);
   12731            0 :          status = db_get_data(hDB, hKeyInit, full_name, &size, TID_LINK);
   12732              :       }
   12733            0 :       cm_msg(MERROR, "merge_records", "Invalid link \"%s\"", full_name);
   12734              :    } else {
   12735            0 :       cm_msg(MERROR, "merge_records", "aborting on unexpected failure of db_find_key(%s), status %d", full_name, status);
   12736            0 :       abort();
   12737              :    }
   12738            4 : }
   12739              : 
   12740              : static int _global_open_count; // FIXME: this is not thread-safe
   12741              : 
   12742            4 : static void check_open_keys(HNDLE hDB, HNDLE hKey, KEY * pkey, INT level, void *info)
   12743              : {
   12744            4 :    if (pkey->notify_count)
   12745            0 :       _global_open_count++;
   12746            4 : }
   12747              : 
   12748              : /**dox***************************************************************/
   12749              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
   12750              : 
   12751              : /********************************************************************/
   12752              : /**
   12753              : Create a record. If a part of the record exists alreay,
   12754              : merge it with the init_str (use values from the init_str only when they are
   12755              : not in the existing record).
   12756              : 
   12757              : This functions creates a ODB sub-tree according to an ASCII
   12758              : representation of that tree. See db_copy() for a description. It can be used to
   12759              : create a sub-tree which exactly matches a C structure. The sub-tree can then
   12760              : later mapped to the C structure ("hot-link") via the function db_open_record().
   12761              : 
   12762              : If a sub-tree exists already which exactly matches the ASCII representation, it is
   12763              : not modified. If part of the tree exists, it is merged with the ASCII representation
   12764              : where the ODB values have priority, only values not present in the ODB are
   12765              : created with the default values of the ASCII representation. It is therefore
   12766              : recommended that before creating an ODB hot-link the function
   12767              : db_create_record() is called to insure that the ODB tree and the C structure
   12768              : contain exactly the same values in the same order.
   12769              : 
   12770              : Following example creates a record under /Equipment/Trigger/Settings,
   12771              : opens a hot-link between that record and a local C structure
   12772              : trigger_settings and registers a callback function trigger_update()
   12773              : which gets called each time the record is changed.
   12774              : \code
   12775              : struct {
   12776              :   INT level1;
   12777              :   INT level2;
   12778              : } trigger_settings;
   12779              : char *trigger_settings_str =
   12780              : "[Settings]\n\
   12781              : level1 = INT : 0\n\
   12782              : level2 = INT : 0";
   12783              : void trigger_update(INT hDB, INT hkey, void *info)
   12784              : {
   12785              :   printf("New levels: %d %d\n",
   12786              :     trigger_settings.level1,
   12787              :     trigger_settings.level2);
   12788              : }
   12789              : main()
   12790              : {
   12791              :   HNDLE hDB, hkey;
   12792              :   char[128] info;
   12793              :   ...
   12794              :   cm_get_experiment_database(&hDB, NULL);
   12795              :   db_create_record(hDB, 0, "/Equipment/Trigger", trigger_settings_str);
   12796              :   db_find_key(hDB, 0,"/Equipment/Trigger/Settings", &hkey);
   12797              :   db_open_record(hDB, hkey, &trigger_settings,
   12798              :     sizeof(trigger_settings), MODE_READ, trigger_update, info);
   12799              :   ...
   12800              : }
   12801              : \endcode
   12802              : @param hDB          ODB handle obtained via cm_get_experiment_database().
   12803              : @param hKey         Handle for key where search starts, zero for root.
   12804              : @param orig_key_name     Name of key to search, can contain directories.
   12805              : @param init_str     Initialization string in the format of the db_copy/db_save functions.
   12806              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_FULL, DB_NO_ACCESS, DB_OPEN_RECORD
   12807              : */
   12808            2 : INT db_create_record(HNDLE hDB, HNDLE hKey, const char *orig_key_name, const char *init_str)
   12809              : {
   12810              :    char str[256], key_name[256], *buffer;
   12811              :    INT status, size, buffer_size;
   12812              :    HNDLE hKeyTmp, hKeyTmpO, hKeyOrig, hSubkey;
   12813              : 
   12814            2 :    if (rpc_is_remote())
   12815            0 :       return rpc_call(RPC_DB_CREATE_RECORD, hDB, hKey, orig_key_name, init_str);
   12816              : 
   12817              :    /* make this function atomic */
   12818            2 :    db_lock_database(hDB);
   12819              : 
   12820              :    /* strip trailing '/' */
   12821            2 :    mstrlcpy(key_name, orig_key_name, sizeof(key_name));
   12822            2 :    if (strlen(key_name) > 1 && key_name[strlen(key_name) - 1] == '/')
   12823            0 :       key_name[strlen(key_name) - 1] = 0;
   12824              : 
   12825              :    /* merge temporay record and original record */
   12826            2 :    status = db_find_key(hDB, hKey, key_name, &hKeyOrig);
   12827            2 :    if (status == DB_SUCCESS) {
   12828            2 :       assert(hKeyOrig != 0);
   12829              : #ifdef CHECK_OPEN_RECORD
   12830              :       /* check if key or subkey is opened */
   12831            2 :       _global_open_count = 0; // FIXME: this is not thread safe
   12832            2 :       db_scan_tree_link(hDB, hKeyOrig, 0, check_open_keys, NULL);
   12833            2 :       if (_global_open_count) {
   12834            0 :          db_unlock_database(hDB);
   12835            0 :          return DB_OPEN_RECORD;
   12836              :       }
   12837              : #endif
   12838              :       /* create temporary records */
   12839            2 :       sprintf(str, "/System/Tmp/%sI", ss_tid_to_string(ss_gettid()).c_str());
   12840              :       //printf("db_create_record str [%s]\n", str);
   12841            2 :       db_find_key(hDB, 0, str, &hKeyTmp);
   12842            2 :       if (hKeyTmp)
   12843            0 :          db_delete_key(hDB, hKeyTmp, FALSE);
   12844            2 :       db_create_key(hDB, 0, str, TID_KEY);
   12845            2 :       status = db_find_key(hDB, 0, str, &hKeyTmp);
   12846            2 :       if (status != DB_SUCCESS) {
   12847            0 :          db_unlock_database(hDB);
   12848            0 :          return status;
   12849              :       }
   12850              : 
   12851            2 :       sprintf(str, "/System/Tmp/%sO", ss_tid_to_string(ss_gettid()).c_str());
   12852              :       //printf("db_create_record str [%s]\n", str);
   12853            2 :       db_find_key(hDB, 0, str, &hKeyTmpO);
   12854            2 :       if (hKeyTmpO)
   12855            0 :          db_delete_key(hDB, hKeyTmpO, FALSE);
   12856            2 :       db_create_key(hDB, 0, str, TID_KEY);
   12857            2 :       status = db_find_key(hDB, 0, str, &hKeyTmpO);
   12858            2 :       if (status != DB_SUCCESS) {
   12859            0 :          db_unlock_database(hDB);
   12860            0 :          return status;
   12861              :       }
   12862              : 
   12863            2 :       status = db_paste(hDB, hKeyTmp, init_str);
   12864            2 :       if (status != DB_SUCCESS) {
   12865            0 :          db_unlock_database(hDB);
   12866            0 :          return status;
   12867              :       }
   12868              : 
   12869            2 :       buffer_size = 10000;
   12870            2 :       buffer = (char *) malloc(buffer_size);
   12871            2 :       assert(buffer != NULL);
   12872              : 
   12873              :       do {
   12874            2 :          size = buffer_size;
   12875            2 :          status = db_copy(hDB, hKeyOrig, buffer, &size, "");
   12876            2 :          if (status == DB_TRUNCATED) {
   12877            0 :             buffer_size += 10000;
   12878            0 :             buffer = (char *) realloc(buffer, buffer_size);
   12879            0 :             assert(buffer != NULL);
   12880            0 :             continue;
   12881              :          }
   12882            2 :          if (status != DB_SUCCESS) {
   12883            0 :             free(buffer);
   12884            0 :             db_unlock_database(hDB);
   12885            0 :             return status;
   12886              :          }
   12887              : 
   12888            2 :       } while (status != DB_SUCCESS);
   12889              : 
   12890            2 :       status = db_paste(hDB, hKeyTmpO, buffer);
   12891            2 :       if (status != DB_SUCCESS) {
   12892            0 :          free(buffer);
   12893            0 :          db_unlock_database(hDB);
   12894            0 :          return status;
   12895              :       }
   12896              : 
   12897              :       /* merge temporay record and original record */
   12898            2 :       db_scan_tree_link(hDB, hKeyTmpO, 0, merge_records, NULL);
   12899              : 
   12900              :       /* delete original record */
   12901              :       while (1) {
   12902            4 :          db_enum_link(hDB, hKeyOrig, 0, &hSubkey);
   12903            4 :          if (!hSubkey)
   12904            2 :             break;
   12905              : 
   12906            2 :          status = db_delete_key(hDB, hSubkey, FALSE);
   12907            2 :          if (status != DB_SUCCESS) {
   12908            0 :             free(buffer);
   12909            0 :             db_unlock_database(hDB);
   12910            0 :             return status;
   12911              :          }
   12912              :       }
   12913              : 
   12914              :       /* copy merged record to original record */
   12915              :       do {
   12916            2 :          size = buffer_size;
   12917            2 :          status = db_copy(hDB, hKeyTmp, buffer, &size, "");
   12918            2 :          if (status == DB_TRUNCATED) {
   12919            0 :             buffer_size += 10000;
   12920            0 :             buffer = (char *) realloc(buffer, buffer_size);
   12921            0 :             continue;
   12922              :          }
   12923            2 :          if (status != DB_SUCCESS) {
   12924            0 :             free(buffer);
   12925            0 :             db_unlock_database(hDB);
   12926            0 :             return status;
   12927              :          }
   12928              : 
   12929            2 :       } while (status != DB_SUCCESS);
   12930              : 
   12931            2 :       status = db_paste(hDB, hKeyOrig, buffer);
   12932            2 :       if (status != DB_SUCCESS) {
   12933            0 :          free(buffer);
   12934            0 :          db_unlock_database(hDB);
   12935            0 :          return status;
   12936              :       }
   12937              : 
   12938              :       /* delete temporary records */
   12939            2 :       db_delete_key(hDB, hKeyTmp, FALSE);
   12940            2 :       db_delete_key(hDB, hKeyTmpO, FALSE);
   12941              : 
   12942            2 :       free(buffer);
   12943            2 :       buffer = NULL;
   12944            0 :    } else if (status == DB_NO_KEY) {
   12945              :       /* create fresh record */
   12946            0 :       db_create_key(hDB, hKey, key_name, TID_KEY);
   12947            0 :       status = db_find_key(hDB, hKey, key_name, &hKeyTmp);
   12948            0 :       if (status != DB_SUCCESS) {
   12949            0 :          db_unlock_database(hDB);
   12950            0 :          return status;
   12951              :       }
   12952              : 
   12953            0 :       status = db_paste(hDB, hKeyTmp, init_str);
   12954            0 :       if (status != DB_SUCCESS) {
   12955            0 :          db_unlock_database(hDB);
   12956            0 :          return status;
   12957              :       }
   12958              :    } else {
   12959            0 :       cm_msg(MERROR, "db_create_record", "aborting on unexpected failure of db_find_key(%s), status %d", key_name, status);
   12960            0 :       abort();
   12961              :    }
   12962              : 
   12963            2 :    db_unlock_database(hDB);
   12964              : 
   12965            2 :    return DB_SUCCESS;
   12966              : }
   12967              : 
   12968              : /********************************************************************/
   12969              : /**
   12970              : This function ensures that a certain ODB subtree matches
   12971              : a given C structure, by comparing the init_str with the
   12972              : current ODB structure. If the record does not exist at all,
   12973              : it is created with the default values in init_str. If it
   12974              : does exist but does not match the variables in init_str,
   12975              : the function returns an error if correct=FALSE or calls
   12976              : db_create_record() if correct=TRUE.
   12977              : @param hDB          ODB handle obtained via cm_get_experiment_database().
   12978              : @param hKey     Handle for key where search starts, zero for root.
   12979              : @param keyname  Name of key to search, can contain directories.
   12980              : @param rec_str  ASCII representation of ODB record in the format
   12981              : @param correct  If TRUE, correct ODB record if necessary
   12982              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_KEY, DB_STRUCT_MISMATCH
   12983              : */
   12984            0 : INT db_check_record(HNDLE hDB, HNDLE hKey, const char *keyname, const char *rec_str, BOOL correct)
   12985              : {
   12986              :    char line[MAX_STRING_LENGTH];
   12987              :    char title[MAX_STRING_LENGTH];
   12988              :    char key_name[MAX_STRING_LENGTH];
   12989              :    char info_str[MAX_STRING_LENGTH + 50];
   12990              :    char *pc;
   12991              :    const char *pold, *rec_str_orig;
   12992              :    DWORD tid;
   12993              :    INT i, j, n_data, string_length, status;
   12994              :    HNDLE hKeyRoot, hKeyTest;
   12995              :    KEY key;
   12996              :    int bad_string_length;
   12997              : 
   12998            0 :    if (rpc_is_remote())
   12999            0 :       return rpc_call(RPC_DB_CHECK_RECORD, hDB, hKey, keyname, rec_str, correct);
   13000              : 
   13001              :    /* check if record exists */
   13002            0 :    status = db_find_key(hDB, hKey, keyname, &hKeyRoot);
   13003              : 
   13004              :    /* create record if not */
   13005            0 :    if (status == DB_NO_KEY) {
   13006            0 :       if (correct)
   13007            0 :          return db_create_record(hDB, hKey, keyname, rec_str);
   13008            0 :       return DB_NO_KEY;
   13009              :    }
   13010              : 
   13011            0 :    assert(hKeyRoot);
   13012              : 
   13013            0 :    title[0] = 0;
   13014            0 :    rec_str_orig = rec_str;
   13015              : 
   13016            0 :    db_get_key(hDB, hKeyRoot, &key);
   13017            0 :    if (key.type == TID_KEY)
   13018              :       /* get next subkey which is not of type TID_KEY */
   13019            0 :       db_get_next_link(hDB, hKeyRoot, &hKeyTest);
   13020              :    else
   13021              :       /* test root key itself */
   13022            0 :       hKeyTest = hKeyRoot;
   13023              : 
   13024            0 :    if (hKeyTest == 0 && *rec_str != 0) {
   13025            0 :       if (correct)
   13026            0 :          return db_create_record(hDB, hKey, keyname, rec_str_orig);
   13027              : 
   13028            0 :       return DB_STRUCT_MISMATCH;
   13029              :    }
   13030              : 
   13031              :    do {
   13032            0 :       if (*rec_str == 0)
   13033            0 :          break;
   13034              : 
   13035            0 :       for (i = 0; *rec_str != '\n' && *rec_str && i < MAX_STRING_LENGTH; i++)
   13036            0 :          line[i] = *rec_str++;
   13037              : 
   13038            0 :       if (i == MAX_STRING_LENGTH) {
   13039            0 :          cm_msg(MERROR, "db_check_record", "line too long");
   13040            0 :          return DB_TRUNCATED;
   13041              :       }
   13042              : 
   13043            0 :       line[i] = 0;
   13044            0 :       if (*rec_str == '\n')
   13045            0 :          rec_str++;
   13046              : 
   13047              :       /* check if it is a section title */
   13048            0 :       if (line[0] == '[') {
   13049              :          /* extract title and append '/' */
   13050            0 :          strcpy(title, line + 1);
   13051            0 :          if (strchr(title, ']'))
   13052            0 :             *strchr(title, ']') = 0;
   13053            0 :          if (title[0] && title[strlen(title) - 1] != '/')
   13054            0 :             strcat(title, "/");
   13055              :       } else {
   13056              :          /* valid data line if it includes '=' and no ';' */
   13057            0 :          if (strchr(line, '=') && line[0] != ';') {
   13058              :             /* copy type info and data */
   13059            0 :             pc = strchr(line, '=') + 1;
   13060            0 :             while (*pc == ' ')
   13061            0 :                pc++;
   13062            0 :             strcpy(info_str, pc);
   13063              : 
   13064              :             /* extract key name */
   13065            0 :             *strchr(line, '=') = 0;
   13066              : 
   13067            0 :             pc = &line[strlen(line) - 1];
   13068            0 :             while (*pc == ' ')
   13069            0 :                *pc-- = 0;
   13070              : 
   13071            0 :             mstrlcpy(key_name, line, sizeof(key_name));
   13072              : 
   13073              :             /* evaluate type info */
   13074            0 :             strcpy(line, info_str);
   13075            0 :             if (strchr(line, ' '))
   13076            0 :                *strchr(line, ' ') = 0;
   13077              : 
   13078            0 :             n_data = 1;
   13079            0 :             if (strchr(line, '[')) {
   13080            0 :                n_data = atol(strchr(line, '[') + 1);
   13081            0 :                *strchr(line, '[') = 0;
   13082              :             }
   13083              : 
   13084            0 :             for (tid = 0; tid < TID_LAST; tid++)
   13085            0 :                if (strcmp(rpc_tid_name(tid), line) == 0)
   13086            0 :                   break;
   13087            0 :             if (tid == TID_LAST) {
   13088            0 :                for (tid = 0; tid < TID_LAST; tid++)
   13089            0 :                   if (strcmp(rpc_tid_name_old(tid), line) == 0)
   13090            0 :                      break;
   13091              :             }
   13092              : 
   13093            0 :             string_length = 0;
   13094              : 
   13095            0 :             if (tid == TID_LAST)
   13096            0 :                cm_msg(MERROR, "db_check_record", "found unknown data type \"%s\" in ODB file", line);
   13097              :             else {
   13098              :                /* skip type info */
   13099            0 :                pc = info_str;
   13100            0 :                while (*pc != ' ' && *pc)
   13101            0 :                   pc++;
   13102            0 :                while ((*pc == ' ' || *pc == ':') && *pc)
   13103            0 :                   pc++;
   13104              : 
   13105            0 :                if (n_data > 1) {
   13106            0 :                   info_str[0] = 0;
   13107            0 :                   if (!*rec_str)
   13108            0 :                      break;
   13109              : 
   13110            0 :                   for (j = 0; *rec_str != '\n' && *rec_str; j++)
   13111            0 :                      info_str[j] = *rec_str++;
   13112            0 :                   info_str[j] = 0;
   13113            0 :                   if (*rec_str == '\n')
   13114            0 :                      rec_str++;
   13115              :                }
   13116              : 
   13117            0 :                for (i = 0; i < n_data; i++) {
   13118              :                   /* strip trailing \n */
   13119            0 :                   pc = &info_str[strlen(info_str) - 1];
   13120            0 :                   while (*pc == '\n' || *pc == '\r')
   13121            0 :                      *pc-- = 0;
   13122              : 
   13123            0 :                   if (tid == TID_STRING || tid == TID_LINK) {
   13124            0 :                      if (!string_length) {
   13125            0 :                         if (info_str[1] == '=')
   13126            0 :                            string_length = -1;
   13127              :                         else {
   13128            0 :                            pc = strchr(info_str, '[');
   13129            0 :                            if (pc != NULL)
   13130            0 :                               string_length = atoi(pc + 1);
   13131              :                            else
   13132            0 :                               string_length = -1;
   13133              :                         }
   13134            0 :                         if (string_length > MAX_STRING_LENGTH) {
   13135            0 :                            string_length = MAX_STRING_LENGTH;
   13136            0 :                            cm_msg(MERROR, "db_check_record", "found string exceeding MAX_STRING_LENGTH");
   13137              :                         }
   13138              :                      }
   13139              : 
   13140            0 :                      if (string_length == -1) {
   13141              :                         /* multi-line string */
   13142            0 :                         if (strstr(rec_str, "\n====#$@$#====\n") != NULL) {
   13143            0 :                            string_length = (POINTER_T) strstr(rec_str, "\n====#$@$#====\n") - (POINTER_T) rec_str + 1;
   13144              : 
   13145            0 :                            rec_str = strstr(rec_str, "\n====#$@$#====\n") + strlen("\n====#$@$#====\n");
   13146              :                         } else
   13147            0 :                            cm_msg(MERROR, "db_check_record", "found multi-line string without termination sequence");
   13148              :                      } else {
   13149            0 :                         if (strchr(info_str, ']'))
   13150            0 :                            pc = strchr(info_str, ']') + 1;
   13151              :                         else
   13152            0 :                            pc = info_str + 2;
   13153            0 :                         while (*pc && *pc != ' ')
   13154            0 :                            pc++;
   13155            0 :                         while (*pc && *pc == ' ')
   13156            0 :                            pc++;
   13157              : 
   13158              :                         /* limit string size */
   13159            0 :                         *(pc + string_length - 1) = 0;
   13160              :                      }
   13161              :                   } else {
   13162            0 :                      pc = info_str;
   13163              : 
   13164            0 :                      if (n_data > 1 && info_str[0] == '[') {
   13165            0 :                         pc = strchr(info_str, ']') + 1;
   13166            0 :                         while (*pc && *pc == ' ')
   13167            0 :                            pc++;
   13168              :                      }
   13169              :                   }
   13170              : 
   13171            0 :                   if (i < n_data - 1) {
   13172            0 :                      info_str[0] = 0;
   13173            0 :                      if (!*rec_str)
   13174            0 :                         break;
   13175              : 
   13176            0 :                      pold = rec_str;
   13177              : 
   13178            0 :                      for (j = 0; *rec_str != '\n' && *rec_str; j++)
   13179            0 :                         info_str[j] = *rec_str++;
   13180            0 :                      info_str[j] = 0;
   13181            0 :                      if (*rec_str == '\n')
   13182            0 :                         rec_str++;
   13183              : 
   13184              :                      /* test if valid data */
   13185            0 :                      if (tid != TID_STRING && tid != TID_LINK) {
   13186            0 :                         if (info_str[0] == 0 || (strchr(info_str, '=')
   13187            0 :                                                  && strchr(info_str, ':')))
   13188            0 :                            rec_str = pold;
   13189              :                      }
   13190              :                   }
   13191              :                }
   13192              : 
   13193              :                /* if rec_str contains key, but not ODB, return mismatch */
   13194            0 :                if (!hKeyTest) {
   13195            0 :                   if (correct)
   13196            0 :                      return db_create_record(hDB, hKey, keyname, rec_str_orig);
   13197              : 
   13198            0 :                   return DB_STRUCT_MISMATCH;
   13199              :                }
   13200              : 
   13201            0 :                status = db_get_key(hDB, hKeyTest, &key);
   13202            0 :                assert(status == DB_SUCCESS);
   13203              : 
   13204            0 :                bad_string_length = 0;
   13205              : 
   13206            0 :                if (key.type == TID_STRING) {
   13207              :                   //printf("key name [%s], tid %d/%d, num_values %d/%d, string length %d/%d\n", key_name, key.type, tid, key.num_values, n_data, string_length, key.item_size);
   13208            0 :                   if (string_length > 0 && string_length != key.item_size) {
   13209            0 :                      bad_string_length = 1;
   13210              :                   }
   13211              :                }
   13212              : 
   13213              :                /* check rec_str vs. ODB key */
   13214            0 :                if (!equal_ustring(key.name, key_name) || key.type != tid || key.num_values != n_data || bad_string_length) {
   13215              :                   //printf("miscompare key name [%s], tid %d/%d, num_values %d/%d, string length %d/%d\n", key_name, key.type, tid, key.num_values, n_data, key.item_size, string_length);
   13216            0 :                   if (correct)
   13217            0 :                      return db_create_record(hDB, hKey, keyname, rec_str_orig);
   13218              : 
   13219            0 :                   return DB_STRUCT_MISMATCH;
   13220              :                }
   13221              : 
   13222              :                /* get next key in ODB */
   13223            0 :                db_get_next_link(hDB, hKeyTest, &hKeyTest);
   13224              :             }
   13225              :          }
   13226              :       }
   13227              :    } while (TRUE);
   13228              : 
   13229            0 :    return DB_SUCCESS;
   13230              : }
   13231              : 
   13232              : /********************************************************************/
   13233              : /**
   13234              : Open a record. Create a local copy and maintain an automatic update.
   13235              : 
   13236              : This function opens a hot-link between an ODB sub-tree and a local
   13237              : structure. The sub-tree is copied to the structure automatically every time it is
   13238              : modified by someone else. Additionally, a callback function can be declared
   13239              : which is called after the structure has been updated. The callback function
   13240              : receives the database handle and the key handle as parameters.
   13241              : 
   13242              : Problems might occur if the ODB sub-tree contains values which don't match the
   13243              : C structure. Although the structure size is checked against the sub-tree size, no
   13244              : checking can be done if the type and order of the values in the structure are the
   13245              : same than those in the ODB sub-tree. Therefore it is recommended to use the
   13246              : function db_create_record() before db_open_record() is used which
   13247              : ensures that both are equivalent.
   13248              : 
   13249              : The access mode might either be MODE_READ or MODE_WRITE. In read mode,
   13250              : the ODB sub-tree is automatically copied to the local structure when modified by
   13251              : other clients. In write mode, the local structure is copied to the ODB sub-tree if it
   13252              : has been modified locally. This update has to be manually scheduled by calling
   13253              : db_send_changed_records() periodically in the main loop. The system
   13254              : keeps a copy of the local structure to determine if its contents has been changed.
   13255              : 
   13256              : If MODE_ALLOC is or'ed with the access mode, the memory for the structure is
   13257              : allocated internally. The structure pointer must contain a pointer to a pointer to
   13258              : the structure. The internal memory is released when db_close_record() is
   13259              : called.
   13260              : - To open a record in write mode.
   13261              : \code
   13262              : struct {
   13263              :   INT level1;
   13264              :   INT level2;
   13265              : } trigger_settings;
   13266              : char *trigger_settings_str =
   13267              : "[Settings]\n\
   13268              : level1 = INT : 0\n\
   13269              : level2 = INT : 0";
   13270              : main()
   13271              : {
   13272              :   HNDLE hDB, hkey, i=0;
   13273              :   ...
   13274              :   cm_get_experiment_database(&hDB, NULL);
   13275              :   db_create_record(hDB, 0, "/Equipment/Trigger", trigger_settings_str);
   13276              :   db_find_key(hDB, 0,"/Equipment/Trigger/Settings", &hkey);
   13277              :   db_open_record(hDB, hkey, &trigger_settings, sizeof(trigger_settings)
   13278              :                   , MODE_WRITE, NULL);
   13279              :   do
   13280              :   {
   13281              :     trigger_settings.level1 = i++;
   13282              :     db_send_changed_records()
   13283              :     status = cm_yield(1000);
   13284              :   } while (status != RPC_SHUTDOWN && status != SS_ABORT);
   13285              :   ...
   13286              : }
   13287              : \endcode
   13288              : @param hDB          ODB handle obtained via cm_get_experiment_database().
   13289              : @param hKey         Handle for key where search starts, zero for root.
   13290              : @param ptr          If access_mode includes MODE_ALLOC:
   13291              :                     Address of pointer which points to the record data after
   13292              :                     the call if access_mode includes not MODE_ALLOC:
   13293              :                     Address of record if ptr==NULL, only the dispatcher is called.
   13294              : @param rec_size     Record size in bytes
   13295              : @param access_mode Mode for opening record, either MODE_READ or
   13296              :                     MODE_WRITE. May be or'ed with MODE_ALLOC to
   13297              :                     let db_open_record allocate the memory for the record.
   13298              : @param (*dispatcher)   Function which gets called when record is updated.The
   13299              :                     argument list composed of: HNDLE hDB, HNDLE hKey, void *info
   13300              : @param info Additional info passed to the dispatcher.
   13301              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_MEMORY, DB_NO_ACCESS, DB_STRUCT_SIZE_MISMATCH
   13302              : */
   13303            0 : INT db_open_record(HNDLE hDB, HNDLE hKey, void *ptr, INT rec_size,
   13304              :                    WORD access_mode, void (*dispatcher) (INT, INT, void *), void *info)
   13305              : {
   13306              :    INT idx, status, size;
   13307              :    KEY key;
   13308              :    void *data;
   13309              : 
   13310              :    /* allocate new space for the local record list */
   13311            0 :    if (_record_list_entries == 0) {
   13312            0 :       _record_list = (RECORD_LIST *) malloc(sizeof(RECORD_LIST));
   13313            0 :       assert(_record_list != NULL);
   13314            0 :       memset(_record_list, 0, sizeof(RECORD_LIST));
   13315            0 :       if (_record_list == NULL) {
   13316            0 :          cm_msg(MERROR, "db_open_record", "not enough memory");
   13317            0 :          return DB_NO_MEMORY;
   13318              :       }
   13319              : 
   13320            0 :       _record_list_entries = 1;
   13321            0 :       idx = 0;
   13322              :    } else {
   13323              :       /* check for a deleted entry */
   13324            0 :       for (idx = 0; idx < _record_list_entries; idx++)
   13325            0 :          if (!_record_list[idx].handle)
   13326            0 :             break;
   13327              : 
   13328              :       /* if not found, create new one */
   13329            0 :       if (idx == _record_list_entries) {
   13330            0 :          _record_list = (RECORD_LIST *) realloc(_record_list, sizeof(RECORD_LIST) * (_record_list_entries + 1));
   13331            0 :          if (_record_list == NULL) {
   13332            0 :             cm_msg(MERROR, "db_open_record", "not enough memory");
   13333            0 :             return DB_NO_MEMORY;
   13334              :          }
   13335              : 
   13336            0 :          memset(&_record_list[_record_list_entries], 0, sizeof(RECORD_LIST));
   13337              : 
   13338            0 :          _record_list_entries++;
   13339              :       }
   13340              :    }
   13341              : 
   13342            0 :    db_get_key(hDB, hKey, &key);
   13343              :  
   13344              :    /* check record size */
   13345            0 :    status = db_get_record_size(hDB, hKey, 0, &size);
   13346            0 :    if (status != DB_SUCCESS) {
   13347            0 :       _record_list_entries--;
   13348            0 :       cm_msg(MERROR, "db_open_record", "cannot get record size, db_get_record_size() status %d", status);
   13349            0 :       return DB_NO_MEMORY;
   13350              :    }
   13351              : 
   13352            0 :    if (size != rec_size && ptr != NULL) {
   13353            0 :       _record_list_entries--;
   13354            0 :       std::string path = db_get_path(hDB, hKey);
   13355            0 :       cm_msg(MERROR, "db_open_record", "struct size mismatch for \"%s\" (expected size: %d, size in ODB: %d)", path.c_str(), rec_size, size);
   13356            0 :       return DB_STRUCT_SIZE_MISMATCH;
   13357            0 :    }
   13358              : 
   13359              :    /* check for read access */
   13360            0 :    if (((key.access_mode & MODE_EXCLUSIVE) && (access_mode & MODE_WRITE))
   13361            0 :        || (!(key.access_mode & MODE_WRITE) && (access_mode & MODE_WRITE))
   13362            0 :        || (!(key.access_mode & MODE_READ) && (access_mode & MODE_READ))) {
   13363            0 :       _record_list_entries--;
   13364            0 :       return DB_NO_ACCESS;
   13365              :    }
   13366              : 
   13367            0 :    if (access_mode & MODE_ALLOC) {
   13368            0 :       data = malloc(size);
   13369              : 
   13370            0 :       if (data == NULL) {
   13371            0 :          _record_list_entries--;
   13372            0 :          cm_msg(MERROR, "db_open_record", "not enough memory, malloc(%d) returned NULL", size);
   13373            0 :          return DB_NO_MEMORY;
   13374              :       }
   13375              : 
   13376            0 :       memset(data, 0, size);
   13377              : 
   13378            0 :       *((void **) ptr) = data;
   13379              :    } else {
   13380            0 :       data = ptr;
   13381              :    }
   13382              : 
   13383              :    /* copy record to local memory */
   13384            0 :    if (access_mode & MODE_READ && data != NULL) {
   13385            0 :       status = db_get_record(hDB, hKey, data, &size, 0);
   13386            0 :       if (status != DB_SUCCESS) {
   13387            0 :          _record_list_entries--;
   13388            0 :          cm_msg(MERROR, "db_open_record", "cannot get record, db_get_record() status %d", status);
   13389            0 :          return DB_NO_MEMORY;
   13390              :       }
   13391              :    }
   13392              : 
   13393              :    /* copy local record to ODB */
   13394            0 :    if (access_mode & MODE_WRITE) {
   13395              :       /* only write to ODB if not in MODE_ALLOC */
   13396            0 :       if ((access_mode & MODE_ALLOC) == 0) {
   13397            0 :          status = db_set_record(hDB, hKey, data, size, 0);
   13398            0 :          if (status != DB_SUCCESS) {
   13399            0 :             _record_list_entries--;
   13400            0 :             cm_msg(MERROR, "db_open_record", "cannot set record, db_set_record() status %d", status);
   13401            0 :             return DB_NO_MEMORY;
   13402              :          }
   13403              :       }
   13404              : 
   13405              :       /* init a local copy of the record */
   13406            0 :       _record_list[idx].copy = malloc(size);
   13407            0 :       if (_record_list[idx].copy == NULL) {
   13408            0 :          cm_msg(MERROR, "db_open_record", "not enough memory");
   13409            0 :          return DB_NO_MEMORY;
   13410              :       }
   13411              : 
   13412            0 :       memcpy(_record_list[idx].copy, data, size);
   13413              :    }
   13414              : 
   13415              :    /* initialize record list */
   13416            0 :    _record_list[idx].handle = hKey;
   13417            0 :    _record_list[idx].hDB = hDB;
   13418            0 :    _record_list[idx].access_mode = access_mode;
   13419            0 :    _record_list[idx].data = data;
   13420            0 :    _record_list[idx].buf_size = size;
   13421            0 :    _record_list[idx].dispatcher = dispatcher;
   13422            0 :    _record_list[idx].info = info;
   13423              : 
   13424              :    /* add record entry in database structure */
   13425            0 :    return db_add_open_record(hDB, hKey, (WORD) (access_mode & ~MODE_ALLOC));
   13426              : }
   13427              : 
   13428              : /********************************************************************/
   13429              : /**
   13430              : Open a record. Create a local copy and maintain an automatic update.
   13431              : 
   13432              : This function is same as db_open_record(), except that it calls
   13433              : db_check_record(), db_get_record1() and db_create_record()
   13434              : to ensure that the ODB structure matches
   13435              : 
   13436              : Parameters are the same as for db_open_record():
   13437              : 
   13438              : @param hDB          ODB handle obtained via cm_get_experiment_database().
   13439              : @param hKey         Handle for key where search starts, zero for root.
   13440              : @param ptr          If access_mode includes MODE_ALLOC:
   13441              :                     Address of pointer which points to the record data after
   13442              :                     the call if access_mode includes not MODE_ALLOC:
   13443              :                     Address of record if ptr==NULL, only the dispatcher is called.
   13444              : @param rec_size     Record size in bytes
   13445              : @param access_mode Mode for opening record, either MODE_READ or
   13446              :                     MODE_WRITE. May be or'ed with MODE_ALLOC to
   13447              :                     let db_open_record allocate the memory for the record.
   13448              : @param (*dispatcher)   Function which gets called when record is updated.The
   13449              :                     argument list composed of: HNDLE hDB, HNDLE hKey, void *info
   13450              : @param info Additional info passed to the dispatcher.
   13451              : @param rec_str  ASCII representation of ODB record in the format
   13452              : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_MEMORY, DB_NO_ACCESS, DB_STRUCT_SIZE_MISMATCH
   13453              : */
   13454            0 : INT db_open_record1(HNDLE hDB, HNDLE hKey, void *ptr, INT rec_size,
   13455              :                     WORD access_mode, void (*dispatcher) (INT, INT, void *), void *info,
   13456              :                      const char *rec_str)
   13457              : {
   13458            0 :    if (rec_str) {
   13459              :       int status;
   13460            0 :       if (rec_size) {
   13461              :          char* pbuf;
   13462            0 :          int size = rec_size;
   13463            0 :          pbuf = (char*)malloc(size);
   13464            0 :          assert(pbuf != NULL);
   13465            0 :          status = db_get_record1(hDB, hKey, pbuf, &size, 0, rec_str);
   13466            0 :          free(pbuf);
   13467            0 :          if (status != DB_SUCCESS)
   13468            0 :             return status;
   13469              :       }
   13470              : 
   13471            0 :       status = db_check_record(hDB, hKey, "", rec_str, TRUE);
   13472            0 :       if (status != DB_SUCCESS)
   13473            0 :          return status;
   13474              :    }
   13475              : 
   13476            0 :    return db_open_record(hDB, hKey, ptr, rec_size, access_mode, dispatcher, info);
   13477              : }
   13478              : 
   13479              : /********************************************************************/
   13480              : /**
   13481              : Close a record previously opend with db_open_record.
   13482              : @param hDB          ODB handle obtained via cm_get_experiment_database().
   13483              : @param hKey Handle for key where search starts, zero for root.
   13484              : @return DB_SUCCESS, DB_INVALID_HANDLE
   13485              : */
   13486            0 : INT db_close_record(HNDLE hDB, HNDLE hKey)
   13487              : {
   13488              : #ifdef LOCAL_ROUTINES
   13489              :    {
   13490              :       INT i;
   13491              : 
   13492            0 :       for (i = 0; i < _record_list_entries; i++)
   13493            0 :          if (_record_list[i].handle == hKey && _record_list[i].hDB == hDB)
   13494            0 :             break;
   13495              : 
   13496            0 :       if (i == _record_list_entries)
   13497            0 :          return DB_INVALID_HANDLE;
   13498              : 
   13499              :       /* remove record entry from database structure */
   13500            0 :       db_remove_open_record(hDB, hKey, TRUE);
   13501              : 
   13502              :       /* free local memory */
   13503            0 :       if (_record_list[i].access_mode & MODE_ALLOC) {
   13504            0 :          free(_record_list[i].data);
   13505            0 :          _record_list[i].data = NULL;
   13506              :       }
   13507              : 
   13508            0 :       if (_record_list[i].access_mode & MODE_WRITE) {
   13509            0 :          free(_record_list[i].copy);
   13510            0 :          _record_list[i].copy = NULL;
   13511              :       }
   13512              : 
   13513            0 :       memset(&_record_list[i], 0, sizeof(RECORD_LIST));
   13514              :    }
   13515              : #endif                          /* LOCAL_ROUTINES */
   13516              : 
   13517            0 :    return DB_SUCCESS;
   13518              : }
   13519              : 
   13520              : /********************************************************************/
   13521              : /**
   13522              : Release local memory for open records.
   13523              : This routines is called by db_close_all_databases() and
   13524              : cm_disconnect_experiment()
   13525              : @return DB_SUCCESS, DB_INVALID_HANDLE
   13526              : */
   13527            2 : INT db_close_all_records()
   13528              : {
   13529              :    INT i;
   13530              : 
   13531            2 :    for (i = 0; i < _record_list_entries; i++) {
   13532            0 :       if (_record_list[i].handle) {
   13533            0 :          if (_record_list[i].access_mode & MODE_WRITE) {
   13534            0 :             free(_record_list[i].copy);
   13535            0 :             _record_list[i].copy = NULL;
   13536              :          }
   13537              : 
   13538            0 :          if (_record_list[i].access_mode & MODE_ALLOC) {
   13539            0 :             free(_record_list[i].data);
   13540            0 :             _record_list[i].data = NULL;
   13541              :          }
   13542              : 
   13543            0 :          memset(&_record_list[i], 0, sizeof(RECORD_LIST));
   13544              :       }
   13545              :    }
   13546              : 
   13547            2 :    if (_record_list_entries > 0) {
   13548            0 :       _record_list_entries = 0;
   13549            0 :       free(_record_list);
   13550            0 :       _record_list = NULL;
   13551              :    }
   13552              : 
   13553            2 :    return DB_SUCCESS;
   13554              : }
   13555              : 
   13556              : /********************************************************************/
   13557              : /**
   13558              : db_open_record() and db_watch() event handler
   13559              : 
   13560              : @param hDB          ODB handle obtained via cm_get_experiment_database().
   13561              : @param hKey         Handle for key which changed.
   13562              : @param index        Index for array keys.
   13563              : @return DB_SUCCESS, DB_INVALID_HANDLE
   13564              : */
   13565            0 : INT db_update_record_local(INT hDB, INT hKeyRoot, INT hKey, int index)
   13566              : {
   13567              :    INT i, size, status;
   13568              : 
   13569            0 :    status = DB_INVALID_HANDLE;
   13570              : 
   13571              :    /* check all record entries for matching key */
   13572            0 :    for (i = 0; i < _record_list_entries; i++)
   13573            0 :       if (_record_list[i].handle == hKeyRoot) {
   13574            0 :          status = DB_SUCCESS;
   13575              : 
   13576              :          /* get updated data if record not opened in write mode */
   13577            0 :          if ((_record_list[i].access_mode & MODE_WRITE) == 0) {
   13578            0 :             size = _record_list[i].buf_size;
   13579            0 :             if (_record_list[i].data != NULL) {
   13580            0 :                status = db_get_record(hDB, hKeyRoot, _record_list[i].data, &size, 0); // db_open_record() update
   13581              :                //printf("db_open_record update status %d, size %d %d\n", status, _record_list[i].buf_size, size);
   13582              :             }
   13583              :             
   13584              :             /* call dispatcher if requested */
   13585            0 :             if (_record_list[i].dispatcher)
   13586            0 :                _record_list[i].dispatcher(hDB, hKeyRoot, _record_list[i].info);
   13587              :          }
   13588              :       }
   13589              : 
   13590              :    /* check all watch entries for matching key */
   13591            0 :    for (i = 0; i < _watch_list_entries; i++)
   13592            0 :       if (_watch_list[i].handle == hKeyRoot) {
   13593            0 :          status = DB_SUCCESS;
   13594              :          
   13595              :          /* call dispatcher if requested */
   13596            0 :          if (_watch_list[i].dispatcher)
   13597            0 :             _watch_list[i].dispatcher(hDB, hKey, index, _watch_list[i].info);
   13598              :       }
   13599              : 
   13600            0 :    return status;
   13601              : }
   13602              : 
   13603              : /********************************************************************/
   13604              : /**
   13605              : Relay db_open_record() and db_watch() notification to the remote client.
   13606              : @param hDB          ODB handle obtained via cm_get_experiment_database().
   13607              : @param hKey         Handle for key which changed.
   13608              : @param index        Index for array keys.
   13609              : @param s            client socket.
   13610              : @return DB_SUCCESS, DB_INVALID_HANDLE
   13611              : */
   13612            0 : INT db_update_record_mserver(INT hDB, INT hKeyRoot, INT hKey, int index, int client_socket)
   13613              : {
   13614              :    char buffer[32];
   13615              : 
   13616            0 :    int convert_flags = rpc_get_convert_flags();
   13617              :    
   13618            0 :    NET_COMMAND* nc = (NET_COMMAND *) buffer;
   13619              :    
   13620            0 :    nc->header.routine_id = MSG_ODB;
   13621            0 :    nc->header.param_size = 4 * sizeof(INT);
   13622            0 :    *((INT *) nc->param) = hDB;
   13623            0 :    *((INT *) nc->param + 1) = hKeyRoot;
   13624            0 :    *((INT *) nc->param + 2) = hKey;
   13625            0 :    *((INT *) nc->param + 3) = index;
   13626              :    
   13627            0 :    if (convert_flags) {
   13628            0 :       rpc_convert_single(&nc->header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
   13629            0 :       rpc_convert_single(&nc->header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
   13630            0 :       rpc_convert_single(&nc->param[0], TID_UINT32, RPC_OUTGOING, convert_flags);
   13631            0 :       rpc_convert_single(&nc->param[4], TID_UINT32, RPC_OUTGOING, convert_flags);
   13632            0 :       rpc_convert_single(&nc->param[8], TID_UINT32, RPC_OUTGOING, convert_flags);
   13633            0 :       rpc_convert_single(&nc->param[12], TID_UINT32, RPC_OUTGOING, convert_flags);
   13634              :    }
   13635              :    
   13636              :    /* send the update notification to the client */
   13637            0 :    send_tcp(client_socket, buffer, sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT), 0);
   13638              :    
   13639            0 :    return DB_SUCCESS;
   13640              : }
   13641              : 
   13642              : /********************************************************************/
   13643              : /**
   13644              : Send all records to the ODB which were changed locally since the last
   13645              : call to this function.
   13646              : 
   13647              : This function is valid if used in conjunction with db_open_record()
   13648              : under the condition the record is open as MODE_WRITE access code.
   13649              : 
   13650              : - Full example dbchange.c which can be compiled as follow
   13651              : \code
   13652              : gcc -DOS_LINUX -I/midas/include -o dbchange dbchange.c
   13653              : /midas/linux/lib/libmidas.a -lutil}
   13654              : 
   13655              : \begin{verbatim}
   13656              : //------- dbchange.c
   13657              : #include "midas.h"
   13658              : #include "msystem.h"
   13659              : \endcode
   13660              : 
   13661              : \code
   13662              : //-------- BOF dbchange.c
   13663              : typedef struct {
   13664              :     INT    my_number;
   13665              :     float   my_rate;
   13666              : } MY_STATISTICS;
   13667              : 
   13668              : MY_STATISTICS myrec;
   13669              : 
   13670              : #define MY_STATISTICS(_name) char *_name[] = {\
   13671              : "My Number = INT : 0",\
   13672              : "My Rate = FLOAT : 0",\
   13673              : "",\
   13674              : NULL }
   13675              : 
   13676              : HNDLE hDB, hKey;
   13677              : 
   13678              : // Main
   13679              : int main(unsigned int argc,char **argv)
   13680              : {
   13681              :   char      host_name[HOST_NAME_LENGTH];
   13682              :   char      expt_name[HOST_NAME_LENGTH];
   13683              :   INT       lastnumber, status, msg;
   13684              :   BOOL      debug=FALSE;
   13685              :   char      i, ch;
   13686              :   DWORD     update_time, mainlast_time;
   13687              :   MY_STATISTICS (my_stat);
   13688              : 
   13689              :   // set default
   13690              :   host_name[0] = 0;
   13691              :   expt_name[0] = 0;
   13692              : 
   13693              :   // get default
   13694              :   cm_get_environment(host_name, sizeof(host_name), expt_name, sizeof(expt_name));
   13695              : 
   13696              :   // get parameters
   13697              :   for (i=1 ; i<argc ; i++)
   13698              :   {
   13699              :     if (argv[i][0] == '-' && argv[i][1] == 'd')
   13700              :       debug = TRUE;
   13701              :     else if (argv[i][0] == '-')
   13702              :     {
   13703              :       if (i+1 >= argc || argv[i+1][0] == '-')
   13704              :         goto usage;
   13705              :       if (strncmp(argv[i],"-e",2) == 0)
   13706              :         strcpy(expt_name, argv[++i]);
   13707              :       else if (strncmp(argv[i],"-h",2)==0)
   13708              :         strcpy(host_name, argv[++i]);
   13709              :     }
   13710              :     else
   13711              :     {
   13712              :    usage:
   13713              :       printf("usage: dbchange [-h <Hostname>] [-e <Experiment>]\n");
   13714              :       return 0;
   13715              :     }
   13716              :   }
   13717              : 
   13718              :   // connect to experiment
   13719              :   status = cm_connect_experiment(host_name, expt_name, "dbchange", 0);
   13720              :   if (status != CM_SUCCESS)
   13721              :     return 1;
   13722              : 
   13723              :   // Connect to DB
   13724              :   cm_get_experiment_database(&hDB, &hKey);
   13725              : 
   13726              :   // Create a default structure in ODB
   13727              :   db_create_record(hDB, 0, "My statistics", strcomb1(my_stat).c_str());
   13728              : 
   13729              :   // Retrieve key for that strucutre in ODB
   13730              :   if (db_find_key(hDB, 0, "My statistics", &hKey) != DB_SUCCESS)
   13731              :   {
   13732              :     cm_msg(MERROR, "mychange", "cannot find My statistics");
   13733              :     goto error;
   13734              :   }
   13735              : 
   13736              :   // Hot link this structure in Write mode
   13737              :   status = db_open_record(hDB, hKey, &myrec, sizeof(MY_STATISTICS), MODE_WRITE, NULL, NULL);
   13738              :   if (status != DB_SUCCESS)
   13739              :   {
   13740              :     cm_msg(MERROR, "mychange", "cannot open My statistics record");
   13741              :     goto error;
   13742              :   }
   13743              : 
   13744              :   // initialize ss_getchar()
   13745              :   ss_getchar(0);
   13746              : 
   13747              :   // Main loop
   13748              :   do
   13749              :   {
   13750              :     // Update local structure
   13751              :     if ((ss_millitime() - update_time) > 100)
   13752              :     {
   13753              :       myrec.my_number += 1;
   13754              :       if (myrec.my_number - lastnumber) {
   13755              :         myrec.my_rate = 1000.f * (float) (myrec.my_number - lastnumber)
   13756              :           / (float) (ss_millitime() - update_time);
   13757              :       }
   13758              :       update_time = ss_millitime();
   13759              :       lastnumber = myrec.my_number;
   13760              :     }
   13761              : 
   13762              :     // Publish local structure to ODB (db_send_changed_record)
   13763              :     if ((ss_millitime() - mainlast_time) > 5000)
   13764              :     {
   13765              :       db_send_changed_records();                   // <------- Call
   13766              :       mainlast_time = ss_millitime();
   13767              :     }
   13768              : 
   13769              :     // Check for keyboard interaction
   13770              :     ch = 0;
   13771              :     while (ss_kbhit())
   13772              :     {
   13773              :       ch = ss_getchar(0);
   13774              :       if (ch == -1)
   13775              :         ch = getchar();
   13776              :       if ((char) ch == '!')
   13777              :         break;
   13778              :     }
   13779              :     msg = cm_yield(20);
   13780              :   } while (msg != RPC_SHUTDOWN && msg != SS_ABORT && ch != '!');
   13781              : 
   13782              :  error:
   13783              :   cm_disconnect_experiment();
   13784              :   return 1;
   13785              : }
   13786              : //-------- EOF dbchange.c
   13787              : \endcode
   13788              : @return DB_SUCCESS
   13789              : */
   13790            0 : INT db_send_changed_records()
   13791              : {
   13792              :    INT i;
   13793              : 
   13794            0 :    for (i = 0; i < _record_list_entries; i++)
   13795            0 :       if (_record_list[i].access_mode & MODE_WRITE) {
   13796            0 :          if (memcmp(_record_list[i].copy, _record_list[i].data, _record_list[i].buf_size) != 0) {
   13797            0 :             if (rpc_is_remote()) {
   13798            0 :                int align = ss_get_struct_align();
   13799            0 :                rpc_call(RPC_DB_SET_RECORD|RPC_NO_REPLY, _record_list[i].hDB, _record_list[i].handle, _record_list[i].data, _record_list[i].buf_size, align);
   13800              :             } else {
   13801            0 :                db_set_record(_record_list[i].hDB, _record_list[i].handle, _record_list[i].data, _record_list[i].buf_size, 0);
   13802              :             }
   13803            0 :             memcpy(_record_list[i].copy, _record_list[i].data, _record_list[i].buf_size);
   13804              :          }
   13805              :       }
   13806              : 
   13807            0 :    return DB_SUCCESS;
   13808              : }
   13809              : 
   13810              : /*------------------------------------------------------------------*/
   13811              : 
   13812              : /********************************************************************/
   13813              : /**
   13814              :  Watch an ODB subtree. The callback function gets called whenever a
   13815              :  key in the watched subtree changes. The callback function
   13816              :  receives 4 parameters: the database handle, the key handle of the entry that
   13817              :  changed (could be a child if you're watching a directory), the index that changed
   13818              :  (if it was part of an array), a user-specified 'info' parameter.
   13819              :  
   13820              :  @param hDB          ODB handle obtained via cm_get_experiment_database().
   13821              :  @param hKey         Handle for key at top of subtree to watch, zero for root.
   13822              :  @param (*dispatcher)   Function which gets called when record is updated.The
   13823              :  argument list composed of: HNDLE hDB, HNDLE hKey, INT idx, void* info
   13824              :  @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_MEMORY, DB_NO_ACCESS, DB_STRUCT_SIZE_MISMATCH
   13825              :  */
   13826            2 : INT db_watch(HNDLE hDB, HNDLE hKey, void (*dispatcher) (INT, INT, INT, void*), void* info)
   13827              : {
   13828              :    INT idx, status;
   13829              :    KEY key;
   13830              :    
   13831              :    /* check for valid key */
   13832            2 :    assert(hKey);
   13833              :    
   13834              :    /* allocate new space for the local record list */
   13835            2 :    if (_watch_list_entries == 0) {
   13836            2 :       _watch_list = (WATCH_LIST *) malloc(sizeof(WATCH_LIST));
   13837            2 :       assert(_watch_list != NULL);
   13838            2 :       memset(_watch_list, 0, sizeof(WATCH_LIST));
   13839            2 :       if (_watch_list == NULL) {
   13840            0 :          cm_msg(MERROR, "db_watch", "not enough memory");
   13841            0 :          return DB_NO_MEMORY;
   13842              :       }
   13843              :       
   13844            2 :       _watch_list_entries = 1;
   13845            2 :       idx = 0;
   13846              :    } else {
   13847              :       /* check for a deleted entry */
   13848            0 :       for (idx = 0; idx < _watch_list_entries; idx++)
   13849            0 :          if (!_watch_list[idx].handle)
   13850            0 :             break;
   13851              :       
   13852              :       /* if not found, create new one */
   13853            0 :       if (idx == _watch_list_entries) {
   13854            0 :          _watch_list = (WATCH_LIST *) realloc(_watch_list, sizeof(WATCH_LIST) * (_watch_list_entries + 1));
   13855            0 :          if (_watch_list == NULL) {
   13856            0 :             cm_msg(MERROR, "db_watch", "not enough memory");
   13857            0 :             return DB_NO_MEMORY;
   13858              :          }
   13859              :          
   13860            0 :          memset(&_watch_list[_watch_list_entries], 0, sizeof(WATCH_LIST));
   13861              :          
   13862            0 :          _watch_list_entries++;
   13863              :       }
   13864              :    }
   13865              :    
   13866              :    /* check key */
   13867            2 :    status = db_get_key(hDB, hKey, &key);
   13868            2 :    if (status != DB_SUCCESS) {
   13869            0 :       _watch_list_entries--;
   13870            0 :       std::string path = db_get_path(hDB, hKey);
   13871            0 :       cm_msg(MERROR, "db_watch", "cannot get key \"%s\"", path.c_str());
   13872            0 :       return DB_NO_MEMORY;
   13873            0 :    }
   13874              :    
   13875              :    /* check for read access */
   13876            2 :    if (!(key.access_mode & MODE_READ)) {
   13877            0 :       _watch_list_entries--;
   13878            0 :       std::string path = db_get_path(hDB, hKey);
   13879            0 :       cm_msg(MERROR, "db_watch", "cannot get key \"%s\"", path.c_str());
   13880            0 :       return DB_NO_ACCESS;
   13881            0 :    }
   13882              :    
   13883              :    /* initialize record list */
   13884            2 :    _watch_list[idx].handle = hKey;
   13885            2 :    _watch_list[idx].hDB = hDB;
   13886            2 :    _watch_list[idx].dispatcher = dispatcher;
   13887            2 :    _watch_list[idx].info = info;
   13888              :    
   13889              :    /* add record entry in database structure */
   13890            2 :    return db_add_open_record(hDB, hKey, MODE_WATCH);
   13891              : }
   13892              : 
   13893              : 
   13894              : /********************************************************************/
   13895              : /**
   13896              :  Remove watch callback from a key previously watched with db_watch.
   13897              :  @param hDB          ODB handle obtained via cm_get_experiment_database().
   13898              :  @param hKey         Handle for key, zero for root.
   13899              :  @return DB_SUCCESS, DB_INVALID_HANDLE
   13900              :  */
   13901            2 : INT db_unwatch(HNDLE hDB, HNDLE hKey)
   13902              : {
   13903              : #ifdef LOCAL_ROUTINES
   13904              :    {
   13905              :    INT i;
   13906              :    
   13907            2 :    for (i = 0; i < _watch_list_entries; i++)
   13908            2 :       if (_watch_list[i].handle == hKey && _watch_list[i].hDB == hDB)
   13909            2 :          break;
   13910              :    
   13911            2 :    if (i == _watch_list_entries)
   13912            0 :       return DB_INVALID_HANDLE;
   13913              :    
   13914              :    /* remove record entry from database structure */
   13915            2 :    db_remove_open_record(hDB, hKey, TRUE);
   13916              :    
   13917            2 :    memset(&_watch_list[i], 0, sizeof(WATCH_LIST));
   13918              :    }
   13919              : #endif                          /* LOCAL_ROUTINES */
   13920              :    
   13921            2 :    return DB_SUCCESS;
   13922              : }
   13923              : 
   13924              : /********************************************************************/
   13925              : /**
   13926              :  Closes all watched variables.
   13927              :  This routines is called by db_close_all_databases() and
   13928              :  cm_disconnect_experiment()
   13929              :  @return DB_SUCCESS, DB_INVALID_HANDLE
   13930              :  */
   13931            2 : INT db_unwatch_all()
   13932              : {
   13933            4 :    for (int i = _watch_list_entries-1; i >= 0 ; i--) {
   13934            2 :       if ((_watch_list[i].hDB == 0) && (_watch_list[i].handle == 0)) {
   13935              :          // empty or deleted watch list entry
   13936            0 :          continue;
   13937              :       }
   13938            2 :       db_unwatch(_watch_list[i].hDB, _watch_list[i].handle);
   13939              :    }
   13940              :    
   13941            2 :    return DB_SUCCESS;
   13942              : }
   13943              : 
   13944              : /*------------------------------------------------------------------*/
   13945              : 
   13946              : /* C++ wrapper for db_get_value */
   13947              : 
   13948           51 : INT EXPRT db_get_value_string(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, int index, std::string* s, BOOL create, int create_string_length)
   13949              : {
   13950              :    int status;
   13951              :    int hkey;
   13952              : 
   13953              :    //printf("db_get_value_string: key_name [%s], index %d, string [%s], create %d, create_string_length %d\n", key_name, index, s->c_str(), create, create_string_length);
   13954              : 
   13955           51 :    if (index > 0 && create) {
   13956            0 :       cm_msg(MERROR, "db_get_value_string", "cannot resize odb string arrays, please use db_resize_string() instead");
   13957            0 :       return DB_OUT_OF_RANGE;
   13958              :    }
   13959              : 
   13960           51 :    status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
   13961           51 :    if (status == DB_SUCCESS) {
   13962              :       KEY key;
   13963           47 :       status = db_get_key(hdb, hkey, &key);
   13964           47 :       if (status != DB_SUCCESS)
   13965            0 :          return status;
   13966           47 :       if (index < 0 || index >= key.num_values)
   13967            0 :          return DB_OUT_OF_RANGE;
   13968           47 :       int size = key.item_size;
   13969           47 :       if (size == 0) {
   13970            0 :          if (s)
   13971            0 :             *s = "";
   13972              :          //printf("db_get_value_string: return empty string, item_size %d\n", key.item_size);
   13973            0 :          return DB_SUCCESS;
   13974              :       }
   13975           47 :       char* buf = (char*)malloc(size);
   13976           47 :       assert(buf != NULL);
   13977           47 :       status = db_get_data_index(hdb, hkey, buf, &size, index, TID_STRING);
   13978           47 :       if (status != DB_SUCCESS) {
   13979            0 :          free(buf);
   13980            0 :          return status;
   13981              :       }
   13982           47 :       if (s)
   13983           47 :          *s = buf;
   13984           47 :       free(buf);
   13985              :       //printf("db_get_value_string: return [%s], len %d, item_size %d, size %d\n", s->c_str(), s->length(), key.item_size, size);
   13986           47 :       return DB_SUCCESS;
   13987            4 :    } else if (!create) {
   13988              :       // does not exist and not asked to create it
   13989            0 :       return status;
   13990              :    } else {
   13991              :       //printf("db_get_value_string: creating [%s]\n", key_name);
   13992            4 :       status = db_create_key(hdb, hKeyRoot, key_name, TID_STRING);
   13993            4 :       if (status != DB_SUCCESS)
   13994            0 :          return status;
   13995            4 :       status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
   13996            4 :       if (status != DB_SUCCESS)
   13997            0 :          return status;
   13998            4 :       assert(s != NULL);
   13999            4 :       if (create_string_length == 0) {
   14000            4 :          int size = s->length() + 1; // 1 byte for final \0
   14001            4 :          status = db_set_data_index(hdb, hkey, s->c_str(), size, index, TID_STRING);
   14002              :       } else {
   14003            0 :          char* buf = (char*)malloc(create_string_length);
   14004            0 :          assert(buf);
   14005            0 :          mstrlcpy(buf, s->c_str(), create_string_length);
   14006            0 :          status = db_set_data_index(hdb, hkey, buf, create_string_length, index, TID_STRING);
   14007            0 :          free(buf);
   14008              :       }
   14009            4 :       if (status != DB_SUCCESS)
   14010            0 :          return status;
   14011              :       //printf("db_get_value_string: created with value [%s]\n", s->c_str());
   14012            4 :       return DB_SUCCESS;
   14013              :    }
   14014              :    // NOT REACHED
   14015              : }
   14016              : 
   14017              : /* C++ wrapper for db_set_value */
   14018              : 
   14019            1 : INT EXPRT db_set_value_string(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const std::string* s)
   14020              : {
   14021            1 :    assert(s != NULL);
   14022            1 :    int size = s->length() + 1; // 1 byte for final \0
   14023              :    //printf("db_set_value_string: key_name [%s], string [%s], size %d\n", key_name, s->c_str(), size);
   14024            1 :    return db_set_value(hDB, hKeyRoot, key_name, s->c_str(), size, 1, TID_STRING);
   14025              : }
   14026              : 
   14027              : /********************************************************************/
   14028              : /**
   14029              : Change size of string arrays.
   14030              : 
   14031              : This function can change the number of elements and the string element length of an array of strings.
   14032              : @param hDB  ODB handle obtained via cm_get_experiment_database().
   14033              : @param hKey Handle for key where search starts, zero for root.
   14034              : @param key_name Odb key name, if NULL, will resize ODB entry pointed to by hKey
   14035              : @param num_values New number of array elements, if 0, remains unchanged
   14036              : @param max_string_length New max string length for array elements, if 0, remains unchanged
   14037              : @return DB_SUCCESS, or error from db_find_key, db_create_key, db_get_data(), db_set_data()
   14038              : */
   14039            0 : INT EXPRT db_resize_string(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, int num_values, int max_string_length)
   14040              : {
   14041              :    int status;
   14042              :    int hkey;
   14043              : 
   14044              :    //printf("db_resize_string: key_name [%s], num_values %d, max_string_length %d\n", key_name, num_values, max_string_length);
   14045              : 
   14046            0 :    int old_num_values = 0;
   14047            0 :    int old_item_size = 0;
   14048            0 :    int old_size = 0;
   14049            0 :    char* old_data = NULL;
   14050              : 
   14051            0 :    if (key_name) {
   14052            0 :       status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
   14053              :    } else {
   14054            0 :       hkey = hKeyRoot;
   14055            0 :       status = DB_SUCCESS;
   14056              :    }
   14057            0 :    if (status == DB_SUCCESS) {
   14058              :       KEY key;
   14059            0 :       status = db_get_key(hdb, hkey, &key);
   14060            0 :       if (status != DB_SUCCESS)
   14061            0 :          return status;
   14062            0 :       old_num_values = key.num_values;
   14063            0 :       old_item_size = key.item_size;
   14064            0 :       old_size = old_num_values * old_item_size;
   14065            0 :       if (old_size > 0) {
   14066            0 :          old_data = (char*)malloc(old_size);
   14067            0 :          assert(old_data != NULL);
   14068            0 :          int size = old_size;
   14069            0 :          status = db_get_data(hdb, hkey, old_data, &size, TID_STRING);
   14070            0 :          if (status != DB_SUCCESS) {
   14071            0 :             free(old_data);
   14072            0 :             return status;
   14073              :          }
   14074            0 :          assert(size == old_size);
   14075              :       }
   14076              :    } else {
   14077            0 :       status = db_create_key(hdb, hKeyRoot, key_name, TID_STRING);
   14078            0 :       if (status != DB_SUCCESS)
   14079            0 :          return status;
   14080            0 :       status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
   14081            0 :       if (status != DB_SUCCESS)
   14082            0 :          return status;
   14083              :    }
   14084              : 
   14085              :    //printf("old_num_values %d, old_item_size %d, old_size %d\n", old_num_values, old_item_size, old_size);
   14086              : 
   14087            0 :    int item_size = max_string_length;
   14088              : 
   14089            0 :    if (item_size < 1)
   14090            0 :       item_size = old_item_size;
   14091              : 
   14092            0 :    if (num_values < 1)
   14093            0 :       num_values = old_num_values;
   14094              : 
   14095            0 :    int new_size = num_values * item_size;
   14096            0 :    char* new_data = (char*)malloc(new_size);
   14097            0 :    assert(new_data);
   14098              : 
   14099            0 :    memset(new_data, 0, new_size);
   14100              : 
   14101            0 :    if (old_data) {
   14102            0 :       int num = old_num_values;
   14103            0 :       if (num > num_values)
   14104            0 :          num = num_values;
   14105              : 
   14106              :       //printf("new num_values %d, item_size %d, new_size %d, old_size %d, to copy %d values\n", num_values, item_size, new_size, old_size, num);
   14107              : 
   14108            0 :       for (int i=0; i<num; i++) {
   14109            0 :          const char* old_ptr = old_data + i*old_item_size;
   14110            0 :          char* new_ptr = new_data + i*item_size;
   14111            0 :          mstrlcpy(new_ptr, old_ptr, item_size);
   14112              :       }
   14113              :    }
   14114              : 
   14115            0 :    status = db_set_data(hdb, hkey, new_data, new_size, num_values, TID_STRING);
   14116              : 
   14117            0 :    if (old_data)
   14118            0 :       free(old_data);
   14119            0 :    if (new_data)
   14120            0 :       free(new_data);
   14121              :    
   14122            0 :    return status;
   14123              : }
   14124              : 
   14125            0 : MJsonNode* db_scl(HNDLE hDB)
   14126              : {
   14127              : #ifdef LOCAL_ROUTINES
   14128            0 :    MJsonNode* scl = MJsonNode::MakeObject();
   14129            0 :    MJsonNode* clients = MJsonNode::MakeArray();
   14130              : 
   14131            0 :    scl->AddToObject("now", MJsonNode::MakeNumber(ss_time_sec()));
   14132            0 :    scl->AddToObject("clients", clients);
   14133              : 
   14134              :    /* lock database */
   14135            0 :    db_lock_database(hDB);
   14136              : 
   14137            0 :    DATABASE *pdb = &_database[hDB - 1];
   14138            0 :    DATABASE_HEADER *pheader = pdb->database_header;
   14139              : 
   14140            0 :    DWORD now = ss_millitime();
   14141              : 
   14142              :    /* list clients */
   14143            0 :    for (int i = 0; i < pheader->max_client_index; i++) {
   14144            0 :       DATABASE_CLIENT *pclient = &pheader->client[i];
   14145            0 :       if (pclient->pid) {
   14146            0 :          MJsonNode* c = MJsonNode::MakeObject();
   14147            0 :          c->AddToObject("slot", MJsonNode::MakeNumber(i));
   14148            0 :          c->AddToObject("pid", MJsonNode::MakeNumber(pclient->pid));
   14149            0 :          c->AddToObject("name", MJsonNode::MakeString(pclient->name));
   14150            0 :          std::string path = msprintf("/System/Clients/%d/Host", pclient->pid);
   14151            0 :          const KEY* pkey = db_find_pkey_locked(pheader, NULL, path.c_str(), NULL, NULL);
   14152            0 :          if (pkey) {
   14153            0 :             int host_size = pkey->total_size;
   14154            0 :             char* host = (char*)malloc(host_size);
   14155            0 :             assert(host != NULL);
   14156            0 :             db_get_data_locked(pheader, pkey, 0, host, &host_size, TID_STRING, NULL);
   14157            0 :             c->AddToObject("host", MJsonNode::MakeString(host));
   14158            0 :             free(host);
   14159              :          }
   14160            0 :          c->AddToObject("watchdog_timeout_millisec", MJsonNode::MakeNumber(pclient->watchdog_timeout));
   14161              :          // "now" and "last_activity" is millisecond time wrapped around at 32 bits, must do unsigned 32-bit math here, not in javascript
   14162            0 :          c->AddToObject("last_activity_millisec", MJsonNode::MakeNumber(now - pclient->last_activity));
   14163            0 :          clients->AddToArray(c);
   14164            0 :       }
   14165              :    }
   14166              : 
   14167            0 :    db_unlock_database(hDB);
   14168              : 
   14169            0 :    return scl;
   14170              : #else
   14171              :    return MJsonNode::MakeNull();
   14172              : #endif
   14173              : }
   14174              : 
   14175            0 : MJsonNode* db_sor(HNDLE hDB, const char* root_path)
   14176              : {
   14177              :    //printf("db_sor(%s)\n", root_path);
   14178            0 :    assert(root_path != NULL);
   14179              : #ifdef LOCAL_ROUTINES
   14180            0 :    size_t root_path_len = strlen(root_path);
   14181            0 :    MJsonNode* sor = MJsonNode::MakeArray();
   14182              : 
   14183              :    /* lock database */
   14184            0 :    db_lock_database(hDB);
   14185              : 
   14186            0 :    DATABASE *pdb = &_database[hDB - 1];
   14187            0 :    DATABASE_HEADER *pheader = pdb->database_header;
   14188              : 
   14189              :    /* list clients */
   14190            0 :    for (int i = 0; i < pheader->max_client_index; i++) {
   14191            0 :       DATABASE_CLIENT *pclient = &pheader->client[i];
   14192            0 :       if (pclient->pid) {
   14193            0 :          for (int j = 0; j < pclient->max_index; j++) {
   14194            0 :             std::string path = db_get_path_locked(pheader, pclient->open_record[j].handle);
   14195            0 :             if (path.length() < root_path_len)
   14196            0 :                continue;
   14197            0 :             if (strncmp(root_path, path.c_str(), root_path_len) != 0)
   14198            0 :                continue;
   14199            0 :             MJsonNode* c = MJsonNode::MakeObject();
   14200            0 :             c->AddToObject("name", MJsonNode::MakeString(pclient->name));
   14201              :             //c->AddToObject("handle", MJsonNode::MakeNumber(pclient->open_record[j].handle));
   14202            0 :             c->AddToObject("access_mode", MJsonNode::MakeNumber(pclient->open_record[j].access_mode));
   14203            0 :             c->AddToObject("flags", MJsonNode::MakeNumber(pclient->open_record[j].flags));
   14204            0 :             c->AddToObject("path", MJsonNode::MakeString(path.c_str()));
   14205            0 :             sor->AddToArray(c);
   14206            0 :          }
   14207              :       }
   14208              :    }
   14209              : 
   14210            0 :    db_unlock_database(hDB);
   14211              : 
   14212            0 :    return sor;
   14213              : #else
   14214              :    return MJsonNode::MakeNull();
   14215              : #endif
   14216              : }
   14217              : 
   14218              : /** @} *//* end of odbfunctionc */
   14219              : 
   14220              : /* emacs
   14221              :  * Local Variables:
   14222              :  * tab-width: 8
   14223              :  * c-basic-offset: 3
   14224              :  * indent-tabs-mode: nil
   14225              :  * End:
   14226              :  */
        

Generated by: LCOV version 2.0-1