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

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