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

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