db_open.c

Go to the documentation of this file.
00001 /*                       D B _ O P E N . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 1988-2006 United States Government as represented by
00005  * the U.S. Army Research Laboratory.
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public License
00009  * as published by the Free Software Foundation; either version 2 of
00010  * the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful, but
00013  * WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Library General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this file; see the file named COPYING for more
00019  * information.
00020  */
00021 
00022 /** @addtogroup dbio */
00023 
00024 /*@{*/
00025 /** @file db_open.c
00026  *
00027  * Functions -
00028  *      db_open         Open the database
00029  *      db_create       Create a new database
00030  *      db_close        Close a database, releasing dynamic memory
00031  *      db_clone_dbi    Clone a given database instance
00032  *
00033  *  Authors -
00034  *      Michael John Muuss
00035  *      Robert Jon Reschly Jr.
00036  *
00037  *  Source -
00038  *      SECAD/VLD Computing Consortium, Bldg 394
00039  *      The U. S. Army Ballistic Research Laboratory
00040  *      Aberdeen Proving Ground, Maryland  21005-5066
00041  *
00042  */
00043 
00044 #ifndef lint
00045 static const char RCSid[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/db_open.c,v 14.22 2006/09/16 02:04:24 lbutler Exp $ (BRL)";
00046 #endif
00047 
00048 #include "common.h"
00049 
00050 #include <stdio.h>
00051 #ifdef HAVE_UNISTD_H
00052 #  include <unistd.h>
00053 #endif
00054 #include <fcntl.h>
00055 #ifdef HAVE_STRING_H
00056 #  include <string.h>
00057 #else
00058 #  include <strings.h>
00059 #endif
00060 #ifdef HAVE_UNIX_IO
00061 # include <sys/types.h>
00062 # include <sys/stat.h>
00063 #endif
00064 
00065 #include "machine.h"
00066 #include "vmath.h"
00067 #include "raytrace.h"
00068 #include "db.h"
00069 #include "wdb.h"
00070 
00071 #include "./debug.h"
00072 
00073 #ifndef SEEK_SET
00074 # define SEEK_SET       0
00075 #endif
00076 
00077 #define DEFAULT_DB_TITLE "Untitled BRL-CAD Database"
00078 
00079 
00080 /**
00081  *                      D B _ O P E N
00082  *
00083  *  Open the named database.
00084  *  The 'mode' parameter specifies read-only or read-write mode.
00085  *
00086  *  As a convenience, dbi_filepath is a C-style argv array of dirs to search
00087  *  when attempting to open related files (such as data files for EBM
00088  *  solids or texture-maps).  The default values are "." and the
00089  *  directory containing the ".g" file.  They may be overriden by
00090  *  setting the environment variable BRLCAD_FILE_PATH.
00091  *
00092  *  Returns:
00093  *      DBI_NULL        error
00094  *      db_i *          success
00095  */
00096 struct db_i *
00097 db_open(const char *name,
00098         const char *mode)
00099 {
00100         register struct db_i    *dbip = DBI_NULL;
00101         register int            i;
00102         char **argv;
00103 
00104         if(RT_G_DEBUG&DEBUG_DB) bu_log("db_open(%s, %s)\n", name, mode );
00105 
00106         if( mode && mode[0] == 'r' && mode[1] == '\0' )  {
00107                 struct bu_mapped_file   *mfp;
00108                 /* Read-only mode */
00109                 mfp = bu_open_mapped_file( name, "db_i" );
00110                 if( mfp == NULL )  goto fail;
00111 
00112                 /* Is this a re-use of a previously mapped file? */
00113                 if( mfp->apbuf )  {
00114                         dbip = (struct db_i *)mfp->apbuf;
00115                         RT_CK_DBI(dbip);
00116                         dbip->dbi_uses++;
00117                         if(RT_G_DEBUG&DEBUG_DB)
00118                                 bu_log("db_open(%s) dbip=x%x: reused previously mapped file\n", name, dbip);
00119                         return dbip;
00120                 }
00121 
00122                 BU_GETSTRUCT( dbip, db_i );
00123                 dbip->dbi_mf = mfp;
00124                 dbip->dbi_eof = mfp->buflen;
00125                 dbip->dbi_inmem = mfp->buf;
00126                 dbip->dbi_mf->apbuf = (genptr_t)dbip;
00127 
00128 #ifdef HAVE_UNIX_IO
00129                 /* Do this too, so we can seek around on the file */
00130                 if( (dbip->dbi_fd = open( name, O_RDONLY )) < 0 )
00131                         goto fail;
00132                 if( (dbip->dbi_fp = fdopen( dbip->dbi_fd, "r" )) == NULL )
00133                         goto fail;
00134 #else /* HAVE_UNIX_IO */
00135 #if defined(_WIN32) && !defined(__CYGWIN__)
00136                 if( (dbip->dbi_fp = fopen( name, "rb")) == NULL )
00137                         goto fail;
00138 #else
00139                 if( (dbip->dbi_fp = fopen( name, "r")) == NULL )
00140                         goto fail;
00141 #endif
00142                 dbip->dbi_fd = -1;
00143 #endif
00144                 dbip->dbi_read_only = 1;
00145         }  else  {
00146                 /* Read-write mode */
00147                 BU_GETSTRUCT( dbip, db_i );
00148                 dbip->dbi_eof = -1L;
00149 
00150 #               ifdef HAVE_UNIX_IO
00151                         if( (dbip->dbi_fd = open( name, O_RDWR )) < 0 )
00152                                 goto fail;
00153                         if( (dbip->dbi_fp = fdopen( dbip->dbi_fd, "r+w" )) == NULL )
00154                                 goto fail;
00155 #               else /* HAVE_UNIX_IO */
00156 #if defined(_WIN32) && !defined(__CYGWIN__)
00157                         if( (dbip->dbi_fp = fopen( name, "r+b")) == NULL )
00158                                 goto fail;
00159 #else
00160                         if( (dbip->dbi_fp = fopen( name, "r+w")) == NULL )
00161                                 goto fail;
00162 #endif
00163                         dbip->dbi_fd = -1;
00164 #               endif
00165                 dbip->dbi_read_only = 0;
00166         }
00167 
00168         /* Initialize fields */
00169         for( i=0; i<RT_DBNHASH; i++ )
00170                 dbip->dbi_Head[i] = DIR_NULL;
00171 
00172         dbip->dbi_local2base = 1.0;             /* mm */
00173         dbip->dbi_base2local = 1.0;
00174         dbip->dbi_title = (char *)0;
00175         dbip->dbi_uses = 1;
00176 
00177         /* Record the filename and file path */
00178         dbip->dbi_filename = bu_strdup(name);
00179 
00180         /* XXX At some point, expand with getenv("BRLCAD_FILE_PATH"); */
00181         argv = (char **)bu_malloc( 3 * sizeof(char *), "dbi_filepath[3]" );
00182         argv[0] = bu_strdup( "." );
00183         argv[1] = bu_dirname( name );
00184         argv[2] = NULL;
00185         dbip->dbi_filepath = argv;
00186 
00187         /* determine version */
00188         dbip->dbi_version = db_get_version( dbip );
00189 
00190         bu_ptbl_init( &dbip->dbi_clients, 128, "dbi_clients[]" );
00191         dbip->dbi_magic = DBI_MAGIC;            /* Now it's valid */
00192 
00193         if(RT_G_DEBUG&DEBUG_DB)
00194                 bu_log("db_open(%s) dbip=x%x\n", dbip->dbi_filename, dbip);
00195         return dbip;
00196 fail:
00197         if(RT_G_DEBUG&DEBUG_DB)
00198                 bu_log("db_open(%s) FAILED\n", name);
00199         if(dbip) bu_free( (char *)dbip, "struct db_i" );
00200         return DBI_NULL;
00201 }
00202 
00203 /**
00204  *                      D B _ C R E A T E
00205  *
00206  *  Create a new database containing just a header record,
00207  *  regardless of whether the database previously existed or not,
00208  *  and open it for reading and writing.
00209  *
00210  *  New in BRL-CAD Release 6.0 is that this routine also calls
00211  *  db_dirbuild(), so the caller shouldn't.
00212  *
00213  *
00214  *  Returns:
00215  *      DBI_NULL        error
00216  *      db_i *          success
00217  */
00218 struct db_i *
00219 db_create(const char *name,
00220           int        version)
00221 {
00222         FILE    *fp;
00223         struct db_i     *dbip;
00224         int result;
00225 
00226         if (RT_G_DEBUG & DEBUG_DB)
00227             bu_log("db_create(%s, %d)\n", name, version );
00228 
00229 #if defined(_WIN32) && !defined(__CYGWIN__)
00230         fp = fopen( name, "w+b" );
00231 #else
00232         fp = fopen( name, "w" );
00233 #endif
00234         if (fp == NULL )  {
00235             perror(name);
00236             return(DBI_NULL);
00237         }
00238 
00239         if (version == 5) {
00240             result = db5_fwrite_ident(fp, DEFAULT_DB_TITLE, 1.0);
00241         } else if (version == 4) {
00242             result = db_fwrite_ident(fp, DEFAULT_DB_TITLE, 1.0);
00243         } else {
00244             bu_log("WARNING: db_create() was provided an unrecognized version number: %d\n", version);
00245             result = db5_fwrite_ident(fp, DEFAULT_DB_TITLE, 1.0);
00246         }
00247 
00248         (void)fclose(fp);
00249 
00250         if (result < 0)
00251             return DBI_NULL;
00252 
00253         if( (dbip = db_open( name, "r+w" ) ) == DBI_NULL )
00254             return DBI_NULL;
00255 
00256         /* Do a quick scan to determine version, find _GLOBAL, etc. */
00257         if( db_dirbuild( dbip ) < 0 )
00258             return DBI_NULL;
00259 
00260         return dbip;
00261 }
00262 
00263 /**
00264  *                      D B _ C L O S E _ C L I E N T
00265  *
00266  *  De-register a client of this database instance, if provided, and
00267  *  close out the instance.
00268  */
00269 void
00270 db_close_client(struct db_i *dbip, long int *client)
00271 {
00272         RT_CK_DBI(dbip);
00273         if (client) {
00274             (void)bu_ptbl_rm( &dbip->dbi_clients, client );
00275         }
00276         db_close(dbip);
00277 }
00278 
00279 /**
00280  *                      D B _ C L O S E
00281  *
00282  *  Close a database, releasing dynamic memory
00283  *  Wait until last user is done, though.
00284  */
00285 void
00286 db_close(register struct db_i *dbip)
00287 {
00288         register int            i;
00289         register struct directory *dp, *nextdp;
00290 
00291         RT_CK_DBI(dbip);
00292         if(RT_G_DEBUG&DEBUG_DB) bu_log("db_close(%s) x%x uses=%d\n",
00293                 dbip->dbi_filename, dbip, dbip->dbi_uses );
00294 
00295         bu_semaphore_acquire(BU_SEM_LISTS);
00296         if( (--dbip->dbi_uses) > 0 )  {
00297             bu_semaphore_release(BU_SEM_LISTS);
00298             /* others are still using this database */
00299             return;
00300         }
00301         bu_semaphore_release(BU_SEM_LISTS);
00302 
00303         /* ready to free the database -- use count is now zero */
00304 
00305         /* free up any mapped files */
00306         if( dbip->dbi_mf )  {
00307                 /*
00308                  *  We're using an instance of a memory mapped file.
00309                  *  We have two choices:
00310                  *  Either deassociate from the memory mapped file
00311                  *  by clearing dbi_mf->apbuf, or
00312                  *  keeping our already-scanned dbip ready for
00313                  *  further use, with our dbi_uses counter at 0.
00314                  *  For speed of re-open, at the price of some address space,
00315                  *  the second choice is taken.
00316                  */
00317                 bu_close_mapped_file( dbip->dbi_mf );
00318                 bu_free_mapped_files( 0 );
00319                 dbip->dbi_mf = (struct bu_mapped_file *)NULL;
00320         }
00321 
00322         /* try to ensure/encourage that the file is written out */
00323         db_sync(dbip);
00324 
00325 #ifdef HAVE_UNIX_IO
00326         (void)close( dbip->dbi_fd );
00327 #endif
00328         if (dbip->dbi_fp) {
00329             fclose( dbip->dbi_fp );
00330         }
00331         if( dbip->dbi_title )
00332                 bu_free( dbip->dbi_title, "dbi_title" );
00333         if( dbip->dbi_filename )
00334                 bu_free( dbip->dbi_filename, "dbi_filename" );
00335 
00336         db_free_anim( dbip );
00337         rt_color_free();                /* Free MaterHead list */
00338 
00339         /* Release map of database holes */
00340         rt_mempurge( &(dbip->dbi_freep) );
00341         rt_memclose();
00342 
00343         dbip->dbi_inmem = NULL;         /* sanity */
00344 
00345         bu_ptbl_free(&dbip->dbi_clients);
00346 
00347         /* Free all directory entries */
00348         for( i=0; i < RT_DBNHASH; i++ )  {
00349                 for( dp = dbip->dbi_Head[i]; dp != DIR_NULL; )  {
00350                         RT_CK_DIR(dp);
00351                         nextdp = dp->d_forw;
00352                         RT_DIR_FREE_NAMEP(dp);  /* frees d_namep */
00353 
00354                         /* Put 'dp' back on the freelist */
00355                         dp->d_forw = rt_uniresource.re_directory_hd;
00356                         rt_uniresource.re_directory_hd = dp;
00357 
00358                         /* null'ing the forward pointer here is a huge
00359                          * memory leak as it causes the loss of all
00360                          * nodes on the freelist except the first.
00361                          * (so don't do it)
00362                          */
00363                         /* dp->d_forw = NULL; */
00364 
00365                         dp = nextdp;
00366                 }
00367                 dbip->dbi_Head[i] = DIR_NULL;   /* sanity*/
00368         }
00369 
00370         if (dbip->dbi_filepath != NULL) {
00371           if (dbip->dbi_filepath[0] != NULL)
00372             bu_free((char *)dbip->dbi_filepath[0], "dbip->dbi_filepath");
00373           if (dbip->dbi_filepath[1] != NULL)
00374             bu_free((char *)dbip->dbi_filepath[1], "dbip->dbi_filepath");
00375           bu_free((char *)dbip->dbi_filepath, "dbip->dbi_filepath");
00376         }
00377 
00378         bu_free( (char *)dbip, "struct db_i" );
00379 }
00380 
00381 /**
00382  *                      D B _ D U M P
00383  *
00384  *  Dump a full copy of one database into another.
00385  *  This is a good way of committing a ".inmem" database to a ".g" file.
00386  *  The input is a database instance, the output is a LIBWDB object,
00387  *  which could be a disk file or another database instance.
00388  *
00389  *  Returns -
00390  *      -1      error
00391  *      0       success
00392  */
00393 int
00394 db_dump(struct rt_wdb *wdbp, struct db_i *dbip)
00395                                 /* output */
00396                                 /* input */
00397 {
00398         register int            i;
00399         register struct directory *dp;
00400         struct bu_external      ext;
00401 
00402         RT_CK_DBI(dbip);
00403         RT_CK_WDB(wdbp);
00404 
00405         /* Output all directory entries */
00406         for( i=0; i < RT_DBNHASH; i++ )  {
00407                 for( dp = dbip->dbi_Head[i]; dp != DIR_NULL; dp = dp->d_forw )  {
00408                         RT_CK_DIR(dp);
00409 /* XXX Need to go to internal form, if database versions don't match */
00410                         if( db_get_external( &ext, dp, dbip ) < 0 )  {
00411                                 bu_log("db_dump() read failed on %s, skipping\n", dp->d_namep );
00412                                 continue;
00413                         }
00414                         if( wdb_export_external( wdbp, &ext, dp->d_namep, dp->d_flags, dp->d_minor_type ) < 0 )  {
00415                                 bu_log("db_dump() write failed on %s, aborting\n", dp->d_namep);
00416                                 bu_free_external( &ext );
00417                                 return -1;
00418                         }
00419                         bu_free_external( &ext );
00420                 }
00421         }
00422         return 0;
00423 }
00424 
00425 /**
00426  *                      D B _ C L O N E _ D B I
00427  *
00428  *  Obtain an additional instance of this same database.  A new client
00429  *  is registered at the same time if one is specified.
00430  */
00431 struct db_i *
00432 db_clone_dbi(struct db_i *dbip, long int *client)
00433 {
00434         RT_CK_DBI(dbip);
00435 
00436         dbip->dbi_uses++;
00437         if (client) {
00438             bu_ptbl_ins_unique( &dbip->dbi_clients, client );
00439         }
00440         return dbip;
00441 }
00442 
00443 /**
00444  *                      D B _ S Y N C
00445  *
00446  *  Ensure that the on-disk database has been completely written
00447  *  out of the operating system's cache.
00448  */
00449 void
00450 db_sync(struct db_i *dbip)
00451 {
00452         RT_CK_DBI(dbip);
00453 
00454         bu_semaphore_acquire(BU_SEM_SYSCALL);
00455 
00456         /* flush the file */
00457         (void)fflush(dbip->dbi_fp);
00458 
00459 #ifdef HAVE_FSYNC
00460         /* make sure it's written out */
00461         (void)fsync(dbip->dbi_fd);
00462 #else
00463 #  ifndef _WIN32
00464         /* try the whole filesystem if sans fsync() */
00465         sync();
00466 #  endif /* _WIN32 */
00467 #endif
00468 
00469         bu_semaphore_release(BU_SEM_SYSCALL);
00470 }
00471 
00472 /*@}*/
00473 /*
00474  * Local Variables:
00475  * mode: C
00476  * tab-width: 8
00477  * c-basic-offset: 4
00478  * indent-tabs-mode: t
00479  * End:
00480  * ex: shiftwidth=4 tabstop=8
00481  */

Generated on Mon Sep 18 01:24:49 2006 for BRL-CAD by  doxygen 1.4.6