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