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