db5_io.c

Go to the documentation of this file.
00001 /*                        D B 5 _ I O . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 2004-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 db5 */
00023 
00024 /*@{*/
00025 /** @file db5_io.c
00026  *  Handle import/export and IO of v5 database objects.
00027  *
00028  *  Author -
00029  *      Michael John Muuss
00030  *
00031  *  Source -
00032  *      The U. S. Army Research Laboratory
00033  *      Aberdeen Proving Ground, Maryland  21005-5068  USA
00034  *
00035  */
00036 
00037 #ifndef lint
00038 static const char RCSid[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/db5_io.c,v 14.17 2006/09/16 02:04:24 lbutler Exp $ (ARL)";
00039 #endif
00040 
00041 #include "common.h"
00042 
00043 #include <stdio.h>
00044 #ifdef HAVE_STRING_H
00045 #  include <string.h>
00046 #else
00047 #  include <strings.h>
00048 #endif
00049 
00050 #include "machine.h"
00051 #include "bu.h"
00052 #include "vmath.h"
00053 #include "bn.h"
00054 #include "db5.h"
00055 #include "raytrace.h"
00056 
00057 #include "mater.h"
00058 
00059 #include "./debug.h"
00060 
00061 /* Number of bytes used for each value of DB5HDR_WIDTHCODE_* */
00062 const int db5_enc_len[4] = {
00063         1,
00064         2,
00065         4,
00066         8
00067 };
00068 
00069 /**
00070  *                      D B 5 _ H E A D E R _ I S _ V A L I D
00071  *
00072  *  Verify that this is a valid header for a BRL-CAD v5 database.
00073  *
00074  *  Returns -
00075  *      0       Not valid v5 header
00076  *      1       Valid v5 header
00077  */
00078 int
00079 db5_header_is_valid(const unsigned char *hp)
00080 {
00081         const struct db5_ondisk_header *odp = (const struct db5_ondisk_header *)hp;
00082 
00083         if( odp->db5h_magic1 != DB5HDR_MAGIC1 )  return 0;
00084         if( hp[7] != DB5HDR_MAGIC2 )  return 0;
00085 
00086         /* hflags */
00087         if( (odp->db5h_hflags & DB5HDR_HFLAGS_DLI_MASK) != DB5HDR_HFLAGS_DLI_HEADER_OBJECT )
00088                 return 0;
00089         if( (odp->db5h_hflags & DB5HDR_HFLAGS_NAME_PRESENT) )  return 0;
00090         if( ((odp->db5h_hflags & DB5HDR_HFLAGS_OBJECT_WIDTH_MASK) >> DB5HDR_HFLAGS_OBJECT_WIDTH_SHIFT)
00091             != DB5HDR_WIDTHCODE_8BIT )  return 0;
00092 
00093         /* aflags */
00094         if( (odp->db5h_aflags & DB5HDR_AFLAGS_ZZZ_MASK) != DB5_ZZZ_UNCOMPRESSED )  return 0;
00095         if( odp->db5h_aflags & DB5HDR_AFLAGS_PRESENT )  return 0;
00096         if( ((odp->db5h_aflags & DB5HDR_AFLAGS_WIDTH_MASK) >> DB5HDR_AFLAGS_WIDTH_SHIFT)
00097             != DB5HDR_WIDTHCODE_8BIT )  return 0;
00098 
00099         /* bflags */
00100         if( (odp->db5h_bflags & DB5HDR_BFLAGS_ZZZ_MASK) != DB5_ZZZ_UNCOMPRESSED )  return 0;
00101         if( odp->db5h_bflags & DB5HDR_BFLAGS_PRESENT )  return 0;
00102         if( ((odp->db5h_bflags & DB5HDR_BFLAGS_WIDTH_MASK) >> DB5HDR_BFLAGS_WIDTH_SHIFT)
00103             != DB5HDR_WIDTHCODE_8BIT )  return 0;
00104 
00105         /* major and minor type */
00106         if( odp->db5h_major_type != DB5_MAJORTYPE_RESERVED )  return 0;
00107         if( odp->db5h_minor_type != 0 )  return 0;
00108 
00109         /* Check length, known to be 8-bit.  Header len=1 8-byte chunk. */
00110         if( hp[6] != 1 )  return 0;
00111 
00112         return 1;               /* valid */
00113 }
00114 
00115 /**
00116  *                      D B 5 _ S E L E C T _ L E N G T H _ E N C O D I N G
00117  *
00118  *  Given a number to encode, decide which is the smallest encoding format
00119  *  which will contain it.
00120  */
00121 int
00122 db5_select_length_encoding(long int len)
00123 {
00124         if( len <= 255 )  return DB5HDR_WIDTHCODE_8BIT;
00125         if( len <= 65535 )  return DB5HDR_WIDTHCODE_16BIT;
00126         if( len < 0x7ffffffe )  return DB5HDR_WIDTHCODE_32BIT;
00127         return DB5HDR_WIDTHCODE_64BIT;
00128 }
00129 
00130 /**
00131  *                      D B 5 _ D E C O D E _ L E N G T H
00132  *
00133  *  Given a variable-width length field in network order (XDR),
00134  *  store it in *lenp.
00135  *
00136  *  This routine processes unsigned values.
00137  *
00138  *  Returns -
00139  *      The number of bytes of input that were decoded.
00140  */
00141 int
00142 db5_decode_length(long int *lenp, const unsigned char *cp, int format)
00143 {
00144         switch( format )  {
00145         case DB5HDR_WIDTHCODE_8BIT:
00146                 *lenp = (*cp);
00147                 return 1;
00148         case DB5HDR_WIDTHCODE_16BIT:
00149                 *lenp = BU_GSHORT(cp);
00150                 return 2;
00151         case DB5HDR_WIDTHCODE_32BIT:
00152                 *lenp = BU_GLONG(cp);
00153                 return 4;
00154         case DB5HDR_WIDTHCODE_64BIT:
00155 #if defined(IRIX64)
00156                 if( sizeof(long) >= 8 )  {
00157                         *lenp = BU_GLONGLONG(cp);
00158                         return 8;
00159                 }
00160 #endif
00161                 bu_bomb("db5_decode_length(): encountered 64-bit length on 32-bit machine\n");
00162         }
00163         bu_bomb("db5_decode_length(): unknown width code\n");
00164         return 0;
00165 }
00166 
00167 /**
00168  *                      D B 5 _ D E C O D E _ S I G N E D
00169  *
00170  *  Given a variable-width length field in network order (XDR),
00171  *  store it in *lenp.
00172  *
00173  *  This routine processes signed values.
00174  *
00175  *  Returns -
00176  *      The number of bytes of input that were decoded.
00177  */
00178 int
00179 db5_decode_signed(long int *lenp, const unsigned char *cp, int format)
00180 {
00181         switch( format )  {
00182         case DB5HDR_WIDTHCODE_8BIT:
00183                 if( (*lenp = (*cp)) & 0x80 ) *lenp |= (-1L ^ 0xFF);
00184                 return 1;
00185         case DB5HDR_WIDTHCODE_16BIT:
00186                 if( (*lenp = BU_GSHORT(cp)) & 0x8000 )  *lenp |= (-1L ^ 0xFFFF);
00187                 return 2;
00188         case DB5HDR_WIDTHCODE_32BIT:
00189                 if( (*lenp = BU_GLONG(cp)) & 0x80000000 )
00190                         *lenp |= (-1L ^ 0xFFFFFFFF);
00191                 return 4;
00192         case DB5HDR_WIDTHCODE_64BIT:
00193 #if defined(IRIX64)
00194                 if( sizeof(long) >= 8 )  {
00195                         *lenp = BU_GLONGLONG(cp);
00196                         return 8;
00197                 }
00198 #endif
00199                 bu_bomb("db5_decode_length(): encountered 64-bit length on 32-bit machine\n");
00200         }
00201         bu_bomb("db5_decode_length(): unknown width code\n");
00202         return 0;
00203 }
00204 
00205 /**
00206  *                      D B 5 _ E N C O D E _ L E N G T H
00207  *
00208  *  Given a value and a variable-width format spec,
00209  *  store it in network order (XDR).
00210  *
00211  *  Returns -
00212  *      pointer to next available byte.
00213  */
00214 unsigned char *
00215 db5_encode_length(
00216         unsigned char   *cp,
00217         long            val,
00218         int             format)
00219 {
00220         switch( format )  {
00221         case DB5HDR_WIDTHCODE_8BIT:
00222                 *cp = val & 0xFF;
00223                 return cp+1;
00224         case DB5HDR_WIDTHCODE_16BIT:
00225                 return bu_pshort( cp, (short)val );
00226         case DB5HDR_WIDTHCODE_32BIT:
00227                 return bu_plong( cp, val );
00228         case DB5HDR_WIDTHCODE_64BIT:
00229 #if defined(IRIX64)
00230 #endif
00231                 bu_bomb("db5_encode_length(): encountered 64-bit length\n");
00232         }
00233         bu_bomb("db5_encode_length(): unknown width code\n");
00234         return 0;
00235 }
00236 
00237 /**
00238  *                      D B 5 _ C R A C K _ D I S K _ H E A D E R
00239  *
00240  *  Returns -
00241  *      0 on success
00242  *      -1 on error
00243  */
00244 int
00245 db5_crack_disk_header(struct db5_raw_internal *rip, const unsigned char *cp)
00246 {
00247         if( cp[0] != DB5HDR_MAGIC1 )  {
00248                 bu_log("db5_crack_disk_header() bad magic1 -- database has become corrupted\n expected x%x, got x%x\n",
00249                         DB5HDR_MAGIC1, cp[0]);
00250                 if( cp[0] == 'I' ) {
00251                   bu_log ("Concatenation of different database versions detected.\n");
00252                   bu_log ("Run 'dbupgrade' on all databases before concatenation (cat command).\n");
00253                 }
00254                 return 0;
00255         }
00256 
00257         /* hflags */
00258         rip->h_dli = (cp[1] & DB5HDR_HFLAGS_DLI_MASK);
00259         rip->h_object_width = (cp[1] & DB5HDR_HFLAGS_OBJECT_WIDTH_MASK) >>
00260                 DB5HDR_HFLAGS_OBJECT_WIDTH_SHIFT;
00261         rip->h_name_present = (cp[1] & DB5HDR_HFLAGS_NAME_PRESENT);
00262         rip->h_name_hidden = (cp[1] & DB5HDR_HFLAGS_HIDDEN_OBJECT);
00263         rip->h_name_width = (cp[1] & DB5HDR_HFLAGS_NAME_WIDTH_MASK) >>
00264                 DB5HDR_HFLAGS_NAME_WIDTH_SHIFT;
00265 
00266         /* aflags */
00267         rip->a_width = (cp[2] & DB5HDR_AFLAGS_WIDTH_MASK) >>
00268                 DB5HDR_AFLAGS_WIDTH_SHIFT;
00269         rip->a_present = (cp[2] & DB5HDR_AFLAGS_PRESENT);
00270         rip->a_zzz = (cp[2] & DB5HDR_AFLAGS_ZZZ_MASK);
00271 
00272         /* bflags */
00273         rip->b_width = (cp[3] & DB5HDR_BFLAGS_WIDTH_MASK) >>
00274                 DB5HDR_BFLAGS_WIDTH_SHIFT;
00275         rip->b_present = (cp[3] & DB5HDR_BFLAGS_PRESENT);
00276         rip->b_zzz = (cp[3] & DB5HDR_BFLAGS_ZZZ_MASK);
00277 
00278         rip->major_type = cp[4];
00279         rip->minor_type = cp[5];
00280 
00281         if(RT_G_DEBUG&DEBUG_DB) bu_log("db5_crack_disk_header()\n\
00282         h_dli=%d, h_object_width=%d, h_name_present=%d, h_name_width=%d,\n\
00283         a_width=%d, a_present=%d, a_zzz=%d,\n\
00284         b_width=%d, b_present=%d, b_zzz=%d, major=%d, minor=%d\n",
00285                 rip->h_dli,
00286                 rip->h_object_width,
00287                 rip->h_name_present,
00288                 rip->h_name_width,
00289                 rip->a_width,
00290                 rip->a_present,
00291                 rip->a_zzz,
00292                 rip->b_width,
00293                 rip->b_present,
00294                 rip->b_zzz,
00295                 rip->major_type,
00296                 rip->minor_type );
00297 
00298         return 0;
00299 }
00300 
00301 /**
00302  *                      D B 5 _ G E T _ R A W _ I N T E R N A L _ P T R
00303  *
00304  *  Returns -
00305  *      on success, pointer to first unused byte
00306  *      NULL, on error
00307  */
00308 const unsigned char *
00309 db5_get_raw_internal_ptr( struct db5_raw_internal *rip, const unsigned char *ip)
00310 {
00311         const unsigned char     *cp = ip;
00312 
00313         if( db5_crack_disk_header( rip, cp ) < 0 )  return NULL;
00314         cp += sizeof(struct db5_ondisk_header);
00315 
00316         cp += db5_decode_length( &rip->object_length, cp, rip->h_object_width );
00317         rip->object_length <<= 3;       /* cvt 8-byte chunks to byte count */
00318 
00319         if( rip->object_length < sizeof(struct db5_ondisk_header) )  {
00320                 bu_log("db5_get_raw_internal_ptr(): object_length=%ld is too short, database is corrupted\n",
00321                         rip->object_length);
00322                 return NULL;
00323         }
00324 
00325         /* Verify trailing magic number */
00326         if( ip[rip->object_length-1] != DB5HDR_MAGIC2 )  {
00327                 bu_log("db5_get_raw_internal_ptr() bad magic2 -- database has become corrupted.\n expected x%x, got x%x\n",
00328                         DB5HDR_MAGIC2, ip[rip->object_length-1] );
00329                 return NULL;
00330         }
00331 
00332         BU_INIT_EXTERNAL( &rip->name );
00333         BU_INIT_EXTERNAL( &rip->body );
00334         BU_INIT_EXTERNAL( &rip->attributes );
00335 
00336         /* Grab name, if present */
00337         if( rip->h_name_present )  {
00338                 cp += db5_decode_length( &rip->name.ext_nbytes,
00339                         cp, rip->h_name_width );
00340                 rip->name.ext_buf = (genptr_t)cp;       /* discard const */
00341                 cp += rip->name.ext_nbytes;
00342         }
00343 
00344         /* Point to attributes, if present */
00345         if( rip->a_present )  {
00346                 cp += db5_decode_length( &rip->attributes.ext_nbytes,
00347                         cp, rip->a_width );
00348                 rip->attributes.ext_buf = (genptr_t)cp; /* discard const */
00349                 cp += rip->attributes.ext_nbytes;
00350         }
00351 
00352         /* Point to body, if present */
00353         if( rip->b_present )  {
00354                 cp += db5_decode_length( &rip->body.ext_nbytes,
00355                         cp, rip->b_width );
00356                 rip->body.ext_buf = (genptr_t)cp;       /* discard const */
00357                 cp += rip->body.ext_nbytes;
00358         }
00359 
00360         rip->buf = NULL;        /* no buffer needs freeing */
00361 
00362         return ip + rip->object_length;
00363 }
00364 
00365 /**
00366  *                      D B 5 _ G E T _ R A W _ I N T E R N A L _ F P
00367  *
00368  *  Returns -
00369  *      0 on success
00370  *      -1 on EOF
00371  *      -2 on error
00372  */
00373 int
00374 db5_get_raw_internal_fp(struct db5_raw_internal *rip, FILE *fp)
00375 {
00376         struct db5_ondisk_header        header;
00377         unsigned char                   lenbuf[8];
00378         int                             count = 0;
00379         int                             used;
00380         long                            want, got;
00381         unsigned char                   *cp;
00382 
00383         if( fread( (unsigned char *)&header, sizeof header, 1, fp ) != 1  )  {
00384                 if( feof(fp) )  return -1;
00385                 bu_log("db5_get_raw_internal_fp(): fread header error\n");
00386                 return -2;
00387         }
00388         if( db5_crack_disk_header( rip, (unsigned char *)&header ) < 0 )
00389                 return -2;
00390         used = sizeof(header);
00391 
00392         switch( rip->h_object_width )  {
00393         case DB5HDR_WIDTHCODE_8BIT:
00394                 count = 1;
00395                 break;
00396         case DB5HDR_WIDTHCODE_16BIT:
00397                 count = 2;
00398                 break;
00399         case DB5HDR_WIDTHCODE_32BIT:
00400                 count = 4;
00401                 break;
00402         case DB5HDR_WIDTHCODE_64BIT:
00403                 count = 8;
00404         }
00405         if( fread( lenbuf, count, 1, fp )  != 1 )  {
00406                 bu_log("db5_get_raw_internal_fp(): fread lenbuf error\n");
00407                 return -2;
00408         }
00409         used += db5_decode_length( &rip->object_length, lenbuf, rip->h_object_width );
00410         rip->object_length <<= 3;       /* cvt 8-byte chunks to byte count */
00411 
00412         if( rip->object_length < sizeof(struct db5_ondisk_header) )  {
00413                 bu_log("db5_get_raw_internal_fp(): object_length=%ld is too short, database is corrupted\n",
00414                         rip->object_length);
00415                 return -1;
00416         }
00417 
00418         /* Now that we finally know how large the object is, get it all */
00419         rip->buf = (unsigned char *)bu_malloc( rip->object_length, "raw v5 object" );
00420 
00421         *((struct db5_ondisk_header *)rip->buf) = header;       /* struct copy */
00422         bcopy( lenbuf, rip->buf+sizeof(header), count );
00423 
00424         cp = rip->buf+used;
00425         want = rip->object_length-used;
00426         BU_ASSERT_LONG( want, >, 0 );
00427         if( (got = fread( cp, 1, want, fp )) != want ) {
00428                 bu_log("db5_get_raw_internal_fp(), want=%ld, got=%ld, database is too short\n",
00429                         want, got );
00430                 return -2;
00431         }
00432 
00433         /* Verify trailing magic number */
00434         if( rip->buf[rip->object_length-1] != DB5HDR_MAGIC2 )  {
00435                 bu_log("db5_get_raw_internal_fp() bad magic2 -- database has become corrupted.\n expected x%x, got x%x\n",
00436                         DB5HDR_MAGIC2, rip->buf[rip->object_length-1] );
00437                 return -2;
00438         }
00439 
00440         BU_INIT_EXTERNAL( &rip->name );
00441         BU_INIT_EXTERNAL( &rip->body );
00442         BU_INIT_EXTERNAL( &rip->attributes );
00443 
00444         /* Grab name, if present */
00445         if( rip->h_name_present )  {
00446                 cp += db5_decode_length( &rip->name.ext_nbytes,
00447                         cp, rip->h_name_width );
00448                 rip->name.ext_buf = (genptr_t)cp;       /* discard const */
00449                 cp += rip->name.ext_nbytes;
00450         }
00451 
00452         /* Point to attributes, if present */
00453         if( rip->a_present )  {
00454                 cp += db5_decode_length( &rip->attributes.ext_nbytes,
00455                         cp, rip->a_width );
00456                 rip->attributes.ext_buf = (genptr_t)cp; /* discard const */
00457                 cp += rip->attributes.ext_nbytes;
00458         }
00459 
00460         /* Point to body, if present */
00461         if( rip->b_present )  {
00462                 cp += db5_decode_length( &rip->body.ext_nbytes,
00463                         cp, rip->b_width );
00464                 rip->body.ext_buf = (genptr_t)cp;       /* discard const */
00465                 cp += rip->body.ext_nbytes;
00466         }
00467 
00468         return 0;               /* success */
00469 }
00470 
00471 /**
00472  *                      D B 5 _ E X P O R T _ O B J E C T 3
00473  *
00474  *  A routine for merging together the three optional
00475  *  parts of an object into the final on-disk format.
00476  *  Results in extra data copies, but serves as a starting point for testing.
00477  *  Any of name, attrib, and body may be null.
00478  */
00479 void
00480 db5_export_object3(
00481         struct bu_external *out,
00482         int dli,
00483         const char *name,
00484         const unsigned char hidden,
00485         const struct bu_external *attrib,
00486         const struct bu_external *body,
00487         int major,
00488         int minor,
00489         int a_zzz,
00490         int b_zzz )
00491 {
00492         struct db5_ondisk_header *odp;
00493         register unsigned char  *cp;
00494         long    namelen = 0;
00495         long    need;
00496         int     h_width, n_width, a_width, b_width;
00497         long    togo;
00498 
00499         /*
00500          *  First, compute an upper bound on the size buffer needed.
00501          *  Over-estimate on the length fields just to keep it simple.
00502          */
00503         need = sizeof(struct db5_ondisk_header);
00504         need += 8;      /* for object_length */
00505         if( name )  {
00506                 namelen = strlen(name) + 1;     /* includes null */
00507                 if( namelen > 1 )  {
00508                         n_width = db5_select_length_encoding(namelen);
00509                         need += namelen + db5_enc_len[n_width];
00510                 } else {
00511                         name = NULL;
00512                         namelen = 0;
00513                         n_width = 0;
00514                 }
00515         } else {
00516                 n_width = 0;
00517         }
00518         if( attrib )  {
00519                 BU_CK_EXTERNAL(attrib);
00520                 if( attrib->ext_nbytes > 0 )  {
00521                         a_width = db5_select_length_encoding(attrib->ext_nbytes);
00522                         need += attrib->ext_nbytes + db5_enc_len[a_width];
00523                 } else {
00524                         attrib = NULL;
00525                         a_width = 0;
00526                 }
00527         } else {
00528                 a_width = 0;
00529         }
00530         if( body )  {
00531                 BU_CK_EXTERNAL(body);
00532                 if( body->ext_nbytes > 0 )  {
00533                         b_width = db5_select_length_encoding(body->ext_nbytes);
00534                         need += body->ext_nbytes + db5_enc_len[b_width];
00535                 } else {
00536                         body = NULL;
00537                         b_width = 0;
00538                 }
00539         } else {
00540                 b_width = 0;
00541         }
00542         need += 8;      /* pad and magic2 */
00543 
00544         /* Allocate the buffer for the combined external representation */
00545         out->ext_magic = BU_EXTERNAL_MAGIC;
00546         out->ext_buf = bu_malloc( need, "external object3" );
00547         out->ext_nbytes = need;         /* will be trimmed, below */
00548 
00549         /* Determine encoding for the header length field */
00550         h_width = db5_select_length_encoding( (need+7)>>3 );
00551 
00552         /* prepare combined external object */
00553         odp = (struct db5_ondisk_header *)out->ext_buf;
00554         odp->db5h_magic1 = DB5HDR_MAGIC1;
00555 
00556         /* hflags */
00557         odp->db5h_hflags = (h_width << DB5HDR_HFLAGS_OBJECT_WIDTH_SHIFT) |
00558                         (dli & DB5HDR_HFLAGS_DLI_MASK);
00559         if( name )  {
00560                 odp->db5h_hflags |= DB5HDR_HFLAGS_NAME_PRESENT |
00561                         (n_width << DB5HDR_HFLAGS_NAME_WIDTH_SHIFT);
00562 
00563         }
00564         if( hidden ) {
00565                 odp->db5h_hflags |= DB5HDR_HFLAGS_HIDDEN_OBJECT;
00566         }
00567 
00568         /* aflags */
00569         odp->db5h_aflags = a_width << DB5HDR_AFLAGS_WIDTH_SHIFT;
00570         if( attrib )  odp->db5h_aflags |= DB5HDR_AFLAGS_PRESENT;
00571         odp->db5h_aflags |= a_zzz & DB5HDR_AFLAGS_ZZZ_MASK;
00572 
00573         /* bflags */
00574         odp->db5h_bflags = b_width << DB5HDR_BFLAGS_WIDTH_SHIFT;
00575         if( body )  odp->db5h_bflags |= DB5HDR_BFLAGS_PRESENT;
00576         odp->db5h_bflags |= b_zzz & DB5HDR_BFLAGS_ZZZ_MASK;
00577 
00578         if( a_zzz || b_zzz )  bu_bomb("db5_export_object3: compression not supported yet\n");
00579 
00580         /* Object_Type */
00581         odp->db5h_major_type = major;
00582         odp->db5h_minor_type = minor;
00583 
00584         /* Build up the rest of the record */
00585         cp = ((unsigned char *)out->ext_buf) + sizeof(struct db5_ondisk_header);
00586         cp = db5_encode_length( cp, 7L, h_width );      /* will be replaced below */
00587 
00588         if( name )  {
00589                 cp = db5_encode_length( cp, namelen, n_width );
00590                 bcopy( name, cp, namelen );     /* includes null */
00591                 cp += namelen;
00592         }
00593 
00594         if( attrib )  {
00595                 /* minimum buffer length is a one byte attribute name, followed by a NULL name termination,
00596                  * followed by no bytes (for an empty value), followed by a NULL value termination,
00597                  * followed by a NULL attribute-value termination. Minimum is 4 bytes
00598                  */
00599 BU_ASSERT_PTR( attrib->ext_nbytes, >=, 4 );
00600                 cp = db5_encode_length( cp, attrib->ext_nbytes, a_width );
00601                 bcopy( attrib->ext_buf, cp, attrib->ext_nbytes );
00602                 cp += attrib->ext_nbytes;
00603         }
00604 
00605         if( body )  {
00606                 cp = db5_encode_length( cp, body->ext_nbytes, b_width );
00607                 bcopy( body->ext_buf, cp, body->ext_nbytes );
00608                 cp += body->ext_nbytes;
00609         }
00610 
00611         togo = cp - ((unsigned char *)out->ext_buf) + 1;
00612         togo &= 7;
00613         if( togo != 0 )  {
00614                 togo = 8 - togo;
00615                 while( togo-- > 0 )  *cp++ = '\0';
00616         }
00617         *cp++ = DB5HDR_MAGIC2;
00618 
00619         /* Verify multiple of 8 */
00620         togo = cp - ((unsigned char *)out->ext_buf);
00621         BU_ASSERT_LONG( togo&7, ==, 0 );
00622 
00623         /* Finally, go back to the header and write the actual object length */
00624         cp = ((unsigned char *)out->ext_buf) + sizeof(struct db5_ondisk_header);
00625         cp = db5_encode_length( cp, togo>>3, h_width );
00626 
00627         out->ext_nbytes = togo;
00628         BU_ASSERT_LONG( out->ext_nbytes, >=, 8 );
00629 }
00630 
00631 /**
00632  *                      D B 5 _ M A K E _ F R E E _ O B J E C T _ H D R
00633  *
00634  *  Make only the front (header) portion of a free object.
00635  *  This is used when operating on very large contiguous free objects
00636  *  in the database (e.g. 50 MBytes).
00637  */
00638 void
00639 db5_make_free_object_hdr( struct bu_external *ep, long length )
00640 {
00641         struct db5_ondisk_header *odp;
00642         int             h_width;
00643         unsigned char   *cp;
00644 
00645         BU_CK_EXTERNAL(ep);
00646 
00647         BU_ASSERT_LONG( length, >=, 8 );
00648         BU_ASSERT_LONG( length&7, ==, 0 );
00649 
00650         /* Reserve enough space to hold any free header, even w/64-bit len */
00651         ep->ext_nbytes = 8+8;
00652         ep->ext_buf = bu_calloc( 1, ep->ext_nbytes, "db5_make_free_object_hdr" );
00653 
00654         /* Determine encoding for the header length field */
00655         h_width = db5_select_length_encoding( length>>3 );
00656 
00657         /* prepare header of external object */
00658         odp = (struct db5_ondisk_header *)ep->ext_buf;
00659         odp->db5h_magic1 = DB5HDR_MAGIC1;
00660         odp->db5h_hflags = (h_width << DB5HDR_HFLAGS_OBJECT_WIDTH_SHIFT) |
00661                         DB5HDR_HFLAGS_DLI_FREE_STORAGE;
00662 
00663         cp = ((unsigned char *)ep->ext_buf) + sizeof(struct db5_ondisk_header);
00664         cp = db5_encode_length( cp, length>>3, h_width );
00665 }
00666 
00667 /**
00668  *                      D B 5 _ M A K E _ F R E E _ O B J E C T
00669  *
00670  *  Make a complete, zero-filled, free object.
00671  *  Note that free objects can sometimes get quite large.
00672  */
00673 void
00674 db5_make_free_object( struct bu_external *ep, long length )
00675 {
00676         struct db5_ondisk_header *odp;
00677         int             h_width;
00678         unsigned char   *cp;
00679 
00680         BU_CK_EXTERNAL(ep);
00681 
00682         BU_ASSERT_LONG( length, >=, 8 );
00683         BU_ASSERT_LONG( length&7, ==, 0 );
00684 
00685         ep->ext_buf = bu_calloc( 1, length, "db5_make_free_object" );
00686         ep->ext_nbytes = length;
00687 
00688         /* Determine encoding for the header length field */
00689         h_width = db5_select_length_encoding( length>>3 );
00690 
00691         /* prepare combined external object */
00692         odp = (struct db5_ondisk_header *)ep->ext_buf;
00693         odp->db5h_magic1 = DB5HDR_MAGIC1;
00694         odp->db5h_hflags = (h_width << DB5HDR_HFLAGS_OBJECT_WIDTH_SHIFT) |
00695                         DB5HDR_HFLAGS_DLI_FREE_STORAGE;
00696 
00697         cp = ((unsigned char *)ep->ext_buf) + sizeof(struct db5_ondisk_header);
00698         cp = db5_encode_length( cp, length>>3, h_width );
00699 
00700         cp = ((unsigned char *)ep->ext_buf) + length-1;
00701         *cp = DB5HDR_MAGIC2;
00702 }
00703 
00704 /**
00705  *                      D B 5 _ I M P O R T _ A T T R I B U T E S
00706  *
00707  *  Convert the on-disk encoding into a handy easy-to-use
00708  *  bu_attribute_value_set structure.
00709  *  Take advantage of the readonly_min/readonly_max capability
00710  *  so that we don't have to bu_strdup() each string, but can
00711  *  simply point to it in the provided buffer *ap.
00712  *  Important implication:  don't free *ap until you're done with this avs.
00713  *
00714  *  The upshot of this is that bu_avs_add() and bu_avs_remove() can
00715  *  be safely used with this *avs.
00716  *
00717  *  Returns -
00718  *      >0      count of attributes successfully imported
00719  *      -1      Error, mal-formed input
00720  */
00721 int
00722 db5_import_attributes( struct bu_attribute_value_set *avs, const struct bu_external *ap )
00723 {
00724         const char      *cp;
00725         const char      *ep;
00726         int             count = 0;
00727         struct bu_attribute_value_pair *app;
00728 
00729         BU_CK_EXTERNAL(ap);
00730 
00731         BU_ASSERT_LONG( ap->ext_nbytes, >=, 4 );
00732 
00733         /* First pass -- count number of attributes */
00734         cp = (const char *)ap->ext_buf;
00735         ep = (const char *)ap->ext_buf+ap->ext_nbytes;
00736 
00737         /* Null "name" string indicates end of attribute list */
00738         while( *cp != '\0' )  {
00739             const char *name = cp;
00740             if( cp >= ep )  {
00741                 bu_log("db5_import_attributes() ran off end of buffer, database is probably corrupted\n");
00742                 return -1;
00743             }
00744             cp += strlen(cp)+1; /* value */
00745             cp += strlen(cp)+1; /* next name */
00746             count++;
00747         }
00748         /* Ensure we're exactly at the end */
00749         BU_ASSERT_PTR( cp+1, ==, ep );
00750 
00751         /* not really needed for AVS_ADD since bu_avs_add will
00752          * incrementally allocate as it needs it. but one alloc is
00753          * better than many in case there are many attributes.
00754          */
00755         bu_avs_init( avs, count, "db5_import_attributes" );
00756 
00757         /* Second pass -- populate attributes.  Peek inside struct for non AVS_ADD. */
00758 
00759 /* Use AVS_ADD to copy the values from the external buffer instead of
00760  * using them directly without copying.  This presumes ap will not get
00761  * free'd before we're done with the avs.  Preference should be to
00762  * leave AVS_ADD undefined for performance reasons.
00763  */
00764 #define AVS_ADD 1
00765 
00766 #if AVS_ADD
00767         cp = (const char *)ap->ext_buf;
00768         while( *cp != '\0' )  {
00769             const char *name = cp;  /* name */
00770             cp += strlen(cp)+1; /* value */
00771             bu_avs_add( avs, name, cp );
00772             cp += strlen(cp)+1; /* next name */
00773         }
00774 #else
00775         /* Conserve malloc/free activity -- use strings in input buffer */
00776         /* Signal region of memory that input comes from */
00777         cp = (const char *)ap->ext_buf;
00778         app = avs->avp;
00779         while( *cp != '\0' )  {
00780             app->name = cp;  /* name */
00781             cp += strlen(cp)+1;
00782             app->value = cp;  /* value */
00783             cp += strlen(cp)+1;
00784             app++;
00785             avs->count++;
00786         }
00787 
00788         /* expand the readonly section if necessary */
00789         if ( (!avs->readonly_min) || ((genptr_t)avs->readonly_min > (genptr_t)ap->ext_buf) ) {
00790             avs->readonly_min = (genptr_t)ap->ext_buf;
00791         }
00792         if ( (!avs->readonly_max) || ((genptr_t)avs->readonly_max < (genptr_t)(avs->readonly_min + ap->ext_nbytes)) ) {
00793             avs->readonly_max = (genptr_t)avs->readonly_min + ap->ext_nbytes;
00794         }
00795 #endif
00796 
00797         BU_ASSERT_PTR( cp+1, ==, ep );
00798         BU_ASSERT_LONG( avs->count, <=, avs->max );
00799         BU_ASSERT_LONG( avs->count, ==, count );
00800 
00801         if(bu_debug & BU_DEBUG_AVS) {
00802             bu_avs_print(avs, "db5_import_attributes");
00803         }
00804         return avs->count;
00805 }
00806 
00807 /**
00808  *                      D B 5 _ E X P O R T _ A T T R I B U T E S
00809  *
00810  *  Encode the attribute-value pair information into the external
00811  *  on-disk format.
00812  *
00813  *  The on-disk encoding is:
00814  *
00815  *      aname1 NULL value1 NULL ... anameN NULL valueN NULL NULL
00816  *
00817  *  'ext' is initialized on behalf of the caller.
00818  */
00819 void
00820 db5_export_attributes( struct bu_external *ext, const struct bu_attribute_value_set *avs )
00821 {
00822         int     need = 0;
00823         const struct bu_attribute_value_pair    *avpp;
00824         char    *cp;
00825         int     i;
00826 
00827         BU_CK_AVS( avs );
00828         avpp = avs->avp;
00829 
00830         BU_INIT_EXTERNAL(ext);
00831         if( avs->count <= 0 )  return;
00832 if(bu_debug & BU_DEBUG_AVS)  bu_avs_print(avs, "db5_export_attributes");
00833 
00834         /* First pass -- determine how much space is required */
00835         for( i = 0; i < avs->count; i++, avpp++ )  {
00836                 need += strlen( avpp->name ) + strlen( avpp->value ) + 2;
00837         }
00838         if( need <= 0 )  return;
00839         need += 1;              /* for final null */
00840 
00841         ext->ext_nbytes = need;
00842         ext->ext_buf = bu_malloc( need, "external attributes" );
00843 
00844         /* Second pass -- store in external form */
00845         cp = (char *)ext->ext_buf;
00846         avpp = avs->avp;
00847         for( i = 0; i < avs->count; i++, avpp++ )  {
00848                 need = strlen( avpp->name ) + 1;
00849                 bcopy( avpp->name, cp, need );
00850                 cp += need;
00851 
00852                 need = strlen( avpp->value ) + 1;
00853                 bcopy( avpp->value, cp, need );
00854                 cp += need;
00855         }
00856         *cp++ = '\0';
00857         need = cp - ((char *)ext->ext_buf);
00858         BU_ASSERT_LONG( need, ==, ext->ext_nbytes );
00859 }
00860 
00861 
00862 
00863 /**
00864  *                      D B 5 _ R E P L A C E _ A T T R I B U T E S
00865  *
00866  *  Replace the attributes of a given database object.
00867  *  For efficiency, this is done without looking at the object body at all.
00868  *
00869  *  Contents of the bu_attribute_value_set are freed, but not the struct itself.
00870  *
00871  *  Returns -
00872  *      0 on success
00873  *      <0 on error
00874  */
00875 int
00876 db5_replace_attributes( struct directory *dp, struct bu_attribute_value_set *avsp, struct db_i *dbip )
00877 {
00878         struct bu_external      ext;
00879         struct db5_raw_internal raw;
00880         struct bu_external      attr;
00881         struct bu_external      ext2;
00882         int                     ret;
00883 
00884         RT_CK_DIR(dp);
00885         BU_CK_AVS(avsp);
00886         RT_CK_DBI(dbip);
00887 
00888         if(RT_G_DEBUG&DEBUG_DB)  {
00889                 bu_log("db5_replace_attributes(%s) dbip=x%x\n",
00890                         dp->d_namep, dbip );
00891                 bu_avs_print( avsp, "new attributes" );
00892         }
00893 
00894         if( dbip->dbi_read_only )  {
00895                 bu_log("db5_replace_attributes(%s):  READ-ONLY file\n",
00896                         dbip->dbi_filename);
00897                 return -1;
00898         }
00899 
00900         BU_ASSERT_LONG( dbip->dbi_version, ==, 5 );
00901 
00902         if( db_get_external( &ext, dp, dbip ) < 0 )
00903                 return -2;              /* FAIL */
00904 
00905         if (db5_get_raw_internal_ptr(&raw, ext.ext_buf) == NULL) {
00906                 bu_log("db5_replace_attributes(%s):  import failure\n",
00907                         dp->d_namep );
00908                 bu_free_external( &ext );
00909                 return -3;
00910         }
00911 
00912         db5_export_attributes( &attr, avsp );
00913         BU_INIT_EXTERNAL(&ext2);
00914         db5_export_object3( &ext2,
00915                 raw.h_dli,
00916                 dp->d_namep,
00917                 raw.h_name_hidden,
00918                 &attr,
00919                 &raw.body,
00920                 raw.major_type, raw.minor_type,
00921                 raw.a_zzz, raw.b_zzz );
00922 
00923         /* Write it */
00924         ret = db_put_external5( &ext2, dp, dbip );
00925         if( ret < 0 )  bu_log("db5_update_attributes(%s):  db_put_external5() failure\n",
00926                 dp->d_namep );
00927 
00928         bu_free_external( &attr );
00929         bu_free_external( &ext2 );
00930         bu_free_external( &ext );               /* 'raw' is now invalid */
00931         bu_avs_free( avsp );
00932 
00933         return ret;
00934 }
00935 
00936 /**
00937  *                      D B 5 _ U P D A T E _ A T T R I B U T E S
00938  *
00939  *  Update an arbitrary number of attributes on a given database object.
00940  *  For efficiency, this is done without looking at the object body at all.
00941  *
00942  *  Contents of the bu_attribute_value_set are freed, but not the struct itself.
00943  *
00944  *  Returns -
00945  *      0 on success
00946  *      <0 on error
00947  */
00948 int
00949 db5_update_attributes( struct directory *dp, struct bu_attribute_value_set *avsp, struct db_i *dbip )
00950 {
00951         struct bu_external      ext;
00952         struct db5_raw_internal raw;
00953         struct bu_attribute_value_set old_avs;
00954         struct bu_external      attr;
00955         struct bu_external      ext2;
00956         int                     ret;
00957 
00958         RT_CK_DIR(dp);
00959         BU_CK_AVS(avsp);
00960         RT_CK_DBI(dbip);
00961 
00962         if(RT_G_DEBUG&DEBUG_DB)  {
00963                 bu_log("db5_update_attributes(%s) dbip=x%x\n",
00964                         dp->d_namep, dbip );
00965                 bu_avs_print( avsp, "new attributes" );
00966         }
00967 
00968         if( dbip->dbi_read_only )  {
00969                 bu_log("db5_update_attributes(%s):  READ-ONLY file\n",
00970                         dbip->dbi_filename);
00971                 return -1;
00972         }
00973 
00974         BU_ASSERT_LONG( dbip->dbi_version, ==, 5 );
00975 
00976         if( db_get_external( &ext, dp, dbip ) < 0 )
00977                 return -2;              /* FAIL */
00978 
00979         if (db5_get_raw_internal_ptr(&raw, ext.ext_buf) == NULL) {
00980                 bu_log("db5_update_attributes(%s):  import failure\n",
00981                         dp->d_namep );
00982                 bu_free_external( &ext );
00983                 return -3;
00984         }
00985 
00986         bu_avs_init( &old_avs, 4, "db5_update_attributes" );
00987         if( raw.attributes.ext_buf )  {
00988                 if( db5_import_attributes( &old_avs, &raw.attributes ) < 0 )  {
00989                         bu_log("db5_update_attributes(%s):  mal-formed attributes in database\n",
00990                                 dp->d_namep );
00991                         bu_free_external( &ext );
00992                         return -8;
00993                 }
00994         }
00995 
00996         bu_avs_merge( &old_avs, avsp );
00997 
00998         db5_export_attributes( &attr, &old_avs );
00999         BU_INIT_EXTERNAL(&ext2);
01000         db5_export_object3( &ext2,
01001                 raw.h_dli,
01002                 dp->d_namep,
01003                 raw.h_name_hidden,
01004                 &attr,
01005                 &raw.body,
01006                 raw.major_type, raw.minor_type,
01007                 raw.a_zzz, raw.b_zzz );
01008 
01009         /* Write it */
01010         ret = db_put_external5( &ext2, dp, dbip );
01011         if( ret < 0 )  bu_log("db5_update_attributes(%s):  db_put_external5() failure\n",
01012                 dp->d_namep );
01013 
01014         bu_free_external( &attr );
01015         bu_free_external( &ext2 );
01016         bu_free_external( &ext );               /* 'raw' is now invalid */
01017         bu_avs_free( &old_avs );
01018         bu_avs_free( avsp );
01019 
01020         return ret;
01021 }
01022 
01023 /**
01024  *                      D B 5 _ U P D A T E _ A T T R I B U T E
01025  *
01026  *  A convenience routine to update the value of a single attribute.
01027  *
01028  *  Returns -
01029  *      0 on success
01030  *      <0 on error
01031  */
01032 int
01033 db5_update_attribute( const char *obj_name, const char *aname, const char *value, struct db_i *dbip )
01034 {
01035         struct directory        *dp;
01036         struct bu_attribute_value_set avs;
01037 
01038         RT_CK_DBI(dbip);
01039         if( (dp = db_lookup( dbip, obj_name, LOOKUP_NOISY )) == DIR_NULL )
01040                 return -1;
01041 
01042         bu_avs_init( &avs, 2, "db5_update_attribute" );
01043         bu_avs_add( &avs, aname, value );
01044 
01045         return db5_update_attributes( dp, &avs, dbip );
01046 }
01047 
01048 /**
01049  *                      D B 5 _ U P D A T E _ I D E N T
01050  *
01051  *  Update the _GLOBAL object, which in v5 serves the place of the
01052  *  "ident" header record in v4 as the place to stash global information.
01053  *  Since every database will have one of these things,
01054  *  it's no problem to update it.
01055  *
01056  * Returns -
01057  *       0      Success
01058  *      -1      Fatal Error
01059  */
01060 int db5_update_ident( struct db_i *dbip, const char *title, double local2mm )
01061 {
01062         struct bu_attribute_value_set   avs;
01063         struct directory                *dp;
01064         struct bu_vls                   units;
01065         int                             ret;
01066         char *old_title = NULL;
01067 
01068         RT_CK_DBI(dbip);
01069 
01070         if( (dp = db_lookup( dbip, DB5_GLOBAL_OBJECT_NAME, LOOKUP_QUIET )) == DIR_NULL )  {
01071                 struct bu_external      global;
01072                 unsigned char           minor_type=0;
01073 
01074                 bu_log("db5_update_ident() WARNING: %s object is missing, creating new one.\nYou may have lost important global state when you deleted this object.\n",
01075                         DB5_GLOBAL_OBJECT_NAME );
01076 
01077                 /* OK, make one.  It will be empty to start with, updated below. */
01078                 db5_export_object3( &global,
01079                         DB5HDR_HFLAGS_DLI_APPLICATION_DATA_OBJECT,
01080                         DB5_GLOBAL_OBJECT_NAME, DB5HDR_HFLAGS_HIDDEN_OBJECT, NULL, NULL,
01081                         DB5_MAJORTYPE_ATTRIBUTE_ONLY, 0,
01082                         DB5_ZZZ_UNCOMPRESSED, DB5_ZZZ_UNCOMPRESSED );
01083 
01084                 dp = db_diradd( dbip, DB5_GLOBAL_OBJECT_NAME, -1L, 0, 0, (genptr_t)&minor_type);
01085                 dp->d_major_type = DB5_MAJORTYPE_ATTRIBUTE_ONLY;
01086                 if( db_put_external( &global, dp, dbip ) < 0 )  {
01087                         bu_log("db5_update_ident() unable to create replacement %s object!\n", DB5_GLOBAL_OBJECT_NAME );
01088                         bu_free_external(&global);
01089                         return -1;
01090                 }
01091                 bu_free_external(&global);
01092         }
01093 
01094         bu_vls_init( &units );
01095         bu_vls_printf( &units, "%.25e", local2mm );
01096 
01097         bu_avs_init( &avs, 4, "db5_update_ident" );
01098         bu_avs_add( &avs, "title", title );
01099         bu_avs_add( &avs, "units", bu_vls_addr(&units) );
01100 
01101         ret = db5_update_attributes( dp, &avs, dbip );
01102         bu_vls_free( &units );
01103 
01104         /* protect from loosing memory and from freeing what we are
01105            about to dup */
01106         if(dbip->dbi_title) {
01107             old_title = dbip->dbi_title;
01108         }
01109         dbip->dbi_title = bu_strdup(title);
01110         if (old_title) {
01111             bu_free(old_title, "replaced dbi_title with new");
01112         }
01113 
01114         return ret;
01115 }
01116 
01117 /**
01118  *                      D B 5 _ F W R I T E _ I D E N T
01119  *
01120  *  Create a header for a v5 database.
01121  *  This routine has the same calling sequence as db_fwrite_ident()
01122  *  which makes a v4 database header.
01123  *
01124  *  In the v5 database, two database objects must be created to
01125  *  match the semantics of what was in the v4 header:
01126  *
01127  *  First, a database header object.
01128  *
01129  *  Second, create a specially named attribute-only object which
01130  *  contains the attributes "title=" and "units=".
01131  *
01132  *  This routine should only be used by db_create().
01133  *  Everyone else should use db5_update_ident().
01134  *
01135  * Returns -
01136  *       0      Success
01137  *      -1      Fatal Error
01138  */
01139 int
01140 db5_fwrite_ident(FILE *fp, const char *title, double local2mm)
01141 {
01142         struct bu_attribute_value_set avs;
01143         struct bu_vls           units;
01144         struct bu_external      out;
01145         struct bu_external      attr;
01146         int result;
01147 
01148         if( local2mm <= 0 )  {
01149             bu_log("db5_fwrite_ident(%s, %g) local2mm <= 0\n",
01150                    title, local2mm );
01151             return -1;
01152         }
01153 
01154         /* First, write the header object */
01155         db5_export_object3( &out, DB5HDR_HFLAGS_DLI_HEADER_OBJECT,
01156                             NULL, 0, NULL, NULL,
01157                             DB5_MAJORTYPE_RESERVED, 0,
01158                             DB5_ZZZ_UNCOMPRESSED, DB5_ZZZ_UNCOMPRESSED );
01159 
01160         result = bu_fwrite_external( fp, &out );
01161         bu_free_external( &out );
01162         if (result < 0) {
01163             return -1;
01164         }
01165 
01166         /* Second, create the attribute-only object */
01167         bu_vls_init( &units );
01168         bu_vls_printf( &units, "%.25e", local2mm );
01169 
01170         bu_avs_init( &avs, 4, "db5_fwrite_ident" );
01171         bu_avs_add( &avs, "title", title );
01172         bu_avs_add( &avs, "units", bu_vls_addr(&units) );
01173 
01174         db5_export_attributes( &attr, &avs );
01175         db5_export_object3( &out, DB5HDR_HFLAGS_DLI_APPLICATION_DATA_OBJECT,
01176                             DB5_GLOBAL_OBJECT_NAME, DB5HDR_HFLAGS_HIDDEN_OBJECT, &attr, NULL,
01177                             DB5_MAJORTYPE_ATTRIBUTE_ONLY, 0,
01178                             DB5_ZZZ_UNCOMPRESSED, DB5_ZZZ_UNCOMPRESSED );
01179 
01180         result = bu_fwrite_external( fp, &out );
01181         bu_free_external( &out );
01182         bu_free_external( &attr );
01183         bu_avs_free( &avs );
01184         bu_vls_free( &units );
01185         if (result < 0) {
01186             return -1;
01187         }
01188 
01189         return 0;
01190 }
01191 
01192 /**
01193  *                      R T _ D B _ C V T _ T O _ E X T E R N A L 5
01194  *
01195  *  The attributes are taken from ip->idb_avs
01196  *
01197  *  If present, convert attributes to on-disk format.
01198  *  This must happen after exporting the body, in case the
01199  *  ft_export5() method happened to extend the attribute set.
01200  *  Combinations are one "solid" which does this.
01201  *
01202  *  The internal representation is NOT freed, that's the caller's job.
01203  *
01204  *  The 'ext' pointer is accepted in uninitialized form, and
01205  *  an initialized structure is always returned, so that
01206  *  the caller may free it even when an error return is given.
01207  *
01208  *  Returns -
01209  *      0       OK
01210  *      -1      FAIL
01211  */
01212 int
01213 rt_db_cvt_to_external5(
01214         struct bu_external *ext,
01215         const char *name,
01216         const struct rt_db_internal *ip,
01217         double conv2mm,
01218         struct db_i *dbip,
01219         struct resource *resp,
01220         const int major)
01221 {
01222         struct bu_external      attributes;
01223         struct bu_external      body;
01224         int                     minor;
01225 
01226         RT_CK_DB_INTERNAL( ip );
01227         if(dbip) RT_CK_DBI(dbip);       /* may be null */
01228         RT_CK_RESOURCE(resp);
01229         BU_INIT_EXTERNAL( &body );
01230 
01231         minor = ip->idb_type;   /* XXX not necessarily v5 numbers. */
01232 
01233         /* Scale change on export is 1.0 -- no change */
01234         if( ip->idb_meth->ft_export5( &body, ip, conv2mm, dbip, resp, minor ) < 0 )  {
01235                 bu_log("rt_db_cvt_to_external5(%s):  ft_export5 failure\n",
01236                         name);
01237                 bu_free_external( &body );
01238                 BU_INIT_EXTERNAL(ext);
01239                 return -1;              /* FAIL */
01240         }
01241         BU_CK_EXTERNAL( &body );
01242 
01243         /* If present, convert attributes to on-disk format. */
01244         if( ip->idb_avs.magic == BU_AVS_MAGIC )  {
01245                 db5_export_attributes( &attributes, &ip->idb_avs );
01246                 BU_CK_EXTERNAL( &attributes );
01247         } else {
01248                 BU_INIT_EXTERNAL(&attributes);
01249         }
01250 
01251         db5_export_object3( ext, DB5HDR_HFLAGS_DLI_APPLICATION_DATA_OBJECT,
01252                 name, 0, &attributes, &body,
01253                 major, minor,
01254                 DB5_ZZZ_UNCOMPRESSED, DB5_ZZZ_UNCOMPRESSED );
01255         BU_CK_EXTERNAL( ext );
01256         bu_free_external( &body );
01257         bu_free_external( &attributes );
01258 
01259         return 0;               /* OK */
01260 }
01261 
01262 /*
01263  *                      D B _ W R A P _ V 5 _ E X T E R N A L
01264  *
01265  *  Modify name of external object, if necessary.
01266  */
01267 int
01268 db_wrap_v5_external( struct bu_external *ep, const char *name )
01269 {
01270         struct db5_raw_internal raw;
01271         struct bu_external      tmp;
01272 
01273         BU_CK_EXTERNAL(ep);
01274 
01275         /* Crack the external form into parts */
01276         if( db5_get_raw_internal_ptr( &raw, (unsigned char *)ep->ext_buf ) == NULL )  {
01277                 bu_log("db_put_external5(%s) failure in db5_get_raw_internal_ptr()\n",
01278                         name);
01279                 return -1;
01280         }
01281         BU_ASSERT_LONG( raw.h_dli, ==, DB5HDR_HFLAGS_DLI_APPLICATION_DATA_OBJECT );
01282 
01283         /* See if name needs to be changed */
01284         if( raw.name.ext_buf == NULL || strcmp( name, raw.name.ext_buf ) != 0 )  {
01285                 /* Name needs to be changed.  Create new external form.
01286                  * Make temporary copy so input isn't smashed
01287                  * as new external object is constructed.
01288                  */
01289                 tmp = *ep;              /* struct copy */
01290                 BU_INIT_EXTERNAL(ep);
01291 
01292                 db5_export_object3( ep,
01293                         DB5HDR_HFLAGS_DLI_APPLICATION_DATA_OBJECT,
01294                         name,
01295                         raw.h_name_hidden,
01296                         &raw.attributes,
01297                         &raw.body,
01298                         raw.major_type, raw.minor_type,
01299                         raw.a_zzz, raw.b_zzz );
01300                 /* 'raw' is invalid now, 'ep' has new external form. */
01301                 bu_free_external( &tmp );
01302                 return 0;
01303         }
01304 
01305         /* No changes needed, input object is properly named */
01306         return 0;
01307 }
01308 
01309 
01310 /**
01311  *
01312  *                      D B _ P U T _ E X T E R N A L 5
01313  *
01314  *  Given that caller already has an external representation of
01315  *  the database object,  update it to have a new name
01316  *  (taken from dp->d_namep) in that external representation,
01317  *  and write the new object into the database, obtaining different storage if
01318  *  the size has changed.
01319  *
01320  *  Changing the name on a v5 object is a relatively expensive operation.
01321  *
01322  *  Caller is responsible for freeing memory of external representation,
01323  *  using bu_free_external().
01324  *
01325  *  This routine is used to efficiently support MGED's "cp" and "keep"
01326  *  commands, which don't need to import and decompress
01327  *  objects just to rename and copy them.
01328  *
01329  *  Returns -
01330  *      -1      error
01331  *       0      success
01332  */
01333 int
01334 db_put_external5(struct bu_external *ep, struct directory *dp, struct db_i *dbip)
01335 {
01336         RT_CK_DBI(dbip);
01337         RT_CK_DIR(dp);
01338         BU_CK_EXTERNAL(ep);
01339 
01340         if(RT_G_DEBUG&DEBUG_DB) bu_log("db_put_external5(%s) ep=x%x, dbip=x%x, dp=x%x\n",
01341                 dp->d_namep, ep, dbip, dp );
01342 
01343         if( dbip->dbi_read_only )  {
01344                 bu_log("db_put_external5(%s):  READ-ONLY file\n",
01345                         dbip->dbi_filename);
01346                 return -1;
01347         }
01348 
01349         BU_ASSERT_LONG( dbip->dbi_version, ==, 5 );
01350 
01351         /* First, change the name. */
01352         if( db_wrap_v5_external( ep, dp->d_namep ) < 0 )  {
01353                 bu_log("db_put_external5(%s) failure in db_wrap_v5_external()\n",
01354                         dp->d_namep);
01355                 return -1;
01356         }
01357 
01358         /* Second, obtain storage for final object */
01359         if( ep->ext_nbytes != dp->d_len || dp->d_addr == -1L )  {
01360                 if( db5_realloc( dbip, dp, ep ) < 0 )  {
01361                         bu_log("db_put_external(%s) db_realloc5() failed\n", dp->d_namep);
01362                         return -5;
01363                 }
01364         }
01365         BU_ASSERT_LONG( ep->ext_nbytes, ==, dp->d_len );
01366 
01367         if( dp->d_flags & RT_DIR_INMEM )  {
01368                 bcopy( (char *)ep->ext_buf, dp->d_un.ptr, ep->ext_nbytes );
01369                 return 0;
01370         }
01371 
01372         if( db_write( dbip, (char *)ep->ext_buf, ep->ext_nbytes, dp->d_addr ) < 0 )  {
01373                 return -1;
01374         }
01375         return 0;
01376 }
01377 
01378 /**
01379  *                      R T _ D B _ P U T _ I N T E R N A L 5
01380  *
01381  *  Convert the internal representation of a solid to the external one,
01382  *  and write it into the database.
01383  *
01384  *  Applications and middleware shouldn't call this directly, they
01385  *  should use the version-generic interface "rt_db_put_internal()".
01386  *
01387  *  The internal representation is always freed.
01388  *  (Not the pointer, just the contents).
01389  *
01390  *  Returns -
01391  *      <0      error
01392  *       0      success
01393  */
01394 int
01395 rt_db_put_internal5(
01396         struct directory        *dp,
01397         struct db_i             *dbip,
01398         struct rt_db_internal   *ip,
01399         struct resource         *resp,
01400         const int               major)
01401 {
01402         struct bu_external      ext;
01403 
01404         RT_CK_DIR(dp);
01405         RT_CK_DBI(dbip);
01406         RT_CK_DB_INTERNAL( ip );
01407         RT_CK_RESOURCE(resp);
01408 
01409         BU_ASSERT_LONG( dbip->dbi_version, ==, 5 );
01410 
01411         if( rt_db_cvt_to_external5( &ext, dp->d_namep, ip, 1.0, dbip, resp, major ) < 0 )  {
01412                 bu_log("rt_db_put_internal5(%s):  export failure\n",
01413                         dp->d_namep);
01414                 goto fail;
01415         }
01416         BU_CK_EXTERNAL( &ext );
01417 
01418         if( ext.ext_nbytes != dp->d_len || dp->d_addr == -1L )  {
01419                 if( db5_realloc( dbip, dp, &ext ) < 0 )  {
01420                         bu_log("rt_db_put_internal5(%s) db_realloc5() failed\n", dp->d_namep);
01421                         goto fail;
01422                 }
01423         }
01424         BU_ASSERT_LONG( ext.ext_nbytes, ==, dp->d_len );
01425 
01426         if( dp->d_flags & RT_DIR_INMEM )  {
01427                 bcopy( (char *)ext.ext_buf, dp->d_un.ptr, ext.ext_nbytes );
01428                 goto ok;
01429         }
01430 
01431         if( db_write( dbip, (char *)ext.ext_buf, ext.ext_nbytes, dp->d_addr ) < 0 )  {
01432                 goto fail;
01433         }
01434 ok:
01435         bu_free_external( &ext );
01436         rt_db_free_internal( ip, resp );
01437         return 0;                       /* OK */
01438 
01439 fail:
01440         bu_free_external( &ext );
01441         rt_db_free_internal( ip, resp );
01442         return -2;              /* FAIL */
01443 }
01444 
01445 /**
01446  *                      R T _ D B _ E X T E R N A L 5 _ T O _ I N T E R N A L 5
01447  *
01448  *  Given an object in external form, convert it to internal form.
01449  *  The caller is responsible for freeing the external form.
01450  *
01451  *  Returns -
01452  *      <0      On error
01453  *      id      On success.
01454  */
01455 int
01456 rt_db_external5_to_internal5(
01457         struct rt_db_internal           *ip,
01458         const struct bu_external        *ep,
01459         const char                      *name,
01460         const struct db_i               *dbip,
01461         const mat_t                     mat,
01462         struct resource                 *resp)
01463 {
01464         register int            id;
01465         struct db5_raw_internal raw;
01466 
01467         BU_CK_EXTERNAL(ep);
01468         RT_CK_DB_INTERNAL(ip);
01469         RT_CK_DBI(dbip);
01470 
01471         BU_ASSERT_LONG( dbip->dbi_version, ==, 5 );
01472 
01473         if (db5_get_raw_internal_ptr(&raw, ep->ext_buf) == NULL) {
01474                 bu_log("rt_db_external5_to_internal5(%s):  import failure\n",
01475                         name );
01476                 return -3;
01477         }
01478 
01479         if(( raw.major_type == DB5_MAJORTYPE_BRLCAD )
01480          ||( raw.major_type == DB5_MAJORTYPE_BINARY_UNIF)) {
01481                 /* As a convenience to older ft_import routines */
01482                 if( mat == NULL )  mat = bn_mat_identity;
01483         } else {
01484                 bu_log("rt_db_external5_to_internal5(%s):  unable to import non-BRL-CAD object, major=%d\n",
01485                         name, raw.major_type );
01486                 return -1;              /* FAIL */
01487         }
01488 
01489         if (ip->idb_avs.magic != BU_AVS_MAGIC) {
01490             bu_avs_init_empty( &ip->idb_avs );
01491         }
01492 
01493         /* If attributes are present in the object, make them available
01494          * in the internal form.
01495          */
01496         if( raw.attributes.ext_buf )  {
01497                 if( db5_import_attributes( &ip->idb_avs, &raw.attributes ) < 0 )  {
01498                         bu_log("rt_db_external5_to_internal5(%s):  mal-formed attributes in database\n",
01499                                 name );
01500                         return -8;
01501                 }
01502         }
01503 
01504         if( !raw.body.ext_buf )  {
01505                 bu_log("rt_db_external5_to_internal5(%s):  object has no body\n",
01506                         name );
01507                 return -4;
01508         }
01509 
01510         /*
01511          *      XXX     This is a kludge, but it works for starters
01512          */
01513         switch ( raw.major_type ) {
01514             case DB5_MAJORTYPE_BRLCAD:
01515                 id = raw.minor_type; break;
01516             case DB5_MAJORTYPE_BINARY_UNIF:
01517                 id = ID_BINUNIF; break;
01518             default:
01519                 bu_log("rt_db_external5_to_internal5(%s): don't yet handle major_type %d\n", name, raw.major_type);
01520                 return -1;
01521         }
01522         /* ip has already been initialized, and should not be re-initted */
01523         if( rt_functab[id].ft_import5( ip, &raw.body, mat, dbip, resp, raw.minor_type ) < 0 )  {
01524                 bu_log("rt_db_external5_to_internal5(%s):  import failure\n",
01525                         name );
01526                 rt_db_free_internal( ip, resp );
01527                 return -1;              /* FAIL */
01528         }
01529         /* Don't free &raw.body */
01530 
01531         RT_CK_DB_INTERNAL( ip );
01532         ip->idb_major_type = raw.major_type;
01533         ip->idb_minor_type = raw.minor_type;
01534         ip->idb_meth = &rt_functab[id];
01535 
01536         return id;                      /* OK */
01537 }
01538 
01539 /**
01540  *                      R T _ D B _ G E T _ I N T E R N A L 5
01541  *
01542  *  Get an object from the database, and convert it into it's internal
01543  *  representation.
01544  *
01545  *  Applications and middleware shouldn't call this directly, they
01546  *  should use the generic interface "rt_db_get_internal()".
01547  *
01548  *  Returns -
01549  *      <0      On error
01550  *      id      On success.
01551  */
01552 int
01553 rt_db_get_internal5(
01554         struct rt_db_internal   *ip,
01555         const struct directory  *dp,
01556         const struct db_i       *dbip,
01557         const mat_t             mat,
01558         struct resource         *resp)
01559 {
01560         struct bu_external      ext;
01561         int                     ret;
01562 
01563         BU_INIT_EXTERNAL(&ext);
01564         RT_INIT_DB_INTERNAL(ip);
01565 
01566         BU_ASSERT_LONG( dbip->dbi_version, ==, 5 );
01567 
01568         if( db_get_external( &ext, dp, dbip ) < 0 )
01569                 return -2;              /* FAIL */
01570 
01571         ret = rt_db_external5_to_internal5( ip, &ext, dp->d_namep, dbip, mat, resp );
01572         bu_free_external(&ext);
01573         return ret;
01574 }
01575 
01576 /*
01577  *  XXX The material head should be attached to the db_i, not global.
01578  */
01579 void
01580 db5_export_color_table( struct bu_vls *ostr, struct db_i *dbip )
01581 {
01582         struct mater *mp;
01583 
01584         BU_CK_VLS(ostr);
01585         RT_CK_DBI(dbip);
01586 
01587         for( mp = rt_material_head; mp != MATER_NULL; mp = mp->mt_forw )  {
01588                 bu_vls_printf(ostr,
01589                         "{%d %d %d %d %d} ",
01590                         mp->mt_low,
01591                         mp->mt_high,
01592                         mp->mt_r,
01593                         mp->mt_g,
01594                         mp->mt_b );
01595         }
01596 }
01597 
01598 /**
01599  *                      D B 5 _ I M P O R T _ C O L O R _ T A B L E
01600  */
01601 void
01602 db5_import_color_table( char *cp )
01603 {
01604         char    *sp = cp;
01605         int     low, high, r, g, b;
01606 
01607         while( (sp = strchr( sp, '{' )) != NULL )  {
01608                 sp++;
01609                 if( sscanf( sp, "%d %d %d %d %d", &low, &high, &r, &g, &b ) != 5 )  break;
01610                 rt_color_addrec( low, high, r, g, b, -1L );
01611         }
01612 }
01613 
01614 /**
01615  *                      D B 5 _ P U T _ C O L O R _ T A B L E
01616  *
01617  *  Put the old region-id-color-table into the global object.
01618  *  A null attribute is set if the material table is empty.
01619  *
01620  *  Returns -
01621  *      <0      error
01622  *      0       OK
01623  */
01624 int
01625 db5_put_color_table( struct db_i *dbip )
01626 {
01627         struct bu_vls   str;
01628         int     ret;
01629 
01630         RT_CK_DBI(dbip);
01631         BU_ASSERT_LONG( dbip->dbi_version, ==, 5 );
01632 
01633         bu_vls_init(&str);
01634         db5_export_color_table( &str, dbip );
01635 
01636         ret = db5_update_attribute( DB5_GLOBAL_OBJECT_NAME,
01637                 "regionid_colortable", bu_vls_addr(&str), dbip );
01638 
01639         bu_vls_free( &str );
01640         return ret;
01641 }
01642 
01643 /**                     D B _ G E T _ A T T R I B U T E S
01644  *
01645  *      Get attributes for an object pointed to by *dp
01646  *
01647  *      returns:
01648  *              0 - all is well
01649  *              <0 - error
01650  */
01651 int
01652 db5_get_attributes( const struct db_i *dbip, struct bu_attribute_value_set *avs, const struct directory *dp )
01653 {
01654         struct bu_external      ext;
01655         struct db5_raw_internal raw;
01656 
01657         RT_CK_DBI( dbip );
01658 
01659         if( dbip->dbi_version < 5 )
01660                 return 0;       /* not an error, just no attributes */
01661 
01662         RT_CK_DIR( dp );
01663 
01664         BU_INIT_EXTERNAL(&ext);
01665 
01666         if( db_get_external( &ext, dp, dbip ) < 0 )
01667                 return -1;              /* FAIL */
01668 
01669         if (db5_get_raw_internal_ptr(&raw, ext.ext_buf) == NULL) {
01670                 bu_free_external( &ext );
01671                 return -2;
01672         }
01673 
01674         if( raw.attributes.ext_buf )  {
01675                 if( db5_import_attributes( avs, &raw.attributes ) < 0 ) {
01676                         bu_free_external( &ext );
01677                         return -3;
01678                 }
01679         }
01680 
01681         bu_free_external( &ext );
01682         return 0;
01683 }
01684 
01685 /*@}*/
01686 /*
01687  * Local Variables:
01688  * mode: C
01689  * tab-width: 8
01690  * c-basic-offset: 4
01691  * indent-tabs-mode: t
01692  * End:
01693  * ex: shiftwidth=4 tabstop=8
01694  */

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