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

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