//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 1997 // // File: parmio.h // //-------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////////// // PARMIO.H: Parameter file I/O routines // ////////////////////////////////////////////////////////////////////////////////// #ifndef _PARMIO_H_ #define _PARMIO_H_ #include "stlstream.h" // Parameter defining a named, nestable and iteratable item in a // parameter file. There are currently two type: blocks and values. // A block is a grouping of values and other blocks. A value is // a name = value pair. Blocks are bounded by {}, values are terminated // by ';'. struct PARMBLK { enum EPBLK { EPB_NONE, // nothing EPB_VAL, // simple name = value syntax EPB_BLK, // named block EPB_MAX }; ZSTR _zsName; // Name of value or block int _indx; // index (-1 for "not present") EPBLK _eBlk; // type of block PARMBLK ( EPBLK eBlk = EPB_NONE, SZC szc = NULL, int indx = -1 ) : _eBlk( eBlk ), _indx(indx) { if ( szc ) _zsName = szc; } bool operator == ( const PARMBLK & pblk ) const; bool operator != ( const PARMBLK & pblk ) const; bool operator > ( const PARMBLK & pblk ) const; bool operator < ( const PARMBLK & pblk ) const; }; // Define a stack of PARMBLKs; used for output parameter file writing class STKPARMBLK : public vector {}; // Extended descriptor for a block read in from a parameter file. // Contains starting and ending offsets within the positionable stream. struct PARMBLKIN : PARMBLK { int _iblkEnd; // index of last+1 item/block in scope of this block streamoff _offsBeg; // Starting offset in the stream streamoff _offsEnd; // Ending offset in the stream streamoff _offsData; // Starting offset of the data in the block PARMBLKIN ( EPBLK eBlk = EPB_NONE, SZC szc = NULL, int indx = -1 ) : PARMBLK(eBlk,szc,indx), _iblkEnd(-1), _offsBeg(-1), _offsEnd(-1), _offsData(-1) { } bool operator == ( const PARMBLKIN & pblkin ) const; bool operator != ( const PARMBLKIN & pblkin ) const; bool operator > ( const PARMBLKIN & pblkin ) const; bool operator < ( const PARMBLKIN & pblkin ) const; }; // Define a stack of input parameter blocks for parameter file reading class STKPARMBLKIN : public vector {}; ////////////////////////////////////////////////////////////////////////////////// // // Class PARMOUTSTREAM. An output stream of parameters. // // Blocks and values are written out in sequence. Blocks // are opened, filled and closed using member functions // and function templates. All unclosed blocks are are // closed automatically during close(). // ////////////////////////////////////////////////////////////////////////////////// class PARMOUTSTREAM : public ofstream { public: PARMOUTSTREAM (); ~ PARMOUTSTREAM (); void close (); void StartBlock ( SZC szc = NULL, int indx = -1 ); void StartItem ( SZC szc = NULL, int indx = -1 ); bool BEndBlock (); bool BEndItem () { return BEndBlock(); } void nl (); ofstream & Stream () { return (ofstream&) self; } protected: STKPARMBLK _stkblk; void StartChunk ( PARMBLK::EPBLK eBlk, SZC szc = NULL, int indx = -1 ); }; ////////////////////////////////////////////////////////////////////////////////// // // Class PARMINSTREAM. An input stream of parameters. // // The input text stream is read once during scan(), and a table of // all blocks and values is built. The scan creates an outermost block // defining the entire file. Then other blocks are added as discovered, // and their starting and ending points are recorded. // // To use, construct, open() and scan(). Then, find the named section // (or value) in question using ifind(), which returns the scoping level. // Then, either construct an Iterator (nested class) to walk through the // values at that level or use ifind() to locate specific items by name. // ////////////////////////////////////////////////////////////////////////////////// enum EC_PARM_PARSE { ECPP_PARSE_ERROR = EC_ERR_MIN, ECPP_UNMATCHED_QUOTE, ECPP_UNEXPECTED_EOF, ECPP_INVALID_CLUMP, ECPP_INVALID_NAME, ECPP_INVALID_BODY, }; class PARMINSTREAM : public ifstream { public: PARMINSTREAM (); ~ PARMINSTREAM (); void close(); // Build the rapid-access table void Scan (); // Find a block or item at the given level; -1 means "current level", // zero means outermost level. Returns index of block or -1 if // not found. int IblkFind ( SZC szcName, int index = -1, int iblkOuter = -1 ); // Return the next data block in the array or // false if there are no more items. const PARMBLKIN * Pparmblk ( int iblk, int iblkOuter = -1 ); // position the stream to process the parameter bool BSeekBlk ( int iblk ); // read the parameter into a string bool BSeekBlkString ( int iblk, ZSTR & zsParam ); // Pretty-print the block stack with nesting information void Dump (); // Return true if the block index is legal bool BBlkOk ( int iblk ) const { return iblk >= 0 || iblk < _stkblk.size(); } int Cblk () const { return _stkblk.size() ; } ifstream & Stream () { return (ifstream&) self; } class Iterator { public: Iterator( PARMINSTREAM & fprm, SZC szcBlock = NULL, int index = -1, int iblkOuter = -1 ); const PARMBLKIN * PblkNext (); protected: PARMINSTREAM & _fprm; int _iblkOuter; int _iblk; }; friend class Iterator; protected: STKPARMBLKIN _stkblk; // The block array int _iline; // The current line number (parsing) ZSTR _zsWhite; // White space character set void ThrowParseError ( SZC szcError, int iline = -1, EC_PARM_PARSE ecpp = ECPP_PARSE_ERROR ); int IGetc (); bool BIswhite ( char ch ); void ScanQuote ( char ch ); void ScanClump (); void ScanBlock ( char ch ); PARMBLK::EPBLK EpblkScanItemBody ( streamoff & offsData ); int IScanUnwhite ( bool bEofOk = false ); void ScanItemList (); void ScanItemUnit (); void ScanItemDesc ( ZSTR & zsName, int & indx ) ; void ScanName ( ZSTR & szName ); }; ////////////////////////////////////////////////////////////////////////////////// // Inline functions ////////////////////////////////////////////////////////////////////////////////// inline PARMINSTREAM & operator >> (PARMINSTREAM & is, ZSTR & zs) { ios_base::iostate _St = ios_base::goodbit; zs.erase(); const ifstream::sentry _Ok(is); if (_Ok) { _TRY_IO_BEGIN size_t _N = 0 < is.width() && is.width() < zs.max_size() ? is.width() : zs.max_size(); int _C = is.rdbuf()->sgetc(); bool bmeta = false; bool bfirst = true; for (; 0 < --_N; _C = is.rdbuf()->snextc()) { if(char_traits::eq_int_type(char_traits::eof(), _C)) { _St |= ios_base::eofbit; break; } else if ( ! bmeta && _C == CH_DELM_STR ) { if ( ! bfirst ) { is.rdbuf()->snextc(); break; } } else if ( _C == CH_META && ! bmeta ) { bmeta = true; } else { bmeta = false; zs.append(1, char_traits::to_char_type(_C)); } bfirst = false; } _CATCH_IO_(is); } else { _THROW1(runtime_error("file exhausted extracting string")); } is.width(0); is.setstate(_St); return is; } ////////////////////////////////////////////////////////////////////////////////// // Template functions for parameter streams ////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////// // Write SZC: no corresponding read, since no buffer exists ////////////////////////////////////////////////////////////////////////////////// inline PARMOUTSTREAM & operator << (PARMOUTSTREAM & ofs, SZC szc) { ofs << CH_DELM_STR; for ( ; *szc ; ) { char ch = *szc++; if ( ch == CH_DELM_STR || ch == CH_META ) { if (char_traits::eq_int_type(char_traits::eof(), ofs.rdbuf()->sputc(CH_META))) break; } if (char_traits::eq_int_type(char_traits::eof(), ofs.rdbuf()->sputc(ch))) break; } ofs << CH_DELM_STR; return ofs; } ////////////////////////////////////////////////////////////////////////////////// // Read and write ZSTRs ////////////////////////////////////////////////////////////////////////////////// inline PARMOUTSTREAM & operator << (PARMOUTSTREAM & ofs, const ZSTR & zs) { ofs << CH_DELM_STR; for ( int ich = 0; ich < zs.size(); ++ich) { char ch = zs.at(ich); if ( ch == CH_DELM_STR || ch == CH_META ) { if (char_traits::eq_int_type(char_traits::eof(), ofs.rdbuf()->sputc(CH_META))) break; } if (char_traits::eq_int_type(char_traits::eof(), ofs.rdbuf()->sputc(ch))) break; } if ( ich < zs.size() ) _THROW1(runtime_error("file exhausted inserting string")); ofs << CH_DELM_STR; return ofs; } ////////////////////////////////////////////////////////////////////////////////// // Simple parameter output routines using insertion ////////////////////////////////////////////////////////////////////////////////// template inline PARMOUTSTREAM & AddParamValue ( PARMOUTSTREAM & fprm, const T & t, SZC szc, int indx = -1 ) { fprm.StartItem( szc, indx ); fprm << (const T &) t; fprm.BEndItem(); return fprm; } ////////////////////////////////////////////////////////////////////////////////// // Simple parameter input routines using extraction ////////////////////////////////////////////////////////////////////////////////// template inline bool BGetParamValue ( PARMINSTREAM & fprm, T & t, SZC szc, int index = -1, int iblkOuter = -1 ) { int iblk = fprm.IblkFind(szc, index, iblkOuter); if ( iblk < 0 ) return false; fprm.BSeekBlk(iblk); fprm >> t; return true; } #endif