odb.c

Go to the documentation of this file.
00001 /********************************************************************\
00002 
00003   Name:         ODB.C
00004   Created by:   Stefan Ritt
00005 
00006   Contents:     MIDAS online database functions
00007 
00008   $Id:$
00009 
00010 \********************************************************************/
00011 
00012 /**dox***************************************************************/
00013 /** @file odb.c
00014 The Online Database file
00015 */
00016 
00017 /** @defgroup odbcode The odb.c
00018  */
00019 /** @defgroup odbfunctionc Midas ODB Functions (db_xxx)
00020  */
00021 
00022 /**dox***************************************************************/
00023 /** @addtogroup odbcode
00024 *  
00025  *  @{  */
00026 
00027 /**dox***************************************************************/
00028 /** @addtogroup odbfunctionc
00029 *  
00030  *  @{  */
00031 
00032 /**dox***************************************************************/
00033 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00034 
00035 #include "midas.h"
00036 #include "msystem.h"
00037 #include "mxml.h"
00038 #include <assert.h>
00039 
00040 /*------------------------------------------------------------------*/
00041 
00042 /********************************************************************\
00043 *                                                                    *
00044 *                 db_xxx  -  Database Functions                      *
00045 *                                                                    *
00046 \********************************************************************/
00047 
00048 /* Globals */
00049 
00050 DATABASE *_database;
00051 INT _database_entries = 0;
00052 
00053 static RECORD_LIST *_record_list;
00054 static INT _record_list_entries = 0;
00055 
00056 extern char *tid_name[];
00057 
00058 INT db_save_xml_key(HNDLE hDB, HNDLE hKey, INT level, MXML_WRITER *writer);
00059 
00060 /*------------------------------------------------------------------*/
00061 
00062 /********************************************************************\
00063 *                                                                    *
00064 *            Shared Memory Allocation                                *
00065 *                                                                    *
00066 \********************************************************************/
00067 
00068 /*------------------------------------------------------------------*/
00069 void *malloc_key(DATABASE_HEADER * pheader, INT size)
00070 {
00071    FREE_DESCRIP *pfree, *pfound, *pprev = NULL;
00072 
00073    if (size == 0)
00074       return NULL;
00075 
00076    /* quadword alignment for alpha CPU */
00077    size = ALIGN8(size);
00078 
00079    /* search for free block */
00080    pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
00081 
00082    while (pfree->size < size && pfree->next_free) {
00083       pprev = pfree;
00084       pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
00085    }
00086 
00087    /* return if not enough memory */
00088    if (pfree->size < size)
00089       return 0;
00090 
00091    pfound = pfree;
00092 
00093    /* if found block is first in list, correct pheader */
00094    if (pfree == (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key)) {
00095       if (size < pfree->size) {
00096          /* free block is only used partially */
00097          pheader->first_free_key += size;
00098          pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
00099 
00100          pfree->size = pfound->size - size;
00101          pfree->next_free = pfound->next_free;
00102       } else {
00103          /* free block is used totally */
00104          pheader->first_free_key = pfree->next_free;
00105       }
00106    } else {
00107       /* check if free block is used totally */
00108       if (pfound->size - size < sizeof(FREE_DESCRIP)) {
00109          /* skip block totally */
00110          pprev->next_free = pfound->next_free;
00111       } else {
00112          /* decrease free block */
00113          pfree = (FREE_DESCRIP *) ((char *) pfound + size);
00114 
00115          pfree->size = pfound->size - size;
00116          pfree->next_free = pfound->next_free;
00117 
00118          pprev->next_free = (PTYPE) pfree - (PTYPE) pheader;
00119       }
00120    }
00121 
00122 
00123    memset(pfound, 0, size);
00124 
00125    return pfound;
00126 }
00127 
00128 /*------------------------------------------------------------------*/
00129 void free_key(DATABASE_HEADER * pheader, void *address, INT size)
00130 {
00131    FREE_DESCRIP *pfree, *pprev, *pnext;
00132 
00133    if (size == 0)
00134       return;
00135 
00136    /* quadword alignment for alpha CPU */
00137    size = ALIGN8(size);
00138 
00139    pfree = (FREE_DESCRIP *) address;
00140    pprev = NULL;
00141 
00142    /* clear current block */
00143    memset(address, 0, size);
00144 
00145    /* if key comes before first free block, adjust pheader */
00146    if ((PTYPE) address - (PTYPE) pheader < pheader->first_free_key) {
00147       pfree->size = size;
00148       pfree->next_free = pheader->first_free_key;
00149       pheader->first_free_key = (PTYPE) address - (PTYPE) pheader;
00150    } else {
00151       /* find last free block before current block */
00152       pprev = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
00153 
00154       while (pprev->next_free < (PTYPE) address - (PTYPE) pheader) {
00155          if (pprev->next_free <= 0) {
00156             cm_msg(MERROR, "free_key",
00157                    "database is corrupted: pprev=0x%x, pprev->next_free=%d",
00158                    pprev, pprev->next_free);
00159             return;
00160          }
00161          pprev = (FREE_DESCRIP *) ((char *) pheader + pprev->next_free);
00162       }
00163 
00164       pfree->size = size;
00165       pfree->next_free = pprev->next_free;
00166 
00167       pprev->next_free = (PTYPE) pfree - (PTYPE) pheader;
00168    }
00169 
00170    /* try to melt adjacent free blocks after current block */
00171    pnext = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
00172    if ((PTYPE) pnext == (PTYPE) pfree + pfree->size) {
00173       pfree->size += pnext->size;
00174       pfree->next_free = pnext->next_free;
00175 
00176       memset(pnext, 0, pnext->size);
00177    }
00178 
00179    /* try to melt adjacent free blocks before current block */
00180    if (pprev && pprev->next_free == (PTYPE) pprev - (PTYPE) pheader + pprev->size) {
00181       pprev->size += pfree->size;
00182       pprev->next_free = pfree->next_free;
00183 
00184       memset(pfree, 0, pfree->size);
00185    }
00186 }
00187 
00188 /*------------------------------------------------------------------*/
00189 void *malloc_data(DATABASE_HEADER * pheader, INT size)
00190 {
00191    FREE_DESCRIP *pfree, *pfound, *pprev = NULL;
00192 
00193    if (size == 0)
00194       return NULL;
00195 
00196    /* quadword alignment for alpha CPU */
00197    size = ALIGN8(size);
00198 
00199    /* search for free block */
00200    pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
00201 
00202    while (pfree->size < size && pfree->next_free) {
00203       pprev = pfree;
00204       pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
00205    }
00206 
00207    /* return if not enough memory */
00208    if (pfree->size < size)
00209       return 0;
00210 
00211    pfound = pfree;
00212 
00213    /* if found block is first in list, correct pheader */
00214    if (pfree == (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data)) {
00215       if (size < pfree->size) {
00216          /* free block is only used partially */
00217          pheader->first_free_data += size;
00218          pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
00219 
00220          pfree->size = pfound->size - size;
00221          pfree->next_free = pfound->next_free;
00222       } else {
00223          /* free block is used totally */
00224          pheader->first_free_data = pfree->next_free;
00225       }
00226    } else {
00227       /* check if free block is used totally */
00228       if (pfound->size - size < sizeof(FREE_DESCRIP)) {
00229          /* skip block totally */
00230          pprev->next_free = pfound->next_free;
00231       } else {
00232          /* decrease free block */
00233          pfree = (FREE_DESCRIP *) ((char *) pfound + size);
00234 
00235          pfree->size = pfound->size - size;
00236          pfree->next_free = pfound->next_free;
00237 
00238          pprev->next_free = (PTYPE) pfree - (PTYPE) pheader;
00239       }
00240    }
00241 
00242    /* zero memeory */
00243    memset(pfound, 0, size);
00244 
00245    return pfound;
00246 }
00247 
00248 /*------------------------------------------------------------------*/
00249 void free_data(DATABASE_HEADER * pheader, void *address, INT size)
00250 {
00251    FREE_DESCRIP *pfree, *pprev, *pnext;
00252 
00253    if (size == 0)
00254       return;
00255 
00256    /* quadword alignment for alpha CPU */
00257    size = ALIGN8(size);
00258 
00259    pfree = (FREE_DESCRIP *) address;
00260    pprev = NULL;
00261 
00262    /* clear current block */
00263    memset(address, 0, size);
00264 
00265    /* if data comes before first free block, adjust pheader */
00266    if ((PTYPE) address - (PTYPE) pheader < pheader->first_free_data) {
00267       pfree->size = size;
00268       pfree->next_free = pheader->first_free_data;
00269       pheader->first_free_data = (PTYPE) address - (PTYPE) pheader;
00270    } else {
00271       /* find last free block before current block */
00272       pprev = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
00273 
00274       while (pprev->next_free < (PTYPE) address - (PTYPE) pheader) {
00275          if (pprev->next_free <= 0) {
00276             cm_msg(MERROR, "free_data",
00277                    "database is corrupted: pprev=0x%x, pprev->next_free=%d",
00278                    pprev, pprev->next_free);
00279             return;
00280          }
00281 
00282          pprev = (FREE_DESCRIP *) ((char *) pheader + pprev->next_free);
00283       }
00284 
00285       pfree->size = size;
00286       pfree->next_free = pprev->next_free;
00287 
00288       pprev->next_free = (PTYPE) pfree - (PTYPE) pheader;
00289    }
00290 
00291    /* try to melt adjacent free blocks after current block */
00292    pnext = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
00293    if ((PTYPE) pnext == (PTYPE) pfree + pfree->size) {
00294       pfree->size += pnext->size;
00295       pfree->next_free = pnext->next_free;
00296 
00297       memset(pnext, 0, pnext->size);
00298    }
00299 
00300    /* try to melt adjacent free blocks before current block */
00301    if (pprev && pprev->next_free == (PTYPE) pprev - (PTYPE) pheader + pprev->size) {
00302       pprev->size += pfree->size;
00303       pprev->next_free = pfree->next_free;
00304 
00305       memset(pfree, 0, pfree->size);
00306    }
00307 }
00308 
00309 /*------------------------------------------------------------------*/
00310 void *realloc_data(DATABASE_HEADER * pheader, void *address, INT old_size, INT new_size)
00311 {
00312    void *tmp = NULL, *pnew;
00313 
00314    if (old_size) {
00315       tmp = malloc(old_size);
00316       if (tmp == NULL)
00317          return NULL;
00318 
00319       memcpy(tmp, address, old_size);
00320       free_data(pheader, address, old_size);
00321    }
00322 
00323    pnew = malloc_data(pheader, new_size);
00324 
00325    if (pnew && old_size)
00326       memcpy(pnew, tmp, old_size < new_size ? old_size : new_size);
00327 
00328    if (old_size)
00329       free(tmp);
00330 
00331    return pnew;
00332 }
00333 
00334 /*------------------------------------------------------------------*/
00335 char *strcomb(char **list)
00336 /* convert list of strings into single string to be used by db_paste() */
00337 {
00338    INT i, j;
00339    static char *str = NULL;
00340 
00341    /* counter number of chars */
00342    for (i = 0, j = 0; list[i]; i++)
00343       j += strlen(list[i]) + 1;
00344    j += 1;
00345 
00346    if (str == NULL)
00347       str = (char *) malloc(j);
00348    else
00349       str = (char *) realloc(str, j);
00350 
00351    str[0] = 0;
00352    for (i = 0; list[i]; i++) {
00353       strcat(str, list[i]);
00354       strcat(str, "\n");
00355    }
00356 
00357    return str;
00358 }
00359 
00360 /*------------------------------------------------------------------*/
00361 INT print_key_info(HNDLE hDB, HNDLE hKey, KEY * pkey, INT level, void *info)
00362 {
00363    int i;
00364    char *p;
00365 
00366    p = (char *) info;
00367 
00368    sprintf(p + strlen(p), "%08X  %08X  %04X    ",
00369            (int)(hKey - sizeof(DATABASE_HEADER)),
00370            (int)(pkey->data - sizeof(DATABASE_HEADER)), (int)pkey->total_size);
00371 
00372    for (i = 0; i < level; i++)
00373       sprintf(p + strlen(p), "  ");
00374 
00375    sprintf(p + strlen(p), "%s\n", pkey->name);
00376 
00377    return SUCCESS;
00378 }
00379 
00380 INT db_show_mem(HNDLE hDB, char *result, INT buf_size, BOOL verbose)
00381 {
00382    DATABASE_HEADER *pheader;
00383    INT total_size_key, total_size_data;
00384    FREE_DESCRIP *pfree;
00385 
00386    db_lock_database(hDB);
00387 
00388    pheader = _database[hDB - 1].database_header;
00389 
00390    sprintf(result,
00391            "Database header size is 0x%04X, all following values are offset by this!\nKey area  0x00000000 - 0x%08X\nData area 0x%08X - 0x%08X\n\n",
00392            (int)sizeof(DATABASE_HEADER), pheader->key_size - 1,
00393            pheader->key_size, pheader->key_size + pheader->data_size);
00394 
00395    strcat(result, "Keylist:\n");
00396    strcat(result, "--------\n");
00397    total_size_key = 0;
00398    pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
00399 
00400    while ((PTYPE) pfree != (PTYPE) pheader) {
00401       total_size_key += pfree->size;
00402       sprintf(result + strlen(result),
00403               "Free block at 0x%08X, size 0x%08X, next 0x%08X\n",
00404               (int)((PTYPE) pfree - (PTYPE) pheader - sizeof(DATABASE_HEADER)),
00405               pfree->size,
00406               pfree->next_free ? (int)(pfree->next_free - sizeof(DATABASE_HEADER)) : 0);
00407       pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
00408    }
00409 
00410    strcat(result, "\nData:\n");
00411    strcat(result, "-----\n");
00412    total_size_data = 0;
00413    pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
00414 
00415    while ((PTYPE) pfree != (PTYPE) pheader) {
00416       total_size_data += pfree->size;
00417       sprintf(result + strlen(result),
00418               "Free block at 0x%08X, size 0x%08X, next 0x%08X\n",
00419               (int)((PTYPE) pfree - (PTYPE) pheader - sizeof(DATABASE_HEADER)),
00420               pfree->size,
00421               pfree->next_free ? (int)(pfree->next_free - sizeof(DATABASE_HEADER)) : 0);
00422       pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
00423    }
00424    sprintf(result + strlen(result),
00425            "\nTotal size: %1d (0x%08X) keylist, %1d (0x%08X) data\n",
00426            total_size_key, total_size_key, total_size_data, total_size_data);
00427    sprintf(result + strlen(result),
00428            "\nFree: %1d (%1.1lf%%) keylist, %1d (%1.1lf%%) data\n",
00429            total_size_key,
00430            100 * (double) total_size_key / pheader->key_size,
00431            total_size_data, 100 * (double) total_size_data / pheader->data_size);
00432 
00433    if (verbose) {
00434       sprintf(result + strlen(result), "\n\n");
00435       sprintf(result + strlen(result), "Key       Data      Size\n");
00436       sprintf(result + strlen(result), "------------------------\n");
00437       db_scan_tree(hDB, pheader->root_key, 0, print_key_info, result);
00438    }
00439 
00440    db_unlock_database(hDB);
00441 
00442    return DB_SUCCESS;
00443 }
00444 
00445 /*------------------------------------------------------------------*/
00446 static int db_validate_key_offset(DATABASE_HEADER * pheader, int offset)
00447 /* check if key offset lies in valid range */
00448 {
00449    if (offset != 0 && offset < (int) sizeof(DATABASE_HEADER))
00450       return 0;
00451 
00452    if (offset > (int) sizeof(DATABASE_HEADER) + pheader->key_size)
00453       return 0;
00454 
00455    return 1;
00456 }
00457 
00458 static int db_validate_data_offset(DATABASE_HEADER * pheader, int offset)
00459 /* check if data offset lies in valid range */
00460 {
00461    if (offset != 0 && offset < (int) sizeof(DATABASE_HEADER))
00462       return 0;
00463 
00464    if (offset > (int) sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size)
00465       return 0;
00466 
00467    return 1;
00468 }
00469 
00470 static int db_validate_hkey(DATABASE_HEADER * pheader, HNDLE hKey)
00471 {
00472    return db_validate_key_offset(pheader, hKey);
00473 }
00474 
00475 static int db_validate_key(DATABASE_HEADER * pheader, int recurse,
00476                            const char *path, KEY * pkey)
00477 {
00478    KEYLIST *pkeylist;
00479    int i;
00480    static time_t t_min = 0, t_max;
00481 
00482    if (!db_validate_key_offset(pheader, (PTYPE) pkey - (PTYPE) pheader)) {
00483       cm_msg(MERROR, "db_validate_key",
00484              "Warning: database corruption, key \"%s\", data 0x%08X", path,
00485              pkey->data - sizeof(DATABASE_HEADER));
00486       return 0;
00487    }
00488 
00489    if (!db_validate_data_offset(pheader, pkey->data)) {
00490       cm_msg(MERROR, "db_validate_key",
00491              "Warning: database corruption, data \"%s\", data 0x%08X",
00492              path, pkey->data - sizeof(DATABASE_HEADER));
00493       return 0;
00494    }
00495 
00496    /* check key type */
00497    if (pkey->type >= TID_LAST) {
00498       cm_msg(MERROR, "db_validate_key",
00499              "Warning: invalid key type, key \"%s\", type %d", path, pkey->type);
00500       return 0;
00501    }
00502 
00503    /* check key sizes */
00504    if ((pkey->total_size < 0) || (pkey->total_size > pheader->key_size)) {
00505       cm_msg(MERROR, "db_validate_key",
00506              "Warning: invalid key \"%s\" total_size: %d", path, pkey->total_size);
00507       return 0;
00508    }
00509 
00510    if ((pkey->item_size < 0) || (pkey->item_size > pheader->key_size)) {
00511       cm_msg(MERROR, "db_validate_key",
00512              "Warning: invalid key \"%s\" item_size: %d", path, pkey->item_size);
00513       return 0;
00514    }
00515 
00516    if ((pkey->num_values < 0) || (pkey->num_values > pheader->key_size)) {
00517       cm_msg(MERROR, "db_validate_key",
00518              "Warning: invalid key \"%s\" num_values: %d", path, pkey->num_values);
00519       return 0;
00520    }
00521 
00522    /* check and correct key size */
00523    if (pkey->total_size != pkey->item_size * pkey->num_values) {
00524       cm_msg(MINFO, "db_validate_key",
00525              "Warning: corrected key \"%s\" size: total_size=%d, should be %d*%d=%d",
00526              path, pkey->total_size, pkey->item_size, pkey->num_values,
00527              pkey->item_size * pkey->num_values);
00528       pkey->total_size = pkey->item_size * pkey->num_values;
00529    }
00530 
00531    /* check access mode */
00532    if ((pkey->
00533         access_mode & ~(MODE_READ | MODE_WRITE | MODE_DELETE |
00534                         MODE_EXCLUSIVE | MODE_ALLOC))) {
00535       cm_msg(MERROR, "db_validate_key",
00536              "Warning: invalid access mode, key \"%s\", mode %d", path,
00537              pkey->access_mode);
00538       return 0;
00539    }
00540 
00541    /* check access time, consider valid if within +- 10 years */
00542    if (t_min == 0) {
00543       t_min = ss_time() - 3600 * 24 * 365 * 10;
00544       t_max = ss_time() + 3600 * 24 * 365 * 10;
00545    }
00546 
00547    if (pkey->last_written > 0 &&
00548        (pkey->last_written < t_min || pkey->last_written > t_max)) {
00549       cm_msg(MERROR, "db_validate_key",
00550              "Warning: invalid access time, key \"%s\", time %d", path,
00551              pkey->last_written);
00552       return 0;
00553    }
00554 
00555    if (pkey->type == TID_KEY && recurse) {
00556       /* if key has subkeys, go through whole list */
00557 
00558       pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
00559 
00560       if (pkeylist->num_keys != 0 &&
00561           (pkeylist->first_key == 0
00562            || !db_validate_key_offset(pheader, pkeylist->first_key))) {
00563          cm_msg(MERROR, "db_validate_key",
00564                 "Warning: database corruption, key \"%s\", first_key 0x%08X",
00565                 path, pkeylist->first_key - sizeof(DATABASE_HEADER));
00566          return 0;
00567       }
00568 
00569       /* check if key is in keylist */
00570       pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
00571 
00572       for (i = 0; i < pkeylist->num_keys; i++) {
00573          char buf[1024];
00574          sprintf(buf, "%s/%s", path, pkey->name);
00575 
00576          if (!db_validate_key_offset(pheader, pkey->next_key)) {
00577             cm_msg(MERROR, "db_validate_key",
00578                    "Warning: database corruption, key \"%s\", next_key 0x%08X",
00579                    buf, pkey->next_key - sizeof(DATABASE_HEADER));
00580             return 0;
00581          }
00582 
00583          if (pkey->type == TID_KEY)
00584             if (!db_validate_key(pheader, recurse + 1, buf, pkey))
00585                return 0;
00586 
00587          pkey = (KEY *) ((char *) pheader + pkey->next_key);
00588       }
00589    }
00590 
00591    return 1;
00592 }
00593 
00594 /*------------------------------------------------------------------*/
00595 static int db_validate_db(DATABASE_HEADER * pheader)
00596 {
00597    int total_size_key = 0;
00598    int total_size_data = 0;
00599    double ratio;
00600    FREE_DESCRIP *pfree;
00601 
00602    /* validate the key free list */
00603 
00604    if (!db_validate_key_offset(pheader, pheader->first_free_key)) {
00605       cm_msg(MERROR, "db_validate_db",
00606              "Warning: database corruption, first_free_key 0x%08X",
00607              pheader->first_free_key - sizeof(DATABASE_HEADER));
00608       return 0;
00609    }
00610 
00611    pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
00612 
00613    while ((PTYPE) pfree != (PTYPE) pheader) {
00614       FREE_DESCRIP* nextpfree;
00615 
00616       if (pfree->next_free != 0 && !db_validate_key_offset(pheader, pfree->next_free)) {
00617          cm_msg(MERROR, "db_validate_db",
00618                 "Warning: database corruption, key area next_free 0x%08X",
00619                 pfree->next_free - sizeof(DATABASE_HEADER));
00620          return 0;
00621       }
00622 
00623       total_size_key += pfree->size;
00624       nextpfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
00625 
00626       if (pfree->next_free != 0 && nextpfree == pfree) {
00627          cm_msg(MERROR, "db_validate_db",
00628                 "Warning: database corruption, key area next_free 0x%08X is same as current free",
00629                 pfree - sizeof(DATABASE_HEADER));
00630          return 0;
00631       }
00632 
00633       pfree = nextpfree;
00634    }
00635 
00636    ratio = ((double) (pheader->key_size - total_size_key)) / ((double) pheader->key_size);
00637    if (ratio > 0.9)
00638       cm_msg(MERROR, "db_validate_db",
00639              "Warning: database key area is %.0f%% full", ratio * 100.0);
00640 
00641    if (total_size_key > pheader->key_size) {
00642       cm_msg(MERROR, "db_validate_db",
00643              "Warning: database corruption, total_key_size 0x%08X", total_size_key);
00644       return 0;
00645    }
00646 
00647    /* validate the data free list */
00648 
00649    if (!db_validate_data_offset(pheader, pheader->first_free_data)) {
00650       cm_msg(MERROR, "db_validate_db",
00651              "Warning: database corruption, first_free_data 0x%08X",
00652              pheader->first_free_data - sizeof(DATABASE_HEADER));
00653       return 0;
00654    }
00655 
00656    pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
00657 
00658    while ((PTYPE) pfree != (PTYPE) pheader) {
00659       FREE_DESCRIP* nextpfree;
00660 
00661       if (pfree->next_free != 0 && !db_validate_data_offset(pheader, pfree->next_free)) {
00662          cm_msg(MERROR, "db_validate_db",
00663                 "Warning: database corruption, data area next_free 0x%08X",
00664                 pfree->next_free - sizeof(DATABASE_HEADER));
00665          return 0;
00666       }
00667 
00668       total_size_data += pfree->size;
00669       nextpfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
00670 
00671       if (pfree->next_free != 0 && nextpfree == pfree) {
00672          cm_msg(MERROR, "db_validate_db",
00673                 "Warning: database corruption, data area next_free 0x%08X is same as current free",
00674                 pfree - sizeof(DATABASE_HEADER));
00675          return 0;
00676       }
00677 
00678       pfree = nextpfree;
00679    }
00680 
00681    ratio =
00682        ((double) (pheader->data_size - total_size_data)) / ((double) pheader->data_size);
00683    if (ratio > 0.9)
00684       cm_msg(MERROR, "db_validate_db",
00685              "Warning: database data area is %.0f%% full", ratio * 100.0);
00686 
00687    if (total_size_data > pheader->data_size) {
00688       cm_msg(MERROR, "db_validate_db",
00689              "Warning: database corruption, total_size_data 0x%08X", total_size_key);
00690       return 0;
00691    }
00692 
00693    /* validate the tree of keys, starting from the root key */
00694 
00695    if (!db_validate_key_offset(pheader, pheader->root_key)) {
00696       cm_msg(MERROR, "db_validate_db",
00697              "Warning: database corruption, root_key 0x%08X",
00698              pheader->root_key - sizeof(DATABASE_HEADER));
00699       return 0;
00700    }
00701 
00702    return db_validate_key(pheader, 1, "", (KEY *) ((char *) pheader + pheader->root_key));
00703 }
00704 
00705 /**dox***************************************************************/
00706 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
00707 
00708 /********************************************************************/
00709 /**
00710 Open an online database
00711 @param database_name     Database name.
00712 @param database_size     Initial size of database if not existing
00713 @param client_name       Name of this application
00714 @param hDB          ODB handle obtained via cm_get_experiment_database().
00715 @return DB_SUCCESS, DB_CREATED, DB_INVALID_NAME, DB_NO_MEMORY, 
00716         DB_MEMSIZE_MISMATCH, DB_NO_MUTEX, DB_INVALID_PARAM,
00717         RPC_NET_ERROR
00718 */
00719 INT db_open_database(char *database_name, INT database_size,
00720                      HNDLE * hDB, char *client_name)
00721 {
00722    if (rpc_is_remote())
00723       return rpc_call(RPC_DB_OPEN_DATABASE, database_name, database_size,
00724                       hDB, client_name);
00725 
00726 #ifdef LOCAL_ROUTINES
00727    {
00728       INT i, status;
00729       HNDLE handle;
00730       DATABASE_CLIENT *pclient;
00731       BOOL shm_created;
00732       HNDLE shm_handle;
00733       DATABASE_HEADER *pheader;
00734       KEY *pkey;
00735       KEYLIST *pkeylist;
00736       FREE_DESCRIP *pfree;
00737       BOOL call_watchdog;
00738       DWORD timeout;
00739 
00740       if (database_size < 0 || database_size > 10E7) {
00741          cm_msg(MERROR, "db_open_database", "invalid database size");
00742          return DB_INVALID_PARAM;
00743       }
00744 
00745       /* restrict name length */
00746       if (strlen(database_name) >= NAME_LENGTH)
00747          database_name[NAME_LENGTH] = 0;
00748 
00749       /* allocate new space for the new database descriptor */
00750       if (_database_entries == 0) {
00751          _database = (DATABASE *) malloc(sizeof(DATABASE));
00752          memset(_database, 0, sizeof(DATABASE));
00753          if (_database == NULL) {
00754             *hDB = 0;
00755             return DB_NO_MEMORY;
00756          }
00757 
00758          _database_entries = 1;
00759          i = 0;
00760       } else {
00761          /* check if database already open */
00762          for (i = 0; i < _database_entries; i++)
00763             if (_database[i].attached && equal_ustring(_database[i].name, database_name)) {
00764                /* check if database belongs to this thread */
00765                if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MTHREAD) {
00766                   if (_database[i].index == ss_gettid()) {
00767                      *hDB = i + 1;
00768                      return DB_SUCCESS;
00769                   }
00770                } else {
00771                   *hDB = i + 1;
00772                   return DB_SUCCESS;
00773                }
00774             }
00775 
00776          /* check for a deleted entry */
00777          for (i = 0; i < _database_entries; i++)
00778             if (!_database[i].attached)
00779                break;
00780 
00781          /* if not found, create new one */
00782          if (i == _database_entries) {
00783             _database =
00784                 (DATABASE *) realloc(_database,
00785                                      sizeof(DATABASE) * (_database_entries + 1));
00786             memset(&_database[_database_entries], 0, sizeof(DATABASE));
00787 
00788             _database_entries++;
00789             if (_database == NULL) {
00790                _database_entries--;
00791                *hDB = 0;
00792                return DB_NO_MEMORY;
00793             }
00794          }
00795       }
00796 
00797       handle = (HNDLE) i;
00798 
00799       /* open shared memory region */
00800       status = ss_shm_open(database_name,
00801                            sizeof(DATABASE_HEADER) +
00802                            2 * ALIGN8(database_size / 2),
00803                            (void **) &(_database[(INT) handle].
00804                                        database_header), &shm_handle);
00805 
00806       if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
00807          *hDB = 0;
00808          return DB_INVALID_NAME;
00809       }
00810 
00811       /* shortcut to header */
00812       pheader = _database[handle].database_header;
00813 
00814       /* save name */
00815       strcpy(_database[handle].name, database_name);
00816 
00817       shm_created = (status == SS_CREATED);
00818 
00819       /* clear memeory for debugging */
00820       /* memset(pheader, 0, sizeof(DATABASE_HEADER) + 2*ALIGN8(database_size/2)); */
00821 
00822       if (shm_created && pheader->name[0] == 0) {
00823          /* setup header info if database was created */
00824          memset(pheader, 0, sizeof(DATABASE_HEADER) + 2 * ALIGN8(database_size / 2));
00825 
00826          strcpy(pheader->name, database_name);
00827          pheader->version = DATABASE_VERSION;
00828          pheader->key_size = ALIGN8(database_size / 2);
00829          pheader->data_size = ALIGN8(database_size / 2);
00830          pheader->root_key = sizeof(DATABASE_HEADER);
00831          pheader->first_free_key = sizeof(DATABASE_HEADER);
00832          pheader->first_free_data = sizeof(DATABASE_HEADER) + pheader->key_size;
00833 
00834          /* set up free list */
00835          pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
00836          pfree->size = pheader->key_size;
00837          pfree->next_free = 0;
00838 
00839          pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
00840          pfree->size = pheader->data_size;
00841          pfree->next_free = 0;
00842 
00843          /* create root key */
00844          pkey = (KEY *) malloc_key(pheader, sizeof(KEY));
00845 
00846          /* set key properties */
00847          pkey->type = TID_KEY;
00848          pkey->num_values = 1;
00849          pkey->access_mode = MODE_READ | MODE_WRITE | MODE_DELETE;
00850          strcpy(pkey->name, "root");
00851          pkey->parent_keylist = 0;
00852 
00853          /* create keylist */
00854          pkeylist = (KEYLIST *) malloc_key(pheader, sizeof(KEYLIST));
00855 
00856          /* store keylist in data field */
00857          pkey->data = (PTYPE) pkeylist - (PTYPE) pheader;
00858          pkey->item_size = sizeof(KEYLIST);
00859          pkey->total_size = sizeof(KEYLIST);
00860 
00861          pkeylist->parent = (PTYPE) pkey - (PTYPE) pheader;
00862          pkeylist->num_keys = 0;
00863          pkeylist->first_key = 0;
00864       }
00865 
00866       /* check database version */
00867       if (pheader->version != DATABASE_VERSION) {
00868          cm_msg(MERROR, "db_open_database", "Different database format: Shared memory is %d, program is %d",
00869             pheader->version, DATABASE_VERSION);
00870          return DB_VERSION_MISMATCH;
00871       }
00872 
00873       /* create mutex for the database */
00874       status = ss_mutex_create(database_name, &(_database[handle].mutex));
00875       if (status != SS_SUCCESS && status != SS_CREATED) {
00876          *hDB = 0;
00877          return DB_NO_MUTEX;
00878       }
00879       _database[handle].lock_cnt = 0;
00880 
00881       /* first lock database */
00882       status = db_lock_database(handle + 1);
00883       if (status != DB_SUCCESS)
00884          return status;
00885 
00886       /*
00887          Now we have a DATABASE_HEADER, so let's setup a CLIENT
00888          structure in that database. The information there can also
00889          be seen by other processes.
00890        */
00891 
00892       /*
00893          update the client count
00894        */
00895       pheader->num_clients = 0;
00896       pheader->max_client_index = 0;
00897       for (i = 0; i < MAX_CLIENTS; i++) {
00898          if (pheader->client[i].pid == 0)
00899             continue;
00900          pheader->num_clients++;
00901          pheader->max_client_index = i + 1;
00902       }
00903 
00904       /*fprintf(stderr,"num_clients: %d, max_client: %d\n",pheader->num_clients,pheader->max_client_index); */
00905 
00906       /*
00907          Look for empty client slot
00908        */
00909       for (i = 0; i < MAX_CLIENTS; i++)
00910          if (pheader->client[i].pid == 0)
00911             break;
00912 
00913       if (i == MAX_CLIENTS) {
00914          db_unlock_database(handle + 1);
00915          *hDB = 0;
00916          cm_msg(MERROR, "db_open_database", "maximum number of clients exceeded");
00917          return DB_NO_SLOT;
00918       }
00919 
00920       /* store slot index in _database structure */
00921       _database[handle].client_index = i;
00922 
00923       /*
00924          Save the index of the last client of that database so that later only
00925          the clients 0..max_client_index-1 have to be searched through.
00926        */
00927       pheader->num_clients++;
00928       if (i + 1 > pheader->max_client_index)
00929          pheader->max_client_index = i + 1;
00930 
00931       /* setup database header and client structure */
00932       pclient = &pheader->client[i];
00933 
00934       memset(pclient, 0, sizeof(DATABASE_CLIENT));
00935       /* use client name previously set by bm_set_name */
00936       strcpy(pclient->name, client_name);
00937       pclient->pid = ss_getpid();
00938       pclient->tid = ss_gettid();
00939       pclient->thandle = ss_getthandle();
00940       pclient->num_open_records = 0;
00941 
00942       ss_suspend_get_port(&pclient->port);
00943 
00944       pclient->last_activity = ss_millitime();
00945 
00946       cm_get_watchdog_params(&call_watchdog, &timeout);
00947       pclient->watchdog_timeout = timeout;
00948 
00949       /* check ODB for corruption */
00950       if (!db_validate_db(pheader)) {
00951          /* do not treat corrupted odb as a fatal error- allow the user
00952             to preceed at own risk- the database is already corrupted,
00953             so no further harm can possibly be made. */
00954          /*
00955             db_unlock_database(handle + 1);
00956             *hDB = 0;
00957             return DB_CORRUPTED;
00958           */
00959       }
00960 
00961       /* setup _database entry */
00962       _database[handle].database_data = _database[handle].database_header + 1;
00963       _database[handle].attached = TRUE;
00964       _database[handle].shm_handle = shm_handle;
00965       _database[handle].protect = FALSE;
00966 
00967       /* remember to which connection acutal buffer belongs */
00968       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE)
00969          _database[handle].index = rpc_get_server_acception();
00970       else
00971          _database[handle].index = ss_gettid();
00972 
00973       *hDB = (handle + 1);
00974 
00975       /* setup dispatcher for updated records */
00976       ss_suspend_set_dispatch(CH_IPC, 0, (int (*)(void)) cm_dispatch_ipc);
00977 
00978 
00979       /* remove dead clients */
00980 
00981 #ifdef OS_UNIX
00982 #ifdef ESRCH
00983       /* Only enable this for systems that define ESRCH and hope that
00984          they also support kill(pid,0) */
00985       for (i = 0; i < MAX_CLIENTS; i++) {
00986          int k;
00987 
00988          errno = 0;
00989          kill(pheader->client[i].pid, 0);
00990          if (errno == ESRCH) {
00991             cm_msg(MERROR, "db_open_database",
00992                    "removing client %s, pid %d, index %d because the pid no longer exists",
00993                    pheader->client[i].name, pheader->client[i].pid, i);
00994 
00995             /* decrement notify_count for open records and clear exclusive mode */
00996             for (k = 0; k < pheader->client[i].max_index; k++)
00997                if (pheader->client[i].open_record[k].handle) {
00998                   pkey = (KEY *) ((char *) pheader +
00999                                   pheader->client[i].open_record[k].handle);
01000                   if (pkey->notify_count > 0)
01001                      pkey->notify_count--;
01002 
01003                   if (pheader->client[i].open_record[k].access_mode & MODE_WRITE)
01004                      db_set_mode(handle + 1, pheader->client[i].open_record[k].handle,
01005                                  (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2);
01006                }
01007 
01008             /* clear entry from client structure in database header */
01009             memset(&(pheader->client[i]), 0, sizeof(DATABASE_CLIENT));
01010          }
01011       }
01012 #endif
01013 #endif
01014 
01015       db_unlock_database(handle + 1);
01016 
01017       if (shm_created)
01018          return DB_CREATED;
01019    }
01020 #endif                          /* LOCAL_ROUTINES */
01021 
01022    return DB_SUCCESS;
01023 }
01024 
01025 /********************************************************************/
01026 /**
01027 Close a database
01028 @param   hDB          ODB handle obtained via cm_get_experiment_database().
01029 @return DB_SUCCESS, DB_INVALID_HANDLE, RPC_NET_ERROR 
01030 */
01031 INT db_close_database(HNDLE hDB)
01032 {
01033    if (rpc_is_remote())
01034       return rpc_call(RPC_DB_CLOSE_DATABASE, hDB);
01035 
01036 #ifdef LOCAL_ROUTINES
01037    else {
01038       DATABASE_HEADER *pheader;
01039       DATABASE_CLIENT *pclient;
01040       INT index, destroy_flag, i, j;
01041 
01042       if (hDB > _database_entries || hDB <= 0) {
01043          cm_msg(MERROR, "db_close_database", "invalid database handle");
01044          return DB_INVALID_HANDLE;
01045       }
01046 
01047       /*
01048          Check if database was opened by current thread. This is necessary
01049          in the server process where one thread may not close the database
01050          of other threads.
01051        */
01052 
01053       /* first lock database */
01054       db_lock_database(hDB);
01055 
01056       index = _database[hDB - 1].client_index;
01057       pheader = _database[hDB - 1].database_header;
01058       pclient = &pheader->client[index];
01059 
01060       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
01061           _database[hDB - 1].index != rpc_get_server_acception()) {
01062          db_unlock_database(hDB);
01063          return DB_INVALID_HANDLE;
01064       }
01065 
01066       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
01067           _database[hDB - 1].index != ss_gettid()) {
01068          db_unlock_database(hDB);
01069          return DB_INVALID_HANDLE;
01070       }
01071 
01072       if (!_database[hDB - 1].attached) {
01073          cm_msg(MERROR, "db_close_database", "invalid database handle");
01074          db_unlock_database(hDB);
01075          return DB_INVALID_HANDLE;
01076       }
01077 
01078       /* close all open records */
01079       for (i = 0; i < pclient->max_index; i++)
01080          if (pclient->open_record[i].handle)
01081             db_remove_open_record(hDB, pclient->open_record[i].handle, FALSE);
01082 
01083       /* mark entry in _database as empty */
01084       _database[hDB - 1].attached = FALSE;
01085 
01086       /* clear entry from client structure in database header */
01087       memset(&(pheader->client[index]), 0, sizeof(DATABASE_CLIENT));
01088 
01089       /* calculate new max_client_index entry */
01090       for (i = MAX_CLIENTS - 1; i >= 0; i--)
01091          if (pheader->client[i].pid != 0)
01092             break;
01093       pheader->max_client_index = i + 1;
01094 
01095       /* count new number of clients */
01096       for (i = MAX_CLIENTS - 1, j = 0; i >= 0; i--)
01097          if (pheader->client[i].pid != 0)
01098             j++;
01099       pheader->num_clients = j;
01100 
01101       destroy_flag = (pheader->num_clients == 0);
01102 
01103       /* flush shared memory to disk */
01104       ss_shm_flush(pheader->name, pheader,
01105                    sizeof(DATABASE_HEADER) + 2 * pheader->data_size);
01106 
01107       /* unmap shared memory, delete it if we are the last */
01108       ss_shm_close(pheader->name, pheader, _database[hDB - 1].shm_handle, destroy_flag);
01109 
01110       /* unlock database */
01111       db_unlock_database(hDB);
01112 
01113       /* delete mutex */
01114       ss_mutex_delete(_database[hDB - 1].mutex, destroy_flag);
01115 
01116       /* update _database_entries */
01117       if (hDB == _database_entries)
01118          _database_entries--;
01119 
01120       if (_database_entries > 0)
01121          _database =
01122              (DATABASE *) realloc(_database, sizeof(DATABASE) * (_database_entries));
01123       else {
01124          free(_database);
01125          _database = NULL;
01126       }
01127 
01128       /* if we are the last one, also delete other mutexes */
01129       if (destroy_flag) {
01130          extern INT _mutex_elog, _mutex_alarm;
01131 
01132          if (_mutex_elog)
01133             ss_mutex_delete(_mutex_elog, TRUE);
01134          if (_mutex_alarm)
01135             ss_mutex_delete(_mutex_alarm, TRUE);
01136       }
01137 
01138    }
01139 #endif                          /* LOCAL_ROUTINES */
01140 
01141    return DB_SUCCESS;
01142 }
01143 
01144 /**dox***************************************************************/
01145 #ifndef DOXYGEN_SHOULD_SKIP_THIS
01146 
01147 /*------------------------------------------------------------------*/
01148 INT db_flush_database(HNDLE hDB)
01149 /********************************************************************\
01150 
01151   Routine: db_flush_database
01152 
01153   Purpose: Flushes the shared memory of a database to its disk file.
01154 
01155   Input:
01156     HNDLE  hDB              Handle to the database, which is used as
01157                             an index to the _database array.
01158 
01159   Output:
01160     none
01161 
01162   Function value:
01163     DB_SUCCESS              Successful completion
01164     DB_INVALID_HANDLE       Database handle is invalid
01165     RPC_NET_ERROR           Network error
01166 
01167 \********************************************************************/
01168 {
01169    if (rpc_is_remote())
01170       return rpc_call(RPC_DB_FLUSH_DATABASE, hDB);
01171 
01172 #ifdef LOCAL_ROUTINES
01173    else {
01174       DATABASE_HEADER *pheader;
01175       DATABASE_CLIENT *pclient;
01176       INT index;
01177 
01178       if (hDB > _database_entries || hDB <= 0) {
01179          cm_msg(MERROR, "db_close_database", "invalid database handle");
01180          return DB_INVALID_HANDLE;
01181       }
01182 
01183       /*
01184          Check if database was opened by current thread. This is necessary
01185          in the server process where one thread may not close the database
01186          of other threads.
01187        */
01188 
01189       db_lock_database(hDB);
01190       index = _database[hDB - 1].client_index;
01191       pheader = _database[hDB - 1].database_header;
01192       pclient = &pheader->client[index];
01193 
01194       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
01195           _database[hDB - 1].index != rpc_get_server_acception()) {
01196          db_unlock_database(hDB);
01197          return DB_INVALID_HANDLE;
01198       }
01199 
01200       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
01201           _database[hDB - 1].index != ss_gettid()) {
01202          db_unlock_database(hDB);
01203          return DB_INVALID_HANDLE;
01204       }
01205 
01206       if (!_database[hDB - 1].attached) {
01207          cm_msg(MERROR, "db_close_database", "invalid database handle");
01208          db_unlock_database(hDB);
01209          return DB_INVALID_HANDLE;
01210       }
01211 
01212       /* flush shared memory to disk */
01213       ss_shm_flush(pheader->name, pheader,
01214                    sizeof(DATABASE_HEADER) + 2 * pheader->data_size);
01215       db_unlock_database(hDB);
01216 
01217    }
01218 #endif                          /* LOCAL_ROUTINES */
01219 
01220    return DB_SUCCESS;
01221 }
01222 
01223 /*------------------------------------------------------------------*/
01224 INT db_close_all_databases(void)
01225 /********************************************************************\
01226 
01227   Routine: db_close_all_databases
01228 
01229   Purpose: Close all open databases and open records
01230 
01231   Input:
01232     none
01233 
01234   Output:
01235     none
01236 
01237   Function value:
01238     DB_SUCCESS              Successful completion
01239 
01240 \********************************************************************/
01241 {
01242    INT status;
01243 
01244    if (rpc_is_remote()) {
01245       status = rpc_call(RPC_DB_CLOSE_ALL_DATABASES);
01246       if (status != DB_SUCCESS)
01247          return status;
01248    }
01249 #ifdef LOCAL_ROUTINES
01250    {
01251       INT i;
01252 
01253       for (i = _database_entries; i > 0; i--)
01254          db_close_database(i);
01255    }
01256 #endif                          /* LOCAL_ROUTINES */
01257 
01258    return db_close_all_records();
01259 }
01260 
01261 /*------------------------------------------------------------------*/
01262 INT db_set_client_name(HNDLE hDB, char *client_name)
01263 /********************************************************************\
01264 
01265   Routine: db_set_client_name
01266 
01267   Purpose: Set client name for a database. Used by cm_connect_experiment
01268            if a client name is duplicate and changed.
01269 
01270   Input:
01271     INT  hDB                Handle to database
01272     char *client_name       Name of this application
01273 
01274   Output:
01275 
01276   Function value:
01277     DB_SUCCESS              Successful completion
01278     RPC_NET_ERROR           Network error
01279 
01280 \********************************************************************/
01281 {
01282    if (rpc_is_remote())
01283       return rpc_call(RPC_DB_SET_CLIENT_NAME, hDB, client_name);
01284 
01285 #ifdef LOCAL_ROUTINES
01286    {
01287       DATABASE_HEADER *pheader;
01288       DATABASE_CLIENT *pclient;
01289       INT index;
01290 
01291       index = _database[hDB - 1].client_index;
01292       pheader = _database[hDB - 1].database_header;
01293       pclient = &pheader->client[index];
01294 
01295       strcpy(pclient->name, client_name);
01296    }
01297 #endif                          /* LOCAL_ROUTINES */
01298 
01299    return DB_SUCCESS;
01300 }
01301 
01302 /**dox***************************************************************/
01303 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01304 
01305 /********************************************************************/
01306 /**
01307 Lock a database for exclusive access via system mutex calls.
01308 @param hDB   Handle to the database to lock
01309 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TIMEOUT
01310 */
01311 INT db_lock_database(HNDLE hDB)
01312 {
01313 
01314 #ifdef LOCAL_ROUTINES
01315    int status;
01316 
01317    if (hDB > _database_entries || hDB <= 0) {
01318       cm_msg(MERROR, "db_lock_database", "invalid database handle, aborting...");
01319       abort();
01320       return DB_INVALID_HANDLE;
01321    }
01322 
01323    if (_database[hDB - 1].protect && _database[hDB - 1].database_header != NULL) {
01324       cm_msg(MERROR, "db_lock_database", "internal error: DB already locked, aborting...");
01325       abort();
01326       return DB_NO_MUTEX;
01327    }
01328    
01329    if (_database[hDB - 1].lock_cnt == 0) {
01330       /* wait max. 5 minutes for mutex (required if locking process is being debugged) */
01331       status = ss_mutex_wait_for(_database[hDB - 1].mutex, 5*60*1000);
01332       if (status == SS_TIMEOUT) {
01333          cm_msg(MERROR, "db_lock_database", "timeout obtaining lock for database, exiting...");
01334          exit(1);
01335          return DB_TIMEOUT;
01336       }
01337       if (status != SS_SUCCESS) {
01338          cm_msg(MERROR, "db_lock_database", "cannot lock database, ss_mutex_wait_for() status %d, aborting...",status);
01339          abort();
01340          return DB_NO_MUTEX;
01341       }
01342    }
01343 
01344    _database[hDB - 1].lock_cnt++;
01345 
01346    if (_database[hDB - 1].protect) {
01347       if (_database[hDB - 1].database_header == NULL)
01348          ss_shm_unprotect(_database[hDB - 1].shm_handle,
01349                           (void **) &_database[hDB - 1].database_header);
01350    }
01351 #endif                          /* LOCAL_ROUTINES */
01352    return DB_SUCCESS;
01353 }
01354 
01355 /********************************************************************/
01356 /**
01357 Unlock a database via system mutex calls.
01358 @param hDB   Handle to the database to unlock
01359 @return DB_SUCCESS, DB_INVALID_HANDLE
01360 */
01361 INT db_unlock_database(HNDLE hDB)
01362 {
01363 
01364 #ifdef LOCAL_ROUTINES
01365    if (hDB > _database_entries || hDB <= 0) {
01366       cm_msg(MERROR, "db_unlock_database", "invalid database handle");
01367       return DB_INVALID_HANDLE;
01368    }
01369 
01370    if (_database[hDB - 1].lock_cnt == 1)
01371       ss_mutex_release(_database[hDB - 1].mutex);
01372 
01373    if (_database[hDB - 1].lock_cnt > 0)
01374       _database[hDB - 1].lock_cnt--;
01375 
01376    if (_database[hDB - 1].protect) {
01377       ss_shm_protect(_database[hDB - 1].shm_handle, _database[hDB - 1].database_header);
01378       _database[hDB - 1].database_header = NULL;
01379    }
01380 #endif                          /* LOCAL_ROUTINES */
01381    return DB_SUCCESS;
01382 }
01383 
01384 /********************************************************************/
01385 /**
01386 Protect a database for read/write access outside of the \b db_xxx functions
01387 @param hDB          ODB handle obtained via cm_get_experiment_database().
01388 @return DB_SUCCESS, DB_INVALID_HANDLE
01389 */
01390 INT db_protect_database(HNDLE hDB)
01391 {
01392 #ifdef LOCAL_ROUTINES
01393    if (hDB > _database_entries || hDB <= 0) {
01394       cm_msg(MERROR, "db_unlock_database", "invalid database handle");
01395       return DB_INVALID_HANDLE;
01396    }
01397 
01398    _database[hDB - 1].protect = TRUE;
01399    ss_shm_protect(_database[hDB - 1].shm_handle, _database[hDB - 1].database_header);
01400    _database[hDB - 1].database_header = NULL;
01401 #endif                          /* LOCAL_ROUTINES */
01402    return DB_SUCCESS;
01403 }
01404 
01405 /*---- helper routines ---------------------------------------------*/
01406 
01407 char *extract_key(char *key_list, char *key_name)
01408 {
01409    if (*key_list == '/')
01410       key_list++;
01411 
01412    while (*key_list && *key_list != '/')
01413       *key_name++ = *key_list++;
01414    *key_name = 0;
01415 
01416    return key_list;
01417 }
01418 
01419 BOOL equal_ustring(char *str1, char *str2)
01420 {
01421    if (str1 == NULL && str2 != NULL)
01422       return FALSE;
01423    if (str1 != NULL && str2 == NULL)
01424       return FALSE;
01425    if (str1 == NULL && str2 == NULL)
01426       return TRUE;
01427 
01428    while (*str1)
01429       if (toupper(*str1++) != toupper(*str2++))
01430          return FALSE;
01431 
01432    if (*str2)
01433       return FALSE;
01434 
01435    return TRUE;
01436 }
01437 
01438 /********************************************************************/
01439 /**
01440 Create a new key in a database
01441 @param hDB          ODB handle obtained via cm_get_experiment_database().
01442 @param hKey  Key handle to start with, 0 for root
01443 @param key_name    Name of key in the form "/key/key/key"
01444 @param type        Type of key, one of TID_xxx (see @ref Midas_Data_Types)
01445 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_INVALID_PARAM, DB_FULL, DB_KEY_EXIST, DB_NO_ACCESS
01446 */
01447 INT db_create_key(HNDLE hDB, HNDLE hKey, char *key_name, DWORD type)
01448 {
01449    if (rpc_is_remote())
01450       return rpc_call(RPC_DB_CREATE_KEY, hDB, hKey, key_name, type);
01451 
01452 #ifdef LOCAL_ROUTINES
01453    {
01454       DATABASE_HEADER *pheader;
01455       KEYLIST *pkeylist;
01456       KEY *pkey, *pprev_key, *pkeyparent;
01457       char *pkey_name, str[MAX_STRING_LENGTH];
01458       INT i;
01459 
01460       if (hDB > _database_entries || hDB <= 0) {
01461          cm_msg(MERROR, "db_create_key", "invalid database handle");
01462          return DB_INVALID_HANDLE;
01463       }
01464 
01465       if (!_database[hDB - 1].attached) {
01466          cm_msg(MERROR, "db_create_key", "invalid database handle");
01467          return DB_INVALID_HANDLE;
01468       }
01469 
01470       /* check type */
01471       if (type >= TID_LAST) {
01472          cm_msg(MERROR, "db_create_key", "invalid key type %d", type);
01473          return DB_INVALID_PARAM;
01474       }
01475 
01476       /* lock database */
01477       db_lock_database(hDB);
01478 
01479       pheader = _database[hDB - 1].database_header;
01480       if (!hKey)
01481          hKey = pheader->root_key;
01482       pkey = (KEY *) ((char *) pheader + hKey);
01483 
01484       /* check if hKey argument is correct */
01485       if (!db_validate_hkey(pheader, hKey)) {
01486          db_unlock_database(hDB);
01487          return DB_INVALID_HANDLE;
01488       }
01489 
01490       if (pkey->type != TID_KEY) {
01491          db_unlock_database(hDB);
01492          cm_msg(MERROR, "db_create_key", "key has no subkeys");
01493          return DB_NO_KEY;
01494       }
01495       pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
01496 
01497       pkey_name = key_name;
01498       do {
01499          /* extract single key from key_name */
01500          pkey_name = extract_key(pkey_name, str);
01501          
01502          /* do not allow empty names, like '/dir/dir//dir/' */
01503          if (str[0] == 0) {
01504             db_unlock_database(hDB);
01505             return DB_INVALID_PARAM;
01506          }
01507 
01508          /* check if parent or current directory */
01509          if (strcmp(str, "..") == 0) {
01510             if (pkey->parent_keylist) {
01511                pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
01512                pkey = (KEY *) ((char *) pheader + pkeylist->parent);
01513             }
01514             continue;
01515          }
01516          if (strcmp(str, ".") == 0)
01517             continue;
01518 
01519          /* check if key is in keylist */
01520          pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
01521          pprev_key = NULL;
01522 
01523          for (i = 0; i < pkeylist->num_keys; i++) {
01524             if (!db_validate_key_offset(pheader, pkey->next_key)) {
01525                cm_msg(MERROR, "db_create_key",
01526                       "Warning: database corruption, key %s, next_key 0x%08X",
01527                       key_name, pkey->next_key - sizeof(DATABASE_HEADER));
01528                db_unlock_database(hDB);
01529                return DB_CORRUPTED;
01530             }
01531 
01532             if (equal_ustring(str, pkey->name))
01533                break;
01534 
01535             pprev_key = pkey;
01536             pkey = (KEY *) ((char *) pheader + pkey->next_key);
01537          }
01538 
01539          if (i == pkeylist->num_keys) {
01540             /* not found: create new key */
01541 
01542             /* check parent for write access */
01543             pkeyparent = (KEY *) ((char *) pheader + pkeylist->parent);
01544             if (!(pkeyparent->access_mode & MODE_WRITE) ||
01545                 (pkeyparent->access_mode & MODE_EXCLUSIVE)) {
01546                db_unlock_database(hDB);
01547                return DB_NO_ACCESS;
01548             }
01549 
01550             pkeylist->num_keys++;
01551 
01552             if (*pkey_name == '/' || type == TID_KEY) {
01553                /* create new key with keylist */
01554                pkey = (KEY *) malloc_key(pheader, sizeof(KEY));
01555 
01556                if (pkey == NULL) {
01557                   db_unlock_database(hDB);
01558                   cm_msg(MERROR, "db_create_key", "online database full");
01559                   return DB_FULL;
01560                }
01561 
01562                /* append key to key list */
01563                if (pprev_key)
01564                   pprev_key->next_key = (PTYPE) pkey - (PTYPE) pheader;
01565                else
01566                   pkeylist->first_key = (PTYPE) pkey - (PTYPE) pheader;
01567 
01568                /* set key properties */
01569                pkey->type = TID_KEY;
01570                pkey->num_values = 1;
01571                pkey->access_mode = MODE_READ | MODE_WRITE | MODE_DELETE;
01572                strcpy(pkey->name, str);
01573                pkey->parent_keylist = (PTYPE) pkeylist - (PTYPE) pheader;
01574 
01575                /* find space for new keylist */
01576                pkeylist = (KEYLIST *) malloc_key(pheader, sizeof(KEYLIST));
01577 
01578                if (pkeylist == NULL) {
01579                   db_unlock_database(hDB);
01580                   cm_msg(MERROR, "db_create_key", "online database full");
01581                   return DB_FULL;
01582                }
01583 
01584                /* store keylist in data field */
01585                pkey->data = (PTYPE) pkeylist - (PTYPE) pheader;
01586                pkey->item_size = sizeof(KEYLIST);
01587                pkey->total_size = sizeof(KEYLIST);
01588 
01589                pkeylist->parent = (PTYPE) pkey - (PTYPE) pheader;
01590                pkeylist->num_keys = 0;
01591                pkeylist->first_key = 0;
01592             } else {
01593                /* create new key with data */
01594                pkey = (KEY *) malloc_key(pheader, sizeof(KEY));
01595 
01596                if (pkey == NULL) {
01597                   db_unlock_database(hDB);
01598                   cm_msg(MERROR, "db_create_key", "online database full");
01599                   return DB_FULL;
01600                }
01601 
01602                /* append key to key list */
01603                if (pprev_key)
01604                   pprev_key->next_key = (PTYPE) pkey - (PTYPE) pheader;
01605                else
01606                   pkeylist->first_key = (PTYPE) pkey - (PTYPE) pheader;
01607 
01608                pkey->type = type;
01609                pkey->num_values = 1;
01610                pkey->access_mode = MODE_READ | MODE_WRITE | MODE_DELETE;
01611                strcpy(pkey->name, str);
01612                pkey->parent_keylist = (PTYPE) pkeylist - (PTYPE) pheader;
01613 
01614                /* zero data */
01615                if (type != TID_STRING && type != TID_LINK) {
01616                   pkey->item_size = rpc_tid_size(type);
01617                   pkey->data = (PTYPE) malloc_data(pheader, pkey->item_size);
01618                   pkey->total_size = pkey->item_size;
01619 
01620                   if (pkey->data == 0) {
01621                      db_unlock_database(hDB);
01622                      cm_msg(MERROR, "db_create_key", "online database full");
01623                      return DB_FULL;
01624                   }
01625 
01626                   pkey->data -= (PTYPE) pheader;
01627                } else {
01628                   /* first data is empty */
01629                   pkey->item_size = 0;
01630                   pkey->total_size = 0;
01631                   pkey->data = 0;
01632                }
01633             }
01634          } else {
01635             /* key found: descend */
01636 
01637             /* resolve links */
01638             if (pkey->type == TID_LINK && pkey_name[0]) {
01639                /* copy destination, strip '/' */
01640                strcpy(str, (char *) pheader + pkey->data);
01641                if (str[strlen(str) - 1] == '/')
01642                   str[strlen(str) - 1] = 0;
01643 
01644                /* append rest of key name */
01645                strcat(str, pkey_name);
01646 
01647                db_unlock_database(hDB);
01648 
01649                return db_create_key(hDB, 0, str, type);
01650             }
01651 
01652             if (!(*pkey_name == '/')) {
01653                if ((WORD) pkey->type != type)
01654                   cm_msg(MERROR, "db_create_key", "redefinition of key type mismatch");
01655 
01656                db_unlock_database(hDB);
01657                return DB_KEY_EXIST;
01658             }
01659 
01660             if (pkey->type != TID_KEY) {
01661                db_unlock_database(hDB);
01662                cm_msg(MERROR, "db_create_key", "key used with value and as parent key");
01663                return DB_KEY_EXIST;
01664             }
01665 
01666             pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
01667          }
01668       } while (*pkey_name == '/');
01669 
01670       db_unlock_database(hDB);
01671    }
01672 #endif                          /* LOCAL_ROUTINES */
01673 
01674    return DB_SUCCESS;
01675 }
01676 
01677 /********************************************************************/
01678 /**
01679 Create a link to a key or set the destination of and existing link.
01680 @param hDB           ODB handle obtained via cm_get_experiment_database().
01681 @param hKey          Key handle to start with, 0 for root
01682 @param link_name     Name of key in the form "/key/key/key"
01683 @param destination   Destination of link in the form "/key/key/key"
01684 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_FULL, DB_KEY_EXIST, DB_NO_ACCESS
01685 */
01686 INT db_create_link(HNDLE hDB, HNDLE hKey, char *link_name, char *destination)
01687 {
01688    HNDLE hkey;
01689    int status;
01690 
01691    if (rpc_is_remote())
01692       return rpc_call(RPC_DB_CREATE_LINK, hDB, hKey, link_name, destination);
01693 
01694    /* check if destination exists */
01695    status = db_find_key(hDB, hKey, destination, &hkey);
01696    if (status != DB_SUCCESS) {
01697       cm_msg(MERROR, "db_create_link",
01698              "Link destination \"%s\" does not exist", destination);
01699       return DB_NO_KEY;
01700    }
01701 
01702    return db_set_value(hDB, hKey, link_name, destination,
01703                        strlen(destination) + 1, 1, TID_LINK);
01704 }
01705 
01706 /********************************************************************/
01707 /**
01708 Delete a subtree, using level information (only called internally by db_delete_key())
01709 @internal 
01710 @param hDB          ODB handle obtained via cm_get_experiment_database().
01711 @param hKey  Key handle to start with, 0 for root
01712 @param level            Recursion level, must be zero when
01713 @param follow_links     Follow links when TRUE called from a user routine
01714 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_OPEN_RECORD
01715 */
01716 INT db_delete_key1(HNDLE hDB, HNDLE hKey, INT level, BOOL follow_links)
01717 {
01718 #ifdef LOCAL_ROUTINES
01719    {
01720       DATABASE_HEADER *pheader;
01721       KEYLIST *pkeylist;
01722       KEY *pkey, *pnext_key, *pkey_tmp;
01723       HNDLE hKeyLink;
01724       BOOL deny_delete;
01725       INT status;
01726 
01727       if (hDB > _database_entries || hDB <= 0) {
01728          cm_msg(MERROR, "db_delete_key1", "invalid database handle");
01729          return DB_INVALID_HANDLE;
01730       }
01731 
01732       if (!_database[hDB - 1].attached) {
01733          cm_msg(MERROR, "db_delete_key1", "invalid database handle");
01734          return DB_INVALID_HANDLE;
01735       }
01736 
01737       if (hKey < sizeof(DATABASE_HEADER)) {
01738          cm_msg(MERROR, "db_delete_key1", "invalid key handle");
01739          return DB_INVALID_HANDLE;
01740       }
01741 
01742       /* lock database at the top level */
01743       if (level == 0)
01744          db_lock_database(hDB);
01745 
01746       pheader = _database[hDB - 1].database_header;
01747 
01748       pkey = (KEY *) ((char *) pheader + hKey);
01749 
01750       /* check if hKey argument is correct */
01751       if (!db_validate_hkey(pheader, hKey)) {
01752          db_unlock_database(hDB);
01753          return DB_INVALID_HANDLE;
01754       }
01755 
01756       /* check if someone has opened key or parent */
01757       if (level == 0)
01758          do {
01759             if (pkey->notify_count) {
01760                db_unlock_database(hDB);
01761                return DB_OPEN_RECORD;
01762             }
01763 
01764             if (pkey->parent_keylist == 0)
01765                break;
01766 
01767             pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
01768             pkey = (KEY *) ((char *) pheader + pkeylist->parent);
01769          } while (TRUE);
01770 
01771       pkey = (KEY *) ((char *) pheader + hKey);
01772       pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
01773 
01774       deny_delete = FALSE;
01775 
01776       /* first recures subtree for keys */
01777       if (pkey->type == TID_KEY && pkeylist->first_key) {
01778          pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
01779 
01780          do {
01781             pnext_key = (KEY *) (PTYPE) pkey->next_key;
01782 
01783             status = db_delete_key1(hDB, (PTYPE) pkey - (PTYPE) pheader,
01784                                     level + 1, follow_links);
01785 
01786             if (status == DB_NO_ACCESS)
01787                deny_delete = TRUE;
01788 
01789             if (pnext_key)
01790                pkey = (KEY *) ((char *) pheader + (PTYPE) pnext_key);
01791          } while (pnext_key);
01792       }
01793 
01794       /* follow links if requested */
01795       if (pkey->type == TID_LINK && follow_links) {
01796          status = db_find_key(hDB, 0, (char *) pheader + pkey->data, &hKeyLink);
01797          if (status == DB_SUCCESS && follow_links < 100)
01798             db_delete_key1(hDB, hKeyLink, level + 1, follow_links + 1);
01799 
01800          if (follow_links == 100)
01801             cm_msg(MERROR, "db_delete_key1", "try to delete cyclic link");
01802       }
01803 
01804       pkey = (KEY *) ((char *) pheader + hKey);
01805 
01806       /* return if key was already deleted by cyclic link */
01807       if (pkey->parent_keylist == 0) {
01808          if (level == 0)
01809             db_unlock_database(hDB);
01810          return DB_SUCCESS;
01811       }
01812 
01813       /* now delete key */
01814       if (hKey != pheader->root_key) {
01815          if (!(pkey->access_mode & MODE_DELETE) || deny_delete) {
01816             if (level == 0)
01817                db_unlock_database(hDB);
01818             return DB_NO_ACCESS;
01819          }
01820 
01821          if (pkey->notify_count) {
01822             if (level == 0)
01823                db_unlock_database(hDB);
01824             return DB_OPEN_RECORD;
01825          }
01826 
01827          /* delete key data */
01828          if (pkey->type == TID_KEY)
01829             free_key(pheader, (char *) pheader + pkey->data, pkey->total_size);
01830          else
01831             free_data(pheader, (char *) pheader + pkey->data, pkey->total_size);
01832 
01833          /* unlink key from list */
01834          pnext_key = (KEY *) (PTYPE) pkey->next_key;
01835          pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
01836 
01837          if ((KEY *) ((char *) pheader + pkeylist->first_key) == pkey) {
01838             /* key is first in list */
01839             pkeylist->first_key = (PTYPE) pnext_key;
01840          } else {
01841             /* find predecessor */
01842             pkey_tmp = (KEY *) ((char *) pheader + pkeylist->first_key);
01843             while ((KEY *) ((char *) pheader + pkey_tmp->next_key) != pkey)
01844                pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
01845             pkey_tmp->next_key = (PTYPE) pnext_key;
01846          }
01847 
01848          /* delete key */
01849          free_key(pheader, pkey, sizeof(KEY));
01850          pkeylist->num_keys--;
01851       }
01852 
01853       if (level == 0)
01854          db_unlock_database(hDB);
01855    }
01856 #endif                          /* LOCAL_ROUTINES */
01857 
01858    return DB_SUCCESS;
01859 }
01860 
01861 /********************************************************************/
01862 /**
01863 Delete a subtree in a database starting from a key (including this key).
01864 \code
01865 ...
01866     status = db_find_link(hDB, 0, str, &hkey);
01867     if (status != DB_SUCCESS)
01868     {
01869       cm_msg(MINFO,"my_delete"," "Cannot find key %s", str);
01870       return;
01871     }
01872 
01873     status = db_delete_key(hDB, hkey, FALSE);
01874     if (status != DB_SUCCESS)
01875     {
01876       cm_msg(MERROR,"my_delete"," "Cannot delete key %s", str);
01877       return;
01878     }
01879   ...
01880 \endcode
01881 @param hDB          ODB handle obtained via cm_get_experiment_database().
01882 @param hKey         for key where search starts, zero for root.
01883 @param follow_links Follow links when TRUE.
01884 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_ACCESS, DB_OPEN_RECORD
01885 */
01886 INT db_delete_key(HNDLE hDB, HNDLE hKey, BOOL follow_links)
01887 {
01888    if (rpc_is_remote())
01889       return rpc_call(RPC_DB_DELETE_KEY, hDB, hKey, follow_links);
01890 
01891    return db_delete_key1(hDB, hKey, 0, follow_links);
01892 }
01893 
01894 /********************************************************************/
01895 /**
01896 Returns key handle for a key with a specific name.
01897 
01898 Keys can be accessed by their name including the directory
01899 or by a handle. A key handle is an internal offset to the shared memory
01900 where the ODB lives and allows a much faster access to a key than via its
01901 name.
01902 
01903 The function db_find_key() must be used to convert a key name to a handle.
01904 Most other database functions use this key handle in various operations.
01905 \code
01906 HNDLE hkey, hsubkey;
01907 // use full name, start from root
01908 db_find_key(hDB, 0, "/Runinfo/Run number", &hkey);
01909 // start from subdirectory
01910 db_find_key(hDB, 0, "/Runinfo", &hkey);
01911 db_find_key(hdb, hkey, "Run number", &hsubkey);
01912 \endcode
01913 @param hDB          ODB handle obtained via cm_get_experiment_database().
01914 @param hKey Handle for key where search starts, zero for root.
01915 @param key_name Name of key to search, can contain directories.
01916 @param subhKey Returned handle of key, zero if key cannot be found.
01917 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_ACCESS, DB_NO_KEY
01918 */
01919 INT db_find_key(HNDLE hDB, HNDLE hKey, char *key_name, HNDLE * subhKey)
01920 {
01921    if (rpc_is_remote())
01922       return rpc_call(RPC_DB_FIND_KEY, hDB, hKey, key_name, subhKey);
01923 
01924 #ifdef LOCAL_ROUTINES
01925    {
01926       DATABASE_HEADER *pheader;
01927       KEYLIST *pkeylist;
01928       KEY *pkey;
01929       char *pkey_name, str[MAX_STRING_LENGTH];
01930       INT i, status;
01931 
01932       *subhKey = 0;
01933 
01934       if (hDB > _database_entries || hDB <= 0) {
01935          cm_msg(MERROR, "db_find_key", "invalid database handle");
01936          return DB_INVALID_HANDLE;
01937       }
01938 
01939       if (!_database[hDB - 1].attached) {
01940          cm_msg(MERROR, "db_find_key", "invalid database handle");
01941          return DB_INVALID_HANDLE;
01942       }
01943 
01944       db_lock_database(hDB);
01945 
01946       pheader = _database[hDB - 1].database_header;
01947 
01948       if (!hKey)
01949          hKey = pheader->root_key;
01950 
01951       pkey = (KEY *) ((char *) pheader + hKey);
01952 
01953       /* check if hKey argument is correct */
01954       if (!db_validate_hkey(pheader, hKey)) {
01955          db_unlock_database(hDB);
01956          return DB_INVALID_HANDLE;
01957       }
01958 
01959       if (pkey->type != TID_KEY) {
01960          cm_msg(MERROR, "db_find_key", "key has no subkeys");
01961          *subhKey = 0;
01962          db_unlock_database(hDB);
01963          return DB_NO_KEY;
01964       }
01965       pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
01966 
01967       if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
01968          if (!(pkey->access_mode & MODE_READ)) {
01969             *subhKey = 0;
01970             db_unlock_database(hDB);
01971             return DB_NO_ACCESS;
01972          }
01973 
01974          *subhKey = (PTYPE) pkey - (PTYPE) pheader;
01975 
01976          db_unlock_database(hDB);
01977          return DB_SUCCESS;
01978       }
01979 
01980       pkey_name = key_name;
01981       do {
01982          /* extract single subkey from key_name */
01983          pkey_name = extract_key(pkey_name, str);
01984 
01985          /* strip trailing '[n]' */
01986          if (strchr(str, '[') && str[strlen(str)-1] == ']')
01987             *strchr(str, '[') = 0;
01988 
01989          /* check if parent or current directory */
01990          if (strcmp(str, "..") == 0) {
01991             if (pkey->parent_keylist) {
01992                pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
01993                pkey = (KEY *) ((char *) pheader + pkeylist->parent);
01994             }
01995             continue;
01996          }
01997          if (strcmp(str, ".") == 0)
01998             continue;
01999 
02000          /* check if key is in keylist */
02001          pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
02002 
02003          for (i = 0; i < pkeylist->num_keys; i++) {
02004             if (pkey->name[0] == 0 || !db_validate_key_offset(pheader, pkey->next_key)) {
02005                cm_msg(MERROR, "db_find_key",
02006                       "Warning: database corruption, key %s, next_key 0x%08X",
02007                       key_name, pkey->next_key - sizeof(DATABASE_HEADER));
02008                *subhKey = 0;
02009                db_unlock_database(hDB);
02010                return DB_CORRUPTED;
02011             }
02012 
02013             if (equal_ustring(str, pkey->name))
02014                break;
02015 
02016             pkey = (KEY *) ((char *) pheader + pkey->next_key);
02017          }
02018 
02019          if (i == pkeylist->num_keys) {
02020             *subhKey = 0;
02021             db_unlock_database(hDB);
02022             return DB_NO_KEY;
02023          }
02024 
02025          /* resolve links */
02026          if (pkey->type == TID_LINK) {
02027             /* copy destination, strip '/' */
02028             strcpy(str, (char *) pheader + pkey->data);
02029             if (str[strlen(str) - 1] == '/')
02030                str[strlen(str) - 1] = 0;
02031 
02032             /* append rest of key name if existing */
02033             if (pkey_name[0]) {
02034                strcat(str, pkey_name);
02035                db_unlock_database(hDB);
02036                return db_find_key(hDB, 0, str, subhKey);
02037             } else {
02038                /* if last key in chain is a link, return its destination */
02039                db_unlock_database(hDB);
02040                status = db_find_link(hDB, 0, str, subhKey);
02041                if (status == DB_NO_KEY)
02042                   return DB_INVALID_LINK;
02043                return status;
02044             }
02045          }
02046 
02047          /* key found: check if last in chain */
02048          if (*pkey_name == '/') {
02049             if (pkey->type != TID_KEY) {
02050                *subhKey = 0;
02051                db_unlock_database(hDB);
02052                return DB_NO_KEY;
02053             }
02054          }
02055 
02056          /* descend one level */
02057          pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
02058 
02059       } while (*pkey_name == '/' && *(pkey_name + 1));
02060 
02061       *subhKey = (PTYPE) pkey - (PTYPE) pheader;
02062 
02063       db_unlock_database(hDB);
02064    }
02065 #endif                          /* LOCAL_ROUTINES */
02066 
02067    return DB_SUCCESS;
02068 }
02069 
02070 /**dox***************************************************************/
02071 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02072 
02073 /*------------------------------------------------------------------*/
02074 INT db_find_key1(HNDLE hDB, HNDLE hKey, char *key_name, HNDLE * subhKey)
02075 /********************************************************************\
02076 
02077   Routine: db_find_key1
02078 
02079   Purpose: Same as db_find_key, but without DB locking
02080 
02081   Input:
02082     HNDLE  bufer_handle     Handle to the database
02083     HNDLE  hKey             Key handle to start the search
02084     char   *key_name        Name of key in the form "/key/key/key"
02085 
02086   Output:
02087     INT    *handle          Key handle
02088 
02089   Function value:
02090     DB_SUCCESS              Successful completion
02091     DB_INVALID_HANDLE       Database handle is invalid
02092     DB_NO_KEY               Key doesn't exist
02093     DB_NO_ACCESS            No access to read key
02094 
02095 \********************************************************************/
02096 {
02097    if (rpc_is_remote())
02098       return rpc_call(RPC_DB_FIND_KEY, hDB, hKey, key_name, subhKey);
02099 
02100 #ifdef LOCAL_ROUTINES
02101    {
02102       DATABASE_HEADER *pheader;
02103       KEYLIST *pkeylist;
02104       KEY *pkey;
02105       char *pkey_name, str[MAX_STRING_LENGTH];
02106       INT i;
02107 
02108       *subhKey = 0;
02109 
02110       if (hDB > _database_entries || hDB <= 0) {
02111          cm_msg(MERROR, "db_find_key", "invalid database handle");
02112          return DB_INVALID_HANDLE;
02113       }
02114 
02115       if (!_database[hDB - 1].attached) {
02116          cm_msg(MERROR, "db_find_key", "invalid database handle");
02117          return DB_INVALID_HANDLE;
02118       }
02119 
02120       pheader = _database[hDB - 1].database_header;
02121       if (!hKey)
02122          hKey = pheader->root_key;
02123       pkey = (KEY *) ((char *) pheader + hKey);
02124 
02125       /* check if hKey argument is correct */
02126       if (!db_validate_hkey(pheader, hKey)) {
02127          db_unlock_database(hDB);
02128          return DB_INVALID_HANDLE;
02129       }
02130 
02131       if (pkey->type != TID_KEY) {
02132          cm_msg(MERROR, "db_find_key", "key has no subkeys");
02133          *subhKey = 0;
02134          return DB_NO_KEY;
02135       }
02136       pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
02137 
02138       if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
02139          if (!(pkey->access_mode & MODE_READ)) {
02140             *subhKey = 0;
02141             return DB_NO_ACCESS;
02142          }
02143 
02144          *subhKey = (PTYPE) pkey - (PTYPE) pheader;
02145 
02146          return DB_SUCCESS;
02147       }
02148 
02149       pkey_name = key_name;
02150       do {
02151          /* extract single subkey from key_name */
02152          pkey_name = extract_key(pkey_name, str);
02153 
02154          /* check if parent or current directory */
02155          if (strcmp(str, "..") == 0) {
02156             if (pkey->parent_keylist) {
02157                pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
02158                pkey = (KEY *) ((char *) pheader + pkeylist->parent);
02159             }
02160             continue;
02161          }
02162          if (strcmp(str, ".") == 0)
02163             continue;
02164 
02165          /* check if key is in keylist */
02166          pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
02167 
02168          for (i = 0; i < pkeylist->num_keys; i++) {
02169             if (equal_ustring(str, pkey->name))
02170                break;
02171 
02172             pkey = (KEY *) ((char *) pheader + pkey->next_key);
02173          }
02174 
02175          if (i == pkeylist->num_keys) {
02176             *subhKey = 0;
02177             return DB_NO_KEY;
02178          }
02179 
02180          /* resolve links */
02181          if (pkey->type == TID_LINK) {
02182             /* copy destination, strip '/' */
02183             strcpy(str, (char *) pheader + pkey->data);
02184             if (str[strlen(str) - 1] == '/')
02185                str[strlen(str) - 1] = 0;
02186 
02187             /* append rest of key name if existing */
02188             if (pkey_name[0]) {
02189                strcat(str, pkey_name);
02190                return db_find_key1(hDB, 0, str, subhKey);
02191             } else {
02192                /* if last key in chain is a link, return its destination */
02193                return db_find_link1(hDB, 0, str, subhKey);
02194             }
02195          }
02196 
02197          /* key found: check if last in chain */
02198          if (*pkey_name == '/') {
02199             if (pkey->type != TID_KEY) {
02200                *subhKey = 0;
02201                db_unlock_database(hDB);
02202                return DB_NO_KEY;
02203             }
02204          }
02205 
02206          /* descend one level */
02207          pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
02208 
02209       } while (*pkey_name == '/' && *(pkey_name + 1));
02210 
02211       *subhKey = (PTYPE) pkey - (PTYPE) pheader;
02212    }
02213 #endif                          /* LOCAL_ROUTINES */
02214 
02215    return DB_SUCCESS;
02216 }
02217 
02218 /*------------------------------------------------------------------*/
02219 INT db_find_link(HNDLE hDB, HNDLE hKey, char *key_name, HNDLE * subhKey)
02220 /********************************************************************\
02221 
02222   Routine: db_find_link
02223 
02224   Purpose: Find a key or link by name and return its handle
02225            (internal address). The only difference of this routine
02226            compared with db_find_key is that if the LAST key in
02227            the chain is a link, it is NOT evaluated. Links not being
02228            the last in the chain are evaluated.
02229 
02230   Input:
02231     HNDLE  bufer_handle     Handle to the database
02232     HNDLE  hKey       Key handle to start the search
02233     char   *key_name        Name of key in the form "/key/key/key"
02234 
02235   Output:
02236     INT    *handle          Key handle
02237 
02238   Function value:
02239     DB_SUCCESS              Successful completion
02240     DB_INVALID_HANDLE       Database handle is invalid
02241     DB_NO_KEY               Key doesn't exist
02242     DB_NO_ACCESS            No access to read key
02243 
02244 \********************************************************************/
02245 {
02246    if (rpc_is_remote())
02247       return rpc_call(RPC_DB_FIND_LINK, hDB, hKey, key_name, subhKey);
02248 
02249 #ifdef LOCAL_ROUTINES
02250    {
02251       DATABASE_HEADER *pheader;
02252       KEYLIST *pkeylist;
02253       KEY *pkey;
02254       char *pkey_name, str[MAX_STRING_LENGTH];
02255       INT i;
02256 
02257       *subhKey = 0;
02258 
02259       if (hDB > _database_entries || hDB <= 0) {
02260          cm_msg(MERROR, "db_find_link", "Invalid database handle");
02261          return DB_INVALID_HANDLE;
02262       }
02263 
02264       if (!_database[hDB - 1].attached) {
02265          cm_msg(MERROR, "db_find_link", "invalid database handle");
02266          return DB_INVALID_HANDLE;
02267       }
02268 
02269       db_lock_database(hDB);
02270 
02271       pheader = _database[hDB - 1].database_header;
02272       if (!hKey)
02273          hKey = pheader->root_key;
02274       pkey = (KEY *) ((char *) pheader + hKey);
02275 
02276       /* check if hKey argument is correct */
02277       if (!db_validate_hkey(pheader, hKey)) {
02278          db_unlock_database(hDB);
02279          return DB_INVALID_HANDLE;
02280       }
02281 
02282       if (pkey->type != TID_KEY) {
02283          cm_msg(MERROR, "db_find_link", "key has no subkeys");
02284          db_unlock_database(hDB);
02285          return DB_NO_KEY;
02286       }
02287       pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
02288 
02289       if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
02290          if (!(pkey->access_mode & MODE_READ)) {
02291             *subhKey = 0;
02292             db_unlock_database(hDB);
02293             return DB_NO_ACCESS;
02294          }
02295 
02296          *subhKey = (PTYPE) pkey - (PTYPE) pheader;
02297 
02298          db_unlock_database(hDB);
02299          return DB_SUCCESS;
02300       }
02301 
02302       pkey_name = key_name;
02303       do {
02304          /* extract single subkey from key_name */
02305          pkey_name = extract_key(pkey_name, str);
02306 
02307          /* check if parent or current directory */
02308          if (strcmp(str, "..") == 0) {
02309             if (pkey->parent_keylist) {
02310                pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
02311                pkey = (KEY *) ((char *) pheader + pkeylist->parent);
02312             }
02313             continue;
02314          }
02315          if (strcmp(str, ".") == 0)
02316             continue;
02317 
02318          /* check if key is in keylist */
02319          pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
02320 
02321          for (i = 0; i < pkeylist->num_keys; i++) {
02322             if (!db_validate_key_offset(pheader, pkey->next_key)) {
02323                cm_msg(MERROR, "db_find_link",
02324                       "Warning: database corruption, key \"%s\", next_key 0x%08X",
02325                       key_name, pkey->next_key - sizeof(DATABASE_HEADER));
02326                *subhKey = 0;
02327                db_unlock_database(hDB);
02328                return DB_CORRUPTED;
02329             }
02330 
02331             if (equal_ustring(str, pkey->name))
02332                break;
02333 
02334             pkey = (KEY *) ((char *) pheader + pkey->next_key);
02335          }
02336 
02337          if (i == pkeylist->num_keys) {
02338             *subhKey = 0;
02339             db_unlock_database(hDB);
02340             return DB_NO_KEY;
02341          }
02342 
02343          /* resolve links if not last in chain */
02344          if (pkey->type == TID_LINK && *pkey_name == '/') {
02345             /* copy destination, strip '/' */
02346             strcpy(str, (char *) pheader + pkey->data);
02347             if (str[strlen(str) - 1] == '/')
02348                str[strlen(str) - 1] = 0;
02349 
02350             /* append rest of key name */
02351             strcat(str, pkey_name);
02352             db_unlock_database(hDB);
02353             return db_find_link(hDB, 0, str, subhKey);
02354          }
02355 
02356          /* key found: check if last in chain */
02357          if ((*pkey_name == '/')) {
02358             if (pkey->type != TID_KEY) {
02359                *subhKey = 0;
02360                db_unlock_database(hDB);
02361                return DB_NO_KEY;
02362             }
02363          }
02364 
02365          /* descend one level */
02366          pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
02367 
02368       } while (*pkey_name == '/' && *(pkey_name + 1));
02369 
02370       *subhKey = (PTYPE) pkey - (PTYPE) pheader;
02371 
02372       db_unlock_database(hDB);
02373    }
02374 #endif                          /* LOCAL_ROUTINES */
02375 
02376    return DB_SUCCESS;
02377 }
02378 
02379 /*------------------------------------------------------------------*/
02380 INT db_find_link1(HNDLE hDB, HNDLE hKey, char *key_name, HNDLE * subhKey)
02381 /********************************************************************\
02382 
02383   Routine: db_find_link1
02384 
02385   Purpose: Same ad db_find_link, but without DB locking
02386 
02387   Input:
02388     HNDLE  bufer_handle     Handle to the database
02389     HNDLE  hKey       Key handle to start the search
02390     char   *key_name        Name of key in the form "/key/key/key"
02391 
02392   Output:
02393     INT    *handle          Key handle
02394 
02395   Function value:
02396     DB_SUCCESS              Successful completion
02397     DB_INVALID_HANDLE       Database handle is invalid
02398     DB_NO_KEY               Key doesn't exist
02399     DB_NO_ACCESS            No access to read key
02400 
02401 \********************************************************************/
02402 {
02403    if (rpc_is_remote())
02404       return rpc_call(RPC_DB_FIND_LINK, hDB, hKey, key_name, subhKey);
02405 
02406 #ifdef LOCAL_ROUTINES
02407    {
02408       DATABASE_HEADER *pheader;
02409       KEYLIST *pkeylist;
02410       KEY *pkey;
02411       char *pkey_name, str[MAX_STRING_LENGTH];
02412       INT i;
02413 
02414       *subhKey = 0;
02415 
02416       if (hDB > _database_entries || hDB <= 0) {
02417          cm_msg(MERROR, "db_find_link", "Invalid database handle");
02418          return DB_INVALID_HANDLE;
02419       }
02420 
02421       if (!_database[hDB - 1].attached) {
02422          cm_msg(MERROR, "db_find_link", "invalid database handle");
02423          return DB_INVALID_HANDLE;
02424       }
02425 
02426       pheader = _database[hDB - 1].database_header;
02427       if (!hKey)
02428          hKey = pheader->root_key;
02429       pkey = (KEY *) ((char *) pheader + hKey);
02430 
02431       /* check if hKey argument is correct */
02432       if (!db_validate_hkey(pheader, hKey)) {
02433          db_unlock_database(hDB);
02434          return DB_INVALID_HANDLE;
02435       }
02436 
02437       if (pkey->type != TID_KEY) {
02438          cm_msg(MERROR, "db_find_link", "key has no subkeys");
02439          return DB_NO_KEY;
02440       }
02441       pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
02442 
02443       if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
02444          if (!(pkey->access_mode & MODE_READ)) {
02445             *subhKey = 0;
02446             return DB_NO_ACCESS;
02447          }
02448 
02449          *subhKey = (PTYPE) pkey - (PTYPE) pheader;
02450 
02451          return DB_SUCCESS;
02452       }
02453 
02454       pkey_name = key_name;
02455       do {
02456          /* extract single subkey from key_name */
02457          pkey_name = extract_key(pkey_name, str);
02458 
02459          /* check if parent or current directory */
02460          if (strcmp(str, "..") == 0) {
02461             if (pkey->parent_keylist) {
02462                pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
02463                pkey = (KEY *) ((char *) pheader + pkeylist->parent);
02464             }
02465             continue;
02466          }
02467          if (strcmp(str, ".") == 0)
02468             continue;
02469 
02470          /* check if key is in keylist */
02471          pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
02472 
02473          for (i = 0; i < pkeylist->num_keys; i++) {
02474             if (!db_validate_key_offset(pheader, pkey->next_key)) {
02475                cm_msg(MERROR, "db_find_link1",
02476                       "Warning: database corruption, key \"%s\", next_key 0x%08X",
02477                       key_name, pkey->next_key - sizeof(DATABASE_HEADER));
02478                *subhKey = 0;
02479                return DB_CORRUPTED;
02480             }
02481 
02482             if (equal_ustring(str, pkey->name))
02483                break;
02484 
02485             pkey = (KEY *) ((char *) pheader + pkey->next_key);
02486          }
02487 
02488          if (i == pkeylist->num_keys) {
02489             *subhKey = 0;
02490             return DB_NO_KEY;
02491          }
02492 
02493          /* resolve links if not last in chain */
02494          if (pkey->type == TID_LINK && *pkey_name == '/') {
02495             /* copy destination, strip '/' */
02496             strcpy(str, (char *) pheader + pkey->data);
02497             if (str[strlen(str) - 1] == '/')
02498                str[strlen(str) - 1] = 0;
02499 
02500             /* append rest of key name */
02501             strcat(str, pkey_name);
02502             return db_find_link1(hDB, 0, str, subhKey);
02503          }
02504 
02505          /* key found: check if last in chain */
02506          if ((*pkey_name == '/')) {
02507             if (pkey->type != TID_KEY) {
02508                *subhKey = 0;
02509                return DB_NO_KEY;
02510             }
02511          }
02512 
02513          /* descend one level */
02514          pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
02515 
02516       } while (*pkey_name == '/' && *(pkey_name + 1));
02517 
02518       *subhKey = (PTYPE) pkey - (PTYPE) pheader;
02519    }
02520 #endif                          /* LOCAL_ROUTINES */
02521 
02522    return DB_SUCCESS;
02523 }
02524 
02525 /*------------------------------------------------------------------*/
02526 INT db_scan_tree(HNDLE hDB, HNDLE hKey, INT level,
02527                  INT(*callback) (HNDLE, HNDLE, KEY *, INT, void *), void *info)
02528 /********************************************************************\
02529 
02530   Routine: db_scan_tree
02531 
02532   Purpose: Scan a subtree recursively and call 'callback' for each key
02533 
02534   Input:
02535     HNDLE  hDB              Handle to the database
02536     HNDLE  hKeyRoot         Key to start scan from, 0 for root
02537     INT    level            Recursion level
02538     void   *callback        Callback routine called with params:
02539                               hDB   Copy of hDB
02540                               hKey  Copy of hKey
02541                               key   Key associated with hKey
02542                               INT   Recursion level
02543                               info  Copy of *info
02544     void   *info            Optional data copied to callback routine
02545 
02546   Output:
02547     implicit via callback
02548 
02549   Function value:
02550     DB_SUCCESS              Successful completion
02551     <all error codes of db_get_key>
02552 
02553 \********************************************************************/
02554 {
02555    HNDLE hSubkey;
02556    KEY key;
02557    INT i, status;
02558 
02559    status = db_get_key(hDB, hKey, &key);
02560    if (status != DB_SUCCESS)
02561       return status;
02562 
02563    status = callback(hDB, hKey, &key, level, info);
02564    if (status == 0)
02565       return status;
02566 
02567    if (key.type == TID_KEY) {
02568       for (i = 0;; i++) {
02569          db_enum_key(hDB, hKey, i, &hSubkey);
02570 
02571          if (!hSubkey)
02572             break;
02573 
02574          db_scan_tree(hDB, hSubkey, level + 1, callback, info);
02575       }
02576    }
02577 
02578    return DB_SUCCESS;
02579 }
02580 
02581 /*------------------------------------------------------------------*/
02582 INT db_scan_tree_link(HNDLE hDB, HNDLE hKey, INT level,
02583                       void (*callback) (HNDLE, HNDLE, KEY *, INT, void *), void *info)
02584 /********************************************************************\
02585 
02586   Routine: db_scan_tree_link
02587 
02588   Purpose: Scan a subtree recursively and call 'callback' for each key.
02589            Similar to db_scan_tree but without follwing links.
02590 
02591   Input:
02592     HNDLE  hDB              Handle to the database
02593     HNDLE  hKeyRoot         Key to start scan from, 0 for root
02594     INT    level            Recursion level
02595     void   *callback        Callback routine called with params:
02596                               hDB   Copy of hDB
02597                               hKey  Copy of hKey
02598                               key   Key associated with hKey
02599                               INT   Recursion level
02600                               info  Copy of *info
02601     void   *info            Optional data copied to callback routine
02602 
02603   Output:
02604     implicit via callback
02605 
02606   Function value:
02607     DB_SUCCESS              Successful completion
02608     <all error codes of db_get_key>
02609 
02610 \********************************************************************/
02611 {
02612    HNDLE hSubkey;
02613    KEY key;
02614    INT i, status;
02615 
02616    status = db_get_key(hDB, hKey, &key);
02617    if (status != DB_SUCCESS)
02618       return status;
02619 
02620    callback(hDB, hKey, &key, level, info);
02621 
02622    if (key.type == TID_KEY) {
02623       for (i = 0;; i++) {
02624          db_enum_link(hDB, hKey, i, &hSubkey);
02625 
02626          if (!hSubkey)
02627             break;
02628 
02629          db_scan_tree_link(hDB, hSubkey, level + 1, callback, info);
02630       }
02631    }
02632 
02633    return DB_SUCCESS;
02634 }
02635 
02636 /*------------------------------------------------------------------*/
02637 INT db_get_path(HNDLE hDB, HNDLE hKey, char *path, INT buf_size)
02638 /********************************************************************\
02639 
02640   Routine: db_get_path
02641 
02642   Purpose: Get full path of a key
02643 
02644   Input:
02645     HNDLE  hDB              Handle to the database
02646     HNDLE  hKey             Key handle
02647     INT    buf_size         Maximum size of path buffer (including
02648                             trailing zero)
02649 
02650   Output:
02651     char   path[buf_size]   Path string
02652 
02653   Function value:
02654     DB_SUCCESS              Successful completion
02655     DB_INVALID_HANDLE       Database handle is invalid
02656     DB_NO_MEMORY            path buffer is to small to contain full
02657                             string
02658 
02659 \********************************************************************/
02660 {
02661    if (rpc_is_remote())
02662       return rpc_call(RPC_DB_GET_PATH, hDB, hKey, path, buf_size);
02663 
02664 #ifdef LOCAL_ROUTINES
02665    {
02666       DATABASE_HEADER *pheader;
02667       KEYLIST *pkeylist;
02668       KEY *pkey;
02669       char str[MAX_ODB_PATH];
02670 
02671       if (hDB > _database_entries || hDB <= 0) {
02672          cm_msg(MERROR, "db_get_path", "invalid database handle");
02673          return DB_INVALID_HANDLE;
02674       }
02675 
02676       if (!_database[hDB - 1].attached) {
02677          cm_msg(MERROR, "db_get_path", "invalid database handle");
02678          return DB_INVALID_HANDLE;
02679       }
02680 
02681       db_lock_database(hDB);
02682 
02683       pheader = _database[hDB - 1].database_header;
02684       if (!hKey)
02685          hKey = pheader->root_key;
02686       pkey = (KEY *) ((char *) pheader + hKey);
02687 
02688       /* check if hKey argument is correct */
02689       if (!db_validate_hkey(pheader, hKey)) {
02690          db_unlock_database(hDB);
02691          return DB_INVALID_HANDLE;
02692       }
02693 
02694       if (hKey == pheader->root_key) {
02695          strcpy(path, "/");
02696          db_unlock_database(hDB);
02697          return DB_SUCCESS;
02698       }
02699 
02700       *path = 0;
02701       do {
02702          /* add key name in front of path */
02703          strcpy(str, path);
02704          strcpy(path, "/");
02705          strcat(path, pkey->name);
02706 
02707          if (strlen(path) + strlen(str) + 1 > (DWORD) buf_size) {
02708             *path = 0;
02709             db_unlock_database(hDB);
02710             return DB_NO_MEMORY;
02711          }
02712          strcat(path, str);
02713 
02714          /* find parent key */
02715          pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
02716          pkey = (KEY *) ((char *) pheader + pkeylist->parent);
02717       } while (pkey->parent_keylist);
02718 
02719       db_unlock_database(hDB);
02720    }
02721 #endif                          /* LOCAL_ROUTINES */
02722 
02723    return DB_SUCCESS;
02724 }
02725 
02726 /*------------------------------------------------------------------*/
02727 void db_find_open_records(HNDLE hDB, HNDLE hKey, KEY * key, INT level, void *result)
02728 {
02729 #ifdef LOCAL_ROUTINES
02730    DATABASE_HEADER *pheader;
02731    DATABASE_CLIENT *pclient;
02732    INT i, j;
02733    char line[256], str[80];
02734 
02735    /* check if this key has notify count set */
02736    if (key->notify_count) {
02737       db_get_path(hDB, hKey, str, sizeof(str));
02738       sprintf(line, "%s open %d times by ", str, key->notify_count);
02739 
02740       db_lock_database(hDB);
02741       pheader = _database[hDB - 1].database_header;
02742 
02743       for (i = 0; i < pheader->max_client_index; i++) {
02744          pclient = &pheader->client[i];
02745          for (j = 0; j < pclient->max_index; j++)
02746             if (pclient->open_record[j].handle == hKey)
02747                sprintf(line + strlen(line), "%s ", pclient->name);
02748       }
02749       strcat(line, "\n");
02750       strcat((char *) result, line);
02751 
02752       db_unlock_database(hDB);
02753    }
02754 #endif                          /* LOCAL_ROUTINES */
02755 }
02756 
02757 void db_fix_open_records(HNDLE hDB, HNDLE hKey, KEY * key, INT level, void *result)
02758 {
02759 #ifdef LOCAL_ROUTINES
02760    DATABASE_HEADER *pheader;
02761    DATABASE_CLIENT *pclient;
02762    INT i, j;
02763    char str[256];
02764    KEY *pkey;
02765 
02766    /* check if this key has notify count set */
02767    if (key->notify_count) {
02768       db_lock_database(hDB);
02769       pheader = _database[hDB - 1].database_header;
02770 
02771       for (i = 0; i < pheader->max_client_index; i++) {
02772          pclient = &pheader->client[i];
02773          for (j = 0; j < pclient->max_index; j++)
02774             if (pclient->open_record[j].handle == hKey)
02775                break;
02776          if (j < pclient->max_index)
02777             break;
02778       }
02779       if (i == pheader->max_client_index) {
02780          db_get_path(hDB, hKey, str, sizeof(str));
02781          strcat(str, " fixed\n");
02782          strcat((char *) result, str);
02783 
02784          /* reset notify count */
02785          pkey = (KEY *) ((char *) pheader + hKey);
02786          pkey->notify_count = 0;
02787       }
02788 
02789       db_unlock_database(hDB);
02790    }
02791 #endif                          /* LOCAL_ROUTINES */
02792 }
02793 
02794 INT db_get_open_records(HNDLE hDB, HNDLE hKey, char *str, INT buf_size, BOOL fix)
02795 /********************************************************************\
02796 
02797   Routine: db_get_open_records
02798 
02799   Purpose: Return a string with all open records
02800 
02801   Input:
02802     HNDLE  hDB              Handle to the database
02803     HNDLE  hKey             Key to start search from, 0 for root
02804     INT    buf_size         Size of string
02805     INT    fix              If TRUE, fix records which are open
02806                             but have no client belonging to it.
02807 
02808   Output:
02809     char   *str             Result string. Individual records are
02810                             separated with new lines.
02811 
02812   Function value:
02813     DB_SUCCESS              Successful completion
02814 
02815 \********************************************************************/
02816 {
02817    str[0] = 0;
02818 
02819    if (rpc_is_remote())
02820       return rpc_call(RPC_DB_GET_OPEN_RECORDS, hDB, hKey, str, buf_size);
02821 
02822    if (fix)
02823       db_scan_tree_link(hDB, hKey, 0, db_fix_open_records, str);
02824    else
02825       db_scan_tree_link(hDB, hKey, 0, db_find_open_records, str);
02826 
02827    return DB_SUCCESS;
02828 }
02829 
02830 /**dox***************************************************************/
02831 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02832 
02833 /********************************************************************/
02834 /**
02835 Set value of a single key.
02836 
02837 The function sets a single value or a whole array to a ODB key.
02838 Since the data buffer is of type void, no type checking can be performed by the
02839 compiler. Therefore the type has to be explicitly supplied, which is checked
02840 against the type stored in the ODB. key_name can contain the full path of a key
02841 (like: "/Equipment/Trigger/Settings/Level1") while hkey is zero which refers
02842 to the root, or hkey can refer to a sub-directory (like /Equipment/Trigger)
02843 and key_name is interpreted relative to that directory like "Settings/Level1".
02844 \code
02845 INT level1;
02846   db_set_value(hDB, 0, "/Equipment/Trigger/Settings/Level1",
02847                           &level1, sizeof(level1), 1, TID_INT);
02848 \endcode
02849 @param hDB          ODB handle obtained via cm_get_experiment_database().
02850 @param hKeyRoot Handle for key where search starts, zero for root.
02851 @param key_name Name of key to search, can contain directories.
02852 @param data Address of data.
02853 @param data_size Size of data (in bytes).
02854 @param num_values Number of data elements.
02855 @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types)
02856 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_ACCESS, DB_TYPE_MISMATCH
02857 */
02858 INT db_set_value(HNDLE hDB, HNDLE hKeyRoot, char *key_name, void *data,
02859                  INT data_size, INT num_values, DWORD type)
02860 {
02861    if (rpc_is_remote())
02862       return rpc_call(RPC_DB_SET_VALUE, hDB, hKeyRoot, key_name,
02863                       data, data_size, num_values, type);
02864 
02865 #ifdef LOCAL_ROUTINES
02866    {
02867       DATABASE_HEADER *pheader;
02868       KEY *pkey;
02869       HNDLE hKey;
02870       INT status;
02871 
02872       if (num_values == 0)
02873          return DB_INVALID_PARAM;
02874 
02875       status = db_find_key(hDB, hKeyRoot, key_name, &hKey);
02876       if (status == DB_NO_KEY) {
02877          db_create_key(hDB, hKeyRoot, key_name, type);
02878          status = db_find_link(hDB, hKeyRoot, key_name, &hKey);
02879       }
02880 
02881       if (status != DB_SUCCESS)
02882          return status;
02883 
02884       db_lock_database(hDB);
02885       pheader = _database[hDB - 1].database_header;
02886 
02887       /* get address from handle */
02888       pkey = (KEY *) ((char *) pheader + hKey);
02889 
02890       /* check for write access */
02891       if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
02892          db_unlock_database(hDB);
02893          return DB_NO_ACCESS;
02894       }
02895 
02896       if (pkey->type != type) {
02897          db_unlock_database(hDB);
02898          cm_msg(MERROR, "db_set_value", "\"%s\" is of type %s, not %s",
02899                 key_name, rpc_tid_name(pkey->type), rpc_tid_name(type));
02900          return DB_TYPE_MISMATCH;
02901       }
02902 
02903       /* keys cannot contain data */
02904       if (pkey->type == TID_KEY) {
02905          db_unlock_database(hDB);
02906          cm_msg(MERROR, "db_set_value", "key cannot contain data");
02907          return DB_TYPE_MISMATCH;
02908       }
02909 
02910       if (data_size == 0) {
02911          db_unlock_database(hDB);
02912          cm_msg(MERROR, "db_set_value", "zero data size not allowed");
02913          return DB_TYPE_MISMATCH;
02914       }
02915 
02916       if (type != TID_STRING && type != TID_LINK &&
02917           data_size != rpc_tid_size(type) * num_values) {
02918          db_unlock_database(hDB);
02919          cm_msg(MERROR, "db_set_value",
02920                 "data_size (%d) does not match num_values (%d)", data_size, num_values);
02921          return DB_TYPE_MISMATCH;
02922       }
02923 
02924       /* resize data size if necessary */
02925       if (pkey->total_size < data_size) {
02926          pkey->data =
02927              (PTYPE) realloc_data(pheader, (char *) pheader + pkey->data,
02928                                   pkey->total_size, data_size);
02929 
02930          if (pkey->data == 0) {
02931             db_unlock_database(hDB);
02932             cm_msg(MERROR, "db_set_value", "online database full");
02933             return DB_FULL;
02934          }
02935 
02936          pkey->data -= (PTYPE) pheader;
02937          pkey->total_size = data_size;
02938       }
02939 
02940       /* set number of values */
02941       pkey->num_values = num_values;
02942 
02943       if (type == TID_STRING || type == TID_LINK)
02944          pkey->item_size = data_size / num_values;
02945       else
02946          pkey->item_size = rpc_tid_size(type);
02947 
02948       /* copy data */
02949       memcpy((char *) pheader + pkey->data, data, data_size);
02950 
02951       /* update time */
02952       pkey->last_written = ss_time();
02953 
02954       db_notify_clients(hDB, hKey, TRUE);
02955       db_unlock_database(hDB);
02956 
02957    }
02958 #endif                          /* LOCAL_ROUTINES */
02959 
02960    return DB_SUCCESS;
02961 }
02962 
02963 /********************************************************************/
02964 /**
02965 Get value of a single key.
02966 
02967 The function returns single values or whole arrays which are contained
02968 in an ODB key. Since the data buffer is of type void, no type checking can be
02969 performed by the compiler. Therefore the type has to be explicitly supplied,
02970 which is checked against the type stored in the ODB. key_name can contain the
02971 full path of a key (like: "/Equipment/Trigger/Settings/Level1") while hkey is
02972 zero which refers to the root, or hkey can refer to a sub-directory
02973 (like: /Equipment/Trigger) and key_name is interpreted relative to that directory
02974 like "Settings/Level1".
02975 \code
02976 INT level1, size;
02977   size = sizeof(level1);
02978   db_get_value(hDB, 0, "/Equipment/Trigger/Settings/Level1",
02979                                    &level1, &size, TID_INT, 0);
02980 \endcode
02981 @param hDB          ODB handle obtained via cm_get_experiment_database().
02982 @param hKeyRoot Handle for key where search starts, zero for root.
02983 @param key_name Name of key to search, can contain directories.
02984 @param data Address of data.
02985 @param buf_size Maximum buffer size on input, number of written bytes on return.
02986 @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types)
02987 @param create If TRUE, create key if not existing
02988 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_ACCESS, DB_TYPE_MISMATCH,
02989 DB_TRUNCATED, DB_NO_KEY
02990 */
02991 INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, char *key_name, void *data,
02992                  INT * buf_size, DWORD type, BOOL create)
02993 {
02994    if (rpc_is_remote())
02995       return rpc_call(RPC_DB_GET_VALUE, hDB, hKeyRoot, key_name,
02996                       data, buf_size, type, create);
02997 
02998 #ifdef LOCAL_ROUTINES
02999    {
03000       DATABASE_HEADER *pheader;
03001       HNDLE hkey;
03002       KEY *pkey;
03003       INT status, size, index;
03004       char *p, path[256], keyname[256];
03005 
03006       if (hDB > _database_entries || hDB <= 0) {
03007          cm_msg(MERROR, "db_get_value", "invalid database handle");
03008          return DB_INVALID_HANDLE;
03009       }
03010 
03011       if (!_database[hDB - 1].attached) {
03012          cm_msg(MERROR, "db_get_value", "invalid database handle");
03013          return DB_INVALID_HANDLE;
03014       }
03015 
03016       /* check if key name contains index */
03017       strlcpy(keyname, key_name, sizeof(keyname));
03018       index = -1;
03019       if (strchr(keyname, '[') && strchr(keyname, ']')) {
03020          for (p = strchr(keyname, '[') + 1; *p && *p != ']'; p++)
03021             if (!isdigit(*p))
03022                break;
03023 
03024          if (*p && *p == ']') {
03025             index = atoi(strchr(keyname, '[') + 1);
03026             *strchr(keyname, '[') = 0;
03027          }
03028       }
03029       
03030       status = db_find_key(hDB, hKeyRoot, keyname, &hkey);
03031       if (status == DB_NO_KEY) {
03032          if (create) {
03033             db_create_key(hDB, hKeyRoot, keyname, type);
03034             status = db_find_key(hDB, hKeyRoot, keyname, &hkey);
03035             if (status != DB_SUCCESS)
03036                return status;
03037 
03038             /* get string size from data size */
03039             if (type == TID_STRING || type == TID_LINK)
03040                size = *buf_size;
03041             else
03042                size = rpc_tid_size(type);
03043 
03044             if (size == 0)
03045                return DB_TYPE_MISMATCH;
03046 
03047             /* set default value if key was created */
03048             status = db_set_value(hDB, hKeyRoot, keyname, data,
03049                                   *buf_size, *buf_size / size, type);
03050          } else
03051             return DB_NO_KEY;
03052       }
03053 
03054       if (status != DB_SUCCESS)
03055          return status;
03056 
03057       /* now lock database */
03058       db_lock_database(hDB);
03059       pheader = _database[hDB - 1].database_header;
03060 
03061       /* get address from handle */
03062       pkey = (KEY *) ((char *) pheader + hkey);
03063 
03064       /* check for correct type */
03065       if (pkey->type != (type)) {
03066          db_unlock_database(hDB);
03067          cm_msg(MERROR, "db_get_value", "\"%s\" is of type %s, not %s",
03068                 keyname, rpc_tid_name(pkey->type), rpc_tid_name(type));
03069          return DB_TYPE_MISMATCH;
03070       }
03071 
03072       /* check for read access */
03073       if (!(pkey->access_mode & MODE_READ)) {
03074          db_unlock_database(hDB);
03075          cm_msg(MERROR, "db_get_value", "%s has no read access", keyname);
03076          return DB_NO_ACCESS;
03077       }
03078 
03079       /* check if buffer is too small */
03080       if ((index == -1 && pkey->num_values * pkey->item_size > *buf_size) ||
03081           (index != -1 && pkey->item_size > *buf_size)) {
03082          memcpy(data, (char *) pheader + pkey->data, *buf_size);
03083          db_unlock_database(hDB);
03084          db_get_path(hDB, hkey, path, sizeof(path));
03085          cm_msg(MERROR, "db_get_value",
03086                 "buffer too small, data truncated for key \"%s\"", path);
03087          return DB_TRUNCATED;
03088       }
03089 
03090       /* check if index in boundaries */
03091       if (index != -1 && index >= pkey->num_values) {
03092          db_unlock_database(hDB);
03093          db_get_path(hDB, hkey, path, sizeof(path));
03094          cm_msg(MERROR, "db_get_value",
03095                 "invalid index \"%d\" for key \"%s\"", index, path);
03096          return DB_INVALID_PARAM;
03097       }
03098 
03099       /* copy key data */
03100       if (index == -1) {
03101          memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
03102          *buf_size = pkey->num_values * pkey->item_size;
03103       } else {
03104          memcpy(data, (char *) pheader + pkey->data + index * pkey->item_size, pkey->item_size);
03105          *buf_size = pkey->item_size;
03106       }
03107 
03108       db_unlock_database(hDB);
03109    }
03110 #endif                          /* LOCAL_ROUTINES */
03111 
03112    return DB_SUCCESS;
03113 }
03114 
03115 /********************************************************************/
03116 /**
03117 Enumerate subkeys from a key, follow links.
03118 
03119 hkey must correspond to a valid ODB directory. The index is
03120 usually incremented in a loop until the last key is reached. Information about the
03121 sub-keys can be obtained with db_get_key(). If a returned key is of type
03122 TID_KEY, it contains itself sub-keys. To scan a whole ODB sub-tree, the
03123 function db_scan_tree() can be used.
03124 \code
03125 INT   i;
03126 HNDLE hkey, hsubkey;
03127 KEY   key;
03128   db_find_key(hdb, 0, "/Runinfo", &hkey);
03129   for (i=0 ; ; i++)
03130   {
03131    db_enum_key(hdb, hkey, i, &hsubkey);
03132    if (!hSubkey)
03133     break; // end of list reached
03134    // print key name
03135    db_get_key(hdb, hkey, &key);
03136    printf("%s\n", key.name);
03137   }
03138 \endcode
03139 @param hDB          ODB handle obtained via cm_get_experiment_database().
03140 @param hKey          Handle for key where search starts, zero for root.
03141 @param index Subkey index, sould be initially 0, then
03142                     incremented in each call until
03143                     *subhKey becomes zero and the function
03144                     returns DB_NO_MORE_SUBKEYS
03145 @param subkey_handle Handle of subkey which can be used in
03146                     db_get_key() and db_get_data()
03147 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_MORE_SUBKEYS
03148 */
03149 INT db_enum_key(HNDLE hDB, HNDLE hKey, INT index, HNDLE * subkey_handle)
03150 {
03151    if (rpc_is_remote())
03152       return rpc_call(RPC_DB_ENUM_KEY, hDB, hKey, index, subkey_handle);
03153 
03154 #ifdef LOCAL_ROUTINES
03155    {
03156       DATABASE_HEADER *pheader;
03157       KEYLIST *pkeylist;
03158       KEY *pkey;
03159       INT i;
03160       char str[256];
03161       HNDLE parent;
03162 
03163       if (hDB > _database_entries || hDB <= 0) {
03164          cm_msg(MERROR, "db_enum_key", "invalid database handle");
03165          return DB_INVALID_HANDLE;
03166       }
03167 
03168       if (!_database[hDB - 1].attached) {
03169          cm_msg(MERROR, "db_enum_key", "invalid database handle");
03170          return DB_INVALID_HANDLE;
03171       }
03172 
03173       *subkey_handle = 0;
03174 
03175       /* first lock database */
03176       db_lock_database(hDB);
03177 
03178       pheader = _database[hDB - 1].database_header;
03179       if (!hKey)
03180          hKey = pheader->root_key;
03181       pkey = (KEY *) ((char *) pheader + hKey);
03182 
03183       /* check if hKey argument is correct */
03184       if (!db_validate_hkey(pheader, hKey)) {
03185          db_unlock_database(hDB);
03186          return DB_INVALID_HANDLE;
03187       }
03188 
03189       if (pkey->type != TID_KEY) {
03190          db_unlock_database(hDB);
03191          return DB_NO_MORE_SUBKEYS;
03192       }
03193       pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
03194 
03195       if (index >= pkeylist->num_keys) {
03196          db_unlock_database(hDB);
03197          return DB_NO_MORE_SUBKEYS;
03198       }
03199 
03200       pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
03201       for (i = 0; i < index; i++)
03202          pkey = (KEY *) ((char *) pheader + pkey->next_key);
03203 
03204       /* resolve links */
03205       if (pkey->type == TID_LINK) {
03206          strcpy(str, (char *) pheader + pkey->data);
03207 
03208          if (*str == '/') {
03209             /* absolute path */
03210             db_unlock_database(hDB);
03211             return db_find_key(hDB, 0, str, subkey_handle);
03212          } else {
03213             /* relative path */
03214             if (pkey->parent_keylist) {
03215                pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
03216                parent = pkeylist->parent;
03217                db_unlock_database(hDB);
03218                return db_find_key(hDB, parent, str, subkey_handle);
03219             } else {
03220                db_unlock_database(hDB);
03221                return db_find_key(hDB, 0, str, subkey_handle);
03222             }
03223          }
03224       }
03225 
03226       *subkey_handle = (PTYPE) pkey - (PTYPE) pheader;
03227       db_unlock_database(hDB);
03228    }
03229 #endif                          /* LOCAL_ROUTINES */
03230 
03231    return DB_SUCCESS;
03232 }
03233 
03234 /**dox***************************************************************/
03235 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03236 
03237 
03238 /*------------------------------------------------------------------*/
03239 INT db_enum_link(HNDLE hDB, HNDLE hKey, INT index, HNDLE * subkey_handle)
03240 /********************************************************************\
03241 
03242   Routine: db_enum_link
03243 
03244   Purpose: Enumerate subkeys from a key, don't follow links
03245 
03246   Input:
03247     HNDLE hDB               Handle to the database
03248     HNDLE hKey              Handle of key to enumerate, zero for the
03249                             root key
03250     INT   index             Subkey index, sould be initially 0, then
03251                             incremented in each call until
03252                             *subhKey becomes zero and the function
03253                             returns DB_NO_MORE_SUBKEYS
03254 
03255   Output:
03256     HNDLE *subkey_handle    Handle of subkey which can be used in
03257                             db_get_key and db_get_data
03258 
03259   Function value:
03260     DB_SUCCESS              Successful completion
03261     DB_INVALID_HANDLE       Database handle is invalid
03262     DB_NO_MORE_SUBKEYS      Last subkey reached
03263 
03264 \********************************************************************/
03265 {
03266    if (rpc_is_remote())
03267       return rpc_call(RPC_DB_ENUM_LINK, hDB, hKey, index, subkey_handle);
03268 
03269 #ifdef LOCAL_ROUTINES
03270    {
03271       DATABASE_HEADER *pheader;
03272       KEYLIST *pkeylist;
03273       KEY *pkey;
03274       INT i;
03275 
03276       if (hDB > _database_entries || hDB <= 0) {
03277          cm_msg(MERROR, "db_enum_link", "invalid database handle");
03278          return DB_INVALID_HANDLE;
03279       }
03280 
03281       if (!_database[hDB - 1].attached) {
03282          cm_msg(MERROR, "db_enum_link", "invalid database handle");
03283          return DB_INVALID_HANDLE;
03284       }
03285 
03286       *subkey_handle = 0;
03287 
03288       /* first lock database */
03289       db_lock_database(hDB);
03290 
03291       pheader = _database[hDB - 1].database_header;
03292       if (!hKey)
03293          hKey = pheader->root_key;
03294       pkey = (KEY *) ((char *) pheader + hKey);
03295 
03296       /* check if hKey argument is correct */
03297       if (!db_validate_hkey(pheader, hKey)) {
03298          db_unlock_database(hDB);
03299          return DB_INVALID_HANDLE;
03300       }
03301 
03302       if (pkey->type != TID_KEY) {
03303          db_unlock_database(hDB);
03304          return DB_NO_MORE_SUBKEYS;
03305       }
03306       pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
03307 
03308       if (index >= pkeylist->num_keys) {
03309          db_unlock_database(hDB);
03310          return DB_NO_MORE_SUBKEYS;
03311       }
03312 
03313       pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
03314       for (i = 0; i < index; i++)
03315          pkey = (KEY *) ((char *) pheader + pkey->next_key);
03316 
03317       *subkey_handle = (PTYPE) pkey - (PTYPE) pheader;
03318       db_unlock_database(hDB);
03319    }
03320 #endif                          /* LOCAL_ROUTINES */
03321 
03322    return DB_SUCCESS;
03323 }
03324 
03325 /*------------------------------------------------------------------*/
03326 INT db_get_next_link(HNDLE hDB, HNDLE hKey, HNDLE * subkey_handle)
03327 /********************************************************************\
03328 
03329   Routine: db_get_next_link
03330 
03331   Purpose: Get next key in ODB after hKey
03332 
03333   Input:
03334     HNDLE hDB               Handle to the database
03335     HNDLE hKey              Handle of key to enumerate, zero for the
03336                             root key
03337 
03338   Output:
03339     HNDLE *subkey_handle    Handle of subkey which can be used in
03340                             db_get_key and db_get_data
03341 
03342   Function value:
03343     DB_SUCCESS              Successful completion
03344     DB_INVALID_HANDLE       Database handle is invalid
03345     DB_NO_MORE_SUBKEYS      Last subkey reached
03346 
03347 \********************************************************************/
03348 {
03349    if (rpc_is_remote())
03350       return rpc_call(RPC_DB_GET_NEXT_LINK, hDB, hKey, subkey_handle);
03351 
03352 #ifdef LOCAL_ROUTINES
03353    {
03354       DATABASE_HEADER *pheader;
03355       KEYLIST *pkeylist;
03356       KEY *pkey;
03357       INT descent;
03358 
03359       if (hDB > _database_entries || hDB <= 0) {
03360          cm_msg(MERROR, "db_enum_link", "invalid database handle");
03361          return DB_INVALID_HANDLE;
03362       }
03363 
03364       if (!_database[hDB - 1].attached) {
03365          cm_msg(MERROR, "db_enum_link", "invalid database handle");
03366          return DB_INVALID_HANDLE;
03367       }
03368 
03369       *subkey_handle = 0;
03370 
03371       /* first lock database */
03372       db_lock_database(hDB);
03373 
03374       pheader = _database[hDB - 1].database_header;
03375       if (!hKey)
03376          hKey = pheader->root_key;
03377       pkey = (KEY *) ((char *) pheader + hKey);
03378 
03379       /* check if hKey argument is correct */
03380       if (!db_validate_hkey(pheader, hKey)) {
03381          db_unlock_database(hDB);
03382          return DB_INVALID_HANDLE;
03383       }
03384 
03385       descent = TRUE;
03386       do {
03387          if (pkey->type != TID_KEY || !descent) {
03388             if (pkey->next_key) {
03389                /* key has next key, return it */
03390                pkey = (KEY *) ((char *) pheader + pkey->next_key);
03391 
03392                if (pkey->type != TID_KEY) {
03393                   *subkey_handle = (PTYPE) pkey - (PTYPE) pheader;
03394                   db_unlock_database(hDB);
03395                   return DB_SUCCESS;
03396                }
03397 
03398                /* key has subkeys, so descent on the next iteration */
03399                descent = TRUE;
03400             } else {
03401                if (pkey->parent_keylist == 0) {
03402                   /* return if we are back to the root key */
03403                   db_unlock_database(hDB);
03404                   return DB_NO_MORE_SUBKEYS;
03405                }
03406 
03407                /* key is last in list, traverse up */
03408                pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
03409 
03410                pkey = (KEY *) ((char *) pheader + pkeylist->parent);
03411                descent = FALSE;
03412             }
03413          } else {
03414             if (descent) {
03415                /* find first subkey */
03416                pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
03417 
03418                if (pkeylist->num_keys == 0) {
03419                   /* if key has no subkeys, look for next key on this level */
03420                   descent = FALSE;
03421                } else {
03422                   /* get first subkey */
03423                   pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
03424 
03425                   if (pkey->type != TID_KEY) {
03426                      *subkey_handle = (PTYPE) pkey - (PTYPE) pheader;
03427                      db_unlock_database(hDB);
03428                      return DB_SUCCESS;
03429                   }
03430                }
03431             }
03432          }
03433 
03434       } while (TRUE);
03435    }
03436 #endif                          /* LOCAL_ROUTINES */
03437 
03438    return DB_SUCCESS;
03439 }
03440 
03441 /**dox***************************************************************/
03442 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03443 
03444 /********************************************************************/
03445 /**
03446 Get key structure from a handle.
03447 
03448 KEY structure has following format:
03449 \code
03450 typedef struct {
03451   DWORD         type;                 // TID_xxx type
03452   INT           num_values;           // number of values
03453   char          name[NAME_LENGTH];    // name of variable
03454   INT           data;                 // Address of variable (offset)
03455   INT           total_size;           // Total size of data block
03456   INT           item_size;            // Size of single data item
03457   WORD          access_mode;          // Access mode
03458   WORD          notify_count;         // Notify counter
03459   INT           next_key;             // Address of next key
03460   INT           parent_keylist;       // keylist to which this key belongs
03461   INT           last_written;         // Time of last write action
03462 } KEY;
03463 \endcode
03464 Most of these values are used for internal purposes, the values which are of
03465 public interest are type, num_values, and name. For keys which contain a
03466 single value, num_values equals to one and total_size equals to
03467 item_size. For keys which contain an array of strings (TID_STRING),
03468 item_size equals to the length of one string.
03469 \code
03470 KEY   key;
03471 HNDLE hkey;
03472 db_find_key(hDB, 0, "/Runinfo/Run number", &hkey);
03473 db_get_key(hDB, hkey, &key);
03474 printf("The run number is of type %s\n", rpc_tid_name(key.type));
03475 \endcode
03476 @param hDB          ODB handle obtained via cm_get_experiment_database().
03477 @param hKey Handle for key where search starts, zero for root.
03478 @param key Pointer to KEY stucture.
03479 @return DB_SUCCESS, DB_INVALID_HANDLE
03480 */
03481 INT db_get_key(HNDLE hDB, HNDLE hKey, KEY * key)
03482 {
03483    if (rpc_is_remote())
03484       return rpc_call(RPC_DB_GET_KEY, hDB, hKey, key);
03485 
03486 #ifdef LOCAL_ROUTINES
03487    {
03488       DATABASE_HEADER *pheader;
03489       KEY *pkey;
03490 
03491       if (hDB > _database_entries || hDB <= 0) {
03492          cm_msg(MERROR, "db_get_key", "invalid database handle");
03493          return DB_INVALID_HANDLE;
03494       }
03495 
03496       if (!_database[hDB - 1].attached) {
03497          cm_msg(MERROR, "db_get_key", "invalid database handle");
03498          return DB_INVALID_HANDLE;
03499       }
03500 
03501       if (hKey < sizeof(DATABASE_HEADER) && hKey != 0) {
03502          cm_msg(MERROR, "db_get_key", "invalid key handle");
03503          return DB_INVALID_HANDLE;
03504       }
03505 
03506       db_lock_database(hDB);
03507 
03508       pheader = _database[hDB - 1].database_header;
03509 
03510       if (!hKey)
03511          hKey = pheader->root_key;
03512 
03513       pkey = (KEY *) ((char *) pheader + hKey);
03514 
03515       /* check if hKey argument is correct */
03516       if (!db_validate_hkey(pheader, hKey)) {
03517          db_unlock_database(hDB);
03518          return DB_INVALID_HANDLE;
03519       }
03520 
03521       if (!pkey->type) {
03522          db_unlock_database(hDB);
03523          cm_msg(MERROR, "db_get_key", "invalid key");
03524          return DB_INVALID_HANDLE;
03525       }
03526 
03527       memcpy(key, pkey, sizeof(KEY));
03528 
03529       db_unlock_database(hDB);
03530 
03531    }
03532 #endif                          /* LOCAL_ROUTINES */
03533 
03534    return DB_SUCCESS;
03535 }
03536 
03537 /********************************************************************/
03538 /**
03539 Get time when key was last modified
03540 @param hDB          ODB handle obtained via cm_get_experiment_database().
03541 @param hKey              Handle of key to operate on
03542 @param delta             Seconds since last update
03543 @return DB_SUCCESS, DB_INVALID_HANDLE
03544 */
03545 INT db_get_key_time(HNDLE hDB, HNDLE hKey, DWORD * delta)
03546 {
03547    if (rpc_is_remote())
03548       return rpc_call(RPC_DB_GET_KEY_TIME, hDB, hKey, delta);
03549 
03550 #ifdef LOCAL_ROUTINES
03551    {
03552       DATABASE_HEADER *pheader;
03553       KEY *pkey;
03554 
03555       if (hDB > _database_entries || hDB <= 0) {
03556          cm_msg(MERROR, "db_get_key", "invalid database handle");
03557          return DB_INVALID_HANDLE;
03558       }
03559 
03560       if (!_database[hDB - 1].attached) {
03561          cm_msg(MERROR, "db_get_key", "invalid database handle");
03562          return DB_INVALID_HANDLE;
03563       }
03564 
03565       if (hKey < sizeof(DATABASE_HEADER)) {
03566          cm_msg(MERROR, "db_get_key", "invalid key handle");
03567          return DB_INVALID_HANDLE;
03568       }
03569 
03570       db_lock_database(hDB);
03571 
03572       pheader = _database[hDB - 1].database_header;
03573       pkey = (KEY *) ((char *) pheader + hKey);
03574 
03575       /* check if hKey argument is correct */
03576       if (!db_validate_hkey(pheader, hKey)) {
03577          db_unlock_database(hDB);
03578          return DB_INVALID_HANDLE;
03579       }
03580 
03581       *delta = ss_time() - pkey->last_written;
03582 
03583       db_unlock_database(hDB);
03584 
03585    }
03586 #endif                          /* LOCAL_ROUTINES */
03587 
03588    return DB_SUCCESS;
03589 }
03590 
03591 /********************************************************************/
03592 /**
03593 Get key info (separate values instead of structure)
03594 @param hDB          ODB handle obtained via cm_get_experiment_database().
03595 @param hKey              Handle of key to operate on
03596 @param name             Key name
03597 @param name_size        Size of the give name (done with sizeof())
03598 @param type             Key type (see @ref Midas_Data_Types).
03599 @param num_values       Number of values in key.
03600 @param item_size        Size of individual key value (used for strings)
03601 @return DB_SUCCESS, DB_INVALID_HANDLE
03602 */
03603 INT db_get_key_info(HNDLE hDB, HNDLE hKey, char *name, INT name_size,
03604                     INT * type, INT * num_values, INT * item_size)
03605 {
03606    if (rpc_is_remote())
03607       return rpc_call(RPC_DB_GET_KEY_INFO, hDB, hKey, name, name_size,
03608                       type, num_values, item_size);
03609 
03610 #ifdef LOCAL_ROUTINES
03611    {
03612       DATABASE_HEADER *pheader;
03613       KEY *pkey;
03614       KEYLIST *pkeylist;
03615 
03616       if (hDB > _database_entries || hDB <= 0) {
03617          cm_msg(MERROR, "db_get_key_info", "invalid database handle");
03618          return DB_INVALID_HANDLE;
03619       }
03620 
03621       if (!_database[hDB - 1].attached) {
03622          cm_msg(MERROR, "db_get_key_info", "invalid database handle");
03623          return DB_INVALID_HANDLE;
03624       }
03625 
03626       if (hKey < sizeof(DATABASE_HEADER)) {
03627          cm_msg(MERROR, "db_get_key_info", "invalid key handle");
03628          return DB_INVALID_HANDLE;
03629       }
03630 
03631       db_lock_database(hDB);
03632 
03633       pheader = _database[hDB - 1].database_header;
03634       pkey = (KEY *) ((char *) pheader + hKey);
03635 
03636       /* check if hKey argument is correct */
03637       if (!db_validate_hkey(pheader, hKey)) {
03638          db_unlock_database(hDB);
03639          return DB_INVALID_HANDLE;
03640       }
03641 
03642       if ((INT) strlen(pkey->name) + 1 > name_size) {
03643          /* truncate name */
03644          memcpy(name, pkey->name, name_size - 1);
03645          name[name_size] = 0;
03646       } else
03647          strcpy(name, pkey->name);
03648 
03649       /* convert "root" to "/" */
03650       if (strcmp(name, "root") == 0)
03651          strcpy(name, "/");
03652 
03653       *type = pkey->type;
03654       *num_values = pkey->num_values;
03655       *item_size = pkey->item_size;
03656 
03657       if (pkey->type == TID_KEY) {
03658          pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
03659          *num_values = pkeylist->num_keys;
03660       }
03661 
03662       db_unlock_database(hDB);
03663    }
03664 #endif                          /* LOCAL_ROUTINES */
03665 
03666    return DB_SUCCESS;
03667 }
03668 
03669 /**dox***************************************************************/
03670 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03671 
03672 
03673 /*------------------------------------------------------------------*/
03674 INT db_rename_key(HNDLE hDB, HNDLE hKey, char *name)
03675 /********************************************************************\
03676 
03677   Routine: db_get_key
03678 
03679   Purpose: Rename a key
03680 
03681   Input:
03682     HNDLE hDB               Handle to the database
03683     HNDLE hKey              Handle of key
03684     char  *name             New key name
03685 
03686   Output:
03687     <none>
03688 
03689   Function value:
03690     DB_SUCCESS              Successful completion
03691     DB_INVALID_HANDLE       Database handle is invalid
03692     DB_INVALID_NAME         Key name contains '/'
03693 
03694 \********************************************************************/
03695 {
03696    if (rpc_is_remote())
03697       return rpc_call(RPC_DB_RENAME_KEY, hDB, hKey, name);
03698 
03699 #ifdef LOCAL_ROUTINES
03700    {
03701       DATABASE_HEADER *pheader;
03702       KEY *pkey;
03703 
03704       if (hDB > _database_entries || hDB <= 0) {
03705          cm_msg(MERROR, "db_rename_key", "invalid database handle");
03706          return DB_INVALID_HANDLE;
03707       }
03708 
03709       if (!_database[hDB - 1].attached) {
03710          cm_msg(MERROR, "db_rename_key", "invalid database handle");
03711          return DB_INVALID_HANDLE;
03712       }
03713 
03714       if (hKey < sizeof(DATABASE_HEADER)) {
03715          cm_msg(MERROR, "db_rename_key", "invalid key handle");
03716          return DB_INVALID_HANDLE;
03717       }
03718 
03719       if (strchr(name, '/')) {
03720          cm_msg(MERROR, "db_rename_key", "key name may not contain \"/\"");
03721          return DB_INVALID_NAME;
03722       }
03723       db_lock_database(hDB);
03724 
03725       pheader = _database[hDB - 1].database_header;
03726       pkey = (KEY *) ((char *) pheader + hKey);
03727 
03728       /* check if hKey argument is correct */
03729       if (!db_validate_hkey(pheader, hKey)) {
03730          db_unlock_database(hDB);
03731          return DB_INVALID_HANDLE;
03732       }
03733 
03734       if (!pkey->type) {
03735          db_unlock_database(hDB);
03736          cm_msg(MERROR, "db_rename_key", "invalid key");
03737          return DB_INVALID_HANDLE;
03738       }
03739 
03740       name[NAME_LENGTH] = 0;
03741       strcpy(pkey->name, name);
03742 
03743       db_unlock_database(hDB);
03744 
03745    }
03746 #endif                          /* LOCAL_ROUTINES */
03747 
03748    return DB_SUCCESS;
03749 }
03750 
03751 /*------------------------------------------------------------------*/
03752 INT db_reorder_key(HNDLE hDB, HNDLE hKey, INT index)
03753 /********************************************************************\
03754 
03755   Routine: db_reorder_key
03756 
03757   Purpose: Reorder key so that key hKey apprears at position 'index'
03758            in keylist (or at bottom if index<0)
03759 
03760   Input:
03761     HNDLE hDB               Handle to the database
03762     HNDLE hKey              Handle of key
03763     INT   index             New positio of key in keylist
03764 
03765   Output:
03766     <none>
03767 
03768   Function value:
03769     DB_SUCCESS              Successful completion
03770     DB_INVALID_HANDLE       Database handle is invalid
03771     DB_NO_ACCESS            Key is locked for write
03772     DB_OPEN_RECORD          Key, subkey or parent key is open
03773 
03774 \********************************************************************/
03775 {
03776    if (rpc_is_remote())
03777       return rpc_call(RPC_DB_REORDER_KEY, hDB, hKey, index);
03778 
03779 #ifdef LOCAL_ROUTINES
03780    {
03781       DATABASE_HEADER *pheader;
03782       KEY *pkey, *pprev_key, *pnext_key, *pkey_tmp;
03783       KEYLIST *pkeylist;
03784       INT i;
03785 
03786       if (hDB > _database_entries || hDB <= 0) {
03787          cm_msg(MERROR, "db_rename_key", "invalid database handle");
03788          return DB_INVALID_HANDLE;
03789       }
03790 
03791       if (!_database[hDB - 1].attached) {
03792          cm_msg(MERROR, "db_rename_key", "invalid database handle");
03793          return DB_INVALID_HANDLE;
03794       }
03795 
03796       if (hKey < sizeof(DATABASE_HEADER)) {
03797          cm_msg(MERROR, "db_rename_key", "invalid key handle");
03798          return DB_INVALID_HANDLE;
03799       }
03800 
03801       db_lock_database(hDB);
03802 
03803       pheader = _database[hDB - 1].database_header;
03804       pkey = (KEY *) ((char *) pheader + hKey);
03805 
03806       /* check if hKey argument is correct */
03807       if (!db_validate_hkey(pheader, hKey)) {
03808          db_unlock_database(hDB);
03809          return DB_INVALID_HANDLE;
03810       }
03811 
03812       if (!pkey->type) {
03813          db_unlock_database(hDB);
03814          cm_msg(MERROR, "db_reorder_key", "invalid key");
03815          return DB_INVALID_HANDLE;
03816       }
03817 
03818       if (!(pkey->access_mode & MODE_WRITE)) {
03819          db_unlock_database(hDB);
03820          return DB_NO_ACCESS;
03821       }
03822 
03823       /* check if someone has opened key or parent */
03824       do {
03825          if (pkey->notify_count) {
03826             db_unlock_database(hDB);
03827             return DB_OPEN_RECORD;
03828          }
03829 
03830          if (pkey->parent_keylist == 0)
03831             break;
03832 
03833          pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
03834          pkey = (KEY *) ((char *) pheader + pkeylist->parent);
03835       } while (TRUE);
03836 
03837       pkey = (KEY *) ((char *) pheader + hKey);
03838       pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
03839 
03840       /* first remove key from list */
03841       pnext_key = (KEY *) (PTYPE) pkey->next_key;
03842 
03843       if ((KEY *) ((char *) pheader + pkeylist->first_key) == pkey) {
03844          /* key is first in list */
03845          pkeylist->first_key = (PTYPE) pnext_key;
03846       } else {
03847          /* find predecessor */
03848          pkey_tmp = (KEY *) ((char *) pheader + pkeylist->first_key);
03849          while ((KEY *) ((char *) pheader + pkey_tmp->next_key) != pkey)
03850             pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
03851          pkey_tmp->next_key = (PTYPE) pnext_key;
03852       }
03853 
03854       /* add key to list at proper index */
03855       pkey_tmp = (KEY *) ((char *) pheader + pkeylist->first_key);
03856       if (index < 0 || index >= pkeylist->num_keys - 1) {
03857          /* add at bottom */
03858 
03859          /* find last key */
03860          for (i = 0; i < pkeylist->num_keys - 2; i++) {
03861             pprev_key = pkey_tmp;
03862             pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
03863          }
03864 
03865          pkey_tmp->next_key = (PTYPE) pkey - (PTYPE) pheader;
03866          pkey->next_key = 0;
03867       } else {
03868          if (index == 0) {
03869             /* add at top */
03870             pkey->next_key = pkeylist->first_key;
03871             pkeylist->first_key = (PTYPE) pkey - (PTYPE) pheader;
03872          } else {
03873             /* add at position index */
03874             for (i = 0; i < index - 1; i++)
03875                pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
03876 
03877             pkey->next_key = pkey_tmp->next_key;
03878             pkey_tmp->next_key = (PTYPE) pkey - (PTYPE) pheader;
03879          }
03880       }
03881 
03882       db_unlock_database(hDB);
03883 
03884    }
03885 #endif                          /* LOCAL_ROUTINES */
03886 
03887    return DB_SUCCESS;
03888 }
03889 
03890 /**dox***************************************************************/
03891 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03892 
03893 
03894 /********************************************************************/
03895 /**
03896 Get key data from a handle
03897 
03898 The function returns single values or whole arrays which are contained
03899 in an ODB key. Since the data buffer is of type void, no type checking can be
03900 performed by the compiler. Therefore the type has to be explicitly supplied,
03901 which is checked against the type stored in the ODB.
03902 \code
03903   HNLDE hkey;
03904   INT   run_number, size;
03905   // get key handle for run number
03906   db_find_key(hDB, 0, "/Runinfo/Run number", &hkey);
03907   // return run number
03908   size = sizeof(run_number);
03909   db_get_data(hDB, hkey, &run_number, &size,TID_INT);
03910 \endcode
03911 @param hDB          ODB handle obtained via cm_get_experiment_database().
03912 @param hKey         Handle for key where search starts, zero for root.
03913 @param data         Pointer to the return data. 
03914 @param buf_size     Size of data buffer.
03915 @param type         Type of key, one of TID_xxx (see @ref Midas_Data_Types).
03916 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TRUNCATED, DB_TYPE_MISMATCH
03917 */
03918 INT db_get_data(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, DWORD type)
03919 {
03920    if (rpc_is_remote())
03921       return rpc_call(RPC_DB_GET_DATA, hDB, hKey, data, buf_size, type);
03922 
03923 #ifdef LOCAL_ROUTINES
03924    {
03925       DATABASE_HEADER *pheader;
03926       KEY *pkey;
03927 
03928       if (hDB > _database_entries || hDB <= 0) {
03929          cm_msg(MERROR, "db_get_data", "Invalid database handle");
03930          return DB_INVALID_HANDLE;
03931       }
03932 
03933       if (!_database[hDB - 1].attached) {
03934          cm_msg(MERROR, "db_get_data", "invalid database handle");
03935          return DB_INVALID_HANDLE;
03936       }
03937 
03938       if (hKey < sizeof(DATABASE_HEADER)) {
03939          cm_msg(MERROR, "db_get_data", "invalid key handle");
03940          return DB_INVALID_HANDLE;
03941       }
03942 
03943       db_lock_database(hDB);
03944 
03945       pheader = _database[hDB - 1].database_header;
03946       pkey = (KEY *) ((char *) pheader + hKey);
03947 
03948       /* check if hKey argument is correct */
03949       if (!db_validate_hkey(pheader, hKey)) {
03950          db_unlock_database(hDB);
03951          return DB_INVALID_HANDLE;
03952       }
03953 
03954       /* check for read access */
03955       if (!(pkey->access_mode & MODE_READ)) {
03956          db_unlock_database(hDB);
03957          return DB_NO_ACCESS;
03958       }
03959 
03960       if (!pkey->type) {
03961          db_unlock_database(hDB);
03962          cm_msg(MERROR, "db_get_data", "invalid key");
03963          return DB_INVALID_HANDLE;
03964       }
03965 
03966       if (pkey->type != type) {
03967          db_unlock_database(hDB);
03968          cm_msg(MERROR, "db_get_data", "\"%s\" is of type %s, not %s",
03969                 pkey->name, rpc_tid_name(pkey->type), rpc_tid_name(type));
03970          return DB_TYPE_MISMATCH;
03971       }
03972 
03973       /* keys cannot contain data */
03974       if (pkey->type == TID_KEY) {
03975          db_unlock_database(hDB);
03976          cm_msg(MERROR, "db_get_data", "Key cannot contain data");
03977          return DB_TYPE_MISMATCH;
03978       }
03979 
03980       /* check if key has data */
03981       if (pkey->data == 0) {
03982          memset(data, 0, *buf_size);
03983          *buf_size = 0;
03984          db_unlock_database(hDB);
03985          return DB_SUCCESS;
03986       }
03987 
03988       /* check if buffer is too small */
03989       if (pkey->num_values * pkey->item_size > *buf_size) {
03990          memcpy(data, (char *) pheader + pkey->data, *buf_size);
03991          db_unlock_database(hDB);
03992          cm_msg(MERROR, "db_get_data", "data for key \"%s\" truncated", pkey->name);
03993          return DB_TRUNCATED;
03994       }
03995 
03996       /* copy key data */
03997       memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
03998       *buf_size = pkey->num_values * pkey->item_size;
03999 
04000       db_unlock_database(hDB);
04001 
04002    }
04003 #endif                          /* LOCAL_ROUTINES */
04004 
04005    return DB_SUCCESS;
04006 }
04007 
04008 /**dox***************************************************************/
04009 #ifndef DOXYGEN_SHOULD_SKIP_THIS
04010 
04011 /*------------------------------------------------------------------*/
04012 INT db_get_data1(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size,
04013                  DWORD type, INT * num_values)
04014 /********************************************************************\
04015 
04016   Routine: db_get_data1
04017 
04018   Purpose: Get key data from a handle, return number of values
04019 
04020   Input:
04021     HNDLE  hDB              Handle to the database
04022     HNDLE  hKey             Handle of key
04023     INT    *buf_size        Size of data buffer
04024     DWORD  type             Type of data
04025 
04026   Output:
04027     void   *data            Key data
04028     INT    *buf_size        Size of key data
04029     INT    *num_values      Number of values
04030 
04031   Function value:
04032     DB_SUCCESS              Successful completion
04033     DB_INVALID_HANDLE       Database handle is invalid
04034     DB_TRUNCATED            Return buffer is smaller than key data
04035     DB_TYPE_MISMATCH        Type mismatch
04036 
04037 \********************************************************************/
04038 {
04039    if (rpc_is_remote())
04040       return rpc_call(RPC_DB_GET_DATA, hDB, hKey, data, buf_size, type);
04041 
04042 #ifdef LOCAL_ROUTINES
04043    {
04044       DATABASE_HEADER *pheader;
04045       KEY *pkey;
04046 
04047       if (hDB > _database_entries || hDB <= 0) {
04048          cm_msg(MERROR, "db_get_data", "Invalid database handle");
04049          return DB_INVALID_HANDLE;
04050       }
04051 
04052       if (!_database[hDB - 1].attached) {
04053          cm_msg(MERROR, "db_get_data", "invalid database handle");
04054          return DB_INVALID_HANDLE;
04055       }
04056 
04057       if (hKey < sizeof(DATABASE_HEADER)) {
04058          cm_msg(MERROR, "db_get_data", "invalid key handle");
04059          return DB_INVALID_HANDLE;
04060       }
04061 
04062       db_lock_database(hDB);
04063 
04064       pheader = _database[hDB - 1].database_header;
04065       pkey = (KEY *) ((char *) pheader + hKey);
04066 
04067       /* check if hKey argument is correct */
04068       if (!db_validate_hkey(pheader, hKey)) {
04069          db_unlock_database(hDB);
04070          return DB_INVALID_HANDLE;
04071       }
04072 
04073       /* check for read access */
04074       if (!(pkey->access_mode & MODE_READ)) {
04075          db_unlock_database(hDB);
04076          return DB_NO_ACCESS;
04077       }
04078 
04079       if (!pkey->type) {
04080          db_unlock_database(hDB);
04081          cm_msg(MERROR, "db_get_data", "invalid key");
04082          return DB_INVALID_HANDLE;
04083       }
04084 
04085       if (pkey->type != type) {
04086          db_unlock_database(hDB);
04087          cm_msg(MERROR, "db_get_data", "\"%s\" is of type %s, not %s",
04088                 pkey->name, rpc_tid_name(pkey->type), rpc_tid_name(type));
04089          return DB_TYPE_MISMATCH;
04090       }
04091 
04092       /* keys cannot contain data */
04093       if (pkey->type == TID_KEY) {
04094          db_unlock_database(hDB);
04095          cm_msg(MERROR, "db_get_data", "Key cannot contain data");
04096          return DB_TYPE_MISMATCH;
04097       }
04098 
04099       /* check if key has data */
04100       if (pkey->data == 0) {
04101          memset(data, 0, *buf_size);
04102          *buf_size = 0;
04103          db_unlock_database(hDB);
04104          return DB_SUCCESS;
04105       }
04106 
04107       /* check if buffer is too small */
04108       if (pkey->num_values * pkey->item_size > *buf_size) {
04109          memcpy(data, (char *) pheader + pkey->data, *buf_size);
04110          db_unlock_database(hDB);
04111          cm_msg(MERROR, "db_get_data", "data for key \"%s\" truncated", pkey->name);
04112          return DB_TRUNCATED;
04113       }
04114 
04115       /* copy key data */
04116       memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
04117       *buf_size = pkey->num_values * pkey->item_size;
04118       *num_values = pkey->num_values;
04119 
04120       db_unlock_database(hDB);
04121 
04122    }
04123 #endif                          /* LOCAL_ROUTINES */
04124 
04125    return DB_SUCCESS;
04126 }
04127 
04128 /**dox***************************************************************/
04129 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
04130 
04131 /********************************************************************/
04132 /**
04133 returns a single value of keys containing arrays of values.
04134 
04135 The function returns a single value of keys containing arrays of values.
04136 @param hDB          ODB handle obtained via cm_get_experiment_database().
04137 @param hKey         Handle for key where search starts, zero for root.
04138 @param data         Size of data buffer.
04139 @param buf_size     Return size of the record.
04140 @param index        Index of array [0..n-1].
04141 @param type         Type of key, one of TID_xxx (see @ref Midas_Data_Types).
04142 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TRUNCATED, DB_OUT_OF_RANGE
04143 */
04144 INT db_get_data_index(HNDLE hDB, HNDLE hKey,
04145                       void *data, INT * buf_size, INT index, DWORD type)
04146 {
04147    if (rpc_is_remote())
04148       return rpc_call(RPC_DB_GET_DATA_INDEX, hDB, hKey, data, buf_size, index, type);
04149 
04150 #ifdef LOCAL_ROUTINES
04151    {
04152       DATABASE_HEADER *pheader;
04153       KEY *pkey;
04154       char str[256];
04155 
04156       if (hDB > _database_entries || hDB <= 0) {
04157          cm_msg(MERROR, "db_get_data", "Invalid database handle");
04158          return DB_INVALID_HANDLE;
04159       }
04160 
04161       if (!_database[hDB - 1].attached) {
04162          cm_msg(MERROR, "db_get_data", "invalid database handle");
04163          return DB_INVALID_HANDLE;
04164       }
04165 
04166       if (hKey < sizeof(DATABASE_HEADER)) {
04167          cm_msg(MERROR, "db_get_data", "invalid key handle");
04168          return DB_INVALID_HANDLE;
04169       }
04170 
04171       db_lock_database(hDB);
04172 
04173       pheader = _database[hDB - 1].database_header;
04174       pkey = (KEY *) ((char *) pheader + hKey);
04175 
04176       /* check if hKey argument is correct */
04177       if (!db_validate_hkey(pheader, hKey)) {
04178          db_unlock_database(hDB);
04179          return DB_INVALID_HANDLE;
04180       }
04181 
04182       /* check for read access */
04183       if (!(pkey->access_mode & MODE_READ)) {
04184          db_unlock_database(hDB);
04185          return DB_NO_ACCESS;
04186       }
04187 
04188       if (!pkey->type) {
04189          db_unlock_database(hDB);
04190          cm_msg(MERROR, "db_get_data_index", "invalid key");
04191          return DB_INVALID_HANDLE;
04192       }
04193 
04194       if (pkey->type != type) {
04195          db_unlock_database(hDB);
04196          cm_msg(MERROR, "db_get_data_index",
04197                 "\"%s\" is of type %s, not %s", pkey->name,
04198                 rpc_tid_name(pkey->type), rpc_tid_name(type));
04199          return DB_TYPE_MISMATCH;
04200       }
04201 
04202       /* keys cannot contain data */
04203       if (pkey->type == TID_KEY) {
04204          db_unlock_database(hDB);
04205          cm_msg(MERROR, "db_get_data_index", "Key cannot contain data");
04206          return DB_TYPE_MISMATCH;
04207       }
04208 
04209       /* check if key has data */
04210       if (pkey->data == 0) {
04211          memset(data, 0, *buf_size);
04212          *buf_size = 0;
04213          db_unlock_database(hDB);
04214          return DB_SUCCESS;
04215       }
04216 
04217       /* check if index in range */
04218       if (index < 0 || index >= pkey->num_values) {
04219          memset(data, 0, *buf_size);
04220          db_unlock_database(hDB);
04221 
04222          db_get_path(hDB, hKey, str, sizeof(str));
04223          cm_msg(MERROR, "db_get_data_index",
04224                 "index (%d) exceeds array length (%d) for key \"%s\"",
04225                 index, pkey->num_values, str);
04226          return DB_OUT_OF_RANGE;
04227       }
04228 
04229       /* check if buffer is too small */
04230       if (pkey->item_size > *buf_size) {
04231          /* copy data */
04232          memcpy(data, (char *) pheader + pkey->data + index * pkey->item_size, *buf_size);
04233          db_unlock_database(hDB);
04234          cm_msg(MERROR, "db_get_data_index", "data for key \"%s\" truncated", pkey->name);
04235          return DB_TRUNCATED;
04236       }
04237 
04238       /* copy key data */
04239       memcpy(data, (char *) pheader + pkey->data + index * pkey->item_size,
04240              pkey->item_size);
04241       *buf_size = pkey->item_size;
04242 
04243       db_unlock_database(hDB);
04244 
04245    }
04246 #endif                          /* LOCAL_ROUTINES */
04247 
04248    return DB_SUCCESS;
04249 }
04250 
04251 /********************************************************************/
04252 /**
04253 Set key data from a handle. Adjust number of values if
04254 previous data has different size.
04255 \code
04256 HNLDE hkey;
04257  INT   run_number;
04258  // get key handle for run number
04259  db_find_key(hDB, 0, "/Runinfo/Run number", &hkey);
04260  // set run number
04261  db_set_data(hDB, hkey, &run_number, sizeof(run_number),TID_INT);
04262 \endcode
04263 @param hDB          ODB handle obtained via cm_get_experiment_database().
04264 @param hKey Handle for key where search starts, zero for root.
04265 @param data Buffer from which data gets copied to.
04266 @param buf_size Size of data buffer.
04267 @param num_values Number of data values (for arrays).
04268 @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
04269 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TRUNCATED
04270 */
04271 INT db_set_data(HNDLE hDB, HNDLE hKey,
04272                 void *data, INT buf_size, INT num_values, DWORD type)
04273 {
04274    if (rpc_is_remote())
04275       return rpc_call(RPC_DB_SET_DATA, hDB, hKey, data, buf_size, num_values, type);
04276 
04277 #ifdef LOCAL_ROUTINES
04278    {
04279       DATABASE_HEADER *pheader;
04280       KEY *pkey;
04281 
04282       if (hDB > _database_entries || hDB <= 0) {
04283          cm_msg(MERROR, "db_set_data", "invalid database handle");
04284          return DB_INVALID_HANDLE;
04285       }
04286 
04287       if (!_database[hDB - 1].attached) {
04288          cm_msg(MERROR, "db_set_data", "invalid database handle");
04289          return DB_INVALID_HANDLE;
04290       }
04291 
04292       if (hKey < sizeof(DATABASE_HEADER)) {
04293          cm_msg(MERROR, "db_set_data", "invalid key handle");
04294          return DB_INVALID_HANDLE;
04295       }
04296 
04297       if (num_values == 0)
04298          return DB_INVALID_PARAM;
04299 
04300       db_lock_database(hDB);
04301 
04302       pheader = _database[hDB - 1].database_header;
04303       pkey = (KEY *) ((char *) pheader + hKey);
04304 
04305       /* check if hKey argument is correct */
04306       if (!db_validate_hkey(pheader, hKey)) {
04307          db_unlock_database(hDB);
04308          return DB_INVALID_HANDLE;
04309       }
04310 
04311       /* check for write access */
04312       if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
04313          db_unlock_database(hDB);
04314          return DB_NO_ACCESS;
04315       }
04316 
04317       if (pkey->type != type) {
04318          db_unlock_database(hDB);
04319          cm_msg(MERROR, "db_set_data", "\"%s\" is of type %s, not %s",
04320                 pkey->name, rpc_tid_name(pkey->type), rpc_tid_name(type));
04321          return DB_TYPE_MISMATCH;
04322       }
04323 
04324       /* keys cannot contain data */
04325       if (pkey->type == TID_KEY) {
04326          db_unlock_database(hDB);
04327          cm_msg(MERROR, "db_set_data", "Key cannot contain data");
04328          return DB_TYPE_MISMATCH;
04329       }
04330 
04331       /* if no buf_size given (Java!), calculate it */
04332       if (buf_size == 0)
04333          buf_size = pkey->item_size * num_values;
04334 
04335       /* resize data size if necessary */
04336       if (pkey->total_size != buf_size) {
04337          pkey->data =
04338              (PTYPE) realloc_data(pheader, (char *) pheader + pkey->data,
04339                                   pkey->total_size, buf_size);
04340 
04341          if (pkey->data == 0) {
04342             db_unlock_database(hDB);
04343             cm_msg(MERROR, "db_set_data", "online database full");
04344             return DB_FULL;
04345          }
04346 
04347          pkey->data -= (PTYPE) pheader;
04348          pkey->total_size = buf_size;
04349       }
04350 
04351       /* set number of values */
04352       pkey->num_values = num_values;
04353       if (num_values)
04354          pkey->item_size = buf_size / num_values;
04355 
04356       /* copy data */
04357       memcpy((char *) pheader + pkey->data, data, buf_size);
04358 
04359       /* update time */
04360       pkey->last_written = ss_time();
04361 
04362       db_notify_clients(hDB, hKey, TRUE);
04363       db_unlock_database(hDB);
04364 
04365    }
04366 #endif                          /* LOCAL_ROUTINES */
04367 
04368    return DB_SUCCESS;
04369 }
04370 
04371 /**dox***************************************************************/
04372 #ifndef DOXYGEN_SHOULD_SKIP_THIS
04373 
04374 /*------------------------------------------------------------------*/
04375 INT db_set_num_values(HNDLE hDB, HNDLE hKey, INT num_values)
04376 /********************************************************************\
04377 
04378   Routine: db_set_num_values
04379 
04380   Purpose: Set numbe of values in a key. Extend with zeros or truncate.
04381 
04382   Input:
04383     HNDLE  hDB              Handle to the database
04384     HNDLE  hKey             Handle of key
04385     INT    num_values       Number of data values
04386 
04387   Output:
04388     none
04389 
04390   Function value:
04391     DB_SUCCESS              Successful completion
04392     DB_INVALID_HANDLE       Database handle is invalid
04393 
04394 \********************************************************************/
04395 {
04396    if (rpc_is_remote())
04397       return rpc_call(RPC_DB_SET_NUM_VALUES, hDB, hKey, num_values);
04398 
04399 #ifdef LOCAL_ROUTINES
04400    {
04401       DATABASE_HEADER *pheader;
04402       KEY *pkey;
04403       INT new_size;
04404 
04405       if (hDB > _database_entries || hDB <= 0) {
04406          cm_msg(MERROR, "db_set_data", "invalid database handle");
04407          return DB_INVALID_HANDLE;
04408       }
04409 
04410       if (!_database[hDB - 1].attached) {
04411          cm_msg(MERROR, "db_set_data", "invalid database handle");
04412          return DB_INVALID_HANDLE;
04413       }
04414 
04415       if (hKey < sizeof(DATABASE_HEADER)) {
04416          cm_msg(MERROR, "db_set_data", "invalid key handle");
04417          return DB_INVALID_HANDLE;
04418       }
04419 
04420       if (num_values == 0)
04421          return DB_INVALID_PARAM;
04422 
04423       db_lock_database(hDB);
04424 
04425       pheader = _database[hDB - 1].database_header;
04426       pkey = (KEY *) ((char *) pheader + hKey);
04427 
04428       /* check if hKey argument is correct */
04429       if (!db_validate_hkey(pheader, hKey)) {
04430          db_unlock_database(hDB);
04431          return DB_INVALID_HANDLE;
04432       }
04433 
04434       /* check for write access */
04435       if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
04436          db_unlock_database(hDB);
04437          return DB_NO_ACCESS;
04438       }
04439 
04440       /* keys cannot contain data */
04441       if (pkey->type == TID_KEY) {
04442          db_unlock_database(hDB);
04443          cm_msg(MERROR, "db_set_data", "Key cannot contain data");
04444          return DB_TYPE_MISMATCH;
04445       }
04446 
04447       /* resize data size if necessary */
04448       if (pkey->num_values != num_values) {
04449          new_size = pkey->item_size * num_values;
04450          pkey->data =
04451              (PTYPE) realloc_data(pheader, (char *) pheader + pkey->data,
04452                                   pkey->total_size, new_size);
04453 
04454          if (pkey->data == 0) {
04455             db_unlock_database(hDB);
04456             cm_msg(MERROR, "db_set_data", "online database full");
04457             return DB_FULL;
04458          }
04459 
04460          pkey->data -= (PTYPE) pheader;
04461          pkey->total_size = new_size;
04462          pkey->num_values = num_values;
04463       }
04464 
04465       /* update time */
04466       pkey->last_written = ss_time();
04467 
04468       db_notify_clients(hDB, hKey, TRUE);
04469       db_unlock_database(hDB);
04470 
04471    }
04472 #endif                          /* LOCAL_ROUTINES */
04473 
04474    return DB_SUCCESS;
04475 }
04476 
04477 /**dox***************************************************************/
04478 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
04479 
04480 /********************************************************************/
04481 /**
04482 Set key data for a key which contains an array of values.
04483 
04484 This function sets individual values of a key containing an array.
04485 If the index is larger than the array size, the array is extended and the intermediate
04486 values are set to zero.
04487 @param hDB          ODB handle obtained via cm_get_experiment_database().
04488 @param hKey Handle for key where search starts, zero for root.
04489 @param data Pointer to single value of data.
04490 @param data_size
04491 @param index Size of single data element.
04492 @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
04493 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_ACCESS, DB_TYPE_MISMATCH
04494 */
04495 INT db_set_data_index(HNDLE hDB, HNDLE hKey,
04496                       void *data, INT data_size, INT index, DWORD type)
04497 {
04498    if (rpc_is_remote())
04499       return rpc_call(RPC_DB_SET_DATA_INDEX, hDB, hKey, data, data_size, index, type);
04500 
04501 #ifdef LOCAL_ROUTINES
04502    {
04503       DATABASE_HEADER *pheader;
04504       KEY *pkey;
04505 
04506       if (hDB > _database_entries || hDB <= 0) {
04507          cm_msg(MERROR, "db_set_data_index", "invalid database handle");
04508          return DB_INVALID_HANDLE;
04509       }
04510 
04511       if (!_database[hDB - 1].attached) {
04512          cm_msg(MERROR, "db_set_data_index", "invalid database handle");
04513          return DB_INVALID_HANDLE;
04514       }
04515 
04516       if (hKey < sizeof(DATABASE_HEADER)) {
04517          cm_msg(MERROR, "db_set_data_index", "invalid key handle");
04518          return DB_INVALID_HANDLE;
04519       }
04520 
04521       db_lock_database(hDB);
04522 
04523       pheader = _database[hDB - 1].database_header;
04524       pkey = (KEY *) ((char *) pheader + hKey);
04525 
04526       /* check if hKey argument is correct */
04527       if (!db_validate_hkey(pheader, hKey)) {
04528          db_unlock_database(hDB);
04529          return DB_INVALID_HANDLE;
04530       }
04531 
04532       /* check for write access */
04533       if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
04534          db_unlock_database(hDB);
04535          return DB_NO_ACCESS;
04536       }
04537 
04538       if (pkey->type != type) {
04539          db_unlock_database(hDB);
04540          cm_msg(MERROR, "db_set_data_index",
04541                 "\"%s\" is of type %s, not %s", pkey->name,
04542                 rpc_tid_name(pkey->type), rpc_tid_name(type));
04543          return DB_TYPE_MISMATCH;
04544       }
04545 
04546       /* keys cannot contain data */
04547       if (pkey->type == TID_KEY) {
04548          db_unlock_database(hDB);
04549          cm_msg(MERROR, "db_set_data_index", "key cannot contain data");
04550          return DB_TYPE_MISMATCH;
04551       }
04552 
04553       /* check for valid index */
04554       if (index < 0) {
04555          db_unlock_database(hDB);
04556          cm_msg(MERROR, "db_set_data_index", "invalid index");
04557          return DB_FULL;
04558       }
04559 
04560       /* increase data size if necessary */
04561       if (index >= pkey->num_values || pkey->item_size == 0) {
04562          pkey->data =
04563              (PTYPE) realloc_data(pheader, (char *) pheader + pkey->data,
04564                                   pkey->total_size, data_size * (index + 1));
04565 
04566          if (pkey->data == 0) {
04567             db_unlock_database(hDB);
04568             cm_msg(MERROR, "db_set_data_index", "online database full");
04569             return DB_FULL;
04570          }
04571 
04572          pkey->data -= (PTYPE) pheader;
04573          if (!pkey->item_size)
04574             pkey->item_size = data_size;
04575          pkey->total_size = data_size * (index + 1);
04576          pkey->num_values = index + 1;
04577       }
04578 
04579       /* cut strings which are too long */
04580       if ((type == TID_STRING || type == TID_LINK) &&
04581           (int) strlen((char *) data) + 1 > pkey->item_size)
04582          *((char *) data + pkey->item_size - 1) = 0;
04583 
04584       /* copy data */
04585       memcpy((char *) pheader + pkey->data + index * pkey->item_size,
04586              data, pkey->item_size);
04587 
04588       /* update time */
04589       pkey->last_written = ss_time();
04590 
04591       db_notify_clients(hDB, hKey, TRUE);
04592       db_unlock_database(hDB);
04593 
04594    }
04595 #endif                          /* LOCAL_ROUTINES */
04596 
04597    return DB_SUCCESS;
04598 }
04599 
04600 /**dox***************************************************************/
04601 #ifndef DOXYGEN_SHOULD_SKIP_THIS
04602 
04603 /*------------------------------------------------------------------*/
04604 INT db_set_data_index2(HNDLE hDB, HNDLE hKey, void *data,
04605                        INT data_size, INT index, DWORD type, BOOL bNotify)
04606 /********************************************************************\
04607 
04608   Routine: db_set_data_index2
04609 
04610   Purpose: Set key data for a key which contains an array of values.
04611            Optionally notify clients which have key open.
04612 
04613   Input:
04614     HNDLE  hDB              Handle to the database
04615     HNDLE  hKey             Handle of key to enumerate
04616     void   *data            Pointer to single value of data
04617     INT    data_size        Size of single data element
04618     INT    index            Index of array to change [0..n-1]
04619     DWORD  type             Type of data
04620     BOOL   bNotify          If TRUE, notify clients
04621 
04622   Output:
04623     none
04624 
04625   Function value:
04626     DB_SUCCESS              Successful completion
04627     DB_INVALID_HANDLE       Database handle is invalid
04628     DB_TYPE_MISMATCH        Key was created with different type
04629     DB_NO_ACCESS            No write access
04630 
04631 \********************************************************************/
04632 {
04633    if (rpc_is_remote())
04634       return rpc_call(RPC_DB_SET_DATA_INDEX2, hDB, hKey,
04635                       data, data_size, index, type, bNotify);
04636 
04637 #ifdef LOCAL_ROUTINES
04638    {
04639       DATABASE_HEADER *pheader;
04640       KEY *pkey;
04641 
04642       if (hDB > _database_entries || hDB <= 0) {
04643          cm_msg(MERROR, "db_set_data_index2", "invalid database handle");
04644          return DB_INVALID_HANDLE;
04645       }
04646 
04647       if (!_database[hDB - 1].attached) {
04648          cm_msg(MERROR, "db_set_data_index2", "invalid database handle");
04649          return DB_INVALID_HANDLE;
04650       }
04651 
04652       if (hKey < sizeof(DATABASE_HEADER)) {
04653          cm_msg(MERROR, "db_set_data_index2", "invalid key handle");
04654          return DB_INVALID_HANDLE;
04655       }
04656 
04657       db_lock_database(hDB);
04658 
04659       pheader = _database[hDB - 1].database_header;
04660       pkey = (KEY *) ((char *) pheader + hKey);
04661 
04662       /* check if hKey argument is correct */
04663       if (!db_validate_hkey(pheader, hKey)) {
04664          db_unlock_database(hDB);
04665          return DB_INVALID_HANDLE;
04666       }
04667 
04668       /* check for write access */
04669       if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
04670          db_unlock_database(hDB);
04671          return DB_NO_ACCESS;
04672       }
04673 
04674       if (pkey->type != type) {
04675          db_unlock_database(hDB);
04676          cm_msg(MERROR, "db_set_data_index2",
04677                 "\"%s\" is of type %s, not %s", pkey->name,
04678                 rpc_tid_name(pkey->type), rpc_tid_name(type));
04679          return DB_TYPE_MISMATCH;
04680       }
04681 
04682       /* keys cannot contain data */
04683       if (pkey->type == TID_KEY) {
04684          db_unlock_database(hDB);
04685          cm_msg(MERROR, "db_set_data_index2", "key cannot contain data");
04686          return DB_TYPE_MISMATCH;
04687       }
04688 
04689       /* check for valid index */
04690       if (index < 0) {
04691          db_unlock_database(hDB);
04692          cm_msg(MERROR, "db_set_data_index2", "invalid index");
04693          return DB_FULL;
04694       }
04695 
04696       /* increase key size if necessary */
04697       if (index >= pkey->num_values) {
04698          pkey->data =
04699              (PTYPE) realloc_data(pheader, (char *) pheader + pkey->data,
04700                                   pkey->total_size, data_size * (index + 1));
04701 
04702          if (pkey->data == 0) {
04703             db_unlock_database(hDB);
04704             cm_msg(MERROR, "db_set_data_index2", "online database full");
04705             return DB_FULL;
04706          }
04707 
04708          pkey->data -= (PTYPE) pheader;
04709          if (!pkey->item_size)
04710             pkey->item_size = data_size;
04711          pkey->total_size = data_size * (index + 1);
04712          pkey->num_values = index + 1;
04713       }
04714 
04715       /* cut strings which are too long */
04716       if ((type == TID_STRING || type == TID_LINK) &&
04717           (int) strlen((char *) data) + 1 > pkey->item_size)
04718          *((char *) data + pkey->item_size - 1) = 0;
04719 
04720       /* copy data */
04721       memcpy((char *) pheader + pkey->data + index * pkey->item_size,
04722              data, pkey->item_size);
04723 
04724       /* update time */
04725       pkey->last_written = ss_time();
04726 
04727       if (bNotify)
04728          db_notify_clients(hDB, hKey, TRUE);
04729 
04730       db_unlock_database(hDB);
04731    }
04732 #endif                          /* LOCAL_ROUTINES */
04733 
04734    return DB_SUCCESS;
04735 }
04736 
04737 /*----------------------------------------------------------------------------*/
04738 
04739 INT db_merge_data(HNDLE hDB, HNDLE hKeyRoot, char *name, void *data,
04740                   INT data_size, INT num_values, INT type)
04741 /********************************************************************\
04742 
04743   Routine: db_merge_data
04744 
04745   Purpose: Merge an array with an ODB array. If the ODB array doesn't
04746            exist, create it and fill it with the array. If it exists,
04747            load it in the array. Adjust ODB array size if necessary.
04748 
04749   Input:
04750     HNDLE  hDB              Handle to the database
04751     HNDLE  hKeyRoot         Key handle to start with, 0 for root
04752     cha    *name            Key name relative to hKeyRoot
04753     void   *data            Pointer to data array
04754     INT    data_size        Size of data array
04755     INT    num_values       Number of values in array
04756     DWORD  type             Type of data
04757 
04758   Output:
04759     none
04760 
04761   Function value:
04762     <same as db_set_data>
04763 
04764 \********************************************************************/
04765 {
04766    HNDLE hKey;
04767    INT status, old_size;
04768 
04769    if (num_values == 0)
04770       return DB_INVALID_PARAM;
04771 
04772    status = db_find_key(hDB, hKeyRoot, name, &hKey);
04773    if (status != DB_SUCCESS) {
04774       db_create_key(hDB, hKeyRoot, name, type);
04775       db_find_key(hDB, hKeyRoot, name, &hKey);
04776       status = db_set_data(hDB, hKey, data, data_size, num_values, type);
04777    } else {
04778       old_size = data_size;
04779       db_get_data(hDB, hKey, data, &old_size, type);
04780       status = db_set_data(hDB, hKey, data, data_size, num_values, type);
04781    }
04782 
04783    return status;
04784 }
04785 
04786 /*------------------------------------------------------------------*/
04787 INT db_set_mode(HNDLE hDB, HNDLE hKey, WORD mode, BOOL recurse)
04788 /********************************************************************\
04789 
04790   Routine: db_set_mode
04791 
04792   Purpose: Set access mode of key
04793 
04794   Input:
04795     HNDLE  hDB              Handle to the database
04796     HNDLE  hKey             Key handle
04797     DWORD  mode             Access mode, any or'ed combination of
04798                             MODE_READ, MODE_WRITE, MODE_EXCLUSIVE
04799                             and MODE_DELETE
04800     BOOL   recurse          Recurse subtree if TRUE, also used
04801                             as recurse level
04802 
04803   Output:
04804     none
04805 
04806   Function value:
04807     DB_SUCCESS              Successful completion
04808     DB_INVALID_HANDLE       Database handle is invalid
04809 
04810 \********************************************************************/
04811 {
04812    if (rpc_is_remote())
04813       return rpc_call(RPC_DB_SET_MODE, hDB, hKey, mode, recurse);
04814 
04815 #ifdef LOCAL_ROUTINES
04816    {
04817       DATABASE_HEADER *pheader;
04818       KEYLIST *pkeylist;
04819       KEY *pkey, *pnext_key;
04820       HNDLE hKeyLink;
04821 
04822       if (hDB > _database_entries || hDB <= 0) {
04823          cm_msg(MERROR, "db_set_mode", "invalid database handle");
04824          return DB_INVALID_HANDLE;
04825       }
04826 
04827       if (!_database[hDB - 1].attached) {
04828          cm_msg(MERROR, "db_set_mode", "invalid database handle");
04829          return DB_INVALID_HANDLE;
04830       }
04831 
04832       if (recurse < 2)
04833          db_lock_database(hDB);
04834 
04835       pheader = _database[hDB - 1].database_header;
04836       if (!hKey)
04837          hKey = pheader->root_key;
04838       pkey = (KEY *) ((char *) pheader + hKey);
04839 
04840       /* check if hKey argument is correct */
04841       if (!db_validate_hkey(pheader, hKey)) {
04842          db_unlock_database(hDB);
04843          return DB_INVALID_HANDLE;
04844       }
04845 
04846       pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
04847 
04848       if (pkey->type == TID_KEY && pkeylist->first_key && recurse) {
04849          /* first recurse subtree */
04850          pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
04851 
04852          do {
04853             pnext_key = (KEY *) (PTYPE) pkey->next_key;
04854 
04855             db_set_mode(hDB, (PTYPE) pkey - (PTYPE) pheader, mode, recurse + 1);
04856 
04857             if (pnext_key)
04858                pkey = (KEY *) ((char *) pheader + (PTYPE) pnext_key);
04859          } while (pnext_key);
04860       }
04861 
04862       pkey = (KEY *) ((char *) pheader + hKey);
04863 
04864       /* resolve links */
04865       if (pkey->type == TID_LINK) {
04866          db_unlock_database(hDB);
04867          if (*((char *) pheader + pkey->data) == '/')
04868             db_find_key(hDB, 0, (char *) pheader + pkey->data, &hKeyLink);
04869          else
04870             db_find_key(hDB, hKey, (char *) pheader + pkey->data, &hKeyLink);
04871          if (hKeyLink)
04872             db_set_mode(hDB, hKeyLink, mode, recurse > 0);
04873          db_lock_database(hDB);
04874          pheader = _database[hDB - 1].database_header;
04875          pkey = (KEY *) ((char *) pheader + hKey);
04876       }
04877 
04878       /* now set mode */
04879       pkey->access_mode = mode;
04880 
04881       if (recurse < 2)
04882          db_unlock_database(hDB);
04883    }
04884 #endif                          /* LOCAL_ROUTINES */
04885 
04886    return DB_SUCCESS;
04887 }
04888 
04889 /**dox***************************************************************/
04890 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
04891 
04892 /********************************************************************/
04893 /**
04894 Load a branch of a database from an .ODB file.
04895 
04896 This function is used by the ODBEdit command load. For a
04897 description of the ASCII format, see db_copy(). Data can be loaded relative to
04898 the root of the ODB (hkey equal zero) or relative to a certain key.
04899 @param hDB          ODB handle obtained via cm_get_experiment_database().
04900 @param hKeyRoot Handle for key where search starts, zero for root.
04901 @param filename Filename of .ODB file.
04902 @param bRemote If TRUE, the file is loaded by the server process on the
04903 back-end, if FALSE, it is loaded from the current process
04904 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_FILE_ERROR
04905 */
04906 INT db_load(HNDLE hDB, HNDLE hKeyRoot, char *filename, BOOL bRemote)
04907 {
04908    struct stat stat_buf;
04909    INT hfile, size, n, i, status;
04910    char *buffer;
04911 
04912    if (rpc_is_remote() && bRemote)
04913       return rpc_call(RPC_DB_LOAD, hDB, hKeyRoot, filename);
04914 
04915    /* open file */
04916    hfile = open(filename, O_RDONLY | O_TEXT, 0644);
04917    if (hfile == -1) {
04918       cm_msg(MERROR, "db_load", "file \"%s\" not found", filename);
04919       return DB_FILE_ERROR;
04920    }
04921 
04922    /* allocate buffer with file size */
04923    fstat(hfile, &stat_buf);
04924    size = stat_buf.st_size;
04925    buffer = (char *) malloc(size + 1);
04926 
04927    if (buffer == NULL) {
04928       cm_msg(MERROR, "db_load", "cannot allocate ODB load buffer");
04929       close(hfile);
04930       return DB_NO_MEMORY;
04931    }
04932 
04933    n = 0;
04934 
04935    do {
04936       i = read(hfile, buffer + n, size);
04937       if (i <= 0)
04938          break;
04939       n += i;
04940    } while (TRUE);
04941 
04942    buffer[n] = 0;
04943 
04944    if (strncmp(buffer, "<?xml version=\"1.0\"", 19) == 0) {
04945       status = db_paste_xml(hDB, hKeyRoot, buffer);
04946       if (status != DB_SUCCESS)
04947          printf("Error in file \"%s\"\n", filename);
04948    } else
04949       status = db_paste(hDB, hKeyRoot, buffer);
04950 
04951    close(hfile);
04952    free(buffer);
04953 
04954    return status;
04955 }
04956 
04957 /********************************************************************/
04958 /**
04959 Copy an ODB subtree in ASCII format to a buffer
04960 
04961 This function converts the binary ODB contents to an ASCII.
04962 The function db_paste() can be used to convert the ASCII representation back
04963 to binary ODB contents. The functions db_load() and db_save() internally
04964 use db_copy() and db_paste(). This function converts the binary ODB
04965 contents to an ASCII representation of the form:
04966 - For single value:
04967 \code
04968 [ODB path]
04969  key name = type : value
04970 \endcode
04971 - For strings:
04972 \code
04973 key name = STRING : [size] string contents
04974 \endcode
04975 - For arrayes (type can be BYTE, SBYTE, CHAR, WORD, SHORT, DWORD,
04976 INT, BOOL, FLOAT, DOUBLE, STRING or LINK):
04977 \code
04978 key name = type[size] :
04979  [0] value0
04980  [1] value1
04981  [2] value2
04982  ...
04983 \endcode
04984 @param hDB          ODB handle obtained via cm_get_experiment_database().
04985 @param hKey Handle for key where search starts, zero for root.
04986 @param buffer ASCII buffer which receives ODB contents.
04987 @param buffer_size Size of buffer, returns remaining space in buffer.
04988 @param path Internal use only, must be empty ("").
04989 @return DB_SUCCESS, DB_TRUNCATED, DB_NO_MEMORY
04990 */
04991 INT db_copy(HNDLE hDB, HNDLE hKey, char *buffer, INT * buffer_size, char *path)
04992 {
04993    INT i, j, size, status;
04994    KEY key;
04995    HNDLE hSubkey;
04996    char full_path[MAX_ODB_PATH], str[MAX_STRING_LENGTH * 2];
04997    char *data, line[MAX_STRING_LENGTH * 2];
04998    BOOL bWritten;
04999 
05000    strcpy(full_path, path);
05001 
05002    bWritten = FALSE;
05003 
05004    /* first enumerate this level */
05005    for (i = 0;; i++) {
05006       db_enum_link(hDB, hKey, i, &hSubkey);
05007 
05008       if (i == 0 && !hSubkey) {
05009          /* If key has no subkeys, just write this key */
05010          db_get_key(hDB, hKey, &key);
05011          size = key.total_size;
05012          data = (char *) malloc(size);
05013          if (data == NULL) {
05014             cm_msg(MERROR, "db_copy", "cannot allocate data buffer");
05015             return DB_NO_MEMORY;
05016          }
05017          line[0] = 0;
05018 
05019          if (key.type != TID_KEY) {
05020             db_get_data(hDB, hKey, data, &size, key.type);
05021             if (key.num_values == 1) {
05022                sprintf(line, "%s = %s : ", key.name, tid_name[key.type]);
05023 
05024                if (key.type == TID_STRING && strchr(data, '\n') != NULL) {
05025                   /* multiline string */
05026                   sprintf(line + strlen(line), "[====#$@$#====]\n");
05027 
05028                   /* copy line to buffer */
05029                   if ((INT) (strlen(line) + 1) > *buffer_size) {
05030                      free(data);
05031                      return DB_TRUNCATED;
05032                   }
05033 
05034                   strcpy(buffer, line);
05035                   buffer += strlen(line);
05036                   *buffer_size -= strlen(line);
05037 
05038                   /* copy multiple lines to buffer */
05039                   if (key.item_size > *buffer_size) {
05040                      free(data);
05041                      return DB_TRUNCATED;
05042                   }
05043 
05044                   strcpy(buffer, data);
05045                   buffer += strlen(data);
05046                   *buffer_size -= strlen(data);
05047 
05048                   strcpy(line, "\n====#$@$#====\n");
05049                } else {
05050                   db_sprintf(str, data, key.item_size, 0, key.type);
05051 
05052                   if (key.type == TID_STRING || key.type == TID_LINK)
05053                      sprintf(line + strlen(line), "[%d] ", key.item_size);
05054 
05055                   sprintf(line + strlen(line), "%s\n", str);
05056                }
05057             } else {
05058                sprintf(line, "%s = %s[%d] :\n", key.name,
05059                        tid_name[key.type], key.num_values);
05060 
05061                for (j = 0; j < key.num_values; j++) {
05062                   if (key.type == TID_STRING || key.type == TID_LINK)
05063                      sprintf(line + strlen(line), "[%d] ", key.item_size);
05064                   else
05065                      sprintf(line + strlen(line), "[%d] ", j);
05066 
05067                   db_sprintf(str, data, key.item_size, j, key.type);
05068                   sprintf(line + strlen(line), "%s\n", str);
05069 
05070                   /* copy line to buffer */
05071                   if ((INT) (strlen(line) + 1) > *buffer_size) {
05072                      free(data);
05073                      return DB_TRUNCATED;
05074                   }
05075 
05076                   strcpy(buffer, line);
05077                   buffer += strlen(line);
05078                   *buffer_size -= strlen(line);
05079                   line[0] = 0;
05080                }
05081             }
05082          }
05083 
05084          /* copy line to buffer */
05085          if ((INT) (strlen(line) + 1) > *buffer_size) {
05086             free(data);
05087             return DB_TRUNCATED;
05088          }
05089 
05090          strcpy(buffer, line);
05091          buffer += strlen(line);
05092          *buffer_size -= strlen(line);
05093 
05094          free(data);
05095       }
05096 
05097       if (!hSubkey)
05098          break;
05099 
05100       db_get_key(hDB, hSubkey, &key);
05101       size = key.total_size;
05102       data = (char *) malloc(size);
05103       if (data == NULL) {
05104          cm_msg(MERROR, "db_copy", "cannot allocate data buffer");
05105          return DB_NO_MEMORY;
05106       }
05107 
05108       line[0] = 0;
05109 
05110       if (key.type == TID_KEY) {
05111          /* new line */
05112          if (bWritten) {
05113             if (*buffer_size < 2) {
05114                free(data);
05115                return DB_TRUNCATED;
05116             }
05117 
05118             strcpy(buffer, "\n");
05119             buffer += 1;
05120             *buffer_size -= 1;
05121          }
05122 
05123          strcpy(str, full_path);
05124          if (str[0] && str[strlen(str) - 1] != '/')
05125             strcat(str, "/");
05126          strcat(str, key.name);
05127 
05128          /* recurse */
05129          status = db_copy(hDB, hSubkey, buffer, buffer_size, str);
05130          if (status != DB_SUCCESS) {
05131             free(data);
05132             return status;
05133          }
05134 
05135          buffer += strlen(buffer);
05136          bWritten = FALSE;
05137       } else {
05138          db_get_data(hDB, hSubkey, data, &size, key.type);
05139          if (!bWritten) {
05140             if (path[0] == 0)
05141                sprintf(line, "[.]\n");
05142             else
05143                sprintf(line, "[%s]\n", path);
05144             bWritten = TRUE;
05145          }
05146 
05147          if (key.num_values == 1) {
05148             sprintf(line + strlen(line), "%s = %s : ", key.name, tid_name[key.type]);
05149 
05150             if (key.type == TID_STRING && strchr(data, '\n') != NULL) {
05151                /* multiline string */
05152                sprintf(line + strlen(line), "[====#$@$#====]\n");
05153 
05154                /* ensure string limiter */
05155                data[size - 1] = 0;
05156 
05157                /* copy line to buffer */
05158                if ((INT) (strlen(line) + 1) > *buffer_size) {
05159                   free(data);
05160                   return DB_TRUNCATED;
05161                }
05162 
05163                strcpy(buffer, line);
05164                buffer += strlen(line);
05165                *buffer_size -= strlen(line);
05166 
05167                /* copy multiple lines to buffer */
05168                if (key.item_size > *buffer_size) {
05169                   free(data);
05170                   return DB_TRUNCATED;
05171                }
05172 
05173                strcpy(buffer, data);
05174                buffer += strlen(data);
05175                *buffer_size -= strlen(data);
05176 
05177                strcpy(line, "\n====#$@$#====\n");
05178             } else {
05179                db_sprintf(str, data, key.item_size, 0, key.type);
05180 
05181                if (key.type == TID_STRING || key.type == TID_LINK)
05182                   sprintf(line + strlen(line), "[%d] ", key.item_size);
05183 
05184                sprintf(line + strlen(line), "%s\n", str);
05185             }
05186          } else {
05187             sprintf(line + strlen(line), "%s = %s[%d] :\n", key.name,
05188                     tid_name[key.type], key.num_values);
05189 
05190             for (j = 0; j < key.num_values; j++) {
05191                if (key.type == TID_STRING || key.type == TID_LINK)
05192                   sprintf(line + strlen(line), "[%d] ", key.item_size);
05193                else
05194                   sprintf(line + strlen(line), "[%d] ", j);
05195 
05196                db_sprintf(str, data, key.item_size, j, key.type);
05197                sprintf(line + strlen(line), "%s\n", str);
05198 
05199                /* copy line to buffer */
05200                if ((INT) (strlen(line) + 1) > *buffer_size) {
05201                   free(data);
05202                   return DB_TRUNCATED;
05203                }
05204 
05205                strcpy(buffer, line);
05206                buffer += strlen(line);
05207                *buffer_size -= strlen(line);
05208                line[0] = 0;
05209             }
05210          }
05211 
05212          /* copy line to buffer */
05213          if ((INT) (strlen(line) + 1) > *buffer_size) {
05214             free(data);
05215             return DB_TRUNCATED;
05216          }
05217 
05218          strcpy(buffer, line);
05219          buffer += strlen(line);
05220          *buffer_size -= strlen(line);
05221       }
05222 
05223       free(data);
05224    }
05225 
05226    if (bWritten) {
05227       if (*buffer_size < 2)
05228          return DB_TRUNCATED;
05229 
05230       strcpy(buffer, "\n");
05231       buffer += 1;
05232       *buffer_size -= 1;
05233    }
05234 
05235    return DB_SUCCESS;
05236 }
05237 
05238 /********************************************************************/
05239 /**
05240 Copy an ODB subtree in ASCII format from a buffer
05241 @param hDB          ODB handle obtained via cm_get_experiment_database().
05242 @param hKeyRoot Handle for key where search starts, zero for root.
05243 @param buffer NULL-terminated buffer
05244 @return DB_SUCCESS, DB_TRUNCATED, DB_NO_MEMORY
05245 */
05246 INT db_paste(HNDLE hDB, HNDLE hKeyRoot, char *buffer)
05247 {
05248    char line[MAX_STRING_LENGTH];
05249    char title[MAX_STRING_LENGTH];
05250    char key_name[MAX_STRING_LENGTH];
05251    char data_str[MAX_STRING_LENGTH + 50];
05252    char test_str[MAX_STRING_LENGTH];
05253    char *pc, *pold, *data;
05254    INT data_size;
05255    INT tid, i, j, n_data, string_length, status, size;
05256    HNDLE hKey;
05257    KEY root_key;
05258    BOOL multi_line;
05259 
05260    title[0] = 0;
05261    multi_line = FALSE;
05262 
05263    if (hKeyRoot == 0)
05264       db_find_key(hDB, hKeyRoot, "", &hKeyRoot);
05265 
05266    db_get_key(hDB, hKeyRoot, &root_key);
05267 
05268    /* initial data size */
05269    data_size = 1000;
05270    data = (char *) malloc(data_size);
05271    if (data == NULL) {
05272       cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
05273       return DB_NO_MEMORY;
05274    }
05275 
05276    do {
05277       if (*buffer == 0)
05278          break;
05279 
05280       for (i = 0; *buffer != '\n' && *buffer && i < MAX_STRING_LENGTH; i++)
05281          line[i] = *buffer++;
05282 
05283       if (i == MAX_STRING_LENGTH) {
05284          cm_msg(MERROR, "db_paste", "line too long");
05285          free(data);
05286          return DB_TRUNCATED;
05287       }
05288 
05289       line[i] = 0;
05290       if (*buffer == '\n')
05291          buffer++;
05292 
05293       /* check if it is a section title */
05294       if (line[0] == '[') {
05295          /* extract title and append '/' */
05296          strcpy(title, line + 1);
05297          if (strchr(title, ']'))
05298             *strchr(title, ']') = 0;
05299          if (title[0] && title[strlen(title) - 1] != '/')
05300             strcat(title, "/");
05301       } else {
05302          /* valid data line if it includes '=' and no ';' */
05303          if (strchr(line, '=') && line[0] != ';') {
05304             /* copy type info and data */
05305             pc = strchr(line, '=') + 1;
05306             while (*pc == ' ')
05307                pc++;
05308             strcpy(data_str, pc);
05309 
05310             /* extract key name */
05311             *strchr(line, '=') = 0;
05312 
05313             pc = &line[strlen(line) - 1];
05314             while (*pc == ' ')
05315                *pc-- = 0;
05316 
05317             key_name[0] = 0;
05318             if (title[0] != '.')
05319                strcpy(key_name, title);
05320 
05321             strcat(key_name, line);
05322 
05323             /* evaluate type info */
05324             strcpy(line, data_str);
05325             if (strchr(line, ' '))
05326                *strchr(line, ' ') = 0;
05327 
05328             n_data = 1;
05329             if (strchr(line, '[')) {
05330                n_data = atol(strchr(line, '[') + 1);
05331                *strchr(line, '[') = 0;
05332             }
05333 
05334             for (tid = 0; tid < TID_LAST; tid++)
05335                if (strcmp(tid_name[tid], line) == 0)
05336                   break;
05337 
05338             string_length = 0;
05339 
05340             if (tid == TID_LAST)
05341                cm_msg(MERROR, "db_paste",
05342                       "found unknown data type \"%s\" in ODB file", line);
05343             else {
05344                /* skip type info */
05345                pc = data_str;
05346                while (*pc != ' ' && *pc)
05347                   pc++;
05348                while ((*pc == ' ' || *pc == ':') && *pc)
05349                   pc++;
05350                strcpy(data_str, pc);
05351 
05352                if (n_data > 1) {
05353                   data_str[0] = 0;
05354                   if (!*buffer)
05355                      break;
05356 
05357                   for (j = 0; *buffer != '\n' && *buffer; j++)
05358                      data_str[j] = *buffer++;
05359                   data_str[j] = 0;
05360                   if (*buffer == '\n')
05361                      buffer++;
05362                }
05363 
05364                for (i = 0; i < n_data; i++) {
05365                   /* strip trailing \n */
05366                   pc = &data_str[strlen(data_str) - 1];
05367                   while (*pc == '\n' || *pc == '\r')
05368                      *pc-- = 0;
05369 
05370                   if (tid == TID_STRING || tid == TID_LINK) {
05371                      if (!string_length) {
05372                         if (data_str[1] == '=')
05373                            string_length = -1;
05374                         else
05375                            string_length = atoi(data_str + 1);
05376                         if (string_length > MAX_STRING_LENGTH) {
05377                            string_length = MAX_STRING_LENGTH;
05378                            cm_msg(MERROR, "db_paste",
05379                                   "found string exceeding MAX_STRING_LENGTH");
05380                         }
05381                      }
05382 
05383                      if (string_length == -1) {
05384                         /* multi-line string */
05385                         if (strstr(buffer, "\n====#$@$#====\n") != NULL) {
05386                            string_length =
05387                                (PTYPE) strstr(buffer,
05388                                               "\n====#$@$#====\n") - (PTYPE) buffer + 1;
05389 
05390                            if (string_length >= data_size) {
05391                               data_size += string_length + 100;
05392                               data = (char *) realloc(data, data_size);
05393                               if (data == NULL) {
05394                                  cm_msg(MERROR, "db_paste",
05395                                         "cannot allocate data buffer");
05396                                  return DB_NO_MEMORY;
05397                               }
05398                            }
05399 
05400                            memset(data, 0, data_size);
05401                            strncpy(data, buffer, string_length);
05402                            data[string_length - 1] = 0;
05403                            buffer =
05404                                strstr(buffer,
05405                                       "\n====#$@$#====\n") + strlen("\n====#$@$#====\n");
05406                         } else
05407                            cm_msg(MERROR, "db_paste",
05408                                   "found multi-line string without termination sequence");
05409                      } else {
05410                         pc = data_str + 2;
05411                         while (*pc && *pc != ' ')
05412                            pc++;
05413                         while (*pc && *pc == ' ')
05414                            pc++;
05415 
05416                         /* limit string size */
05417                         *(pc + string_length - 1) = 0;
05418 
05419                         /* increase data buffer if necessary */
05420                         if (string_length * (i + 1) >= data_size) {
05421                            data_size += 1000;
05422                            data = (char *) realloc(data, data_size);
05423                            if (data == NULL) {
05424                               cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
05425                               return DB_NO_MEMORY;
05426                            }
05427                         }
05428 
05429                         strcpy(data + string_length * i, pc);
05430                      }
05431                   } else {
05432                      pc = data_str;
05433 
05434                      if (n_data > 1 && data_str[0] == '[') {
05435                         pc = strchr(data_str, ']') + 1;
05436                         while (*pc && *pc == ' ')
05437                            pc++;
05438                      }
05439 
05440                      db_sscanf(pc, data, &size, i, tid);
05441 
05442                      /* increase data buffer if necessary */
05443                      if (size * (i + 1) >= data_size) {
05444                         data_size += 1000;
05445                         data = (char *) realloc(data, data_size);
05446                         if (data == NULL) {
05447                            cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
05448                            return DB_NO_MEMORY;
05449                         }
05450                      }
05451 
05452                   }
05453 
05454                   if (i < n_data - 1) {
05455                      data_str[0] = 0;
05456                      if (!*buffer)
05457                         break;
05458 
05459                      pold = buffer;
05460 
05461                      for (j = 0; *buffer != '\n' && *buffer; j++)
05462                         data_str[j] = *buffer++;
05463                      data_str[j] = 0;
05464                      if (*buffer == '\n')
05465                         buffer++;
05466 
05467                      /* test if valid data */
05468                      if (tid != TID_STRING && tid != TID_LINK) {
05469                         if (data_str[0] == 0 || (strchr(data_str, '=')
05470                                                  && strchr(data_str, ':')))
05471                            buffer = pold;
05472                      }
05473                   }
05474                }
05475 
05476                /* skip system client entries */
05477                strcpy(test_str, key_name);
05478                test_str[15] = 0;
05479 
05480                if (!equal_ustring(test_str, "/System/Clients")) {
05481                   if (root_key.type != TID_KEY) {
05482                      /* root key is destination key */
05483                      hKey = hKeyRoot;
05484                   } else {
05485                      /* create key and set value */
05486                      if (key_name[0] == '/') {
05487                         status = db_find_link(hDB, 0, key_name, &hKey);
05488                         if (status == DB_NO_KEY) {
05489                            db_create_key(hDB, 0, key_name, tid);
05490                            status = db_find_link(hDB, 0, key_name, &hKey);
05491                         }
05492                      } else {
05493                         status = db_find_link(hDB, hKeyRoot, key_name, &hKey);
05494                         if (status == DB_NO_KEY) {
05495                            db_create_key(hDB, hKeyRoot, key_name, tid);
05496                            status = db_find_link(hDB, hKeyRoot, key_name, &hKey);
05497                         }
05498                      }
05499                   }
05500 
05501                   /* set key data if created sucessfully */
05502                   if (hKey) {
05503                      if (tid == TID_STRING || tid == TID_LINK)
05504                         db_set_data(hDB, hKey, data, string_length * n_data, n_data, tid);
05505                      else
05506                         db_set_data(hDB, hKey, data,
05507                                     rpc_tid_size(tid) * n_data, n_data, tid);
05508                   }
05509                }
05510             }
05511          }
05512       }
05513    } while (TRUE);
05514 
05515    free(data);
05516    return DB_SUCCESS;
05517 }
05518 
05519 /********************************************************************/
05520 /* 
05521   Only internally used by db_paste_xml                             
05522 */
05523 int db_paste_node(HNDLE hDB, HNDLE hKeyRoot, PMXML_NODE node)
05524 {
05525    char type[256], data[256];
05526    int i, status, size, tid, num_values;
05527    HNDLE hKey;
05528    PMXML_NODE child;
05529 
05530    if (strcmp(mxml_get_name(node), "odb") == 0) {
05531       for (i=0 ; i<mxml_get_number_of_children(node) ; i++) {
05532          status = db_paste_node(hDB, hKeyRoot, mxml_subnode(node, i));
05533          if (status != DB_SUCCESS)
05534             return status;
05535       }
05536    } else if (strcmp(mxml_get_name(node), "dir") == 0) {
05537       status = db_find_link(hDB, hKeyRoot, mxml_get_attribute(node, "name"), &hKey);
05538       if (status == DB_NO_KEY) {
05539          db_create_key(hDB, hKeyRoot, mxml_get_attribute(node, "name"), TID_KEY);
05540          status = db_find_link(hDB, hKeyRoot, mxml_get_attribute(node, "name"), &hKey);
05541          if (status != DB_SUCCESS) {
05542             cm_msg(MERROR, "db_paste_node", 
05543                "cannot create key \"%s\" in ODB", mxml_get_attribute(node, "name"));
05544             return status;
05545          }
05546       }
05547 
05548       db_get_path(hDB, hKey, data, sizeof(data));
05549       if (strncmp(data, "/System/Clients", 15) != 0) {
05550          for (i=0 ; i<mxml_get_number_of_children(node) ; i++) {
05551             status = db_paste_node(hDB, hKey, mxml_subnode(node, i));
05552             if (status != DB_SUCCESS)
05553                return status;
05554          }
05555       }
05556    } else if (strcmp(mxml_get_name(node), "key") == 0 || 
05557               strcmp(mxml_get_name(node), "keyarray") == 0) {
05558 
05559       if (strcmp(mxml_get_name(node), "keyarray") == 0)
05560          num_values = atoi(mxml_get_attribute(node, "num_values"));
05561       else
05562          num_values = 0;
05563 
05564       if (mxml_get_attribute(node, "type") == NULL) {
05565          cm_msg(MERROR, "db_paste_node", 
05566             "found key \"%s\" with no type in XML data", mxml_get_name(node));
05567          return DB_TYPE_MISMATCH;
05568       }
05569 
05570       strcpy(type, mxml_get_attribute(node, "type"));
05571       for (tid = 0; tid < TID_LAST; tid++)
05572          if (strcmp(tid_name[tid], type) == 0)
05573             break;
05574       if (tid == TID_LAST) {
05575          cm_msg(MERROR, "db_paste_node", 
05576             "found unknown data type \"%s\" in XML data", type);
05577          return DB_TYPE_MISMATCH;
05578       }
05579 
05580       status = db_find_link(hDB, hKeyRoot, mxml_get_attribute(node, "name"), &hKey);
05581       if (status == DB_NO_KEY) {
05582          db_create_key(hDB, hKeyRoot, mxml_get_attribute(node, "name"), tid);
05583          status = db_find_link(hDB, hKeyRoot, mxml_get_attribute(node, "name"), &hKey);
05584          if (status != DB_SUCCESS) {
05585             cm_msg(MERROR, "db_paste_node", 
05586                "cannot create key \"%s\" in ODB", mxml_get_attribute(node, "name"));
05587             return status;
05588          }
05589       }
05590 
05591       if (num_values) {
05592          /* evaluate array */
05593          for (i=0 ; i<mxml_get_number_of_children(node) ; i++) {
05594             child = mxml_subnode(node, i);
05595             if (tid == TID_STRING || tid == TID_LINK) {
05596                size = atoi(mxml_get_attribute(node, "size"));
05597                db_set_data_index(hDB, hKey, mxml_get_value(child), size, i, tid);
05598             } else {
05599                db_sscanf(mxml_get_value(child), data, &size, 0, tid);
05600                db_set_data_index(hDB, hKey, data, rpc_tid_size(tid), i, tid);
05601             }
05602          }
05603 
05604       } else { // single value
05605          if (tid == TID_STRING || tid == TID_LINK) {
05606             size = atoi(mxml_get_attribute(node, "size"));
05607             if (mxml_get_value(node) == NULL)
05608                db_set_data(hDB, hKey, "", size, 1, tid);
05609             else
05610                db_set_data(hDB, hKey, mxml_get_value(node), size, 1, tid);
05611          } else {
05612             db_sscanf(mxml_get_value(node), data, &size, 0, tid);
05613             db_set_data(hDB, hKey, data, rpc_tid_size(tid), 1, tid);
05614          }
05615       }
05616    }
05617 
05618    return DB_SUCCESS;
05619 }
05620 
05621 /********************************************************************/
05622 /**
05623 Paste an ODB subtree in XML format from a buffer
05624 @param hDB          ODB handle obtained via cm_get_experiment_database().
05625 @param hKeyRoot Handle for key where search starts, zero for root.
05626 @param buffer NULL-terminated buffer
05627 @return DB_SUCCESS, DB_INVALID_PARAM, DB_NO_MEMORY, DB_TYPE_MISMATCH
05628 */
05629 INT db_paste_xml(HNDLE hDB, HNDLE hKeyRoot, char *buffer)
05630 {
05631    char error[256];
05632    INT status;
05633    PMXML_NODE tree, node;
05634 
05635    if (hKeyRoot == 0)
05636       db_find_key(hDB, hKeyRoot, "", &hKeyRoot);
05637 
05638    /* parse XML buffer */
05639    tree = mxml_parse_buffer(buffer, error, sizeof(error));
05640    if (tree == NULL) {
05641       puts(error);
05642       return DB_TYPE_MISMATCH;
05643    }
05644 
05645    node = mxml_find_node(tree, "odb");
05646    if (node == NULL) {
05647       puts("Cannot find element \"odb\" in XML data");
05648       return DB_TYPE_MISMATCH;
05649    }
05650 
05651    status = db_paste_node(hDB, hKeyRoot, node);
05652 
05653    mxml_free_tree(tree);
05654 
05655    return status;
05656 }
05657 
05658 /********************************************************************/
05659 /**
05660 Copy an ODB subtree in XML format to a buffer
05661 
05662 @param hDB          ODB handle obtained via cm_get_experiment_database().
05663 @param hKey Handle for key where search starts, zero for root.
05664 @param buffer ASCII buffer which receives ODB contents.
05665 @param buffer_size Size of buffer, returns remaining space in buffer.
05666 @return DB_SUCCESS, DB_TRUNCATED, DB_NO_MEMORY
05667 */
05668 INT db_copy_xml(HNDLE hDB, HNDLE hKey, char *buffer, INT * buffer_size)
05669 {
05670 #ifdef LOCAL_ROUTINES
05671    {
05672    INT len;
05673    char *p;
05674    MXML_WRITER *writer;
05675 
05676    /* open file */
05677    writer = mxml_open_buffer();
05678    if (writer == NULL) {
05679       cm_msg(MERROR, "db_copy_xml", "Cannot allocate buffer");
05680       return DB_NO_MEMORY;
05681    }
05682 
05683    /* write XML header */
05684    mxml_start_element(writer, "odb");
05685    mxml_write_attribute(writer, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
05686    mxml_write_attribute(writer, "xsi:noNamespaceSchemaLocation", "http://midas.psi.ch/odb.xsd");
05687 
05688    db_save_xml_key(hDB, hKey, 0, writer);
05689    
05690    mxml_end_element(writer); // "odb"
05691    p = mxml_close_buffer(writer);
05692 
05693    strlcpy(buffer, p, *buffer_size);
05694    len = strlen(p);
05695    free(p);
05696    if (len > *buffer_size) {
05697       *buffer_size = 0;
05698       return DB_TRUNCATED;
05699    }
05700 
05701    *buffer_size -= len;
05702    }
05703 #endif                          /* LOCAL_ROUTINES */
05704 
05705    return DB_SUCCESS;
05706 }
05707 
05708 /**dox***************************************************************/
05709 #ifndef DOXYGEN_SHOULD_SKIP_THIS
05710 
05711 /*------------------------------------------------------------------*/
05712 void name2c(char *str)
05713 /********************************************************************\
05714 
05715   Routine: name2c
05716 
05717   Purpose: Convert key name to C name. Internal use only.
05718 
05719 \********************************************************************/
05720 {
05721    if (*str >= '0' && *str <= '9')
05722       *str = '_';
05723 
05724    while (*str) {
05725       if (!(*str >= 'a' && *str <= 'z') &&
05726           !(*str >= 'A' && *str <= 'Z') && !(*str >= '0' && *str <= '9'))
05727          *str = '_';
05728       *str = (char) tolower(*str);
05729       str++;
05730    }
05731 }
05732 
05733 /*------------------------------------------------------------------*/
05734 static void db_save_tree_struct(HNDLE hDB, HNDLE hKey, int hfile, INT level)
05735 /********************************************************************\
05736 
05737   Routine: db_save_tree_struct
05738 
05739   Purpose: Save database tree as a C structure. Gets called by
05740            db_save_struct(). Internal use only.
05741 
05742 \********************************************************************/
05743 {
05744    INT i, index;
05745    KEY key;
05746    HNDLE hSubkey;
05747    char line[MAX_ODB_PATH], str[MAX_STRING_LENGTH];
05748 
05749    /* first enumerate this level */
05750    for (index = 0;; index++) {
05751       db_enum_key(hDB, hKey, index, &hSubkey);
05752 
05753       if (!hSubkey)
05754          break;
05755 
05756       db_get_key(hDB, hSubkey, &key);
05757 
05758       if (key.type != TID_KEY) {
05759          for (i = 0; i <= level; i++)
05760             write(hfile, "  ", 2);
05761 
05762          switch (key.type) {
05763          case TID_SBYTE:
05764          case TID_CHAR:
05765             strcpy(line, "char");
05766             break;
05767          case TID_SHORT:
05768             strcpy(line, "short");
05769             break;
05770          case TID_FLOAT:
05771             strcpy(line, "float");
05772             break;
05773          case TID_DOUBLE:
05774             strcpy(line, "double");
05775             break;
05776          case TID_BITFIELD:
05777             strcpy(line, "unsigned char");
05778             break;
05779          case TID_STRING:
05780             strcpy(line, "char");
05781             break;
05782          case TID_LINK:
05783             strcpy(line, "char");
05784             break;
05785          default:
05786             strcpy(line, tid_name[key.type]);
05787             break;
05788          }
05789 
05790          strcat(line, "                    ");
05791          strcpy(str, key.name);
05792          name2c(str);
05793 
05794          if (key.num_values > 1)
05795             sprintf(str + strlen(str), "[%d]", key.num_values);
05796          if (key.type == TID_STRING || key.type == TID_LINK)
05797             sprintf(str + strlen(str), "[%d]", key.item_size);
05798 
05799          strcpy(line + 10, str);
05800          strcat(line, ";\n");
05801 
05802          write(hfile, line, strlen(line));
05803       } else {
05804          /* recurse subtree */
05805          for (i = 0; i <= level; i++)
05806             write(hfile, "  ", 2);
05807 
05808          sprintf(line, "struct {\n");
05809          write(hfile, line, strlen(line));
05810          db_save_tree_struct(hDB, hSubkey, hfile, level + 1);
05811 
05812          for (i = 0; i <= level; i++)
05813             write(hfile, "  ", 2);
05814 
05815          strcpy(str, key.name);
05816          name2c(str);
05817 
05818          sprintf(line, "} %s;\n", str);
05819          write(hfile, line, strlen(line));
05820       }
05821    }
05822 }
05823 
05824 /**dox***************************************************************/
05825 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
05826 
05827 /********************************************************************/
05828 /**
05829 Save a branch of a database to an .ODB file
05830 
05831 This function is used by the ODBEdit command save. For a
05832 description of the ASCII format, see db_copy(). Data of the whole ODB can
05833 be saved (hkey equal zero) or only a sub-tree.
05834 @param hDB          ODB handle obtained via cm_get_experiment_database().
05835 @param hKey Handle for key where search starts, zero for root.
05836 @param filename Filename of .ODB file.
05837 @param bRemote Flag for saving database on remote server.
05838 @return DB_SUCCESS, DB_FILE_ERROR
05839 */
05840 INT db_save(HNDLE hDB, HNDLE hKey, char *filename, BOOL bRemote)
05841 {
05842    if (rpc_is_remote() && bRemote)
05843       return rpc_call(RPC_DB_SAVE, hDB, hKey, filename, bRemote);
05844 
05845 #ifdef LOCAL_ROUTINES
05846    {
05847       INT hfile, size, buffer_size, n, status;
05848       char *buffer, path[256];
05849 
05850       /* open file */
05851       hfile = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT, 0644);
05852       if (hfile == -1) {
05853          cm_msg(MERROR, "db_save", "Cannot open file \"%s\"", filename);
05854          return DB_FILE_ERROR;
05855       }
05856 
05857       db_get_path(hDB, hKey, path, sizeof(path));
05858 
05859       buffer_size = 10000;
05860       do {
05861          buffer = (char *) malloc(buffer_size);
05862          if (buffer == NULL) {
05863             cm_msg(MERROR, "db_save", "cannot allocate ODB dump buffer");
05864             break;
05865          }
05866 
05867          size = buffer_size;
05868          status = db_copy(hDB, hKey, buffer, &size, path);
05869          if (status != DB_TRUNCATED) {
05870             n = write(hfile, buffer, buffer_size - size);
05871             free(buffer);
05872 
05873             if (n != buffer_size - size) {
05874                cm_msg(MERROR, "db_save", "cannot save .ODB file");
05875                close(hfile);
05876                return DB_FILE_ERROR;
05877             }
05878             break;
05879          }
05880 
05881          /* increase buffer size if truncated */
05882          free(buffer);
05883          buffer_size *= 2;
05884       } while (1);
05885 
05886       close(hfile);
05887 
05888    }
05889 #endif                          /* LOCAL_ROUTINES */
05890 
05891    return DB_SUCCESS;
05892 }
05893 /*------------------------------------------------------------------*/
05894 
05895 void xml_encode(char *src, int size)
05896 {
05897    int i;
05898    char *dst, *p;
05899 
05900    dst = (char *)malloc(size);
05901    if (dst == NULL)
05902       return;
05903 
05904    *dst = 0;
05905    for (i = 0; i < (int) strlen(src); i++) {
05906       switch (src[i]) {
05907       case '<':
05908          strlcat(dst, "&lt;", size);
05909          break;
05910       case '>':
05911          strlcat(dst, "&gt;", size);
05912          break;
05913       case '&':
05914          strlcat(dst, "&amp;", size);
05915          break;
05916       case '\"':
05917          strlcat(dst, "&quot;",size);
05918          break;
05919       case '\'':
05920          strlcat(dst, "&apos;",size);
05921          break;
05922       default:
05923          if ((int)strlen(dst) >= size) {
05924             free(dst);
05925             return;
05926          }
05927          p = dst+strlen(dst);
05928          *p = src[i];
05929          *(p+1) = 0;
05930       }
05931    }
05932 
05933    strlcpy(src, dst, size);
05934 }
05935 
05936 /*------------------------------------------------------------------*/
05937 
05938 INT db_save_xml_key(HNDLE hDB, HNDLE hKey, INT level, MXML_WRITER *writer)
05939 {
05940    INT i, index, size, status;
05941    char str[MAX_STRING_LENGTH * 2], *data;
05942    HNDLE hSubkey;
05943    KEY key;
05944 
05945    status = db_get_key(hDB, hKey, &key);
05946    if (status != DB_SUCCESS)
05947       return status;
05948 
05949    if (key.type == TID_KEY) {
05950 
05951       /* save opening tag for subtree */
05952       
05953       if (level > 0) {
05954          mxml_start_element(writer, "dir");
05955          mxml_write_attribute(writer, "name", key.name);
05956       }
05957 
05958       for (index = 0;; index++) {
05959          db_enum_key(hDB, hKey, index, &hSubkey);
05960 
05961          if (!hSubkey)
05962             break;
05963 
05964          /* save subtree */
05965          status = db_save_xml_key(hDB, hSubkey, level + 1, writer);
05966          if (status != DB_SUCCESS)
05967             return status;
05968       }
05969 
05970       /* save closing tag for subtree */
05971       if (level > 0)
05972          mxml_end_element(writer);
05973 
05974    } else {
05975 
05976       /* save key value */
05977 
05978       if (key.num_values > 1)
05979          mxml_start_element(writer, "keyarray");
05980       else
05981          mxml_start_element(writer, "key");
05982       mxml_write_attribute(writer, "name", key.name);
05983       mxml_write_attribute(writer, "type", rpc_tid_name(key.type));
05984 
05985       if (key.type == TID_STRING || key.type == TID_LINK) {
05986          sprintf(str, "%d", key.item_size);
05987          mxml_write_attribute(writer, "size", str);
05988       }
05989 
05990       if (key.num_values > 1) {
05991          sprintf(str, "%d", key.num_values);
05992          mxml_write_attribute(writer, "num_values", str);
05993       }
05994 
05995       size = key.total_size;
05996       data = (char *) malloc(size);
05997       if (data == NULL) {
05998          cm_msg(MERROR, "db_save_xml_key", "cannot allocate data buffer");
05999          return DB_NO_MEMORY;
06000       }
06001 
06002       db_get_data(hDB, hKey, data, &size, key.type);
06003 
06004       if (key.num_values == 1) {
06005       
06006          db_sprintf(str, data, key.item_size, 0, key.type);
06007          mxml_write_value(writer, str);
06008          mxml_end_element(writer);
06009       
06010       } else { /* array of values */
06011         
06012          for (i=0 ; i<key.num_values ; i++) {
06013 
06014             mxml_start_element(writer, "value");
06015             db_sprintf(str, data, key.item_size, i, key.type);
06016             mxml_write_value(writer, str);
06017             mxml_end_element(writer);
06018          }
06019 
06020          mxml_end_element(writer); // keyarray
06021       }
06022 
06023       free(data);
06024    }
06025 
06026    return DB_SUCCESS;
06027 }
06028 
06029 /********************************************************************/
06030 /**
06031 Save a branch of a database to an .xml file
06032 
06033 This function is used by the ODBEdit command save to write the contents
06034 of the ODB into a XML file. Data of the whole ODB can
06035 be saved (hkey equal zero) or only a sub-tree.
06036 @param hDB          ODB handle obtained via cm_get_experiment_database().
06037 @param hKey Handle for key where search starts, zero for root.
06038 @param filename Filename of .XML file.
06039 @return DB_SUCCESS, DB_FILE_ERROR
06040 */
06041 INT db_save_xml(HNDLE hDB, HNDLE hKey, char *filename)
06042 {
06043 #ifdef LOCAL_ROUTINES
06044    {
06045    INT status;
06046    char str[256];
06047    MXML_WRITER *writer;
06048 
06049    /* open file */
06050    writer = mxml_open_file(filename);
06051    if (writer == NULL) {
06052       cm_msg(MERROR, "db_save_xml", "Cannot open file \"%s\"", filename);
06053       return DB_FILE_ERROR;
06054    }
06055 
06056    /* write XML header */
06057    mxml_start_element(writer, "odb");
06058    mxml_write_attribute(writer, "filename", filename);
06059    mxml_write_attribute(writer, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
06060 
06061    if (getenv("MIDASSYS"))
06062       strcpy(str, getenv("MIDASSYS"));
06063    else
06064       strcpy(str, "");
06065    strcat(str, DIR_SEPARATOR_STR);
06066    strcat(str, "odb.xsd");
06067    mxml_write_attribute(writer, "xsi:noNamespaceSchemaLocation", str);
06068 
06069    status = db_save_xml_key(hDB, hKey, 0, writer);
06070    
06071    mxml_end_element(writer); // "odb"
06072    mxml_close_file(writer);
06073    }
06074 #endif                          /* LOCAL_ROUTINES */
06075 
06076    return DB_SUCCESS;
06077 }
06078 
06079 /********************************************************************/
06080 /**
06081 Save a branch of a database to a C structure .H file
06082 @param hDB          ODB handle obtained via cm_get_experiment_database().
06083 @param hKey Handle for key where search starts, zero for root.
06084 @param file_name Filename of .ODB file.
06085 @param struct_name Name of structure. If struct_name == NULL,
06086                    the name of the key is used.
06087 @param append      If TRUE, append to end of existing file
06088 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_FILE_ERROR
06089 */
06090 INT db_save_struct(HNDLE hDB, HNDLE hKey, char *file_name, char *struct_name, BOOL append)
06091 {
06092    KEY key;
06093    char str[100], line[100];
06094    INT status, i, fh;
06095 
06096    /* open file */
06097    fh = open(file_name, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0644);
06098 
06099    if (fh == -1) {
06100       cm_msg(MERROR, "db_save_struct", "Cannot open file\"%s\"", file_name);
06101       return DB_FILE_ERROR;
06102    }
06103 
06104    status = db_get_key(hDB, hKey, &key);
06105    if (status != DB_SUCCESS) {
06106       cm_msg(MERROR, "db_save_struct", "cannot find key");
06107       return DB_INVALID_HANDLE;
06108    }
06109 
06110    sprintf(line, "typedef struct {\n");
06111    write(fh, line, strlen(line));
06112    db_save_tree_struct(hDB, hKey, fh, 0);
06113 
06114    if (struct_name && struct_name[0])
06115       strcpy(str, struct_name);
06116    else
06117       strcpy(str, key.name);
06118 
06119    name2c(str);
06120    for (i = 0; i < (int) strlen(str); i++)
06121       str[i] = (char) toupper(str[i]);
06122 
06123    sprintf(line, "} %s;\n\n", str);
06124    write(fh, line, strlen(line));
06125 
06126    close(fh);
06127 
06128    return DB_SUCCESS;
06129 }
06130 
06131 /**dox***************************************************************/
06132 #ifndef DOXYGEN_SHOULD_SKIP_THIS
06133 
06134 /*------------------------------------------------------------------*/
06135 INT db_save_string(HNDLE hDB, HNDLE hKey, char *file_name, char *string_name, BOOL append)
06136 /********************************************************************\
06137 
06138   Routine: db_save_string
06139 
06140   Purpose: Save a branch of a database as a string which can be used
06141            by db_create_record.
06142 
06143   Input:
06144     HNDLE hDB               Handle to the database
06145     HNDLE hKey              Handle of key to start, 0 for root
06146     int   fh                File handle to write to
06147     char  string_name       Name of string. If struct_name == NULL,
06148                             the name of the key is used.
06149 
06150   Output:
06151     none
06152 
06153   Function value:
06154     DB_SUCCESS              Successful completion
06155     DB_INVALID_HANDLE       Database handle is invalid
06156 
06157 \********************************************************************/
06158 {
06159    KEY key;
06160    char str[256], line[256];
06161    INT status, i, size, fh, buffer_size;
06162    char *buffer, *pc;
06163 
06164 
06165    /* open file */
06166    fh = open(file_name, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0644);
06167 
06168    if (fh == -1) {
06169       cm_msg(MERROR, "db_save_string", "Cannot open file\"%s\"", file_name);
06170       return DB_FILE_ERROR;
06171    }
06172 
06173    status = db_get_key(hDB, hKey, &key);
06174    if (status != DB_SUCCESS) {
06175       cm_msg(MERROR, "db_save_string", "cannot find key");
06176       return DB_INVALID_HANDLE;
06177    }
06178 
06179    if (string_name && string_name[0])
06180       strcpy(str, string_name);
06181    else
06182       strcpy(str, key.name);
06183 
06184    name2c(str);
06185    for (i = 0; i < (int) strlen(str); i++)
06186       str[i] = (char) toupper(str[i]);
06187 
06188    sprintf(line, "#define %s(_name) char *_name[] = {\\\n", str);
06189    write(fh, line, strlen(line));
06190 
06191    buffer_size = 10000;
06192    do {
06193       buffer = (char *) malloc(buffer_size);
06194       if (buffer == NULL) {
06195          cm_msg(MERROR, "db_save", "cannot allocate ODB dump buffer");
06196          break;
06197       }
06198 
06199       size = buffer_size;
06200       status = db_copy(hDB, hKey, buffer, &size, "");
06201       if (status != DB_TRUNCATED)
06202          break;
06203 
06204       /* increase buffer size if truncated */
06205       free(buffer);
06206       buffer_size *= 2;
06207    } while (1);
06208 
06209 
06210    pc = buffer;
06211 
06212    do {
06213       i = 0;
06214       line[i++] = '"';
06215       while (*pc != '\n' && *pc != 0) {
06216          if (*pc == '\"' || *pc == '\'')
06217             line[i++] = '\\';
06218          line[i++] = *pc++;
06219       }
06220       strcpy(&line[i], "\",\\\n");
06221       if (i > 0)
06222          write(fh, line, strlen(line));
06223 
06224       if (*pc == '\n')
06225          pc++;
06226 
06227    } while (*pc);
06228 
06229    sprintf(line, "NULL }\n\n");
06230    write(fh, line, strlen(line));
06231 
06232    close(fh);
06233    free(buffer);
06234 
06235    return DB_SUCCESS;
06236 }
06237 
06238 /**dox***************************************************************/
06239 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
06240 
06241 /********************************************************************/
06242 /**
06243 Convert a database value to a string according to its type.
06244 
06245 This function is a convenient way to convert a binary ODB value into a
06246 string depending on its type if is not known at compile time. If it is known, the
06247 normal sprintf() function can be used.
06248 \code
06249 ...
06250   for (j=0 ; j<key.num_values ; j++)
06251   {
06252     db_sprintf(pbuf, pdata, key.item_size, j, key.type);
06253     strcat(pbuf, "\n");
06254   }
06255   ...
06256 \endcode
06257 @param string output ASCII string of data.
06258 @param data Value data.
06259 @param data_size Size of single data element.
06260 @param index Index for array data.
06261 @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
06262 @return DB_SUCCESS
06263 */
06264 INT db_sprintf(char *string, void *data, INT data_size, INT index, DWORD type)
06265 {
06266    if (data_size == 0)
06267       sprintf(string, "<NULL>");
06268    else
06269       switch (type) {
06270       case TID_BYTE:
06271          sprintf(string, "%d", *(((BYTE *) data) + index));
06272          break;
06273       case TID_SBYTE:
06274          sprintf(string, "%d", *(((char *) data) + index));
06275          break;
06276       case TID_CHAR:
06277          sprintf(string, "%c", *(((char *) data) + index));
06278          break;
06279       case TID_WORD:
06280          sprintf(string, "%u", *(((WORD *) data) + index));
06281          break;
06282       case TID_SHORT:
06283          sprintf(string, "%d", *(((short *) data) + index));
06284          break;
06285       case TID_DWORD:
06286          sprintf(string, "%lu", *(((DWORD *) data) + index));
06287          break;
06288       case TID_INT:
06289          sprintf(string, "%d", *(((INT *) data) + index));
06290          break;
06291       case TID_BOOL:
06292          sprintf(string, "%c", *(((BOOL *) data) + index) ? 'y' : 'n');
06293          break;
06294       case TID_FLOAT:
06295          sprintf(string, "%g", *(((float *) data) + index));
06296          break;
06297       case TID_DOUBLE:
06298          sprintf(string, "%lg", *(((double *) data) + index));
06299          break;
06300       case TID_BITFIELD:
06301          /* TBD */
06302          break;
06303       case TID_STRING:
06304       case TID_LINK:
06305          strlcpy(string, ((char *) data) + data_size * index, MAX_STRING_LENGTH);
06306          break;
06307       default:
06308          sprintf(string, "<unknown>");
06309          break;
06310       }
06311 
06312    return DB_SUCCESS;
06313 }
06314 
06315 /**dox***************************************************************/
06316 #ifndef DOXYGEN_SHOULD_SKIP_THIS
06317 
06318 /*------------------------------------------------------------------*/
06319 INT db_sprintfh(char *string, void *data, INT data_size, INT index, DWORD type)
06320 /********************************************************************\
06321 
06322   Routine: db_sprintfh
06323 
06324   Purpose: Convert a database value to a string according to its type
06325            in hex format
06326 
06327   Input:
06328     void  *data             Value data
06329     INT   index             Index for array data
06330     INT   data_size         Size of single data element
06331     DWORD type              Valye type, one of TID_xxx
06332 
06333   Output:
06334     char  *string           ASCII string of data
06335 
06336   Function value:
06337     DB_SUCCESS              Successful completion
06338 
06339 \********************************************************************/
06340 {
06341    if (data_size == 0)
06342       sprintf(string, "<NULL>");
06343    else
06344       switch (type) {
06345       case TID_BYTE:
06346          sprintf(string, "0x%X", *(((BYTE *) data) + index));
06347          break;
06348       case TID_SBYTE:
06349          sprintf(string, "0x%X", *(((char *) data) + index));
06350          break;
06351       case TID_CHAR:
06352          sprintf(string, "%c", *(((char *) data) + index));
06353          break;
06354       case TID_WORD:
06355          sprintf(string, "0x%X", *(((WORD *) data) + index));
06356          break;
06357       case TID_SHORT:
06358          sprintf(string, "0x%hX", *(((short *) data) + index));
06359          break;
06360       case TID_DWORD:
06361          sprintf(string, "0x%lX", *(((DWORD *) data) + index));
06362          break;
06363       case TID_INT:
06364          sprintf(string, "0x%X", *(((INT *) data) + index));
06365          break;
06366       case TID_BOOL:
06367          sprintf(string, "%c", *(((BOOL *) data) + index) ? 'y' : 'n');
06368          break;
06369       case TID_FLOAT:
06370          sprintf(string, "%g", *(((float *) data) + index));
06371          break;
06372       case TID_DOUBLE:
06373          sprintf(string, "%lg", *(((double *) data) + index));
06374          break;
06375       case TID_BITFIELD:
06376          /* TBD */
06377          break;
06378       case TID_STRING:
06379       case TID_LINK:
06380          sprintf(string, "%s", ((char *) data) + data_size * index);
06381          break;
06382       default:
06383          sprintf(string, "<unknown>");
06384          break;
06385       }
06386 
06387    return DB_SUCCESS;
06388 }
06389 
06390 /*------------------------------------------------------------------*/
06391 INT db_sscanf(char *data_str, void *data, INT * data_size, INT i, DWORD tid)
06392 /********************************************************************\
06393 
06394   Routine: db_sscanf
06395 
06396   Purpose: Convert a string to a database value according to its type
06397 
06398   Input:
06399     char  *data_str         ASCII string of data
06400     INT   i                 Index for array data
06401     DWORD tid               Value type, one of TID_xxx
06402 
06403   Output:
06404     void  *data             Value data
06405     INT   *data_size        Size of single data element
06406 
06407   Function value:
06408     DB_SUCCESS              Successful completion
06409 
06410 \********************************************************************/
06411 {
06412    DWORD value;
06413    BOOL hex = FALSE;
06414 
06415    if (data_str == NULL)
06416       return 0;
06417 
06418    *data_size = rpc_tid_size(tid);
06419    if (strncmp(data_str, "0x", 2) == 0) {
06420       hex = TRUE;
06421       sscanf(data_str + 2, "%lx", &value);
06422    }
06423 
06424    switch (tid) {
06425    case TID_BYTE:
06426    case TID_SBYTE:
06427       if (hex)
06428          *((char *) data + i) = (char) value;
06429       else
06430          *((char *) data + i) = (char) atoi(data_str);
06431       break;
06432    case TID_CHAR:
06433       *((char *) data + i) = data_str[0];
06434       break;
06435    case TID_WORD:
06436       if (hex)
06437          *((WORD *) data + i) = (WORD) value;
06438       else
06439          *((WORD *) data + i) = (WORD) atoi(data_str);
06440       break;
06441    case TID_SHORT:
06442       if (hex)
06443          *((short int *) data + i) = (short int) value;
06444       else
06445          *((short int *) data + i) = (short int) atoi(data_str);
06446       break;
06447    case TID_DWORD:
06448       if (!hex)
06449          sscanf(data_str, "%lu", &value);
06450 
06451       *((DWORD *) data + i) = value;
06452       break;
06453    case TID_INT:
06454       if (hex)
06455          *((INT *) data + i) = value;
06456       else
06457          *((INT *) data + i) = atol(data_str);
06458       break;
06459    case TID_BOOL:
06460       if (data_str[0] == 'y' || data_str[0] == 'Y' || atoi(data_str) > 0)
06461          *((BOOL *) data + i) = 1;
06462       else
06463          *((BOOL *) data + i) = 0;
06464       break;
06465    case TID_FLOAT:
06466       *((float *) data + i) = (float) atof(data_str);
06467       break;
06468    case TID_DOUBLE:
06469       *((double *) data + i) = atof(data_str);
06470       break;
06471    case TID_BITFIELD:
06472       /* TBD */
06473       break;
06474    case TID_STRING:
06475    case TID_LINK:
06476       strcpy((char *) data, data_str);
06477       *data_size = strlen(data_str) + 1;
06478       break;
06479    }
06480 
06481    return DB_SUCCESS;
06482 }
06483 
06484 /*------------------------------------------------------------------*/
06485 
06486 #ifdef LOCAL_ROUTINES
06487 
06488 static void db_recurse_record_tree(HNDLE hDB, HNDLE hKey, void **data,
06489                                    INT * total_size, INT base_align,
06490                                    INT * max_align, BOOL bSet, INT convert_flags)
06491 /********************************************************************\
06492 
06493   Routine: db_recurse_record_tree
06494 
06495   Purpose: Recurse a database tree and calculate its size or copy
06496            data. Internal use only.
06497 
06498 \********************************************************************/
06499 {
06500    DATABASE_HEADER *pheader;
06501    KEYLIST *pkeylist;
06502    KEY *pkey;
06503    INT size, align, corr, total_size_tmp;
06504 
06505    /* get first subkey of hKey */
06506    pheader = _database[hDB - 1].database_header;
06507    pkey = (KEY *) ((char *) pheader + hKey);
06508 
06509    pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
06510    if (!pkeylist->first_key)
06511       return;
06512    pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
06513 
06514    /* first browse through this level */
06515    do {
06516       if (pkey->type != TID_KEY) {
06517          /* correct for alignment */
06518          align = 1;
06519 
06520          if (rpc_tid_size(pkey->type))
06521             align = rpc_tid_size(pkey->type) < base_align ?
06522                 rpc_tid_size(pkey->type) : base_align;
06523 
06524          if (max_align && align > *max_align)
06525             *max_align = align;
06526 
06527          corr = VALIGN(*total_size, align) - *total_size;
06528          *total_size += corr;
06529          if (data)
06530             *data = (void *) ((char *) (*data) + corr);
06531 
06532          /* calculate data size */
06533          size = pkey->item_size * pkey->num_values;
06534 
06535          if (data) {
06536             if (bSet) {
06537                /* copy data if there is write access */
06538                if (pkey->access_mode & MODE_WRITE) {
06539                   memcpy((char *) pheader + pkey->data, *data,
06540                          pkey->item_size * pkey->num_values);
06541 
06542                   /* convert data */
06543                   if (convert_flags) {
06544                      if (pkey->num_values > 1)
06545                         rpc_convert_data((char *) pheader + pkey->data,
06546                                          pkey->type, RPC_FIXARRAY,
06547                                          pkey->item_size *
06548                                          pkey->num_values, convert_flags);
06549                      else
06550                         rpc_convert_single((char *) pheader + pkey->data,
06551                                            pkey->type, 0, convert_flags);
06552                   }
06553 
06554                   /* update time */
06555                   pkey->last_written = ss_time();
06556 
06557                   /* notify clients which have key open */
06558                   if (pkey->notify_count)
06559                      db_notify_clients(hDB, (PTYPE) pkey - (PTYPE) pheader, FALSE);
06560                }
06561             } else {
06562                /* copy key data if there is read access */
06563                if (pkey->access_mode & MODE_READ) {
06564                   memcpy(*data, (char *) pheader + pkey->data, pkey->total_size);
06565 
06566                   /* convert data */
06567                   if (convert_flags) {
06568                      if (pkey->num_values > 1)
06569                         rpc_convert_data(*data, pkey->type,
06570                                          RPC_FIXARRAY | RPC_OUTGOING,
06571                                          pkey->item_size *
06572                                          pkey->num_values, convert_flags);
06573                      else
06574                         rpc_convert_single(*data, pkey->type, RPC_OUTGOING,
06575                                            convert_flags);
06576                   }
06577                }
06578             }
06579 
06580             *data = (char *) (*data) + size;
06581          }
06582 
06583          *total_size += size;
06584       } else {
06585          /* align new substructure according to the maximum
06586             align value in this structure */
06587          align = 1;
06588 
06589          total_size_tmp = *total_size;
06590          db_recurse_record_tree(hDB, (PTYPE) pkey - (PTYPE) pheader,
06591                                 NULL, &total_size_tmp,
06592                                 base_align, &align, bSet, convert_flags);
06593 
06594          if (max_align && align > *max_align)
06595             *max_align = align;
06596 
06597          corr = VALIGN(*total_size, align) - *total_size;
06598          *total_size += corr;
06599          if (data)
06600             *data = (void *) ((char *) (*data) + corr);
06601 
06602          /* now copy subtree */
06603          db_recurse_record_tree(hDB, (PTYPE) pkey - (PTYPE) pheader,
06604                                 data, total_size, base_align, NULL, bSet, convert_flags);
06605 
06606          corr = VALIGN(*total_size, align) - *total_size;
06607          *total_size += corr;
06608          if (data)
06609             *data = (void *) ((char *) (*data) + corr);
06610 
06611          if (bSet && pkey->notify_count)
06612             db_notify_clients(hDB, (PTYPE) pkey - (PTYPE) pheader, FALSE);
06613       }
06614 
06615       if (!pkey->next_key)
06616          break;
06617 
06618       pkey = (KEY *) ((char *) pheader + pkey->next_key);
06619    } while (TRUE);
06620 }
06621 
06622 #endif                          /* LOCAL_ROUTINES */
06623 
06624 /**dox***************************************************************/
06625 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
06626 
06627 /********************************************************************/
06628 /**
06629 Calculates the size of a record.
06630 @param hDB          ODB handle obtained via cm_get_experiment_database().
06631 @param hKey Handle for key where search starts, zero for root.
06632 @param align Byte alignment calculated by the stub and
06633               passed to the rpc side to align data
06634               according to local machine. Must be zero
06635               when called from user level
06636 @param buf_size Size of record structure
06637 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TYPE_MISMATCH,
06638 DB_STRUCT_SIZE_MISMATCH, DB_NO_KEY
06639 */
06640 INT db_get_record_size(HNDLE hDB, HNDLE hKey, INT align, INT * buf_size)
06641 {
06642    if (rpc_is_remote()) {
06643       align = ss_get_struct_align();
06644       return rpc_call(RPC_DB_GET_RECORD_SIZE, hDB, hKey, align, buf_size);
06645    }
06646 #ifdef LOCAL_ROUTINES
06647    {
06648       KEY key;
06649       INT status, max_align;
06650 
06651       if (!align)
06652          align = ss_get_struct_align();
06653 
06654       /* check if key has subkeys */
06655       status = db_get_key(hDB, hKey, &key);
06656       if (status != DB_SUCCESS)
06657          return status;
06658 
06659       if (key.type != TID_KEY) {
06660          /* just a single key */
06661          *buf_size = key.item_size * key.num_values;
06662          return DB_SUCCESS;
06663       }
06664 
06665       db_lock_database(hDB);
06666 
06667       /* determine record size */
06668       *buf_size = max_align = 0;
06669       db_recurse_record_tree(hDB, hKey, NULL, buf_size, align, &max_align, 0, 0);
06670 
06671       /* correct for byte padding */
06672       *buf_size = VALIGN(*buf_size, max_align);
06673 
06674       db_unlock_database(hDB);
06675    }
06676 #endif                          /* LOCAL_ROUTINES */
06677 
06678    return DB_SUCCESS;
06679 }
06680 
06681 /********************************************************************/
06682 /**
06683 Copy a set of keys to local memory.
06684 
06685 An ODB sub-tree can be mapped to a C structure automatically via a
06686 hot-link using the function db_open_record() or manually with this function.
06687 Problems might occur if the ODB sub-tree contains values which don't match the
06688 C structure. Although the structure size is checked against the sub-tree size, no
06689 checking can be done if the type and order of the values in the structure are the
06690 same than those in the ODB sub-tree. Therefore it is recommended to use the
06691 function db_create_record() before db_get_record() is used which
06692 ensures that both are equivalent.
06693 \code
06694 struct {
06695   INT level1;
06696   INT level2;
06697 } trigger_settings;
06698 char *trigger_settings_str =
06699 "[Settings]\n\
06700 level1 = INT : 0\n\
06701 level2 = INT : 0";
06702 
06703 main()
06704 {
06705   HNDLE hDB, hkey;
06706   INT   size;
06707   ...
06708   cm_get_experiment_database(&hDB, NULL);
06709   db_create_record(hDB, 0, "/Equipment/Trigger", trigger_settings_str);
06710   db_find_key(hDB, 0, "/Equipment/Trigger/Settings", &hkey);
06711   size = sizeof(trigger_settings);
06712   db_get_record(hDB, hkey, &trigger_settings, &size, 0);
06713   ...
06714 }
06715 \endcode
06716 @param hDB          ODB handle obtained via cm_get_experiment_database().
06717 @param hKey         Handle for key where search starts, zero for root.
06718 @param data         Pointer to the retrieved data.
06719 @param buf_size     Size of data structure, must be obtained via sizeof(RECORD-NAME).
06720 @param align        Byte alignment calculated by the stub and
06721                     passed to the rpc side to align data
06722                     according to local machine. Must be zero
06723                     when called from user level.
06724 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_STRUCT_SIZE_MISMATCH
06725 */
06726 INT db_get_record(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, INT align)
06727 {
06728    if (rpc_is_remote()) {
06729       align = ss_get_struct_align();
06730       return rpc_call(RPC_DB_GET_RECORD, hDB, hKey, data, buf_size, align);
06731    }
06732 #ifdef LOCAL_ROUTINES
06733    {
06734       KEY key;
06735       INT convert_flags, status;
06736       INT total_size;
06737       void *pdata;
06738       char str[256];
06739 
06740       convert_flags = 0;
06741 
06742       if (!align)
06743          align = ss_get_struct_align();
06744       else
06745          /* only convert data if called remotely, as indicated by align != 0 */
06746       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
06747          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
06748 
06749       /* check if key has subkeys */
06750       status = db_get_key(hDB, hKey, &key);
06751       if (status != DB_SUCCESS)
06752          return status;
06753 
06754       if (key.type != TID_KEY) {
06755          /* copy single key */
06756          if (key.item_size * key.num_values != *buf_size) {
06757             cm_msg(MERROR, "db_get_record", "struct size mismatch for \"%s\"", key.name);
06758             return DB_STRUCT_SIZE_MISMATCH;
06759          }
06760 
06761          db_get_data(hDB, hKey, data, buf_size, key.type);
06762 
06763          if (convert_flags) {
06764             if (key.num_values > 1)
06765                rpc_convert_data(data, key.type,
06766                                 RPC_OUTGOING | RPC_FIXARRAY,
06767                                 key.item_size * key.num_values, convert_flags);
06768             else
06769                rpc_convert_single(data, key.type, RPC_OUTGOING, convert_flags);
06770          }
06771 
06772          return DB_SUCCESS;
06773       }
06774 
06775       /* check record size */
06776       db_get_record_size(hDB, hKey, 0, &total_size);
06777       if (total_size != *buf_size) {
06778          db_get_path(hDB, hKey, str, sizeof(str));
06779          cm_msg(MERROR, "db_get_record",
06780                 "struct size mismatch for \"%s\" (%d instead of %d)", str,
06781                 *buf_size, total_size);
06782          return DB_STRUCT_SIZE_MISMATCH;
06783       }
06784 
06785       /* get subkey data */
06786       pdata = data;
06787       total_size = 0;
06788 
06789       db_lock_database(hDB);
06790       db_recurse_record_tree(hDB, hKey, &pdata, &total_size, align,
06791                              NULL, FALSE, convert_flags);
06792       db_unlock_database(hDB);
06793 
06794    }
06795 #endif                          /* LOCAL_ROUTINES */
06796 
06797    return DB_SUCCESS;
06798 }
06799 
06800 /********************************************************************/
06801 /**
06802 Copy a set of keys from local memory to the database.
06803 
06804 An ODB sub-tree can be mapped to a C structure automatically via a
06805 hot-link using the function db_open_record() or manually with this function.
06806 Problems might occur if the ODB sub-tree contains values which don't match the
06807 C structure. Although the structure size is checked against the sub-tree size, no
06808 checking can be done if the type and order of the values in the structure are the
06809 same than those in the ODB sub-tree. Therefore it is recommended to use the
06810 function db_create_record() before using this function.
06811 \code
06812 ...
06813   memset(&lazyst,0,size);
06814   if (db_find_key(hDB, pLch->hKey, "Statistics",&hKeyst) == DB_SUCCESS)
06815     status = db_set_record(hDB, hKeyst, &lazyst, size, 0);
06816   else
06817     cm_msg(MERROR,"task","record %s/statistics not found", pLch->name)
06818 ...
06819 \endcode
06820 @param hDB          ODB handle obtained via cm_get_experiment_database().
06821 @param hKey Handle for key where search starts, zero for root.
06822 @param data Pointer where data is stored.
06823 @param buf_size Size of data structure, must be obtained via sizeof(RECORD-NAME).
06824 @param align  Byte alignment calculated by the stub and
06825               passed to the rpc side to align data
06826               according to local machine. Must be zero
06827               when called from user level.
06828 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TYPE_MISMATCH, DB_STRUCT_SIZE_MISMATCH
06829 */
06830 INT db_set_record(HNDLE hDB, HNDLE hKey, void *data, INT buf_size, INT align)
06831 {
06832    if (rpc_is_remote()) {
06833       align = ss_get_struct_align();
06834       return rpc_call(RPC_DB_SET_RECORD, hDB, hKey, data, buf_size, align);
06835    }
06836 #ifdef LOCAL_ROUTINES
06837    {
06838       KEY key;
06839       INT convert_flags;
06840       INT total_size;
06841       void *pdata;
06842 
06843       convert_flags = 0;
06844 
06845       if (!align)
06846          align = ss_get_struct_align();
06847       else
06848          /* only convert data if called remotely, as indicated by align != 0 */
06849       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
06850          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
06851 
06852       /* check if key has subkeys */
06853       db_get_key(hDB, hKey, &key);
06854       if (key.type != TID_KEY) {
06855          /* copy single key */
06856          if (key.item_size * key.num_values != buf_size) {
06857             cm_msg(MERROR, "db_set_record", "struct size mismatch for \"%s\"", key.name);
06858             return DB_STRUCT_SIZE_MISMATCH;
06859          }
06860 
06861          if (convert_flags) {
06862             if (key.num_values > 1)
06863                rpc_convert_data(data, key.type, RPC_FIXARRAY,
06864                                 key.item_size * key.num_values, convert_flags);
06865             else
06866                rpc_convert_single(data, key.type, 0, convert_flags);
06867          }
06868 
06869          db_set_data(hDB, hKey, data, key.total_size, key.num_values, key.type);
06870          return DB_SUCCESS;
06871       }
06872 
06873       /* check record size */
06874       db_get_record_size(hDB, hKey, 0, &total_size);
06875       if (total_size != buf_size) {
06876          cm_msg(MERROR, "db_set_record", "struct size mismatch for \"%s\"", key.name);
06877          return DB_STRUCT_SIZE_MISMATCH;
06878       }
06879 
06880       /* set subkey data */
06881       pdata = data;
06882       total_size = 0;
06883 
06884       db_lock_database(hDB);
06885       db_recurse_record_tree(hDB, hKey, &pdata, &total_size, align,
06886                              NULL, TRUE, convert_flags);
06887       db_notify_clients(hDB, hKey, TRUE);
06888       db_unlock_database(hDB);
06889    }
06890 #endif                          /* LOCAL_ROUTINES */
06891 
06892    return DB_SUCCESS;
06893 }
06894 
06895 /**dox***************************************************************/
06896 #ifndef DOXYGEN_SHOULD_SKIP_THIS
06897 
06898 /*------------------------------------------------------------------*/
06899 INT db_add_open_record(HNDLE hDB, HNDLE hKey, WORD access_mode)
06900 /********************************************************************\
06901 
06902   Routine: db_add_open_record
06903 
06904   Purpose: Server part of db_open_record. Internal use only.
06905 
06906 \********************************************************************/
06907 {
06908    if (rpc_is_remote())
06909       return rpc_call(RPC_DB_ADD_OPEN_RECORD, hDB, hKey, access_mode);
06910 
06911 #ifdef LOCAL_ROUTINES
06912    {
06913       DATABASE_HEADER *pheader;
06914       DATABASE_CLIENT *pclient;
06915       KEY *pkey;
06916       INT i;
06917 
06918       if (hDB > _database_entries || hDB <= 0) {
06919          cm_msg(MERROR, "db_add_open_record", "invalid database handle");
06920          return DB_INVALID_HANDLE;
06921       }
06922 
06923       /* lock database */
06924       db_lock_database(hDB);
06925 
06926       pheader = _database[hDB - 1].database_header;
06927       pclient = &pheader->client[_database[hDB - 1].client_index];
06928 
06929       /* check if key is already open */
06930       for (i = 0; i < pclient->max_index; i++)
06931          if (pclient->open_record[i].handle == hKey)
06932             break;
06933 
06934       if (i < pclient->max_index) {
06935          db_unlock_database(hDB);
06936          return DB_SUCCESS;
06937       }
06938 
06939       /* not found, search free entry */
06940       for (i = 0; i < pclient->max_index; i++)
06941          if (pclient->open_record[i].handle == 0)
06942             break;
06943 
06944       /* check if maximum number reached */
06945       if (i == MAX_OPEN_RECORDS) {
06946          db_unlock_database(hDB);
06947          return DB_NO_MEMORY;
06948       }
06949 
06950       if (i == pclient->max_index)
06951          pclient->max_index++;
06952 
06953       pclient->open_record[i].handle = hKey;
06954       pclient->open_record[i].access_mode = access_mode;
06955 
06956       /* increment notify_count */
06957       pkey = (KEY *) ((char *) pheader + hKey);
06958 
06959       /* check if hKey argument is correct */
06960       if (!db_validate_hkey(pheader, hKey)) {
06961          db_unlock_database(hDB);
06962          return DB_INVALID_HANDLE;
06963       }
06964 
06965       pkey->notify_count++;
06966 
06967       pclient->num_open_records++;
06968 
06969       /* set exclusive bit if requested */
06970       if (access_mode & MODE_WRITE)
06971          db_set_mode(hDB, hKey, (WORD) (pkey->access_mode | MODE_EXCLUSIVE), 2);
06972 
06973       db_unlock_database(hDB);
06974    }
06975 #endif                          /* LOCAL_ROUTINES */
06976 
06977    return DB_SUCCESS;
06978 }
06979 
06980 /*------------------------------------------------------------------*/
06981 INT db_remove_open_record(HNDLE hDB, HNDLE hKey, BOOL lock)
06982 /********************************************************************\
06983 
06984   Routine: db_remove_open_record
06985 
06986   Purpose: Gets called by db_close_record. Internal use only.
06987 
06988 \********************************************************************/
06989 {
06990    if (rpc_is_remote())
06991       return rpc_call(RPC_DB_REMOVE_OPEN_RECORD, hDB, hKey);
06992 
06993 #ifdef LOCAL_ROUTINES
06994    {
06995       DATABASE_HEADER *pheader;
06996       DATABASE_CLIENT *pclient;
06997       KEY *pkey;
06998       INT i, index;
06999 
07000       if (hDB > _database_entries || hDB <= 0) {
07001          cm_msg(MERROR, "db_remove_open_record", "invalid database handle");
07002          return DB_INVALID_HANDLE;
07003       }
07004 
07005       if (lock)
07006          db_lock_database(hDB);
07007 
07008       pheader = _database[hDB - 1].database_header;
07009       pclient = &pheader->client[_database[hDB - 1].client_index];
07010 
07011       /* search key */
07012       for (index = 0; index < pclient->max_index; index++)
07013          if (pclient->open_record[index].handle == hKey)
07014             break;
07015 
07016       if (index == pclient->max_index) {
07017          if (lock)
07018             db_unlock_database(hDB);
07019 
07020          return DB_INVALID_HANDLE;
07021       }
07022 
07023       /* decrement notify_count */
07024       pkey = (KEY *) ((char *) pheader + hKey);
07025 
07026       if (pkey->notify_count > 0)
07027          pkey->notify_count--;
07028 
07029       pclient->num_open_records--;
07030 
07031       /* remove exclusive flag */
07032       if (pclient->open_record[index].access_mode & MODE_WRITE)
07033          db_set_mode(hDB, hKey, (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2);
07034 
07035       memset(&pclient->open_record[index], 0, sizeof(OPEN_RECORD));
07036 
07037       /* calculate new max_index entry */
07038       for (i = pclient->max_index - 1; i >= 0; i--)
07039          if (pclient->open_record[i].handle != 0)
07040             break;
07041       pclient->max_index = i + 1;
07042 
07043       if (lock)
07044          db_unlock_database(hDB);
07045    }
07046 #endif                          /* LOCAL_ROUTINES */
07047 
07048    return DB_SUCCESS;
07049 }
07050 
07051 /*------------------------------------------------------------------*/
07052 
07053 #ifdef LOCAL_ROUTINES
07054 
07055 INT db_notify_clients(HNDLE hDB, HNDLE hKey, BOOL bWalk)
07056 /********************************************************************\
07057 
07058   Routine: db_notify_clients
07059 
07060   Purpose: Gets called by db_set_xxx functions. Internal use only.
07061 
07062 \********************************************************************/
07063 {
07064    DATABASE_HEADER *pheader;
07065    DATABASE_CLIENT *pclient;
07066    KEY *pkey;
07067    KEYLIST *pkeylist;
07068    INT i, j;
07069    char str[80];
07070 
07071    if (hDB > _database_entries || hDB <= 0) {
07072       cm_msg(MERROR, "db_notify_clients", "invalid database handle");
07073       return DB_INVALID_HANDLE;
07074    }
07075 
07076    pheader = _database[hDB - 1].database_header;
07077 
07078    /* check if key or parent has notify_flag set */
07079    pkey = (KEY *) ((char *) pheader + hKey);
07080 
07081    do {
07082 
07083       /* check which client has record open */
07084       if (pkey->notify_count)
07085          for (i = 0; i < pheader->max_client_index; i++) {
07086             pclient = &pheader->client[i];
07087             for (j = 0; j < pclient->max_index; j++)
07088                if (pclient->open_record[j].handle == hKey) {
07089                   /* send notification to remote process */
07090                   sprintf(str, "O %d %d", hDB, hKey);
07091                   ss_resume(pclient->port, str);
07092                }
07093          }
07094 
07095       if (pkey->parent_keylist == 0 || !bWalk)
07096          return DB_SUCCESS;
07097 
07098       pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
07099       pkey = (KEY *) ((char *) pheader + pkeylist->parent);
07100       hKey = (PTYPE) pkey - (PTYPE) pheader;
07101    } while (TRUE);
07102 
07103 }
07104 
07105 #endif                          /* LOCAL_ROUTINES */
07106 
07107 /*------------------------------------------------------------------*/
07108 void merge_records(HNDLE hDB, HNDLE hKey, KEY * pkey, INT level, void *info)
07109 {
07110    char full_name[256], buffer[10000];
07111    INT status, size;
07112    HNDLE hKeyInit;
07113    KEY initkey, key;
07114 
07115    /* compose name of init key */
07116    db_get_path(hDB, hKey, full_name, sizeof(full_name));
07117    *strchr(full_name, 'O') = 'I';
07118 
07119    /* if key in init record found, copy original data to init data */
07120    status = db_find_key(hDB, 0, full_name, &hKeyInit);
07121    if (status == DB_SUCCESS) {
07122       status = db_get_key(hDB, hKeyInit, &initkey);
07123       assert(status == DB_SUCCESS);
07124       status = db_get_key(hDB, hKey, &key);
07125       assert(status == DB_SUCCESS);
07126 
07127       if (initkey.type != TID_KEY && initkey.type == key.type) {
07128          /* copy data from original key to new key */
07129          size = sizeof(buffer);
07130          status = db_get_data(hDB, hKey, buffer, &size, initkey.type);
07131          assert(status == DB_SUCCESS);
07132          status =
07133              db_set_data(hDB, hKeyInit, buffer, initkey.total_size,
07134                          initkey.num_values, initkey.type);
07135          assert(status == DB_SUCCESS);
07136       }
07137    } else if (status == DB_NO_KEY) {
07138       /* do nothing */
07139    } else if (status == DB_INVALID_LINK) {
07140       status = db_find_link(hDB, 0, full_name, &hKeyInit);
07141       if (status == DB_SUCCESS) {
07142          size = sizeof(full_name);
07143          db_get_data(hDB, hKeyInit, full_name, &size, TID_LINK);
07144       }
07145       cm_msg(MERROR, "merge_records", "Invalid link \"%s\"", full_name);
07146    } else {
07147       cm_msg(MERROR, "merge_records",
07148              "aborting on unexpected failure of db_find_key(%s), status %d",
07149              full_name, status);
07150       abort();
07151    }
07152 }
07153 
07154 static int open_count;
07155 
07156 void check_open_keys(HNDLE hDB, HNDLE hKey, KEY * pkey, INT level, void *info)
07157 {
07158    if (pkey->notify_count)
07159       open_count++;
07160 }
07161 
07162 /**dox***************************************************************/
07163 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07164 
07165 /********************************************************************/
07166 /**
07167 Create a record. If a part of the record exists alreay,
07168 merge it with the init_str (use values from the init_str only when they are
07169 not in the existing record).
07170 
07171 This functions creates a ODB sub-tree according to an ASCII
07172 representation of that tree. See db_copy() for a description. It can be used to
07173 create a sub-tree which exactly matches a C structure. The sub-tree can then
07174 later mapped to the C structure ("hot-link") via the function db_open_record().
07175 
07176 If a sub-tree exists already which exactly matches the ASCII representation, it is
07177 not modified. If part of the tree exists, it is merged with the ASCII representation
07178 where the ODB values have priority, only values not present in the ODB are
07179 created with the default values of the ASCII representation. It is therefore
07180 recommended that before creating an ODB hot-link the function
07181 db_create_record() is called to insure that the ODB tree and the C structure
07182 contain exactly the same values in the same order.
07183 
07184 Following example creates a record under /Equipment/Trigger/Settings,
07185 opens a hot-link between that record and a local C structure
07186 trigger_settings and registers a callback function trigger_update()
07187 which gets called each time the record is changed.
07188 \code
07189 struct {
07190   INT level1;
07191   INT level2;
07192 } trigger_settings;
07193 char *trigger_settings_str =
07194 "[Settings]\n\
07195 level1 = INT : 0\n\
07196 level2 = INT : 0";
07197 void trigger_update(INT hDB, INT hkey, void *info)
07198 {
07199   printf("New levels: %d %d\n",
07200     trigger_settings.level1,
07201     trigger_settings.level2);
07202 }
07203 main()
07204 {
07205   HNDLE hDB, hkey;
07206   char[128] info;
07207   ...
07208   cm_get_experiment_database(&hDB, NULL);
07209   db_create_record(hDB, 0, "/Equipment/Trigger", trigger_settings_str);
07210   db_find_key(hDB, 0,"/Equipment/Trigger/Settings", &hkey);
07211   db_open_record(hDB, hkey, &trigger_settings,
07212     sizeof(trigger_settings), MODE_READ, trigger_update, info);
07213   ...
07214 }
07215 \endcode
07216 @param hDB          ODB handle obtained via cm_get_experiment_database().
07217 @param hKey         Handle for key where search starts, zero for root.
07218 @param orig_key_name     Name of key to search, can contain directories.
07219 @param init_str     Initialization string in the format of the db_copy/db_save functions.
07220 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_FULL, DB_NO_ACCESS, DB_OPEN_RECORD
07221 */
07222 INT db_create_record(HNDLE hDB, HNDLE hKey, char *orig_key_name, char *init_str)
07223 {
07224    char str[256], key_name[256], *buffer;
07225    INT status, size, i, buffer_size;
07226    HNDLE hKeyTmp, hKeyTmpO, hKeyOrig, hSubkey;
07227 
07228    if (rpc_is_remote())
07229       return rpc_call(RPC_DB_CREATE_RECORD, hDB, hKey, orig_key_name, init_str);
07230 
07231    /* make this function atomic */
07232    db_lock_database(hDB);
07233 
07234    /* strip trailing '/' */
07235    strlcpy(key_name, orig_key_name, sizeof(key_name));
07236    if (strlen(key_name) > 1 && key_name[strlen(key_name) - 1] == '/')
07237       key_name[strlen(key_name) - 1] = 0;
07238 
07239    /* merge temporay record and original record */
07240    status = db_find_key(hDB, hKey, key_name, &hKeyOrig);
07241    if (status == DB_SUCCESS) {
07242       assert(hKeyOrig != 0);
07243       /* check if key or subkey is opened */
07244       open_count = 0;
07245       db_scan_tree_link(hDB, hKeyOrig, 0, check_open_keys, NULL);
07246       if (open_count) {
07247          db_unlock_database(hDB);
07248          return DB_OPEN_RECORD;
07249       }
07250 
07251       /* create temporary records */
07252       sprintf(str, "/System/Tmp/%1dI", ss_gettid());
07253       db_find_key(hDB, 0, str, &hKeyTmp);
07254       if (hKeyTmp)
07255          db_delete_key(hDB, hKeyTmp, FALSE);
07256       db_create_key(hDB, 0, str, TID_KEY);
07257       status = db_find_key(hDB, 0, str, &hKeyTmp);
07258       if (status != DB_SUCCESS) {
07259          db_unlock_database(hDB);
07260          return status;
07261       }
07262 
07263       sprintf(str, "/System/Tmp/%1dO", ss_gettid());
07264       db_find_key(hDB, 0, str, &hKeyTmpO);
07265       if (hKeyTmpO)
07266          db_delete_key(hDB, hKeyTmpO, FALSE);
07267       db_create_key(hDB, 0, str, TID_KEY);
07268       status = db_find_key(hDB, 0, str, &hKeyTmpO);
07269       if (status != DB_SUCCESS) {
07270          db_unlock_database(hDB);
07271          return status;
07272       }
07273 
07274       status = db_paste(hDB, hKeyTmp, init_str);
07275       if (status != DB_SUCCESS) {
07276          db_unlock_database(hDB);
07277          return status;
07278       }
07279 
07280       buffer_size = 10000;
07281       buffer = (char *) malloc(buffer_size);
07282       do {
07283          size = buffer_size;
07284          status = db_copy(hDB, hKeyOrig, buffer, &size, "");
07285          if (status == DB_TRUNCATED) {
07286             buffer_size += 10000;
07287             buffer = (char *) realloc(buffer, buffer_size);
07288             continue;
07289          }
07290          if (status != DB_SUCCESS) {
07291             db_unlock_database(hDB);
07292             return status;
07293          }
07294 
07295       } while (status != DB_SUCCESS);
07296 
07297       status = db_paste(hDB, hKeyTmpO, buffer);
07298       if (status != DB_SUCCESS) {
07299          free(buffer);
07300          db_unlock_database(hDB);
07301          return status;
07302       }
07303 
07304       /* merge temporay record and original record */
07305       db_scan_tree_link(hDB, hKeyTmpO, 0, merge_records, NULL);
07306 
07307       /* delete original record */
07308       for (i = 0;; i++) {
07309          db_enum_link(hDB, hKeyOrig, 0, &hSubkey);
07310          if (!hSubkey)
07311             break;
07312 
07313          status = db_delete_key(hDB, hSubkey, FALSE);
07314          if (status != DB_SUCCESS) {
07315             free(buffer);
07316             db_unlock_database(hDB);
07317             return status;
07318          }
07319       }
07320 
07321       /* copy merged record to original record */
07322       do {
07323          size = buffer_size;
07324          status = db_copy(hDB, hKeyTmp, buffer, &size, "");
07325          if (status == DB_TRUNCATED) {
07326             buffer_size += 10000;
07327             buffer = (char *) realloc(buffer, buffer_size);
07328             continue;
07329          }
07330          if (status != DB_SUCCESS) {
07331             free(buffer);
07332             db_unlock_database(hDB);
07333             return status;
07334          }
07335 
07336       } while (status != DB_SUCCESS);
07337 
07338       status = db_paste(hDB, hKeyOrig, buffer);
07339       if (status != DB_SUCCESS) {
07340          free(buffer);
07341          db_unlock_database(hDB);
07342          return status;
07343       }
07344 
07345       /* delete temporary records */
07346       db_delete_key(hDB, hKeyTmp, FALSE);
07347       db_delete_key(hDB, hKeyTmpO, FALSE);
07348 
07349       free(buffer);
07350    } else if (status == DB_NO_KEY) {
07351       /* create fresh record */
07352       db_create_key(hDB, hKey, key_name, TID_KEY);
07353       status = db_find_key(hDB, hKey, key_name, &hKeyTmp);
07354       if (status != DB_SUCCESS) {
07355          db_unlock_database(hDB);
07356          return status;
07357       }
07358 
07359       status = db_paste(hDB, hKeyTmp, init_str);
07360       if (status != DB_SUCCESS) {
07361          db_unlock_database(hDB);
07362          return status;
07363       }
07364    } else {
07365       cm_msg(MERROR, "db_create_record",
07366              "aborting on unexpected failure of db_find_key(%s), status %d",
07367              key_name, status);
07368       abort();
07369    }
07370 
07371    db_unlock_database(hDB);
07372 
07373    return DB_SUCCESS;
07374 }
07375 
07376 /********************************************************************/
07377 /**
07378 This function ensures that a certain ODB subtree matches
07379 a given C structure, by comparing the init_str with the
07380 current ODB structure. If the record does not exist at all,
07381 it is created with the default values in init_str. If it
07382 does exist but does not match the variables in init_str,
07383 the function returns an error if correct=FALSE or calls
07384 db_create_record() if correct=TRUE.
07385 @param hDB          ODB handle obtained via cm_get_experiment_database().
07386 @param hKey     Handle for key where search starts, zero for root.
07387 @param keyname  Name of key to search, can contain directories.
07388 @param rec_str  ASCII representation of ODB record in the format
07389 @param correct  If TRUE, correct ODB record if necessary
07390 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_KEY, DB_STRUCT_MISMATCH
07391 */
07392 INT db_check_record(HNDLE hDB, HNDLE hKey, char *keyname, char *rec_str, BOOL correct)
07393 {
07394    char line[MAX_STRING_LENGTH];
07395    char title[MAX_STRING_LENGTH];
07396    char key_name[MAX_STRING_LENGTH];
07397    char info_str[MAX_STRING_LENGTH + 50];
07398    char *pc, *pold, *rec_str_orig;
07399    DWORD tid;
07400    INT i, j, n_data, string_length, status;
07401    HNDLE hKeyRoot, hKeyTest;
07402    KEY key;
07403    BOOL multi_line;
07404 
07405    if (rpc_is_remote())
07406       return rpc_call(RPC_DB_CHECK_RECORD, hDB, hKey, keyname, rec_str, correct);
07407 
07408    /* check if record exists */
07409    status = db_find_key(hDB, hKey, keyname, &hKeyRoot);
07410 
07411    /* create record if not */
07412    if (status == DB_NO_KEY)
07413       return db_create_record(hDB, hKey, keyname, rec_str);
07414 
07415    assert(hKeyRoot);
07416 
07417    title[0] = 0;
07418    multi_line = FALSE;
07419    rec_str_orig = rec_str;
07420 
07421    db_get_key(hDB, hKeyRoot, &key);
07422    if (key.type == TID_KEY)
07423       /* get next subkey which is not of type TID_KEY */
07424       db_get_next_link(hDB, hKeyRoot, &hKeyTest);
07425    else
07426       /* test root key itself */
07427       hKeyTest = hKeyRoot;
07428 
07429    if (hKeyTest == 0 && *rec_str != 0) {
07430       if (correct)
07431          return db_create_record(hDB, hKey, keyname, rec_str_orig);
07432 
07433       return DB_STRUCT_MISMATCH;
07434    }
07435 
07436    do {
07437       if (*rec_str == 0)
07438          break;
07439 
07440       for (i = 0; *rec_str != '\n' && *rec_str && i < MAX_STRING_LENGTH; i++)
07441          line[i] = *rec_str++;
07442 
07443       if (i == MAX_STRING_LENGTH) {
07444          cm_msg(MERROR, "db_check_record", "line too long");
07445          return DB_TRUNCATED;
07446       }
07447 
07448       line[i] = 0;
07449       if (*rec_str == '\n')
07450          rec_str++;
07451 
07452       /* check if it is a section title */
07453       if (line[0] == '[') {
07454          /* extract title and append '/' */
07455          strcpy(title, line + 1);
07456          if (strchr(title, ']'))
07457             *strchr(title, ']') = 0;
07458          if (title[0] && title[strlen(title) - 1] != '/')
07459             strcat(title, "/");
07460       } else {
07461          /* valid data line if it includes '=' and no ';' */
07462          if (strchr(line, '=') && line[0] != ';') {
07463             /* copy type info and data */
07464             pc = strchr(line, '=') + 1;
07465             while (*pc == ' ')
07466                pc++;
07467             strcpy(info_str, pc);
07468 
07469             /* extract key name */
07470             *strchr(line, '=') = 0;
07471 
07472             pc = &line[strlen(line) - 1];
07473             while (*pc == ' ')
07474                *pc-- = 0;
07475 
07476             key_name[0] = 0;
07477             if (title[0] != '.')
07478                strcpy(key_name, title);
07479 
07480             strcat(key_name, line);
07481 
07482             /* evaluate type info */
07483             strcpy(line, info_str);
07484             if (strchr(line, ' '))
07485                *strchr(line, ' ') = 0;
07486 
07487             n_data = 1;
07488             if (strchr(line, '[')) {
07489                n_data = atol(strchr(line, '[') + 1);
07490                *strchr(line, '[') = 0;
07491             }
07492 
07493             for (tid = 0; tid < TID_LAST; tid++)
07494                if (strcmp(tid_name[tid], line) == 0)
07495                   break;
07496 
07497             string_length = 0;
07498 
07499             if (tid == TID_LAST)
07500                cm_msg(MERROR, "db_check_record",
07501                       "found unknown data type \"%s\" in ODB file", line);
07502             else {
07503                /* skip type info */
07504                pc = info_str;
07505                while (*pc != ' ' && *pc)
07506                   pc++;
07507                while ((*pc == ' ' || *pc == ':') && *pc)
07508                   pc++;
07509 
07510                if (n_data > 1) {
07511                   info_str[0] = 0;
07512                   if (!*rec_str)
07513                      break;
07514 
07515                   for (j = 0; *rec_str != '\n' && *rec_str; j++)
07516                      info_str[j] = *rec_str++;
07517                   info_str[j] = 0;
07518                   if (*rec_str == '\n')
07519                      rec_str++;
07520                }
07521 
07522                for (i = 0; i < n_data; i++) {
07523                   /* strip trailing \n */
07524                   pc = &info_str[strlen(info_str) - 1];
07525                   while (*pc == '\n' || *pc == '\r')
07526                      *pc-- = 0;
07527 
07528                   if (tid == TID_STRING || tid == TID_LINK) {
07529                      if (!string_length) {
07530                         if (info_str[1] == '=')
07531                            string_length = -1;
07532                         else
07533                            string_length = atoi(info_str + 1);
07534                         if (string_length > MAX_STRING_LENGTH) {
07535                            string_length = MAX_STRING_LENGTH;
07536                            cm_msg(MERROR, "db_check_record",
07537                                   "found string exceeding MAX_STRING_LENGTH");
07538                         }
07539                      }
07540 
07541                      if (string_length == -1) {
07542                         /* multi-line string */
07543                         if (strstr(rec_str, "\n====#$@$#====\n") != NULL) {
07544                            string_length =
07545                                (PTYPE) strstr(rec_str,
07546                                               "\n====#$@$#====\n") - (PTYPE) rec_str + 1;
07547 
07548                            rec_str =
07549                                strstr(rec_str,
07550                                       "\n====#$@$#====\n") + strlen("\n====#$@$#====\n");
07551                         } else
07552                            cm_msg(MERROR, "db_check_record",
07553                                   "found multi-line string without termination sequence");
07554                      } else {
07555                         pc = info_str + 2;
07556                         while (*pc && *pc != ' ')
07557                            pc++;
07558                         while (*pc && *pc == ' ')
07559                            pc++;
07560 
07561                         /* limit string size */
07562                         *(pc + string_length - 1) = 0;
07563                      }
07564                   } else {
07565                      pc = info_str;
07566 
07567                      if (n_data > 1 && info_str[0] == '[') {
07568                         pc = strchr(info_str, ']') + 1;
07569                         while (*pc && *pc == ' ')
07570                            pc++;
07571                      }
07572                   }
07573 
07574                   if (i < n_data - 1) {
07575                      info_str[0] = 0;
07576                      if (!*rec_str)
07577                         break;
07578 
07579                      pold = rec_str;
07580 
07581                      for (j = 0; *rec_str != '\n' && *rec_str; j++)
07582                         info_str[j] = *rec_str++;
07583                      info_str[j] = 0;
07584                      if (*rec_str == '\n')
07585                         rec_str++;
07586 
07587                      /* test if valid data */
07588                      if (tid != TID_STRING && tid != TID_LINK) {
07589                         if (info_str[0] == 0 || (strchr(info_str, '=')
07590                                                  && strchr(info_str, ':')))
07591                            rec_str = pold;
07592                      }
07593                   }
07594                }
07595 
07596                /* if rec_str contains key, but not ODB, return mismatch */
07597                if (!hKeyTest) {
07598                   if (correct)
07599                      return db_create_record(hDB, hKey, keyname, rec_str_orig);
07600 
07601                   return DB_STRUCT_MISMATCH;
07602                }
07603 
07604                status = db_get_key(hDB, hKeyTest, &key);
07605                assert(status == DB_SUCCESS);
07606 
07607                /* check rec_str vs. ODB key */
07608                if (!equal_ustring(key.name, key_name) ||
07609                    key.type != tid || key.num_values != n_data) {
07610                   if (correct)
07611                      return db_create_record(hDB, hKey, keyname, rec_str_orig);
07612 
07613                   return DB_STRUCT_MISMATCH;
07614                }
07615 
07616                /* get next key in ODB */
07617                db_get_next_link(hDB, hKeyTest, &hKeyTest);
07618             }
07619          }
07620       }
07621    } while (TRUE);
07622 
07623    return DB_SUCCESS;
07624 }
07625 
07626 /********************************************************************/
07627 /**
07628 Open a record. Create a local copy and maintain an automatic update.
07629 
07630 This function opens a hot-link between an ODB sub-tree and a local
07631 structure. The sub-tree is copied to the structure automatically every time it is
07632 modified by someone else. Additionally, a callback function can be declared
07633 which is called after the structure has been updated. The callback function
07634 receives the database handle and the key handle as parameters.
07635 
07636 Problems might occur if the ODB sub-tree contains values which don't match the
07637 C structure. Although the structure size is checked against the sub-tree size, no
07638 checking can be done if the type and order of the values in the structure are the
07639 same than those in the ODB sub-tree. Therefore it is recommended to use the
07640 function db_create_record() before db_open_record() is used which
07641 ensures that both are equivalent.
07642 
07643 The access mode might either be MODE_READ or MODE_WRITE. In read mode,
07644 the ODB sub-tree is automatically copied to the local structure when modified by
07645 other clients. In write mode, the local structure is copied to the ODB sub-tree if it
07646 has been modified locally. This update has to be manually scheduled by calling
07647 db_send_changed_records() periodically in the main loop. The system
07648 keeps a copy of the local structure to determine if its contents has been changed.
07649 
07650 If MODE_ALLOC is or'ed with the access mode, the memory for the structure is
07651 allocated internally. The structure pointer must contain a pointer to a pointer to
07652 the structure. The internal memory is released when db_close_record() is
07653 called.
07654 - To open a record in write mode.
07655 \code
07656 struct {
07657   INT level1;
07658   INT level2;
07659 } trigger_settings;
07660 char *trigger_settings_str =
07661 "[Settings]\n\
07662 level1 = INT : 0\n\
07663 level2 = INT : 0";
07664 main()
07665 {
07666   HNDLE hDB, hkey, i=0;
07667   ...
07668   cm_get_experiment_database(&hDB, NULL);
07669   db_create_record(hDB, 0, "/Equipment/Trigger", trigger_settings_str);
07670   db_find_key(hDB, 0,"/Equipment/Trigger/Settings", &hkey);
07671   db_open_record(hDB, hkey, &trigger_settings, sizeof(trigger_settings)
07672                   , MODE_WRITE, NULL);
07673   do
07674   {
07675     trigger_settings.level1 = i++;
07676     db_send_changed_records()
07677     status = cm_yield(1000);
07678   } while (status != RPC_SHUTDOWN && status != SS_ABORT);
07679   ...
07680 }
07681 \endcode
07682 @param hDB          ODB handle obtained via cm_get_experiment_database().
07683 @param hKey         Handle for key where search starts, zero for root.
07684 @param ptr          If access_mode includes MODE_ALLOC:
07685                     Address of pointer which points to the record data after 
07686                     the call if access_mode includes not MODE_ALLOC:
07687                     Address of record if ptr==NULL, only the dispatcher is called.
07688 @param rec_size     Record size in bytes                    
07689 @param access_mode Mode for opening record, either MODE_READ or
07690                     MODE_WRITE. May be or'ed with MODE_ALLOC to
07691                     let db_open_record allocate the memory for the record.
07692 @param (*dispatcher)   Function which gets called when record is updated.The
07693                     argument list composed of: HNDLE hDB, HNDLE hKey, void *info
07694 @param info Additional info passed to the dispatcher.
07695 @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_MEMORY, DB_NO_ACCESS, DB_STRUCT_SIZE_MISMATCH
07696 */
07697 INT db_open_record(HNDLE hDB, HNDLE hKey, void *ptr, INT rec_size,
07698                    WORD access_mode, void (*dispatcher) (INT, INT, void *), void *info)
07699 {
07700    INT index, status, size;
07701    KEY key;
07702    void *data;
07703    char str[256];
07704 
07705    /* allocate new space for the local record list */
07706    if (_record_list_entries == 0) {
07707       _record_list = (RECORD_LIST *) malloc(sizeof(RECORD_LIST));
07708       memset(_record_list, 0, sizeof(RECORD_LIST));
07709       if (_record_list == NULL) {
07710          cm_msg(MERROR, "db_open_record", "not enough memory");
07711          return DB_NO_MEMORY;
07712       }
07713 
07714       _record_list_entries = 1;
07715       index = 0;
07716    } else {
07717       /* check for a deleted entry */
07718       for (index = 0; index < _record_list_entries; index++)
07719          if (!_record_list[index].handle)
07720             break;
07721 
07722       /* if not found, create new one */
07723       if (index == _record_list_entries) {
07724          _record_list = (RECORD_LIST *) realloc(_record_list,
07725                                                 sizeof(RECORD_LIST) *
07726                                                 (_record_list_entries + 1));
07727          if (_record_list == NULL) {
07728             cm_msg(MERROR, "db_open_record", "not enough memory");
07729             return DB_NO_MEMORY;
07730          }
07731 
07732          memset(&_record_list[_record_list_entries], 0, sizeof(RECORD_LIST));
07733 
07734          _record_list_entries++;
07735       }
07736    }
07737 
07738    db_get_key(hDB, hKey, &key);
07739 
07740    /* check record size */
07741    status = db_get_record_size(hDB, hKey, 0, &size);
07742    if (status != DB_SUCCESS) {
07743       _record_list_entries--;
07744       cm_msg(MERROR, "db_open_record", "cannot get record size");
07745       return DB_NO_MEMORY;
07746    }
07747    if (size != rec_size && ptr != NULL) {
07748       _record_list_entries--;
07749       db_get_path(hDB, hKey, str, sizeof(str));
07750       cm_msg(MERROR, "db_open_record",
07751              "struct size mismatch for \"%s\" (%d instead of %d)", str, rec_size, size);
07752       return DB_STRUCT_SIZE_MISMATCH;
07753    }
07754 
07755    /* check for read access */
07756    if (((key.access_mode & MODE_EXCLUSIVE) && (access_mode & MODE_WRITE))
07757        || (!(key.access_mode & MODE_WRITE) && (access_mode & MODE_WRITE))
07758        || (!(key.access_mode & MODE_READ) && (access_mode & MODE_READ))) {
07759       _record_list_entries--;
07760       return DB_NO_ACCESS;
07761    }
07762 
07763    if (access_mode & MODE_ALLOC) {
07764       data = malloc(size);
07765       memset(data, 0, size);
07766 
07767       if (data == NULL) {
07768          _record_list_entries--;
07769          cm_msg(MERROR, "db_open_record", "not enough memory");
07770          return DB_NO_MEMORY;
07771       }
07772 
07773       *((void **) ptr) = data;
07774    } else
07775       data = ptr;
07776 
07777    /* copy record to local memory */
07778    if (access_mode & MODE_READ && data != NULL) {
07779       status = db_get_record(hDB, hKey, data, &size, 0);
07780       if (status != DB_SUCCESS) {
07781          _record_list_entries--;
07782          cm_msg(MERROR, "db_open_record", "cannot get record");
07783          return DB_NO_MEMORY;
07784       }
07785    }
07786 
07787    /* copy local record to ODB */
07788    if (access_mode & MODE_WRITE) {
07789       /* only write to ODB if not in MODE_ALLOC */
07790       if ((access_mode & MODE_ALLOC) == 0) {
07791          status = db_set_record(hDB, hKey, data, size, 0);
07792          if (status != DB_SUCCESS) {
07793             _record_list_entries--;
07794             cm_msg(MERROR, "db_open_record", "cannot set record");
07795             return DB_NO_MEMORY;
07796          }
07797       }
07798 
07799       /* init a local copy of the record */
07800       _record_list[index].copy = malloc(size);
07801       if (_record_list[index].copy == NULL) {
07802          cm_msg(MERROR, "db_open_record", "not enough memory");
07803          return DB_NO_MEMORY;
07804       }
07805 
07806       memcpy(_record_list[index].copy, data, size);
07807    }
07808 
07809    /* initialize record list */
07810    _record_list[index].handle = hKey;
07811    _record_list[index].hDB = hDB;
07812    _record_list[index].access_mode = access_mode;
07813    _record_list[index].data = data;
07814    _record_list[index].buf_size = size;
07815    _record_list[index].dispatcher = dispatcher;
07816    _record_list[index].info = info;
07817 
07818    /* add record entry in database structure */
07819    db_add_open_record(hDB, hKey, (WORD) (access_mode & ~MODE_ALLOC));
07820 
07821    return DB_SUCCESS;
07822 }
07823 
07824 /********************************************************************/
07825 /**
07826 Close a record previously opend with db_open_record.
07827 @param hDB          ODB handle obtained via cm_get_experiment_database().
07828 @param hKey Handle for key where search starts, zero for root.
07829 @return DB_SUCCESS, DB_INVALID_HANDLE
07830 */
07831 INT db_close_record(HNDLE hDB, HNDLE hKey)
07832 {
07833 #ifdef LOCAL_ROUTINES
07834    {
07835       INT i;
07836 
07837       for (i = 0; i < _record_list_entries; i++)
07838          if (_record_list[i].handle == hKey && _record_list[i].hDB == hDB)
07839             break;
07840 
07841       if (i == _record_list_entries)
07842          return DB_INVALID_HANDLE;
07843 
07844       /* remove record entry from database structure */
07845       db_remove_open_record(hDB, hKey, TRUE);
07846 
07847       /* free local memory */
07848       if (_record_list[i].access_mode & MODE_ALLOC)
07849          free(_record_list[i].data);
07850 
07851       if (_record_list[i].access_mode & MODE_WRITE)
07852          free(_record_list[i].copy);
07853 
07854       memset(&_record_list[i], 0, sizeof(RECORD_LIST));
07855    }
07856 #endif                          /* LOCAL_ROUTINES */
07857 
07858    return DB_SUCCESS;
07859 }
07860 
07861 /********************************************************************/
07862 /**
07863 Release local memory for open records.
07864 This routines is called by db_close_all_databases() and 
07865 cm_disconnect_experiment()
07866 @return DB_SUCCESS, DB_INVALID_HANDLE
07867 */
07868 INT db_close_all_records()
07869 {
07870    INT i;
07871 
07872    for (i = 0; i < _record_list_entries; i++) {
07873       if (_record_list[i].handle) {
07874          if (_record_list[i].access_mode & MODE_WRITE)
07875             free(_record_list[i].copy);
07876 
07877          if (_record_list[i].access_mode & MODE_ALLOC)
07878             free(_record_list[i].data);
07879 
07880          memset(&_record_list[i], 0, sizeof(RECORD_LIST));
07881       }
07882    }
07883 
07884    if (_record_list_entries > 0) {
07885       _record_list_entries = 0;
07886       free(_record_list);
07887    }
07888 
07889    return DB_SUCCESS;
07890 }
07891 
07892 /********************************************************************/
07893 /**
07894 If called locally, update a record (hDB/hKey) and copy
07895 its new contents to the local copy of it.
07896 
07897 If called from a server, send a network notification to the client.
07898 @param hDB          ODB handle obtained via cm_get_experiment_database().
07899 @param hKey         Handle for key where search starts, zero for root.
07900 @param socket       optional server socket
07901 @return DB_SUCCESS, DB_INVALID_HANDLE
07902 */
07903 INT db_update_record(INT hDB, INT hKey, int socket)
07904 {
07905    INT i, size, convert_flags, status;
07906    char buffer[32];
07907    NET_COMMAND *nc;
07908 
07909    /* check if we are the server */
07910    if (socket) {
07911       convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
07912 
07913       if (convert_flags & CF_ASCII) {
07914          sprintf(buffer, "MSG_ODB&%d&%d", hDB, hKey);
07915          send_tcp(socket, buffer, strlen(buffer) + 1, 0);
07916       } else {
07917          nc = (NET_COMMAND *) buffer;
07918 
07919          nc->header.routine_id = MSG_ODB;
07920          nc->header.param_size = 2 * sizeof(INT);
07921          *((INT *) nc->param) = hDB;
07922          *((INT *) nc->param + 1) = hKey;
07923 
07924          if (convert_flags) {
07925             rpc_convert_single(&nc->header.routine_id, TID_DWORD,
07926                                RPC_OUTGOING, convert_flags);
07927             rpc_convert_single(&nc->header.param_size, TID_DWORD,
07928                                RPC_OUTGOING, convert_flags);
07929             rpc_convert_single(&nc->param[0], TID_DWORD, RPC_OUTGOING, convert_flags);
07930             rpc_convert_single(&nc->param[4], TID_DWORD, RPC_OUTGOING, convert_flags);
07931          }
07932 
07933          /* send the update notification to the client */
07934          send_tcp(socket, buffer, sizeof(NET_COMMAND_HEADER) + 2 * sizeof(INT), 0);
07935       }
07936 
07937       return DB_SUCCESS;
07938    }
07939 
07940    status = DB_INVALID_HANDLE;
07941 
07942    /* check all entries for matching key */
07943    for (i = 0; i < _record_list_entries; i++)
07944       if (_record_list[i].handle == hKey) {
07945          status = DB_SUCCESS;
07946 
07947          /* get updated data if record not opened in write mode */
07948          if ((_record_list[i].access_mode & MODE_WRITE) == 0) {
07949             size = _record_list[i].buf_size;
07950             if (_record_list[i].data != NULL)
07951                db_get_record(hDB, hKey, _record_list[i].data, &size, 0);
07952 
07953             /* call dispatcher if requested */
07954             if (_record_list[i].dispatcher)
07955                _record_list[i].dispatcher(hDB, hKey, _record_list[i].info);
07956          }
07957       }
07958 
07959    return DB_SUCCESS;
07960 }
07961 
07962 /********************************************************************/
07963 /**
07964 Send all records to the ODB which were changed locally since the last
07965 call to this function.
07966 
07967 This function is valid if used in conjunction with db_open_record()
07968 under the condition the record is open as MODE_WRITE access code.
07969 
07970 - Full example dbchange.c which can be compiled as follow
07971 \code
07972 gcc -DOS_LINUX -I/midas/include -o dbchange dbchange.c
07973 /midas/linux/lib/libmidas.a -lutil}
07974 
07975 \begin{verbatim}
07976 //------- dbchange.c
07977 #include "midas.h"
07978 #include "msystem.h"
07979 \endcode
07980 
07981 \code
07982 //-------- BOF dbchange.c
07983 typedef struct {
07984     INT    my_number;
07985     float   my_rate;
07986 } MY_STATISTICS;
07987 
07988 MY_STATISTICS myrec;
07989 
07990 #define MY_STATISTICS(_name) char *_name[] = {\
07991 "My Number = INT : 0",\
07992 "My Rate = FLOAT : 0",\
07993 "",\
07994 NULL }
07995 
07996 HNDLE hDB, hKey;
07997 
07998 // Main
07999 int main(unsigned int argc,char **argv)
08000 {
08001   char      host_name[HOST_NAME_LENGTH];
08002   char      expt_name[HOST_NAME_LENGTH];
08003   INT       lastnumber, status, msg;
08004   BOOL      debug=FALSE;
08005   char      i, ch;
08006   DWORD     update_time, mainlast_time;
08007   MY_STATISTICS (my_stat);
08008 
08009   // set default
08010   host_name[0] = 0;
08011   expt_name[0] = 0;
08012 
08013   // get default
08014   cm_get_environment(host_name, sizeof(host_name), expt_name, sizeof(expt_name));
08015 
08016   // get parameters
08017   for (i=1 ; i<argc ; i++)
08018   {
08019     if (argv[i][0] == '-' && argv[i][1] == 'd')
08020       debug = TRUE;
08021     else if (argv[i][0] == '-')
08022     {
08023       if (i+1 >= argc || argv[i+1][0] == '-')
08024         goto usage;
08025       if (strncmp(argv[i],"-e",2) == 0)
08026         strcpy(expt_name, argv[++i]);
08027       else if (strncmp(argv[i],"-h",2)==0)
08028         strcpy(host_name, argv[++i]);
08029     }
08030     else
08031     {
08032    usage:
08033       printf("usage: dbchange [-h <Hostname>] [-e <Experiment>]\n");
08034       return 0;
08035     }
08036   }
08037 
08038   // connect to experiment
08039   status = cm_connect_experiment(host_name, expt_name, "dbchange", 0);
08040   if (status != CM_SUCCESS)
08041     return 1;
08042 
08043   // Connect to DB
08044   cm_get_experiment_database(&hDB, &hKey);
08045 
08046   // Create a default structure in ODB
08047   db_create_record(hDB, 0, "My statistics", strcomb(my_stat));
08048 
08049   // Retrieve key for that strucutre in ODB
08050   if (db_find_key(hDB, 0, "My statistics", &hKey) != DB_SUCCESS)
08051   {
08052     cm_msg(MERROR, "mychange", "cannot find My statistics");
08053     goto error;
08054   }
08055 
08056   // Hot link this structure in Write mode
08057   status = db_open_record(hDB, hKey, &myrec
08058                           , sizeof(MY_STATISTICS), MODE_WRITE, NULL, NULL);
08059   if (status != DB_SUCCESS)
08060   {
08061     cm_msg(MERROR, "mychange", "cannot open My statistics record");
08062     goto error;
08063   }
08064 
08065   // initialize ss_getchar()
08066   ss_getchar(0);
08067 
08068   // Main loop
08069   do
08070   {
08071     // Update local structure
08072     if ((ss_millitime() - update_time) > 100)
08073     {
08074       myrec.my_number += 1;
08075       if (myrec.my_number - lastnumber) {
08076         myrec.my_rate = 1000.f * (float) (myrec.my_number - lastnumber)
08077           / (float) (ss_millitime() - update_time);
08078       }
08079       update_time = ss_millitime();
08080       lastnumber = myrec.my_number;
08081     }
08082 
08083     // Publish local structure to ODB (db_send_changed_record)
08084     if ((ss_millitime() - mainlast_time) > 5000)
08085     {
08086       db_send_changed_records();                   // <------- Call
08087       mainlast_time = ss_millitime();
08088     }
08089 
08090     // Check for keyboard interaction
08091     ch = 0;
08092     while (ss_kbhit())
08093     {
08094       ch = ss_getchar(0);
08095       if (ch == -1)
08096         ch = getchar();
08097       if ((char) ch == '!')
08098         break;
08099     }
08100     msg = cm_yield(20);
08101   } while (msg != RPC_SHUTDOWN && msg != SS_ABORT && ch != '!');
08102 
08103  error:
08104   cm_disconnect_experiment();
08105   return 1;
08106 }
08107 //-------- EOF dbchange.c
08108 \endcode
08109 @return DB_SUCCESS
08110 */
08111 INT db_send_changed_records()
08112 {
08113    INT i;
08114 
08115    for (i = 0; i < _record_list_entries; i++)
08116       if (_record_list[i].access_mode & MODE_WRITE) {
08117          if (memcmp
08118              (_record_list[i].copy, _record_list[i].data,
08119               _record_list[i].buf_size) != 0) {
08120             /* switch to fast TCP mode temporarily */
08121             if (rpc_is_remote())
08122                rpc_set_option(-1, RPC_OTRANSPORT, RPC_FTCP);
08123             db_set_record(_record_list[i].hDB,
08124                           _record_list[i].handle,
08125                           _record_list[i].data, _record_list[i].buf_size, 0);
08126             if (rpc_is_remote())
08127                rpc_set_option(-1, RPC_OTRANSPORT, RPC_TCP);
08128             memcpy(_record_list[i].copy, _record_list[i].data, _record_list[i].buf_size);
08129          }
08130       }
08131 
08132    return DB_SUCCESS;
08133 }
08134 
08135 /*------------------------------------------------------------------*/
08136 
08137 /**dox***************************************************************/
08138 /** @} */ /* end of odbfunctionc */
08139 
08140 /**dox***************************************************************/
08141 /** @} */ /* end of odbcode */

Midas DOC Version 1.9.5 ---- PSI Stefan Ritt ----
Contributions: Pierre-Andre Amaudruz - Suzannah Daviel - Doxygen - Peter Green - Qing Gu - Greg Hackman - Gertjan Hofman - Paul Knowles - Rudi Meier - Glenn Moloney - Dave Morris - John M O'Donnell - Konstantin Olchanski - Renee Poutissou - Andreas Suter - Jan M.Wouters - Piotr Adam Zolnierczuk