From 0267b05938130bd216df577331125530c4e2df1d Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Tue, 5 Oct 2010 02:15:29 -0500 Subject: [PATCH] add PushReader and PopReader to DSNLEXER, make FILE_LINE_READER own its FILE* --- CHANGELOG.txt | 23 +++++++++++ common/dsnlexer.cpp | 37 ++++++++++------- common/richio.cpp | 13 +++--- cvpcb/readschematicnetlist.cpp | 20 ++++----- include/dsnlexer.h | 35 ++++++++++++---- include/richio.h | 75 +++++++++++++++++++++++++++------- pcbnew/netlist.cpp | 22 ++++------ pcbnew/specctra.cpp | 18 ++++---- 8 files changed, 167 insertions(+), 76 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index bc7bdc0d10..d7fd14a5fd 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -4,6 +4,29 @@ KiCad ChangeLog 2010 Please add newer entries at the top, list the date and your name with email address. +2010-Oct-5 UPDATE Dick Hollenbeck +================================================================================ +++richio: + * LINE_READER now has a GetSource() function which is used in error + reporting. This is typically the name of the file which is supplying the + lines of text, or string "clipboard" if the text is coming from the clipboard. + Derived classes FILE_LINE_READER and STRING_LINE_READER's constructors both + need an additional parameter which identifies the source. + * FILE_LINE_READER now owns the source FILE and will close it in its destructor. + This resulted in the removal of several fclose() statements that had been + there to close a file associated with a FILE_LINE_READER. + * DSNLEXER now supports an internal LINE_READER* stack which is used to handle + nested s-expression files, with the ability to resume from the proper place + in the containing file. There is now PushReader() and PopReader() functions + in DSNLEXER to handle this. No protection is provided against circular + inclusions, but this could be done by searching the stack and comparing + GetSource() values for anything already on the stack before pushing. + Each s-expression grammar is free to define one or more keywords that cause + nesting to occur. That policy choice is not part of DSNLEXER's job. + One example might be: + (inherit (footprint library_uri ftprintname)) + + 2010-oct-03, UPDATE Jean-Pierre Charras ================================================================================ ++gerbview: diff --git a/common/dsnlexer.cpp b/common/dsnlexer.cpp index 66fb642bd8..49d84bf8be 100644 --- a/common/dsnlexer.cpp +++ b/common/dsnlexer.cpp @@ -58,24 +58,18 @@ void DSNLEXER::init() commentsAreTokens = false; - // "start" should never change until we change the reader. The DSN - // format spec supports an include file mechanism but we can add that later - // using a std::stack to hold a stack of LINE_READERs to track nesting. - start = (char*) (*reader); - limit = start; next = start; } + DSNLEXER::DSNLEXER( FILE* aFile, const wxString& aFilename, const KEYWORD* aKeywordTable, unsigned aKeywordCount ) : keywords( aKeywordTable ), keywordCount( aKeywordCount ) { - filename = aFilename; - - reader = new FILE_LINE_READER( aFile, 4096 ); - + FILE_LINE_READER* fileReader = new FILE_LINE_READER( aFile, aFilename, 4096 ); + PushReader( fileReader ); init(); } @@ -85,14 +79,29 @@ DSNLEXER::DSNLEXER( const std::string& aClipboardTxt, keywords( aKeywordTable ), keywordCount( aKeywordCount ) { - filename = _( "clipboard" ); - - reader = new STRING_LINE_READER( aClipboardTxt ); - + STRING_LINE_READER* stringReader = new STRING_LINE_READER( aClipboardTxt, _( "clipboard" ) ); + PushReader( stringReader ); init(); } +void DSNLEXER::PushReader( LINE_READER* aLineReader ) +{ + readerStack.push_back( aLineReader ); + reader = aLineReader; + start = (char*) (*aLineReader); +} + + +void DSNLEXER::PopReader() +{ + readerStack.pop_back(); + reader = &readerStack.back(); + if( reader ) + start = (char*) (*reader); +} + + int DSNLEXER::findToken( const std::string& tok ) { // convert to lower case once, this should be faster than using strcasecmp() @@ -207,7 +216,7 @@ bool DSNLEXER::IsSymbol( int aTok ) void DSNLEXER::ThrowIOError( wxString aText, int charOffset ) throw (IOError) { // append to aText, do not overwrite - aText << wxT(" ") << _("in") << wxT(" \"") << filename + aText << wxT(" ") << _("in") << wxT(" \"") << CurSource() << wxT("\" ") << _("on line") << wxT(" ") << reader->LineNumber() << wxT(" ") << _("at offset") << wxT(" ") << charOffset; diff --git a/common/richio.cpp b/common/richio.cpp index 621c3ebfc2..94813ca0cf 100644 --- a/common/richio.cpp +++ b/common/richio.cpp @@ -49,10 +49,11 @@ LINE_READER::LINE_READER( unsigned aMaxLineLength ) } -FILE_LINE_READER::FILE_LINE_READER( FILE* aFile, unsigned aMaxLineLength ) : - LINE_READER( aMaxLineLength ) +FILE_LINE_READER::FILE_LINE_READER( FILE* aFile, const wxString& aFileName, unsigned aMaxLineLength ) : + LINE_READER( aMaxLineLength ), + fp( aFile ) { - fp = aFile; + source = aFileName; } @@ -81,11 +82,11 @@ int FILE_LINE_READER::ReadLine() throw (IOError) int STRING_LINE_READER::ReadLine() throw (IOError) { - size_t nlOffset = source.find( '\n', ndx ); + size_t nlOffset = lines.find( '\n', ndx ); size_t advance; if( nlOffset == std::string::npos ) - advance = source.length() - ndx; + advance = lines.length() - ndx; else advance = nlOffset - ndx + 1; // include the newline, so +1 @@ -94,7 +95,7 @@ int STRING_LINE_READER::ReadLine() throw (IOError) if( advance > maxLineLength ) throw IOError( _("Line length exceeded") ); - wxASSERT( ndx + advance <= source.length() ); + wxASSERT( ndx + advance <= lines.length() ); memcpy( line, &source[ndx], advance ); diff --git a/cvpcb/readschematicnetlist.cpp b/cvpcb/readschematicnetlist.cpp index 6c954c0b90..269545472c 100644 --- a/cvpcb/readschematicnetlist.cpp +++ b/cvpcb/readschematicnetlist.cpp @@ -113,13 +113,14 @@ int WinEDA_CvpcbFrame::ReadSchematicNetlist() return -1; } - FILE_LINE_READER netlistReader( source, BUFFER_CHAR_SIZE ); + // FILE_LINE_READER will close the file. + FILE_LINE_READER netlistReader( source, m_NetlistFileName.GetFullPath(), BUFFER_CHAR_SIZE ); char* Line = netlistReader; /* Read the file header (must be "( { OrCAD PCB" or "({ OrCAD PCB" ) * or "# EESchema Netlist" */ - netlistReader.ReadLine( ); + netlistReader.ReadLine(); /* test for netlist type PCB2 */ idx = strnicmp( Line, "( {", 3 ); @@ -137,7 +138,6 @@ int WinEDA_CvpcbFrame::ReadSchematicNetlist() wxString msg, Lineconv = CONV_FROM_UTF8( Line ); msg.Printf( _( "Unknown file format <%s>" ), Lineconv.GetData() ); DisplayError( this, msg ); - fclose( source ); return -3; } @@ -263,8 +263,6 @@ int WinEDA_CvpcbFrame::ReadSchematicNetlist() ReadPinConnection( netlistReader, Cmp ); } - fclose( source ); - m_components.sort(); return 0; @@ -274,8 +272,8 @@ int WinEDA_CvpcbFrame::ReadSchematicNetlist() int ReadFootprintFilterList( FILE_LINE_READER& aNetlistReader, COMPONENT_LIST& aComponentsList ) { char* Line = aNetlistReader; - wxString CmpRef; - COMPONENT* Cmp = NULL; + wxString CmpRef; + COMPONENT* Cmp = NULL; for( ; ; ) { @@ -318,16 +316,16 @@ int ReadFootprintFilterList( FILE_LINE_READER& aNetlistReader, COMPONENT_LIST& int ReadPinConnection( FILE_LINE_READER& aNetlistReader, COMPONENT* Cmp ) { - int i, jj; - char* Line = aNetlistReader; - char cbuffer[BUFFER_CHAR_SIZE]; + int i, jj; + char* Line = aNetlistReader; + char cbuffer[BUFFER_CHAR_SIZE]; for( ; ; ) { /* Find beginning of description. */ for( ; ; ) { - if( aNetlistReader.ReadLine( ) == 0 ) + if( aNetlistReader.ReadLine() == 0 ) return -1; /* Remove blanks from the beginning of the line. */ diff --git a/include/dsnlexer.h b/include/dsnlexer.h index ea149e5a56..6143670174 100644 --- a/include/dsnlexer.h +++ b/include/dsnlexer.h @@ -27,6 +27,7 @@ #include #include +#include #include "fctsys.h" @@ -81,12 +82,14 @@ class DSNLEXER char* start; char* limit; - LINE_READER* reader; + typedef boost::ptr_vector READER_STACK; + + READER_STACK readerStack; ///< owns all the LINE_READERs by pointer. + LINE_READER* reader; ///< no ownership. ownership is via readerStack. int stringDelimiter; bool space_in_quoted_tokens; ///< blank spaces within quoted strings bool commentsAreTokens; ///< true if should return comments as tokens - wxString filename; int prevTok; ///< curTok from previous NextTok() call. int curOffset; ///< offset within current line of the current token @@ -164,9 +167,26 @@ public: ~DSNLEXER() { - delete reader; } + /** + * Function PushReader + * manages a stack of LINE_READERs in order to handle nested file inclusion. + * Pushes aLineReader onto the top of a stack of LINE_READERs and makes + * it the current LINE_READER with its own GetSource(), line number and line text. + */ + void PushReader( LINE_READER* aLineReader ); + + /** + * Function PopReader + * deletes the top most LINE_READER from an internal stack of LINE_READERs and + * in the case of FILE_LINE_READER this means the associated FILE is closed. + * The most recently used former LINE_READER on the stack becomes the + * current LINE_READER and its previous position in its input stream and the + * its latest line number should pertain. + */ + void PopReader(); + // Some functions whose return value is best overloaded to return an enum // in a derived class. //----------------------------------- @@ -364,12 +384,13 @@ public: /** * Function CurFilename - * returns the current input filename. - * @return const wxString& - the filename. + * returns the current LINE_READER source. + * @return const wxString& - the source of the lines of text, + * e.g. a filename or "clipboard". */ - const wxString& CurFilename() + const wxString& CurSource() { - return filename; + return reader->GetSource(); } /** diff --git a/include/richio.h b/include/richio.h index 86f664da72..cfc7c05699 100644 --- a/include/richio.h +++ b/include/richio.h @@ -64,8 +64,8 @@ struct IOError /** * Class LINE_READER - * reads single lines of text into its buffer and increments a line number counter. - * It throws an exception if a line is too long. + * is an abstract class from which implementation specific LINE_READERs may + * be derived to read single lines of text and manage a line number counter. */ class LINE_READER { @@ -75,8 +75,15 @@ protected: char* line; unsigned maxLineLength; unsigned capacity; + wxString source; ///< origin of text lines, e.g. filename or "clipboard" public: + + /** + * Constructor LINE_READER + * builds a line reader and fixes the length of the maximum supported + * line length to @a aMaxLineLength. + */ LINE_READER( unsigned aMaxLineLength ); virtual ~LINE_READER() @@ -94,16 +101,42 @@ public: */ virtual int ReadLine() throw (IOError) = 0; + /** + * Function GetSource + * returns the name of the source of the lines in an abstract sense. + * This may be a file or it may be the clipboard or any other source + * of lines of text. The returned string is useful for reporting error + * messages. + */ + const wxString& GetSource() + { + return source; + } + + /** + * Operator char* + * is a casting operator that returns a char* pointer to the start of the + * line buffer. + */ operator char* () { return line; } + /** + * Function Line Number + * returns the line number of the last line read from this LINE_READER. Lines + * start from 1. + */ int LineNumber() { return lineNum; } + /** + * Function Length + * returns the number of bytes in the last line read from this LINE_READER. + */ unsigned Length() { return length; @@ -114,22 +147,32 @@ public: /** * Class FILE_LINE_READER * is a LINE_READER that reads from an open file. File must be already open - * so that this class can exist without and UI policy. + * so that this class can exist without any UI policy. */ class FILE_LINE_READER : public LINE_READER { protected: - FILE* fp; ///< no ownership, no close on destruction + FILE* fp; ///< I own this file public: + /** - * Constructor LINE_READER - * takes an open FILE and the size of the desired line buffer. - * @param aFile An open file in "ascii" mode, not binary mode. - * @param aMaxLineLength The number of bytes to use in the line buffer. + * Constructor FILE_LINE_READER + * takes an open FILE and the size of the desired line buffer and takes + * ownership of the open file, i.e. assumes the obligation to close it. + * + * @param aFile is an open file. + * @param aFileName is the name of the file for error reporting purposes. + * @param aMaxLineLength is the number of bytes to use in the line buffer. */ - FILE_LINE_READER( FILE* aFile, unsigned aMaxLineLength ); + FILE_LINE_READER( FILE* aFile, const wxString& aFileName, unsigned aMaxLineLength ); + + ~FILE_LINE_READER() + { + if( fp ) + fclose( fp ); + } /** * Function ReadLine @@ -143,8 +186,8 @@ public: /** * Function Rewind - * a wrapper to the standard function rewind. - * also clear the current line number + * rewinds the file and resets the line number back to zero. Line number + * will go to 1 on first ReadLine(). */ void Rewind() { @@ -161,25 +204,29 @@ public: class STRING_LINE_READER : public LINE_READER { protected: - std::string source; + std::string lines; size_t ndx; public: /** * Constructor STRING_LINE_READER( const std::string& aString ) + * * @param aString is a source string consisting of one or more lines * of text, where multiple lines are separated with a '\n' character. * The last line does not necessarily need a trailing '\n'. + * @param aSource describes the source of aString for error reporting purposes + * can be anything meaninful, such as wxT( "cliboard" ). */ - STRING_LINE_READER( const std::string& aString ) : + STRING_LINE_READER( const std::string& aString, const wxString& aSource ) : LINE_READER( 4096 ), - source( aString ), + lines( aString ), ndx( 0 ) { // Clipboard text should be nice and _use multiple lines_ so that // we can report _line number_ oriented error messages when parsing. // Therefore a line of 4096 characters max seems more than adequate. + source = aSource; } /** diff --git a/pcbnew/netlist.cpp b/pcbnew/netlist.cpp index 6cf8b6063c..8bb2f11caa 100644 --- a/pcbnew/netlist.cpp +++ b/pcbnew/netlist.cpp @@ -166,6 +166,9 @@ bool WinEDA_PcbFrame::ReadPcbNetlist( const wxString& aNetlistFullFilename, if( !netfile ) return false; + FILE_LINE_READER netlistReader( netfile, aNetlistFullFilename, BUFFER_CHAR_SIZE ); + char* Line = netlistReader; + SetLastNetListRead( aNetlistFullFilename ); if( aMessageWindow ) @@ -186,12 +189,10 @@ bool WinEDA_PcbFrame::ReadPcbNetlist( const wxString& aNetlistFullFilename, wxBusyCursor dummy; // Shows an hourglass while calculating - FILE_LINE_READER netlistReader( netfile, BUFFER_CHAR_SIZE ); - char* Line = netlistReader; /* First, read the netlist: Build the list of footprints to load (new * footprints) */ - while( netlistReader.ReadLine( ) ) + while( netlistReader.ReadLine() ) { Text = StrPurge( Line ); @@ -251,8 +252,8 @@ bool WinEDA_PcbFrame::ReadPcbNetlist( const wxString& aNetlistFullFilename, /* Second read , All footprints are on board, one must update the schematic * info (pad netnames) */ - netlistReader.Rewind( ); - while( netlistReader.ReadLine( ) ) + netlistReader.Rewind(); + while( netlistReader.ReadLine() ) { Text = StrPurge( Line ); @@ -312,8 +313,6 @@ bool WinEDA_PcbFrame::ReadPcbNetlist( const wxString& aNetlistFullFilename, } } - fclose( netfile ); - // Delete footprints not found in netlist: if( aDeleteExtraFootprints ) { @@ -794,7 +793,7 @@ int BuildFootprintsListFromNetlistFile( const wxString& aNetlistFullFilename, if( !netfile ) return -1; - FILE_LINE_READER netlistReader( netfile, BUFFER_CHAR_SIZE ); + FILE_LINE_READER netlistReader( netfile, aNetlistFullFilename, BUFFER_CHAR_SIZE ); char* Line = netlistReader; State = 0; Comment = 0; @@ -842,8 +841,6 @@ int BuildFootprintsListFromNetlistFile( const wxString& aNetlistFullFilename, } } - fclose( netfile ); - return nb_modules_lus; } @@ -899,7 +896,7 @@ int ReadListeModules( const wxString& CmpFullFileName, const wxString* RefCmp, return 0; } - FILE_LINE_READER netlistReader( FichCmp, BUFFER_CHAR_SIZE ); + FILE_LINE_READER netlistReader( FichCmp, CmpFullFileName, BUFFER_CHAR_SIZE ); char* Line = netlistReader; while( netlistReader.ReadLine() ) @@ -947,7 +944,6 @@ int ReadListeModules( const wxString& CmpFullFileName, const wxString* RefCmp, { if( RefCmp->CmpNoCase( refcurrcmp ) == 0 ) // Found! { - fclose( FichCmp ); NameModule = idmod; return 1; } @@ -957,14 +953,12 @@ int ReadListeModules( const wxString& CmpFullFileName, const wxString* RefCmp, if( TimeStamp->CmpNoCase( timestamp ) == 0 && !timestamp.IsEmpty() ) // Found { - fclose( FichCmp ); NameModule = idmod; return 1; } } } - fclose( FichCmp ); return -1; } diff --git a/pcbnew/specctra.cpp b/pcbnew/specctra.cpp index 48e555b670..df70774ac9 100644 --- a/pcbnew/specctra.cpp +++ b/pcbnew/specctra.cpp @@ -677,8 +677,6 @@ void SPECCTRA_DB::readTIME( time_t* time_stamp ) throw( IOError ) void SPECCTRA_DB::LoadPCB( const wxString& filename ) throw( IOError ) { - wxFFile file; - FILE* fp = wxFopen( filename, wxT("r") ); if( !fp ) @@ -686,12 +684,10 @@ void SPECCTRA_DB::LoadPCB( const wxString& filename ) throw( IOError ) ThrowIOError( _("Unable to open file \"%s\""), GetChars(filename) ); } - file.Attach( fp ); // "exception safe" way to close the file. - delete lexer; lexer = 0; - lexer = new DSNLEXER( file.fp(), filename, SPECCTRA_DB::keywords, SPECCTRA_DB::keywordCount ); + lexer = new DSNLEXER( fp, filename, SPECCTRA_DB::keywords, SPECCTRA_DB::keywordCount ); if( nextTok() != T_LEFT ) expecting( T_LEFT ); @@ -702,13 +698,14 @@ void SPECCTRA_DB::LoadPCB( const wxString& filename ) throw( IOError ) SetPCB( new PCB() ); doPCB( pcb ); + + delete lexer; // close the file. + lexer = 0; } void SPECCTRA_DB::LoadSESSION( const wxString& filename ) throw( IOError ) { - wxFFile file; - FILE* fp = wxFopen( filename, wxT("r") ); if( !fp ) @@ -716,12 +713,10 @@ void SPECCTRA_DB::LoadSESSION( const wxString& filename ) throw( IOError ) ThrowIOError( _("Unable to open file \"%s\""), GetChars(filename) ); } - file.Attach( fp ); // "exception safe" way to close the file. - delete lexer; lexer = 0; - lexer = new DSNLEXER( file.fp(), filename, SPECCTRA_DB::keywords, SPECCTRA_DB::keywordCount ); + lexer = new DSNLEXER( fp, filename, SPECCTRA_DB::keywords, SPECCTRA_DB::keywordCount ); if( nextTok() != T_LEFT ) expecting( T_LEFT ); @@ -732,6 +727,9 @@ void SPECCTRA_DB::LoadSESSION( const wxString& filename ) throw( IOError ) SetSESSION( new SESSION() ); doSESSION( session ); + + delete lexer; // close the file. + lexer = 0; }