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

Midas DOC Version 1.9.3 ---- PSI Stefan Ritt ----
Contributions: Pierre-Andre Amaudruz - Suzannah Daviel - Doxygen - Peter Green - Greg Hackman - Gertjan Hofman - Paul Knowles - Rudi Meier - Glenn Moloney - Dave Morris - Konstantin Olchanski - Renee Poutissou - Andreas Suter - Piotr Adam Zolnierczuk