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

Midas DOC Version 2.0.2 ---- 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