#include #include #include #include #include #include #include #define CURVERSION 1.0 // Current version of file format #define COMMENT_CHAR '#' static const int numValidASCIIHeaders = 1; struct headerStorage { const char *string; float version; }; static const headerStorage ASCIIHeader[1] = { { "#VRML V1.0 ascii", /* 20 chars */ 1.0 }, }; float QvInput::isASCIIHeader(const char *string) { for (int i = 0; i < numValidASCIIHeaders; i++) { if (strcmp(ASCIIHeader[i].string, string) == 0) return ASCIIHeader[i].version; } return FALSE; } QvInput::QvInput() { setFilePointer(stdin); } QvInput::~QvInput() { } void QvInput::setFilePointer(FILE *newFP) { fp = newFP; lineNum = 1; version = CURVERSION; readHeader = FALSE; headerOk = TRUE; backBufIndex = -1; } float QvInput::getVersion() { if (! readHeader) (void) checkHeader(); return version; } QvBool QvInput::get(char &c) { QvBool ret; if (backBufIndex >= 0) { c = backBuf.getString()[backBufIndex++]; if (c != '\0') return TRUE; backBuf.makeEmpty(); backBufIndex = -1; } if (! readHeader && ! checkHeader()) return FALSE; if (eof()) { c = (char)EOF; ret = FALSE; } else { int i = getc(fp); if (i == EOF) { c = (char)EOF; ret = FALSE; } else { c = (char) i; ret = TRUE; } } return ret; } QvBool QvInput::read(char &c) { return (skipWhiteSpace() && get(c)); } QvBool QvInput::read(QvString &s) { if (! skipWhiteSpace()) return FALSE; QvBool quoted; char c; char bufStore[256]; char *buf; int bytesLeft; s.makeEmpty(); if (! get(c)) return FALSE; quoted = (c == '\"'); if (! quoted) putBack(c); do { buf = bufStore; bytesLeft = sizeof(bufStore) - 1; while (bytesLeft > 0) { if (! get(*buf)) break; if (quoted) { if (*buf == '\"') break; if (*buf == '\\') { if ((get(c)) && c == '\"') *buf = '\"'; else putBack(c); } if (*buf == '\n') lineNum++; } else if (isspace(*buf)) { putBack(*buf); break; } buf++; bytesLeft--; } *buf = '\0'; s += bufStore; } while (bytesLeft == 0); return TRUE; } QvBool QvInput::read(QvName &n, QvBool validIdent) { QvBool gotChar; if (! skipWhiteSpace()) return FALSE; if (! validIdent) { QvString s; if (! read(s)) return FALSE; n = s; } else { char buf[256]; char *b = buf; char c; if ((gotChar = get(c)) && QvName::isIdentStartChar(c)) { *b++ = c; while ((gotChar = get(c)) && QvName::isIdentChar(c)) { if (b - buf < 255) *b++ = c; } } *b = '\0'; if (gotChar) putBack(c); n = buf; } return TRUE; } #define READ_NUM(reader, readType, num, type) \ QvBool ok; \ if (! skipWhiteSpace()) \ ok = FALSE; \ else { \ readType _tmp; \ ok = reader(_tmp); \ if (ok) \ num = (type) _tmp; \ } \ return ok #define READ_INTEGER(num, type) \ READ_NUM(readInteger, long, num, type) #define READ_UNSIGNED_INTEGER(num, type) \ READ_NUM(readUnsignedInteger, unsigned long, num, type) #define READ_REAL(num, type) \ READ_NUM(readReal, double, num, type) QvBool QvInput::read(int &i) { READ_INTEGER(i, int); } QvBool QvInput::read(unsigned int &i) { READ_UNSIGNED_INTEGER(i, unsigned int); } QvBool QvInput::read(short &s) { READ_INTEGER(s, short); } QvBool QvInput::read(unsigned short &s) { READ_UNSIGNED_INTEGER(s, unsigned short); } QvBool QvInput::read(long &l) { READ_INTEGER(l, long); } QvBool QvInput::read(unsigned long &l) { READ_UNSIGNED_INTEGER(l, unsigned long); } QvBool QvInput::read(float &f) { READ_REAL(f, float); } QvBool QvInput::read(double &d) { READ_REAL(d, double); } QvBool QvInput::eof() const { return feof(fp); } void QvInput::getLocationString(QvString &string) const { char buf[128]; sprintf(buf, "\tOccurred at line %3d", lineNum); string = buf; } void QvInput::putBack(char c) { if (c == (char) EOF) return; if (backBufIndex >= 0) --backBufIndex; else ungetc(c, fp); } void QvInput::putBack(const char *string) { backBuf = string; backBufIndex = 0; } QvBool QvInput::checkHeader() { char c; readHeader = TRUE; if (get(c)) { if (c == COMMENT_CHAR) { char buf[256]; int i = 0; buf[i++] = c; while (get(c) && c != '\n') buf[i++] = c; buf[i] = '\0'; if (c == '\n') lineNum++; if ((version = isASCIIHeader(buf))) return TRUE; } else putBack(c); } QvReadError::post(this, "File does not have a valid header string"); headerOk = FALSE; return FALSE; } QvBool QvInput::skipWhiteSpace() { char c; QvBool gotChar; if (! readHeader && ! checkHeader()) return FALSE; while (TRUE) { while ((gotChar = get(c)) && isspace(c)) if (c == '\n') lineNum++; if (! gotChar) break; if (c == COMMENT_CHAR) { while (get(c) && c != '\n') ; if (eof()) QvReadError::post(this, "EOF reached before end of comment"); else lineNum++; } else { putBack(c); break; } } return TRUE; } QvBool QvInput::readInteger(long &l) { char str[32]; char *s = str; if (readChar(s, '-') || readChar(s, '+')) s++; if (! readUnsignedIntegerString(s)) return FALSE; l = strtol(str, NULL, 0); return TRUE; } QvBool QvInput::readUnsignedInteger(unsigned long &l) { char str[32]; if (! readUnsignedIntegerString(str)) return FALSE; l = strtoul(str, NULL, 0); return TRUE; } QvBool QvInput::readUnsignedIntegerString(char *str) { int minSize = 1; char *s = str; if (readChar(s, '0')) { if (readChar(s + 1, 'x')) { s += 2 + readHexDigits(s + 2); minSize = 3; } else s += 1 + readDigits(s + 1); } else s += readDigits(s); if (s - str < minSize) return FALSE; *s = '\0'; return TRUE; } QvBool QvInput::readReal(double &d) { char str[32]; int n; char *s = str; QvBool gotNum = FALSE; n = readChar(s, '-'); if (n == 0) n = readChar(s, '+'); s += n; if ((n = readDigits(s)) > 0) { gotNum = TRUE; s += n; } if (readChar(s, '.') > 0) { s++; if ((n = readDigits(s)) > 0) { gotNum = TRUE; s += n; } } if (! gotNum) return FALSE; n = readChar(s, 'e'); if (n == 0) n = readChar(s, 'E'); if (n > 0) { s += n; n = readChar(s, '-'); if (n == 0) n = readChar(s, '+'); s += n; if ((n = readDigits(s)) > 0) s += n; else return FALSE; } *s = '\0'; d = atof(str); return TRUE; } int QvInput::readDigits(char *string) { char c, *s = string; while (get(c)) { if (isdigit(c)) *s++ = c; else { putBack(c); break; } } return s - string; } int QvInput::readHexDigits(char *string) { char c, *s = string; while (get(c)) { if (isxdigit(c)) *s++ = c; else { putBack(c); break; } } return s - string; } int QvInput::readChar(char *string, char charToRead) { char c; int ret; if (! get(c)) ret = 0; else if (c == charToRead) { *string = c; ret = 1; } else { putBack(c); ret = 0; } return ret; } void QvInput::addReference(const QvName &name, QvNode *node) { refDict.enter((u_long) name.getString(), (void *) node); node->setName(name); } QvNode * QvInput::findReference(const QvName &name) const { void *node; if (refDict.find((u_long) name.getString(), node)) return (QvNode *) node; return NULL; }