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

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