File: [Development] / inventor / lib / database / src / so / SoInput.c++ (download)
Revision 1.4, Wed Oct 25 23:47:47 2000 UTC (17 years ago) by jlim
Branch: MAIN
CVS Tags: release-2_1_5-9, release-2_1_5-8, release-2_1_5-10, HEAD Changes since 1.3: +33 -33
lines
Prevent conflict with SIZEOF in X11/Xmd.h.
|
/*
*
* Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/NoticeExplan/
*
*/
/*
* Copyright (C) 1990,91 Silicon Graphics, Inc.
*
_______________________________________________________________________
______________ S I L I C O N G R A P H I C S I N C . ____________
|
| $Revision: 1.4 $
|
| Classes:
| SoInput, SoInputFile
|
| Notes: This file includes any machine-dependent `dgl' (data goo lib)
| code for each machine. See dgl.h.
|
| Author(s) : Paul S. Strauss, Gavin Bell, Dave Immel (dgl/cray)
|
______________ S I L I C O N G R A P H I C S I N C . ____________
_______________________________________________________________________
*/
#include <machine.h> // This is included in the Inventor tree
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <Inventor/misc/SoBase.h>
#include <Inventor/SoDB.h>
#include <Inventor/SoInput.h>
#include <Inventor/SoOutput.h>
#include <Inventor/SoPath.h>
#include <Inventor/errors/SoDebugError.h>
#include <Inventor/errors/SoReadError.h>
// Static list of directories to search in. Allocated by SoInput::init.
SbStringList *SoInput::directories = NULL;
#define COMMENT_CHAR '#'
/////////////////////////////////////////////////////////////////////////
//
// Note: The following dgl code is for machine specific implemenations.
// The SGI implementation is defined in dgl.h and does not
// require any functions (macros only).
//
////////////////////// BEGIN CRAY-DEPENDENT CODE ////////////////////////
//
// Following are functions that are used to convert from the
// network format to the host format for CRAY machines:
//
// dgl_ntoh_short(f) network to host - short
// dgl_ntoh_int32(f) network to host - int32_t
// dgl_ntoh_float(PC) network to host - float
// dgl_ntoh_double(PC) network to host - double
//
////////////////////////////////////////////////////////////////////////
#ifdef _CRAY
short dgl_ntoh_short( char *f )
{
int val;
val = f[0] << 8 | f[1];
if (f[0] & 0x80) val |= ~0xffff;
return (val);
}
int32_t dgl_ntoh_int32( char *f )
{
int32_t val;
val = f[0] << 24 | f[1] << 16 | f[2] << 8 | f[3];
if (f[0] & 0x80) val |= ~0xffffffff;
return (val);
}
//
// BIG_IEEE: no conversion necessary (FLOAT)
//
// (Not portable. This routine works on Crays.)
// What IEEE single precision floating point looks like on a Cray
struct ieee_single {
unsigned int zero : 32; // Upper 32 bits are junk
unsigned int sign : 1;
unsigned int exp : 8;
unsigned int mantissa: 23; // 24-bit mantissa with 1 hidden bit
};
// Cray floating point, partitioned for easy conversion to IEEE single
struct cray_single {
unsigned int sign : 1;
unsigned int exp : 15;
unsigned int mantissa : 24;
unsigned int mantissa2: 24;
};
struct cray_double {
unsigned int sign : 1;
unsigned int exp : 15;
unsigned int mantissa : 48;
};
#define CRAY_BIAS 040001
// Cray exponent limits for conversion to IEEE single
#define MAX_CRAY_SNG (0x100 + CRAY_BIAS - IEEE_SNG_BIAS)
#define MIN_CRAY_SNG (0x00 + CRAY_BIAS - IEEE_SNG_BIAS)
static struct cray_single max_sng_cray = { 0, 0x6000, 0, 0 } ;
static struct cray_double max_dbl_cray = { 0, 0x6000, 0 } ;
#define IEEE_SNG_BIAS 0x7f
float dgl_ntoh_float( char *PC ) { // CRAY-dependent
union {
struct ieee_single is;
int32_t l;
} c;
union {
struct cray_single vc;
float iis;
} ieee;
c.l = dgl_ntoh_int32(PC);
if(c.is.exp == 0) { ieee.iis = 0.0; }
else if ( c.is.exp == 0xff ) {
// If the IEEE float we are decoding indicates an IEEE overflow
// condition, we manufacture a Cray overflow condition.
ieee.vc = max_sng_cray ;
}
else {
ieee.vc.sign = c.is.sign;
ieee.vc.exp = c.is.exp - IEEE_SNG_BIAS + CRAY_BIAS;
ieee.vc.mantissa = c.is.mantissa | (1 << 23);
ieee.vc.mantissa2 = 0;
}
return (ieee.iis);
}
// What IEEE double precision floating point looks like on a Cray
// (the first word, anyway. Second word is pure mantissa2.)
struct ieee_double {
unsigned int sign : 1;
unsigned int exp : 11;
unsigned int mantissa : 52;
};
#define IEEE_DBL_BIAS 0x3ff
float dgl_ntoh_double( char *PC ) { // CRAY-dependent
union {
struct ieee_double is;
char l[M_SIZEOF(double)];
} c;
union {
struct cray_double vc;
float iis;
} cr;
memcpy(c.l, PC, M_SIZEOF(double));
if(c.is.exp == 0) { cr.iis = 0.0; }
else if ( c.is.exp == 0xff ) {
// If the IEEE float we are decoding indicates an IEEE overflow
// condition, we manufacture a Cray overflow condition.
cr.vc = max_dbl_cray ;
}
else {
cr.vc.sign = c.is.sign;
cr.vc.exp = c.is.exp - IEEE_DBL_BIAS + CRAY_BIAS;
cr.vc.mantissa = (c.is.mantissa >> 5) | (1 << 47);
}
return (cr.iis);
}
#endif // _CRAY
//////////////////////// END CRAY-DEPENDENT CODE ////////////////////////
////////////////////////////////////////////////////////////////////////
//
// Description:
// Constructor for SoInputFile.
//
// Use: internal
SoInputFile::SoInputFile()
//
////////////////////////////////////////////////////////////////////////
{
// Initialize variables:
name.makeEmpty();
fullName.makeEmpty();
fp = NULL;
buffer = NULL;
curBuf = NULL;
openedHere = FALSE;
binary = FALSE;
readHeader = FALSE;
headerOk = FALSE;
refDict = NULL;
borrowedDict = FALSE;
ivVersion = 0.;
headerString.makeEmpty();
postReadCB = NULL;
CBData = NULL;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Constructor - sets input to stdin by default.
//
// Use: public
SoInput::SoInput()
//
////////////////////////////////////////////////////////////////////////
{
// Create new file and push on stack
curFile = new struct SoInputFile;
files.append((void *) curFile);
// Make it read from stdin
initFile(stdin, "<stdin>", NULL, FALSE);
backBufIndex = -1; // No buffer
tmpBuffer = NULL;
tmpBufSize = 0;
backupBufUsed = FALSE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Init routine-- called by SoDB::init, it sets up the list of
// directories to search in.
//
// Use: internal, static
void
SoInput::init()
//
////////////////////////////////////////////////////////////////////////
{
directories = new SbStringList;
// Default directory search path is current directory
directories->append(new SbString("."));
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Constructor that gets reference dictionary from another SoInput.
//
// Use: internal
SoInput::SoInput(SoInput *dictIn)
//
////////////////////////////////////////////////////////////////////////
{
// Create new file and push on stack
curFile = new struct SoInputFile;
files.append((void *) curFile);
// Make it read from stdin and use the passed dictionary
initFile(stdin, "<stdin>", NULL, FALSE,
(dictIn == NULL ? NULL : dictIn->curFile->refDict));
backBufIndex = -1; // No buffer
tmpBuffer = NULL;
tmpBufSize = 0;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Destructor - closes all input files opened by SoInput.
//
// Use: public
SoInput::~SoInput()
//
////////////////////////////////////////////////////////////////////////
{
closeFile();
// closeFile() leaves the topmost file on the stack, so delete it
if (curFile->refDict != NULL && ! curFile->borrowedDict)
delete curFile->refDict;
delete curFile;
if (tmpBuffer != NULL) {
free (tmpBuffer);
tmpBuffer = NULL;
tmpBufSize = 0;
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Adds a directory to beginning of list of directories to search to
// find named files to open.
//
// Use: public, static
void
SoInput::addDirectoryFirst(const char *dirName)
//
////////////////////////////////////////////////////////////////////////
{
directories->insert(new SbString(dirName), 0);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Adds a directory to end of list of directories to search to
// find named files to open.
//
// Use: public, static
void
SoInput::addDirectoryLast(const char *dirName)
//
////////////////////////////////////////////////////////////////////////
{
directories->append(new SbString(dirName));
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Adds directories that are named in the value of the given
// environment variable to the beginning of the list of directories.
// Directories may be separated by colons or whitespace in the value.
//
// Use: public, static
void
SoInput::addEnvDirectoriesFirst(const char *envVarName)
//
////////////////////////////////////////////////////////////////////////
{
char *dirs = getenv(envVarName);
if (dirs != NULL) {
char *d = strdup(dirs);
char *dir;
int i = 0;
// Parse colon- or space-separated directories from string
dir = strtok(d, ": \t");
while (dir != NULL) {
// Make sure directories are added in the same order
directories->insert(new SbString(dir), i++);
dir = strtok(NULL, ": \t");
}
free(d);
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Adds directories that are named in the value of the given
// environment variable to the end of the list of directories.
// Directories may be separated by colons or whitespace in the value.
//
// Use: public, static
void
SoInput::addEnvDirectoriesLast(const char *envVarName)
//
////////////////////////////////////////////////////////////////////////
{
char *dirs = getenv(envVarName);
if (dirs != NULL) {
char *d = strdup(dirs);
char *dir;
// Parse colon- or space-separated directories from string
dir = strtok(d, ": \t");
while (dir != NULL) {
addDirectoryLast(dir);
dir = strtok(NULL, ": \t");
}
free(d);
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Removes given directory from list.
//
// Use: public, static
void
SoInput::removeDirectory(const char *dirName)
//
////////////////////////////////////////////////////////////////////////
{
int i;
SbString *dir;
for (i = 0; i < directories->getLength(); i++) {
dir = (*directories)[i];
if (*dir == dirName) {
directories->remove(i);
delete dir;
break;
}
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Clears list of directories, including the current directory.
//
// Use: public, static
void
SoInput::clearDirectories()
//
////////////////////////////////////////////////////////////////////////
{
int i;
for (i = 0; i < directories->getLength(); i++)
delete (*directories)[i];
directories->truncate(0);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Returns the list of search directories.
//
// Use: public, static
const SbStringList &
SoInput::getDirectories()
//
////////////////////////////////////////////////////////////////////////
{
return *directories;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Sets file pointer to read from. Clears stack if necessary.
//
// Use: public
void
SoInput::setFilePointer(FILE *newFP) // New file pointer
//
////////////////////////////////////////////////////////////////////////
{
#ifdef DEBUG
if (newFP == NULL)
SoDebugError::postWarning("SoInput::setFilePointer",
"Setting file pointer to NULL - "
"may cause problems");
#endif /* DEBUG */
// Close open files, if any
closeFile();
// Initialize reading from file
initFile(newFP, "<user-defined file pointer>", NULL, FALSE);
if (tmpBuffer == NULL) {
tmpBuffer = malloc(64);
tmpBufSize = 64;
curTmpBuf = (char *)tmpBuffer;
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Opens named file, sets file pointer to result. If it can't open
// the file, it returns FALSE. If okIfNotFound is FALSE (the
// default), it prints an error message if the file could not be
// found.
//
// Use: public
SbBool
SoInput::openFile(const char *fileName, SbBool okIfNotFound)
//
////////////////////////////////////////////////////////////////////////
{
FILE *newFP = NULL;
SbString fullName;
if (fileName != NULL && fileName[0] != '\0') {
newFP = findFile(fileName, fullName);
}
if (newFP == NULL) {
if (! okIfNotFound)
SoReadError::post(this,
"Can't open file \"%s\" for reading", fileName);
return FALSE;
}
// Close open files, if any
closeFile();
// Initialize reading from file
initFile(newFP, fileName, &fullName, TRUE);
if (tmpBuffer == NULL) {
tmpBuffer = malloc(64);
tmpBufSize = 64;
curTmpBuf = (char *)tmpBuffer;
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Opens named file, pushes file pointer to result. If it can't open
// the file, it prints an error message and returns FALSE.
//
// Use: public
SbBool
SoInput::pushFile(const char *fileName) // Name of file
//
////////////////////////////////////////////////////////////////////////
{
SbString fullName;
FILE *newFP = findFile(fileName, fullName);
if (newFP == NULL) {
SoDebugError::post("SoInput::pushFile",
"Can't open file \"%s\" for reading", fileName);
return FALSE;
}
// Allocate a new file structure and push onto stack
curFile = new struct SoInputFile;
files.append(curFile);
// Initialize reading from file
initFile(newFP, fileName, &fullName, TRUE);
if (tmpBuffer == NULL) {
tmpBuffer = malloc(64);
tmpBufSize = 64;
curTmpBuf = (char *)tmpBuffer;
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Closes current files that were opened by SoInput. It does
// nothing to files that were not opened here, so it's always safe
// to call. This also removes all of the files from the stack and
// resets things to read from stdin.
//
// Use: public
void
SoInput::closeFile()
//
////////////////////////////////////////////////////////////////////////
{
int i;
struct SoInputFile *f;
// Close all files opened here
for (i = 0; i < files.getLength(); i++) {
f = (struct SoInputFile *) files[i];
if (f->openedHere)
fclose(f->fp);
// Free up storage used for all but topmost file
if (i > 0) {
delete f->refDict;
delete f;
}
}
// Remove all but the first file from the stack
if (files.getLength() > 1)
files.truncate(1);
// Reset to read from stdin again
initFile(stdin, "<stdin>", NULL, FALSE);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Returns TRUE if currently open file is a valid file;
// that is, it begins with a valid header that has been registered
// through SoDB::registerHeader.
//
// Use: public
SbBool
SoInput::isValidFile()
//
////////////////////////////////////////////////////////////////////////
{
// Make sure the header was read
if (! curFile->readHeader)
(void) checkHeader();
return curFile->headerOk;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Returns pointer to current file, or NULL if reading from buffer.
//
// Use: public
FILE *
SoInput::getCurFile() const
//
////////////////////////////////////////////////////////////////////////
{
return fromBuffer() ? NULL : curFile->fp;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Returns full name of current file, or NULL if reading from buffer.
//
// Use: public
const char *
SoInput::getCurFileName() const
//
////////////////////////////////////////////////////////////////////////
{
return fromBuffer() ? NULL : curFile->fullName.getString();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Sets up buffer to read from and its size.
//
// Use: public
void
SoInput::setBuffer(void *bufPointer, size_t bufSize)
//
////////////////////////////////////////////////////////////////////////
{
// Close open files, if any
closeFile();
// Initialize reading from buffer
curFile->name = "<user-defined buffer in memory>";
curFile->fp = NULL;
curFile->buffer = bufPointer;
curFile->curBuf = (char *) bufPointer;
curFile->bufSize = bufSize;
curFile->lineNum = 1;
curFile->openedHere = FALSE;
// Start with a fresh dictionary
if (curFile->refDict != NULL && ! curFile->borrowedDict)
curFile->refDict->clear();
else
curFile->refDict = new SbDict;
// Assume file is ASCII until header is checked
curFile->binary = FALSE;
curFile->readHeader = FALSE;
curFile->headerOk = TRUE;
curFile->ivVersion = 0.;
curFile->headerString.makeEmpty();
// Delete the temporary buffer if it has been allocated
if (tmpBuffer != NULL) {
free (tmpBuffer);
tmpBuffer = NULL;
tmpBufSize = 0;
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Returns number of bytes read from buffer. Returns 0 if not
// reading from a buffer.
//
// Use: public
size_t
SoInput::getNumBytesRead() const
//
////////////////////////////////////////////////////////////////////////
{
if (! fromBuffer())
return 0;
return curFile->curBuf - (char *) curFile->buffer;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Returns whether current file being read is binary. This may have
// to check the header to determine this info.
//
// Use: public
SbBool
SoInput::isBinary()
//
////////////////////////////////////////////////////////////////////////
{
// Check header if not already done
if (! curFile->readHeader)
(void) checkHeader();
return curFile->binary;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Returns the header of the file being read
//
// Use: public
SbString
SoInput::getHeader()
//
////////////////////////////////////////////////////////////////////////
{
// Check header if not already done
if (! curFile->readHeader)
(void) checkHeader();
return curFile->headerString;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads next character from current file/buffer. Returns FALSE on EOF.
//
// Use: public
SbBool
SoInput::get(char &c)
//
////////////////////////////////////////////////////////////////////////
{
SbBool ret;
// Read from backBuf if it is not empty
if (backBufIndex >= 0) {
c = backBuf.getString()[backBufIndex++];
if (c != '\0')
return TRUE;
// Back buffer ran out of characters
backBuf.makeEmpty();
backBufIndex = -1;
}
if (! curFile->readHeader && ! checkHeader())
return FALSE;
if (eof()) {
c = (char)EOF;
ret = FALSE;
}
else if (curFile->binary) {
if (fromBuffer()) {
c = *curFile->curBuf++;
curFile->curBuf++;
curFile->curBuf++;
curFile->curBuf++;
ret = TRUE;
}
else {
char pad[3];
int i = fread((void *)&c, sizeof(char), 1, curFile->fp);
(void)fread((void *)pad, sizeof(char), 3, curFile->fp);
ret = (i == 1) ? TRUE : FALSE;
}
}
else {
if (! fromBuffer()) {
int i = getc(curFile->fp);
if (i == EOF) {
c = (char)EOF; // Set c to EOF so putpack(c) will fail
ret = FALSE;
}
else {
c = (char) i;
ret = TRUE;
}
}
else {
c = *curFile->curBuf++;
ret = TRUE;
}
}
return ret;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads next ASCII character from current buffer. Returns FALSE on EOF.
//
// Use: public
SbBool
SoInput::getASCIIBuffer(char &c)
//
////////////////////////////////////////////////////////////////////////
{
SbBool ret;
// Read from backBuf if it is not empty
if (backBufIndex >= 0) {
c = backBuf.getString()[backBufIndex++];
if (c != '\0')
return TRUE;
// Back buffer ran out of characters
backBuf.makeEmpty();
backBufIndex = -1;
}
if (freeBytesInBuf() == 0) {
c = (char)EOF;
ret = FALSE;
}
else {
c = *curFile->curBuf++;
ret = TRUE;
}
return ret;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads next ASCII character from current file. Returns FALSE on EOF.
//
// Use: public
SbBool
SoInput::getASCIIFile(char &c)
//
////////////////////////////////////////////////////////////////////////
{
int i;
SbBool ret;
// Read from backBuf if it is not empty
if (backBufIndex >= 0) {
c = backBuf.getString()[backBufIndex++];
if (c != '\0')
return TRUE;
// Back buffer ran out of characters
backBuf.makeEmpty();
backBufIndex = -1;
}
i = getc(curFile->fp);
c = (char)i;
ret = (i == EOF) ? FALSE : TRUE;
return ret;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads a character from current file/buffer after skipping white space.
// Returns FALSE on EOF.
//
// Use: public
SbBool
SoInput::read(char &c)
//
////////////////////////////////////////////////////////////////////////
{
return (skipWhiteSpace() && get(c));
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads a character string from current file/buffer. If the character
// string begins with a double quote, this reads until a matching
// close quote is found. Otherwise, it reads until the next white
// space is found. This returns FALSE on EOF.
//
// Use: public
SbBool
SoInput::read(SbString &s)
//
////////////////////////////////////////////////////////////////////////
{
if (! skipWhiteSpace())
return FALSE;
if (curFile->binary) {
// If there is a string in the putback buffer, use it.
if (backBufIndex >= 0) {
s = backBuf;
backBuf.makeEmpty();
backBufIndex = -1;
return TRUE;
}
//
// Reading from the binary format consists of reading a word
// representing the string length, followed by the string,
// followed by the padding to the word boundary. Put a NULL
// character at the end of the string.
//
if (fromBuffer()) {
if (eof())
return FALSE;
int n = * (int *) curFile->curBuf;
DGL_NTOH_INT32(n, n);
// A marker was read. Return without incrementing the buffer
if (n < 0)
return FALSE;
char buffer[1024], *buf;
if (n > 1023)
buf = new char [n + 1];
else
buf = buffer;
curFile->curBuf += 4;
memcpy(buf, curFile->curBuf, n);
buf[n] = '\0';
curFile->curBuf += (n+3) & ~0003;
s = buf;
if (n > 1023)
delete [] buf;
return TRUE;
}
else {
// Break out the case where an eof is hit. This will only be
// The case in SoFile nodes which don't know how many children
// they have. Reading keeps happening until eof is hit.
int n;
if (fread((void *) &n, sizeof(int), 1, curFile->fp) == 1) {
DGL_NTOH_INT32(n, n);
if (n < 0) {
// A marker was read. Put it in the backup buffer
// so the next read will read it.
int *tint = (int *) backupBuf;
*tint = n;
backupBufUsed = TRUE;
return FALSE;
}
char buffer[1024], *buf;
if (n > 1023)
buf = new char [n + 1];
else
buf = buffer;
SbBool ok =
(fread((void *) buf, sizeof(char), n, curFile->fp) == n);
if (ok) {
int pad = ((n+3) & ~003) - n;
char padbuf[4];
ok = (fread((void *) padbuf, sizeof(char), pad,
curFile->fp) == pad);
if (ok) {
buf[n] = '\0';
s = buf;
}
}
if (n > 1023)
delete [] buf;
if (! ok)
return FALSE;
}
else
s = "";
return TRUE;
}
}
else {
SbBool quoted;
char c;
char bufStore[256];
char *buf;
int bytesLeft;
s.makeEmpty();
// Read first character - if none, EOF
if (! get(c))
return FALSE;
// If quoted string
quoted = (c == '\"');
if (! quoted)
putBack(c);
do {
buf = bufStore;
bytesLeft = sizeof(bufStore) - 1;
// Read a bufferfull
while (bytesLeft > 0) {
// Terminate on EOF
if (! get(*buf))
break;
if (quoted) {
// Terminate on close quote
if (*buf == '\"')
break;
// Check for escaped double quote
if (*buf == '\\') {
if ((get(c)) && c == '\"')
*buf = '\"';
else
putBack(c);
}
if (*buf == '\n')
curFile->lineNum++;
}
// If unquoted string, terminate at whitespace
else if (isspace(*buf)) {
// Put back the whitespace
putBack(*buf);
break;
}
buf++;
bytesLeft--;
}
*buf = '\0';
// Tack the buffer onto the string
s += bufStore;
} while (bytesLeft == 0);
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads a character string SbName from current file/buffer. If the
// character string begins with a double quote, this reads until a
// matching close quote is found. Otherwise, it reads until the
// next white space is found. This returns FALSE on EOF.
//
// If "validIdent" is TRUE, this reads only strings that are valid
// identifiers as defined in the SbName class. The default for this
// is FALSE. Identifiers may not be within quotes.
//
// Use: public
SbBool
SoInput::read(SbName &n, // Name to read into
SbBool validIdent) // TRUE => name must be
// identifier (default is FALSE)
//
////////////////////////////////////////////////////////////////////////
{
SbBool gotChar;
if (! skipWhiteSpace())
return FALSE;
// If binary input or not an identifer, read as just a regular string
if (curFile->binary || ! validIdent) {
SbString s;
if (! read(s)) {
// We may have just discovered EOF when trying to read the
// string. If so, and there's another file open on the
// stack, call this method again to try reading from the
// next file.
if (curFile->binary && eof() && files.getLength() > 1)
return read(n, validIdent);
return FALSE;
}
n = s;
}
else {
// Read identifier, watching for validity
char buf[256];
char *b = buf;
char c;
if (fromBuffer()) {
if ((gotChar = getASCIIBuffer(c)) && SbName::isIdentStartChar(c)) {
*b++ = c;
while ((gotChar= getASCIIBuffer(c)) && SbName::isIdentChar(c)) {
// If the identifier is too long, it will be silently
// truncated.
if (b - buf < 255)
*b++ = c;
}
}
}
else {
if ((gotChar = getASCIIFile(c)) && SbName::isIdentStartChar(c)) {
*b++ = c;
while ((gotChar = getASCIIFile(c)) && SbName::isIdentChar(c)) {
// If the identifier is too long, it will be silently
// truncated.
if (b - buf < 255)
*b++ = c;
}
}
}
*b = '\0';
// Put the terminating character (if any) back in the stream.
if (gotChar)
putBack(c);
n = buf;
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// These all read a number of some type from current file/buffer.
// Returns FALSE on EOF or if no valid number was read.
// These macros are used by all the functions that read integers/reals.
//
// Use: public
//
#define READ_NUM(reader, readType, num, dglFunc, type, dglType) \
SbBool ok; \
if (! skipWhiteSpace()) \
ok = FALSE; \
else if (curFile->binary) { \
int n = M_SIZEOF(dglType); \
int pad = ((n+3) & ~0003) - n; \
dglType tnum; \
if (fromBuffer()) { \
if (eof()) \
ok = FALSE; \
else { \
ok = TRUE; \
dglFunc(curFile->curBuf, (dglType *)&tnum); \
curFile->curBuf += M_SIZEOF(dglType) + pad; \
} \
} \
else { \
if (backupBufUsed == TRUE) { \
num = (type)(*(type *)backupBuf); \
backupBufUsed = FALSE; \
return TRUE; \
} \
char padbuf[4]; \
makeRoomInBuf(M_SIZEOF(dglType)); \
ok = fread(tmpBuffer, M_SIZEOF(dglType), 1, curFile->fp); \
dglFunc((char *)tmpBuffer, (dglType *)&tnum); \
if (pad != 0) \
ok = fread((void *)padbuf, M_SIZEOF(char), pad, curFile->fp); \
} \
num = (type)tnum; \
} \
else { \
readType _tmp; \
ok = reader(_tmp); \
if (ok) \
num = (type) _tmp; \
} \
return ok
#define READ_BIN_ARRAY(array, length, dglFunc, type) \
SbBool ok = TRUE; \
if (! skipWhiteSpace()) \
ok = FALSE; \
else if (fromBuffer()) { \
if (eof()) \
ok = FALSE; \
else { \
dglFunc(curFile->curBuf, (type *)array, length); \
curFile->curBuf += length * M_SIZEOF(type); \
} \
} \
else { \
makeRoomInBuf(length * M_SIZEOF(type)); \
int i = fread(tmpBuffer, M_SIZEOF(type), length, curFile->fp); \
if (i != length) \
return FALSE; \
dglFunc((char *)tmpBuffer, (type *)array, length); \
} \
return ok
#define READ_INTEGER(num, dglFunc, type, dglType) \
READ_NUM(readInteger, int32_t, num, dglFunc, type, dglType)
#define READ_UNSIGNED_INTEGER(num, dFunc, type, dType) \
READ_NUM(readUnsignedInteger, uint32_t, num, dFunc, type, dType)
#define READ_REAL(num, dglFunc, type, dglType) \
READ_NUM(readReal, double, num, dglFunc, type, dglType)
SbBool
SoInput::read(int &i)
{
READ_INTEGER(i, convertInt32, int, int32_t);
}
SbBool
SoInput::read(unsigned int &i)
{
READ_UNSIGNED_INTEGER(i, convertInt32, unsigned int, int32_t);
}
SbBool
SoInput::read(short &s)
{
READ_INTEGER(s, convertInt32, short, int32_t);
}
SbBool
SoInput::read(unsigned short &s)
{
READ_UNSIGNED_INTEGER(s, convertInt32, unsigned short, int32_t);
}
// made redundant by typedef of int32_t
//SbBool
//SoInput::read(int32_t &l)
//{
// READ_INTEGER(l, convertInt32, int32_t, int32_t);
//}
// made redundant by typedef of uint32_t
//SbBool
//SoInput::read(uint32_t &l)
//{
// READ_UNSIGNED_INTEGER(l, convertInt32, uint32_t, int32_t);
//}
SbBool
SoInput::read(float &f)
{
READ_REAL(f, convertFloat, float, float);
}
SbBool
SoInput::read(double &d)
{
READ_REAL(d, convertDouble, double, double);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads an array of unsigned chars from current file/buffer.
//
// Use: public
SbBool
SoInput::readBinaryArray(unsigned char *c, int length)
//
////////////////////////////////////////////////////////////////////////
{
SbBool ok = TRUE;
if (! skipWhiteSpace())
ok = FALSE;
else if (fromBuffer()) {
if (eof())
ok = FALSE;
else {
memcpy((unsigned char *)c, curFile->curBuf, length);
curFile->curBuf += length * M_SIZEOF(unsigned char);
}
}
else {
int i = fread(c, M_SIZEOF(unsigned char), length, curFile->fp);
if (i != length)
return FALSE;
}
return ok;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads an array of int32_ts from current file/buffer.
//
// Use: public
SbBool
SoInput::readBinaryArray(int32_t *l, int length)
//
////////////////////////////////////////////////////////////////////////
{
READ_BIN_ARRAY(l, length, convertInt32Array, int32_t);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads an array of floats from current file/buffer.
//
// Use: public
SbBool
SoInput::readBinaryArray(float *f, int length)
//
////////////////////////////////////////////////////////////////////////
{
READ_BIN_ARRAY(f, length, convertFloatArray, float);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads an array of doubles from current file/buffer.
//
// Use: public
SbBool
SoInput::readBinaryArray(double *d, int length)
//
////////////////////////////////////////////////////////////////////////
{
READ_BIN_ARRAY(d, length, convertDoubleArray, double);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Converts short from network format and puts in buffer.
//
// Use: private
void
SoInput::convertShort(char *from, short *s)
//
////////////////////////////////////////////////////////////////////////
{
int32_t l;
DGL_NTOH_INT32( l, INT32(from) );
*s = (short)l;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Converts long from network format and puts in buffer.
//
// Use: private
void
SoInput::convertInt32(char *from, int32_t *l)
//
////////////////////////////////////////////////////////////////////////
{
DGL_NTOH_INT32( *l, INT32(from) );
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Converts float from network format and puts in buffer.
//
// Use: private
void
SoInput::convertFloat(char *from, float *f)
//
////////////////////////////////////////////////////////////////////////
{
DGL_NTOH_FLOAT( *f, FLOAT(from) );
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Converts double from network format and puts in buffer.
//
// Use: private
void
SoInput::convertDouble(char *from, double *d)
//
////////////////////////////////////////////////////////////////////////
{
DGL_NTOH_DOUBLE( *d, DOUBLE(from) );
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Converts array of shorts in read buffer from network format and
// puts in array.
//
// Use: private
void
SoInput::convertShortArray( char *from,
register short *to,
register int len)
//
////////////////////////////////////////////////////////////////////////
{
register char *b = from;
len >>= 1; // convert bytes to short
while (len > 4) { // unroll the loop a bit
DGL_NTOH_SHORT( to[0], SHORT(b));
DGL_NTOH_SHORT( to[1], SHORT(b + M_SIZEOF(short)));
DGL_NTOH_SHORT( to[2], SHORT(b + M_SIZEOF(short)*2));
DGL_NTOH_SHORT( to[3], SHORT(b + M_SIZEOF(short)*3));
to += 4;
b += M_SIZEOF(short)*4;
len -= 4;
}
while (len-- > 0) {
DGL_NTOH_SHORT( *to, SHORT(b));
to++;
b += M_SIZEOF(short);
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Converts array of int32_ts in read buffer from network format and
// puts in array.
//
// Use: private
void
SoInput::convertInt32Array( char *from,
register int32_t *to,
register int len)
//
////////////////////////////////////////////////////////////////////////
{
register int32_t *t = to;
register char *b = from;
while (len > 4) { // unroll the loop a bit
DGL_NTOH_INT32( t[0], INT32(b));
DGL_NTOH_INT32( t[1], INT32(b + M_SIZEOF(int32_t)));
DGL_NTOH_INT32( t[2], INT32(b + M_SIZEOF(int32_t)*2));
DGL_NTOH_INT32( t[3], INT32(b + M_SIZEOF(int32_t)*3));
t += 4;
b += M_SIZEOF(int32_t)*4;
len -= 4;
}
while (len-- > 0) {
DGL_NTOH_INT32( *t, INT32(b));
t++;
b += M_SIZEOF(int32_t);
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Converts array of floats in read buffer from network format and
// puts in array.
//
// Use: private
void
SoInput::convertFloatArray( char *from,
float *to,
register int len)
//
////////////////////////////////////////////////////////////////////////
{
register float *t = to;
register char *b = from;
while (len > 4) { // unroll the loop a bit
DGL_NTOH_FLOAT( t[0], FLOAT(b));
DGL_NTOH_FLOAT( t[1], FLOAT(b + M_SIZEOF(float)));
DGL_NTOH_FLOAT( t[2], FLOAT(b + M_SIZEOF(float)*2));
DGL_NTOH_FLOAT( t[3], FLOAT(b + M_SIZEOF(float)*3));
t += 4;
b += M_SIZEOF(float)*4;
len -= 4;
}
while (len-- > 0) {
DGL_NTOH_FLOAT( *t, FLOAT(b));
t++;
b += M_SIZEOF(float);
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Converts array of doubles in read buffer from network format and
// puts in array.
//
// Use: private
void
SoInput::convertDoubleArray( char *from,
register double *to,
register int len)
//
////////////////////////////////////////////////////////////////////////
{
register char *b = from;
len >>= 3; // convert bytes to doubles
while (len > 4) { // unroll the loop a bit
DGL_NTOH_DOUBLE( to[0], DOUBLE(b));
DGL_NTOH_DOUBLE( to[1], DOUBLE(b + M_SIZEOF(double)));
DGL_NTOH_DOUBLE( to[2], DOUBLE(b + M_SIZEOF(double)*2));
DGL_NTOH_DOUBLE( to[3], DOUBLE(b + M_SIZEOF(double)*3));
to += 4;
b += M_SIZEOF(double)*4;
len -= 4;
}
while (len-- > 0) {
DGL_NTOH_DOUBLE( *to, DOUBLE(b));
to++;
b += M_SIZEOF(double);
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Returns TRUE if current file/buffer is at EOF.
//
// Use: public
SbBool
SoInput::eof() const
//
////////////////////////////////////////////////////////////////////////
{
if (! fromBuffer())
return feof(curFile->fp);
else
return (freeBytesInBuf() == 0);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Fills in passed string to contain description of current
// location in all open input files.
//
// Use: internal
void
SoInput::getLocationString(SbString &string) const
//
////////////////////////////////////////////////////////////////////////
{
const struct SoInputFile *f;
int i = files.getLength() - 1;
char buf[10000];
string.makeEmpty();
f = (const struct SoInputFile *) files[i];
sprintf(buf, "\tOccurred at line %3d in %s",
f->lineNum, f->fullName.getString());
string = buf;
for (--i ; i >= 0; --i) {
f = (const struct SoInputFile *) files[i];
sprintf(buf, "\n\tIncluded at line %3d in %s",
f->lineNum, f->fullName.getString());
string += buf;
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Puts a just-read character back in input stream
//
// Use: internal
void
SoInput::putBack(char c)
//
////////////////////////////////////////////////////////////////////////
{
// Never put an EOF back in the stream
if (c == (char) EOF)
return;
if (backBufIndex >= 0)
--backBufIndex;
else if (! fromBuffer())
ungetc(c, curFile->fp);
else if (isBinary())
; // Can't do anything???
else
curFile->curBuf--;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Puts a just-read character string back in input stream. It uses
// backBuf to implement this.
//
// Use: internal
void
SoInput::putBack(const char *string)
//
////////////////////////////////////////////////////////////////////////
{
backBuf = string;
backBufIndex = 0;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Opens named file, Returns a file pointer to it. If the file name
// is not absolute, it checks all of the current directories in the
// search path. It returns the full name of the file it found in
// the fullName parameter. Returns NULL on error.
//
// Use: public
FILE *
SoInput::findFile(const char *fileName, SbString &fullName) const
//
////////////////////////////////////////////////////////////////////////
{
FILE *fp;
int i;
// If filename is absolute
if (fileName[0] == '/') {
fullName = fileName;
fp = fopen(fileName, "r");
}
// For relative file names, try each of the directories in the search path
else {
fp = NULL;
for (i = 0; i < directories->getLength(); i++) {
fullName = * (*directories)[i];
fullName += "/";
fullName += fileName;
fp = fopen(fullName.getString(), "r");
if (fp != NULL)
break;
}
}
return fp;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Initializes reading from file pointer. Just sets up some variables.
//
// Use: private
void
SoInput::initFile(FILE *newFP, // New file pointer
const char *fileName, // Name of new file to read
SbString *fullName, // Full name of new file
SbBool openedHere, // TRUE if SoInput opened file
SbDict *refDict) // Dictionary of base references
// (default is NULL: create new dict)
//
////////////////////////////////////////////////////////////////////////
{
curFile->name = fileName;
if (fullName == NULL)
curFile->fullName = fileName;
else
curFile->fullName = *fullName;
curFile->fp = newFP;
curFile->buffer = NULL;
curFile->lineNum = 1;
curFile->openedHere = openedHere;
if (refDict == NULL) {
// Start with a fresh dictionary
if (curFile->refDict != NULL && ! curFile->borrowedDict)
curFile->refDict->clear();
else
curFile->refDict = new SbDict;
curFile->borrowedDict = FALSE;
}
else {
if (curFile->refDict != NULL && ! curFile->borrowedDict)
delete curFile->refDict;
curFile->refDict = refDict;
curFile->borrowedDict = TRUE;
}
// Assume file is ASCII until header is checked
curFile->binary = FALSE;
curFile->readHeader = FALSE;
curFile->headerOk = TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Checks current file/buffer for header comment that determines whether
// it is in ASCII or binary format. The header must be the very
// next thing in the input for this to recognize it. This prints an
// error message and returns FALSE if there is no header and we are
// reading from a file.
//
// Use: private
SbBool
SoInput::checkHeader()
//
////////////////////////////////////////////////////////////////////////
{
SoDBHeaderCB *preCB;
SoDBHeaderCB *postCB;
void *userData;
SbBool isBinary;
float versionNum;
char c;
// Don't need to do this again. This has to be set first here so
// the subsequent reads don't try to do this again.
curFile->readHeader = TRUE;
// See if first character in file is a comment character. If so,
// see if there is a Inventor file header at the beginning. If so,
// determine whether it is ASCII or binary. If there is no valid
// header, assume it is ASCII.
if (get(c)) {
if (c == COMMENT_CHAR) {
char buf[256];
int i = 0;
// Read comment into buffer
buf[i++] = c;
while (get(c) && c != '\n')
buf[i++] = c;
buf[i] = '\0';
if (c == '\n')
curFile->lineNum++;
// Read the file if the header is a registered header, or
// if the file header is a superset of a registered header.
if (SoDB::getHeaderData(buf, isBinary, versionNum,
preCB, postCB, userData, TRUE)) {
if (isBinary) {
curFile->binary = TRUE;
if (tmpBuffer == NULL) {
tmpBuffer = malloc(64);
tmpBufSize = 64;
curTmpBuf = (char *)tmpBuffer;
}
} else {
curFile->binary = FALSE;
}
// Set the Inventor file version associated with the header
setIVVersion(versionNum);
// Invoke the pre-callback associated with the header
if (preCB)
(*preCB)(userData, this);
// Store a pointer to the post-callback for later use
curFile->postReadCB = postCB;
curFile->CBData = userData;
curFile->headerOk = TRUE;
curFile->headerString = buf;
return TRUE;
}
}
// Put non-comment char back in case we are reading from a buffer.
else
putBack(c);
}
// If it gets here, no valid header was found. If we are reading
// from a buffer, we can just assume we are reading ASCII, in the
// latest format:
if (fromBuffer()) {
curFile->binary = FALSE;
return TRUE;
}
// If we are reading from a file, it MUST have a header
SoReadError::post(this, "File does not have a valid header string");
curFile->headerOk = FALSE;
return FALSE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Skips over white space (blanks, tabs, newlines) and Inventor
// comments (from COMMENT_CHAR to end of line). Keeps current line
// number up-to-date. Returns FALSE on error.
//
// Use: private
SbBool
SoInput::skipWhiteSpace()
//
////////////////////////////////////////////////////////////////////////
{
char c;
SbBool gotChar;
// Check for ASCII/binary header if not already done. Since most
// of the read methods call this first, it's a convenient place to
// do it.
if (! curFile->readHeader && ! checkHeader())
return FALSE;
// Don't skip space in binary input. In ASCII, keep going while
// space and comments appear
if (! curFile->binary) {
if (fromBuffer()) {
while (TRUE) {
// Skip over space characters
while ((gotChar = getASCIIBuffer(c)) && isspace(c))
if (c == '\n')
curFile->lineNum++;
if (! gotChar)
break;
// If next character is comment character, flush til end of line
if (c == COMMENT_CHAR) {
while (getASCIIBuffer(c) && c != '\n')
;
if (eof())
SoReadError::post(this,
"EOF reached before end of comment");
else
curFile->lineNum++;
}
else {
putBack(c);
break; // EXIT: hit a non-comment, non-space
}
}
}
else {
while (TRUE) {
// Skip over space characters
while ((gotChar = getASCIIFile(c)) && isspace(c))
if (c == '\n')
curFile->lineNum++;
if (! gotChar)
break;
// If next character is comment character, flush til end of line
if (c == COMMENT_CHAR) {
while (getASCIIFile(c) && c != '\n')
;
if (eof())
SoReadError::post(this,
"EOF reached before end of comment");
else
curFile->lineNum++;
}
else {
putBack(c);
break; // EXIT: hit a non-comment, non-space
}
}
}
}
// If EOF, pop to previous file and skip space again
while (eof() && popFile())
if (! skipWhiteSpace())
return FALSE;
return TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Pops current file from stack. This should be called when current
// file is at EOF and more stuff is needed. This returns FALSE if
// there ain't no more files on the stack.
//
// Use: private
SbBool
SoInput::popFile()
//
////////////////////////////////////////////////////////////////////////
{
// Call the post callback associated with this file type (as determined
// by the file header)
if (curFile->postReadCB)
(*curFile->postReadCB)(curFile->CBData, this);
int depth = files.getLength();
// Nothing to pop if we're already in last file on stack
if (depth == 1)
return FALSE;
// Remove one file
files.truncate(depth - 1);
// Free up structure for current file and set to new one
if (curFile->openedHere)
fclose(curFile->fp);
delete curFile->refDict;
delete curFile;
curFile = (struct SoInputFile *) files[depth - 2];
return TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads a hexadecimal number into a int32_t. Returns FALSE on EOF or
// if no valid hexadecimal number was read.
//
// Use: public
SbBool
SoInput::readHex(uint32_t &l)
{
int i;
char str[32]; // Number can't be longer than this
char *s = str;
SbBool ret;
// Read from backBuf if it is not empty
if (backBufIndex >= 0) {
ret = FALSE;
if (backBufIndex >= 3) {
strcpy(str, backBuf.getString());
ret = TRUE;
}
// Clear the back buffer.
backBuf.makeEmpty();
backBufIndex = -1;
}
// Read from a memory buffer
else if (fromBuffer()) {
skipWhiteSpace();
s = curFile->curBuf;
ret = TRUE;
}
// Read from a file
else {
skipWhiteSpace();
while ((i = getc(curFile->fp)) != EOF) {
*s = (char) i;
if (*s == ',' || *s == ']' || *s == '}' || isspace(*s)) {
putBack(*s);
*s = '\0';
break;
}
s++;
}
ret = (s - str <= 0) ? FALSE : TRUE;
s = str;
}
// Convert the hex string we just got into an uint32_teger
if (ret) {
int i;
int minSize = 3; // Must be at least this many bytes in str
char *save = s;
if (*s++ == '0') {
if (*s == '\0' || *s == ',' || *s == ']' || *s == '}' ||
isspace(*s))
{
l = 0;
curFile->curBuf++;
}
else if (*s == 'x' || *s == 'X') {
s++;
l = 0;
while (*s != '\0' && *s != ',' && *s != ']' && *s != '}' &&
! isspace(*s))
{
i = (int)*s;
if (i >= '0' && i <= '9') {
i -= '0';
l = (l<<4) + i;
}
else if (i >= 'A' && i <= 'F') {
i -= ('A' - 10);
l = (l<<4) + i;
}
else if (i >= 'a' && i <= 'f') {
i -= ('a' - 10);
l = (l<<4) + i;
}
s++;
}
if (fromBuffer()) {
// Make sure we have at least 1 actual digit
if (s - curFile->curBuf < minSize) {
if (fromBuffer())
curFile->curBuf = save;
else
putBack(save);
ret = FALSE;
}
else
curFile->curBuf = s;
}
else if (s - str < minSize) {
if (fromBuffer())
curFile->curBuf = save;
else
putBack(save);
ret = FALSE;
}
}
}
}
return ret;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads a int32_t signed integer. Returns FALSE on EOF or if no
// valid integer was read.
//
// Use: private
SbBool
SoInput::readInteger(int32_t &l)
//
////////////////////////////////////////////////////////////////////////
{
char str[32]; // Number can't be longer than this
char *s = str;
int i;
SbBool ret;
// Read from backBuf if it is not empty
if (backBufIndex >= 0) {
strcpy(str, backBuf.getString());
// Clear the back buffer.
backBuf.makeEmpty();
backBufIndex = -1;
s = str;
ret = TRUE;
}
// Read from a memory buffer
else if (fromBuffer()) {
s = curFile->curBuf;
ret = TRUE;
}
// Read from a file
else {
int i;
while ((i = getc(curFile->fp)) != EOF) {
*s = (char) i;
if (*s == ',' || *s == ']' || *s == '}' || isspace(*s)) {
putBack(*s);
*s = '\0';
break;
}
s++;
}
ret = (s - str <= 0) ? FALSE : TRUE;
s = str;
}
// Convert the string we just got into a int32_t integer
if (ret) {
char *ptr;
char *save = s;
if (*s == '0') {
s++;
// The string just contains a single zero
if (*s == '\0' || *s == ',' || *s == ']' || *s == '}' ||
isspace(*s))
{
l = 0;
ret = TRUE;
}
// A hexadecimal format number
else if (*s == 'x' || *s == 'X') {
s++;
l = 0;
ptr = s;
while (*s != '\0') {
i = (int)*s;
if (i >= '0' && i <= '9') {
i -= '0';
l = (l<<4) + i;
}
else if (i >= 'A' && i <= 'F') {
i -= ('A' - 10);
l = (l<<4) + i;
}
else if (i >= 'a' && i <= 'f') {
i -= ('a' - 10);
l = (l<<4) + i;
}
else { // unrecognized character; stop processing
break;
}
s++;
}
if (s == ptr) {
if (fromBuffer())
s = curFile->curBuf = save;
else
putBack(save);
ret = FALSE;
}
}
// An octal format number
else {
l = 0;
ptr = s;
while ((int)*s >= '0' && (int)*s <= '7') {
i = (int)*s - '0';
l = (l<<3) + i;
s++;
}
if (s == ptr) {
if (fromBuffer())
s = curFile->curBuf = save;
else
putBack(save);
ret = FALSE;
}
}
}
// A decimal format number
else {
int sign = 1;
l = 0;
if (*s == '-' || *s == '+') {
s++;
sign = -1;
}
ptr = s;
while ((int)*s >= '0' && (int)*s <= '9') {
i = (int)*s - '0';
l = l*10 + i;
s++;
}
l *= sign;
if (s == ptr) {
if (fromBuffer())
s = curFile->curBuf = save;
else
putBack(save);
ret = FALSE;
}
}
if (fromBuffer())
curFile->curBuf = s;
}
return ret;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads a int32_t unsigned integer. Returns FALSE on EOF or if no
// valid integer was read.
//
// Use: private
SbBool
SoInput::readUnsignedInteger(uint32_t &l)
//
////////////////////////////////////////////////////////////////////////
{
char str[32]; // Number can't be longer than this
char *s = str;
int i;
SbBool ret;
// Read from backBuf if it is not empty
if (backBufIndex >= 0) {
strcpy(str, backBuf.getString());
// Clear the back buffer.
backBuf.makeEmpty();
backBufIndex = -1;
s = str;
ret = TRUE;
}
// Read from a memory buffer
else if (fromBuffer()) {
s = curFile->curBuf;
ret = TRUE;
}
// Read from a file
else {
int i;
while ((i = getc(curFile->fp)) != EOF) {
*s = (char) i;
if (*s == ',' || *s == ']' || *s == '}' || isspace(*s)) {
putBack(*s);
*s = '\0';
break;
}
s++;
}
ret = (s - str <= 0) ? FALSE : TRUE;
s = str;
}
// Convert the string we just got into an uint32_teger
if (ret) {
char *ptr;
char *save = s;
if (*s == '0') {
s++;
// The string just contains a single zero
if (*s == '\0' || *s == ',' || *s == ']' || *s == '}' ||
isspace(*s))
{
l = 0;
ret = TRUE;
}
// A hexadecimal format number
else if (*s == 'x' || *s == 'X') {
s++;
l = 0;
ptr = s;
while (*s != '\0') {
i = (int)*s;
if (i >= '0' && i <= '9') {
i -= '0';
l = (l<<4) + i;
}
else if (i >= 'A' && i <= 'F') {
i -= ('A' - 10);
l = (l<<4) + i;
}
else if (i >= 'a' && i <= 'f') {
i -= ('a' - 10);
l = (l<<4) + i;
}
else { // unrecognized character; stop processing
break;
}
s++;
}
if (s == ptr) {
if (fromBuffer())
s = curFile->curBuf = save;
else
putBack(save);
ret = FALSE;
}
}
// An octal format number
else {
l = 0;
ptr = s;
while ((int)*s >= '0' && (int)*s <= '7') {
i = (int)*s - '0';
l = (l<<3) + i;
s++;
}
if (s == ptr) {
if (fromBuffer())
s = curFile->curBuf = save;
else
putBack(save);
ret = FALSE;
}
}
}
// A decimal format number
else {
l = 0;
ptr = s;
while ((int)*s >= '0' && (int)*s <= '9') {
i = (int)*s - '0';
l = l*10 + i;
s++;
}
if (s == ptr) {
if (fromBuffer())
s = curFile->curBuf = save;
else
putBack(save);
ret = FALSE;
}
}
if (fromBuffer())
curFile->curBuf = s;
}
return ret;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads a string containing an unsigned integer. Returns FALSE on
// EOF or if no valid integer string was read.
//
// Use: private
SbBool
SoInput::readUnsignedIntegerString(char *str)
//
////////////////////////////////////////////////////////////////////////
{
int minSize = 1; // Must be at least this many bytes in str
char *s = str;
// If the integer begins with '0', it may be in octal or hex
if (readChar(s, '0')) {
// Check for '0x', signifying hex
if (readChar(s + 1, 'x')) {
s += 2 + readHexDigits(s + 2);
minSize = 3;
}
// Read digits after zero. There don't have to be any, though
else
s += 1 + readDigits(s + 1);
}
else
s += readDigits(s);
// Make sure we have at least 1 actual digit
if (s - str < minSize)
return FALSE;
// End string with null byte
*s = '\0';
return TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads a double floating-point real number. Returns FALSE on EOF
// or if no valid real was read. This is used by all real-reading
// methods.
//
// Use: private
SbBool
SoInput::readReal(double &d)
//
////////////////////////////////////////////////////////////////////////
{
int n;
char str[32]; // Number can't be longer than this
char *s = str;
SbBool ret;
// Read from backBuf if it is not empty
if (backBufIndex >= 0) {
n = sscanf(backBuf.getString(), "%lf", &d);
// Clear the back buffer.
backBuf.makeEmpty();
backBufIndex = -1;
ret = (n == 0 || n == EOF) ? FALSE : TRUE;
}
else if (fromBuffer()) {
SbBool gotNum = FALSE;
////////////////////////////////////////////
//
// Leading sign
n = readChar(s, '-');
if (n == 0)
n = readChar(s, '+');
s += n;
////////////////////////////////////////////
//
// Integer before decimal point
if ((n = readDigits(s)) > 0) {
gotNum = TRUE;
s += n;
}
////////////////////////////////////////////
//
// Decimal point
if (readChar(s, '.') > 0) {
s++;
////////////////////////////////////////////
//
// Integer after decimal point (no sign)
if ((n = readDigits(s)) > 0) {
gotNum = TRUE;
s += n;
}
}
// If no number before or after decimal point, there's a problem
if (! gotNum)
return FALSE;
////////////////////////////////////////////
//
// 'e' or 'E' for exponent
n = readChar(s, 'e');
if (n == 0)
n = readChar(s, 'E');
if (n > 0) {
s += n;
////////////////////////////////////////////
//
// Sign for exponent
n = readChar(s, '-');
if (n == 0)
n = readChar(s, '+');
s += n;
////////////////////////////////////////////
//
// Exponent integer
if ((n = readDigits(s)) > 0)
s += n;
else
return FALSE; // Invalid exponent
}
// Terminator
*s = '\0';
d = atof(str);
ret = TRUE;
}
else {
n = fscanf(curFile->fp, "%lf", &d);
ret = (n == 0 || n == EOF) ? FALSE : TRUE;
}
return ret;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads a series of decimal digits into string. Returns number of
// bytes read.
//
// Use: private
int
SoInput::readDigits(char *string)
//
////////////////////////////////////////////////////////////////////////
{
char c, *s = string;
if (fromBuffer()) {
while (getASCIIBuffer(c)) {
if (isdigit(c))
*s++ = c;
else {
putBack(c);
break;
}
}
}
else {
while (getASCIIFile(c)) {
if (isdigit(c))
*s++ = c;
else {
putBack(c);
break;
}
}
}
return s - string;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads a series of hexadecimal digits into string. Returns number
// of bytes read.
//
// Use: private
int
SoInput::readHexDigits(char *string)
//
////////////////////////////////////////////////////////////////////////
{
char c, *s = string;
if (fromBuffer()) {
while (getASCIIBuffer(c)) {
if (isxdigit(c))
*s++ = c;
else {
putBack(c);
break;
}
}
}
else {
while (getASCIIFile(c)) {
if (isxdigit(c))
*s++ = c;
else {
putBack(c);
break;
}
}
}
return s - string;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Reads the given character. Returns the number of bytes read (0 or 1).
//
// Use: private
int
SoInput::readChar(char *string, char charToRead)
//
////////////////////////////////////////////////////////////////////////
{
char c;
int ret;
if (fromBuffer()) {
if (! getASCIIBuffer(c))
ret = 0;
else if (c == charToRead) {
*string = c;
ret = 1;
}
else {
putBack(c);
ret = 0;
}
}
else {
if (! getASCIIFile(c))
ret = 0;
else if (c == charToRead) {
*string = c;
ret = 1;
}
else {
putBack(c);
ret = 0;
}
}
return ret;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Adds a reference to dictionary in current file/buffer.
// The name passed will be a full identifier-- the object's name
// followed by a '+' and the object's reference ID. This routine
// takes care of stripping the name before the '+' and giving the
// object that name. It also notices if the name is an underscore
// followed by nothing but digits (Inventor 1.0 format for
// instances) and leaves the object unnamed in that case.
// Called by SoBase.
//
// Use: private
void
SoInput::addReference(const SbName &name, // Reference name
SoBase *base, SbBool addToGlobalDict)
//
////////////////////////////////////////////////////////////////////////
{
// Enter in dictionary : generates a CC warning...
curFile->refDict->enter((unsigned long) name.getString(), (void *) base);
int length = name.getLength();
if (length == 0) return;
const char *n = name.getString();
// If we're reading a 1.0 file and the name is an '_' followed by
// all digits, don't name the node.
if (n[0] == '_' && curFile->ivVersion == 1.0f) {
int i;
for (i = 1; i < length; i++) {
if (!isdigit(n[i])) break;
}
if (i == length) return;
}
if (addToGlobalDict) {
// Look for the first '+':
char *firstPlus = strchr(n, '+');
if (firstPlus == NULL) {
base->setName(name);
}
else if (firstPlus != n) {
SbName instanceName(SbString(n, 0, firstPlus-n-1));
base->setName(instanceName);
}
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Removes a reference from the current file/buffer dictionary.
//
// Use: private
void
SoInput::removeReference(const SbName &name)
//
////////////////////////////////////////////////////////////////////////
{
curFile->refDict->remove((unsigned long) name.getString());
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Finds a reference in dictionary, returning the base pointer or NULL.
//
// Use: private
SoBase * // Returns pointer to base
SoInput::findReference(const SbName &name) const // Reference name
//
////////////////////////////////////////////////////////////////////////
{
void *base;
// Generates a CC warning. Ho hum.
if (curFile->refDict->find((unsigned long) name.getString(), base))
return (SoBase *) base;
return NULL;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Makes sure temp buffer can contain nBytes more bytes. Returns
// FALSE if this is not possible.
//
// Use: private
SbBool
SoInput::makeRoomInBuf(size_t nBytes)
//
////////////////////////////////////////////////////////////////////////
{
// If already had problems with buffer, stop
if (tmpBuffer == NULL)
return FALSE;
// If buffer not big enough, realloc a bigger one
if (nBytes >= tmpBufSize) {
// While not enough room, double size of buffer
while (nBytes >= tmpBufSize)
tmpBufSize *= 2;
tmpBuffer = realloc(tmpBuffer, tmpBufSize);
// Test for bad reallocation
if (tmpBuffer == NULL)
return FALSE;
}
return TRUE;
}