/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2004-2016 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2008-2016 Wayne Stambaugh * Copyright (C) 2004-2016 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ /** * @file class_library.cpp */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DUPLICATE_NAME_MSG \ _( "Library '%s' has duplicate entry name '%s'.\n" \ "This may cause some unexpected behavior when loading components into a schematic." ) PART_LIB::PART_LIB( int aType, const wxString& aFileName ) : // start @ != 0 so each additional library added // is immediately detectable, zero would not be. m_mod_hash( PART_LIBS::s_modify_generation ) { type = aType; isModified = false; timeStamp = 0; isCache = false; timeStamp = wxDateTime::Now(); versionMajor = 0; // Will be updated after reading the lib file versionMinor = 0; // Will be updated after reading the lib file fileName = aFileName; if( !fileName.IsOk() ) fileName = "unnamed.lib"; } PART_LIB::~PART_LIB() { // When the library is destroyed, all of the alias objects on the heap should be deleted. for( LIB_ALIAS_MAP::iterator it = m_amap.begin(); it != m_amap.end(); ++it ) { wxLogTrace( traceSchLibMem, "Removing alias %s from library %s.", GetChars( it->second->GetName() ), GetChars( GetLogicalName() ) ); LIB_PART* part = it->second->GetPart(); LIB_ALIAS* alias = it->second; delete alias; // When the last alias of a part is destroyed, the part is no longer required and it // too is destroyed. if( part && part->GetAliasCount() == 0 ) delete part; } m_amap.clear(); } void PART_LIB::GetAliasNames( wxArrayString& aNames ) { for( LIB_ALIAS_MAP::iterator it = m_amap.begin(); it!=m_amap.end(); it++ ) { aNames.Add( (*it).first ); } aNames.Sort(); } void PART_LIB::GetEntryTypePowerNames( wxArrayString& aNames ) { for( LIB_ALIAS_MAP::iterator it = m_amap.begin(); it!=m_amap.end(); it++ ) { LIB_ALIAS* alias = it->second; LIB_PART* root = alias->GetPart(); if( !root || !root->IsPower() ) continue; aNames.Add( (*it).first ); } aNames.Sort(); } LIB_ALIAS* PART_LIB::FindAlias( const wxString& aName ) { LIB_ALIAS_MAP::iterator it = m_amap.find( aName ); if( it != m_amap.end() ) return it->second; return NULL; } LIB_PART* PART_LIB::FindPart( const wxString& aName ) { #if 0 && defined(DEBUG) if( !aName.Cmp( "TI_STELLARIS_BOOSTERPACK" ) ) { int breakhere = 1; (void) breakhere; } #endif if( LIB_ALIAS* alias = FindAlias( aName ) ) { return alias->GetPart(); } return NULL; } bool PART_LIB::HasPowerParts() { // return true if at least one power part is found in lib for( LIB_ALIAS_MAP::iterator it = m_amap.begin(); it!=m_amap.end(); it++ ) { LIB_ALIAS* alias = it->second; LIB_PART* root = alias->GetPart(); if( root && root->IsPower() ) return true; } return false; } bool PART_LIB::AddAlias( LIB_ALIAS* aAlias ) { wxASSERT( aAlias ); #if defined(DEBUG) && 0 if( !aAlias->GetName().Cmp( "TI_STELLARIS_BOOSTERPACK" ) ) { int breakhere = 1; (void) breakhere; } #endif LIB_ALIAS_MAP::iterator it = m_amap.find( aAlias->GetName() ); if( it != m_amap.end() ) { wxString msg; msg.Printf( _( "Cannot add duplicate alias '%s' to library '%s'." ), GetChars( aAlias->GetName() ), GetChars( fileName.GetName() ) ); return false; } wxString name = aAlias->GetName(); m_amap[ name ] = aAlias; isModified = true; ++m_mod_hash; return true; } void PART_LIB::AddPart( LIB_PART* aPart ) { // add a clone, not the caller's copy LIB_PART* my_part = new LIB_PART( *aPart ); for( size_t i = 0; i < my_part->m_aliases.size(); i++ ) { wxString aliasname = my_part->m_aliases[i]->GetName(); if( LIB_ALIAS* alias = FindAlias( aliasname ) ) RemoveAlias( alias ); m_amap[ aliasname ] = my_part->m_aliases[i]; } isModified = true; ++m_mod_hash; } LIB_ALIAS* PART_LIB::RemoveAlias( LIB_ALIAS* aEntry ) { wxCHECK_MSG( aEntry != NULL, NULL, "NULL pointer cannot be removed from library." ); LIB_ALIAS_MAP::iterator it = m_amap.find( aEntry->GetName() ); if( it == m_amap.end() ) return NULL; // If the entry pointer doesn't match the name it is mapped to in the library, we // have done something terribly wrong. wxCHECK_MSG( *it->second == aEntry, NULL, "Pointer mismatch while attempting to remove entry <" + aEntry->GetName() + "> from library <" + GetName() + ">." ); LIB_ALIAS* alias = aEntry; LIB_PART* part = alias->GetPart(); alias = part->RemoveAlias( alias ); if( !alias ) { delete part; if( m_amap.size() > 1 ) { LIB_ALIAS_MAP::iterator next = it; next++; if( next == m_amap.end() ) next = m_amap.begin(); alias = next->second; } } m_amap.erase( it ); isModified = true; ++m_mod_hash; return alias; } LIB_PART* PART_LIB::ReplacePart( LIB_PART* aOldPart, LIB_PART* aNewPart ) { wxASSERT( aOldPart != NULL ); wxASSERT( aNewPart != NULL ); /* Remove the old root component. The component will automatically be deleted * when all it's aliases are deleted. Do not place any code that accesses * aOldPart inside this loop that gets evaluated after the last alias is * removed in RemoveAlias(). Failure to heed this warning will result in a * segfault. */ size_t i = aOldPart->m_aliases.size(); while( i > 0 ) { i -= 1; RemoveAlias( aOldPart->m_aliases[ i ] ); } LIB_PART* my_part = new LIB_PART( *aNewPart, this ); // Add new aliases to library alias map. for( i = 0; i < my_part->m_aliases.size(); i++ ) { wxString aname = my_part->m_aliases[ i ]->GetName(); m_amap[ aname ] = my_part->m_aliases[ i ]; } isModified = true; ++m_mod_hash; return my_part; } bool PART_LIB::Load( wxString& aErrorMsg ) { if( fileName.GetFullPath().IsEmpty() ) { aErrorMsg = _( "The component library file name is not set." ); return false; } FILE* file = wxFopen( fileName.GetFullPath(), "rt" ); if( file == NULL ) { aErrorMsg = _( "The file could not be opened." ); return false; } FILE_LINE_READER reader( file, fileName.GetFullPath() ); if( !reader.ReadLine() ) { aErrorMsg = _( "The file is empty!" ); return false; } // There is no header if this is a symbol library. if( type == LIBRARY_TYPE_EESCHEMA ) { char* line = reader.Line(); header = FROM_UTF8( line ); wxStringTokenizer tkn( header ); /* * The file header (first line) in library versions 2.0 and lower * apparently started with EESchema-LIB. Sometime after 2.0, it * was changed to EESchema-LIBRARY. Therefore, the test for * EESchema-LIB will work in both cases. Don't change this unless * backwards compatibility is no longer required. */ if( !tkn.HasMoreTokens() || !tkn.GetNextToken().Upper().StartsWith( "EESCHEMA-LIB" ) ) { aErrorMsg = _( "The file is NOT an Eeschema library!" ); return false; } if( !tkn.HasMoreTokens() ) { aErrorMsg = _( "The file header is missing version and time stamp information." ); return false; } if( tkn.GetNextToken() != "Version" || !tkn.HasMoreTokens() ) { aErrorMsg = "The file header version information is invalid."; return false; } long major, minor; wxStringTokenizer vers( tkn.GetNextToken(), "." ); if( !vers.HasMoreTokens() || !vers.GetNextToken().ToLong( &major ) || major < 1L || !vers.HasMoreTokens() || !vers.GetNextToken().ToLong( & minor ) || minor < 0L || minor > 99 ) { #if 0 // Note for developers: // Not sure this warning is very useful: old designs *must* be always loadable wxLogWarning( "The component library '%s' header version " "number is invalid.\n\nIn future versions of Eeschema this library may not " "load correctly. To resolve this problem open the library in the library " "editor and save it. If this library is the project cache library, save " "the current schematic.", GetChars( GetName() ) ); #endif } else { versionMajor = (int) major; versionMinor = (int) minor; } } while( reader.ReadLine() ) { char * line = reader.Line(); if( type == LIBRARY_TYPE_EESCHEMA && strncasecmp( line, "$HEADER", 7 ) == 0 ) { if( !LoadHeader( reader ) ) { aErrorMsg = _( "An error occurred attempting to read the header." ); return false; } continue; } wxString msg; if( strncasecmp( line, "DEF", 3 ) == 0 ) { // Read one DEF/ENDDEF part entry from library: LIB_PART* part = new LIB_PART( wxEmptyString, this ); if( part->Load( reader, msg ) ) { // Check for duplicate entry names and warn the user about // the potential conflict. if( FindAlias( part->GetName() ) != NULL ) { wxLogWarning( DUPLICATE_NAME_MSG, GetChars( fileName.GetName() ), GetChars( part->GetName() ) ); } LoadAliases( part ); } else { wxLogWarning( _( "Library '%s' component load error %s." ), GetChars( fileName.GetName() ), GetChars( msg ) ); msg.Clear(); delete part; } } } ++m_mod_hash; return true; } void PART_LIB::LoadAliases( LIB_PART* aPart ) { wxCHECK_RET( aPart, "Cannot load aliases of NULL part. Bad programmer!" ); for( size_t i = 0; i < aPart->m_aliases.size(); i++ ) { if( FindAlias( aPart->m_aliases[i]->GetName() ) != NULL ) { wxLogError( DUPLICATE_NAME_MSG, GetChars( fileName.GetName() ), GetChars( aPart->m_aliases[i]->GetName() ) ); } wxString aname = aPart->m_aliases[i]->GetName(); m_amap[ aname ] = aPart->m_aliases[i]; } } bool PART_LIB::LoadHeader( LINE_READER& aLineReader ) { char* line, * text, * data; while( aLineReader.ReadLine() ) { line = (char*) aLineReader; text = strtok( line, " \t\r\n" ); data = strtok( NULL, " \t\r\n" ); if( strcasecmp( text, "TimeStamp" ) == 0 ) timeStamp = atol( data ); if( strcasecmp( text, "$ENDHEADER" ) == 0 ) return true; } return false; } bool PART_LIB::LoadDocs( wxString& aErrorMsg ) { int lineNumber = 0; char line[8000], * name, * text; LIB_ALIAS* entry; FILE* file; wxFileName fn = fileName; fn.SetExt( DOC_EXT ); file = wxFopen( fn.GetFullPath(), "rt" ); if( file == NULL ) { aErrorMsg.Printf( _( "Could not open component document library file '%s'." ), GetChars( fn.GetFullPath() ) ); return false; } if( GetLine( file, line, &lineNumber, sizeof(line) ) == NULL ) { aErrorMsg.Printf( _( "Part document library file '%s' is empty." ), GetChars( fn.GetFullPath() ) ); fclose( file ); return false; } if( strncasecmp( line, DOCFILE_IDENT, 10 ) != 0 ) { aErrorMsg.Printf( _( "File '%s' is not a valid component library document file." ), GetChars( fn.GetFullPath() ) ); fclose( file ); return false; } while( GetLine( file, line, &lineNumber, sizeof(line) ) ) { if( strncmp( line, "$CMP", 4 ) != 0 ) { aErrorMsg.Printf( "$CMP command expected in line %d, aborted.", lineNumber ); fclose( file ); return false; } // Read one $CMP/$ENDCMP part entry from library: name = strtok( line + 5, "\n\r" ); wxString cmpname = FROM_UTF8( name ); entry = FindAlias( cmpname ); while( GetLine( file, line, &lineNumber, sizeof(line) ) ) { if( strncmp( line, "$ENDCMP", 7 ) == 0 ) break; text = strtok( line + 2, "\n\r" ); if( entry ) { switch( line[0] ) { case 'D': entry->SetDescription( FROM_UTF8( text ) ); break; case 'K': entry->SetKeyWords( FROM_UTF8( text ) ); break; case 'F': entry->SetDocFileName( FROM_UTF8( text ) ); break; } } } } fclose( file ); return true; } bool PART_LIB::Save( OUTPUTFORMATTER& aFormatter ) { if( isModified ) { timeStamp = GetNewTimeStamp(); isModified = false; } bool success = true; try { SaveHeader( aFormatter ); for( LIB_ALIAS_MAP::iterator it=m_amap.begin(); it!=m_amap.end(); it++ ) { if( !it->second->IsRoot() ) continue; it->second->GetPart()->Save( aFormatter ); } aFormatter.Print( 0, "#\n#End Library\n" ); } catch( const IO_ERROR& ) { success = false; } return success; } bool PART_LIB::SaveDocs( OUTPUTFORMATTER& aFormatter ) { bool success = true; try { aFormatter.Print( 0, "%s\n", DOCFILE_IDENT ); for( LIB_ALIAS_MAP::iterator it=m_amap.begin(); it!=m_amap.end(); it++ ) { if( !it->second->SaveDoc( aFormatter ) ) success = false; } aFormatter.Print( 0, "#\n#End Doc Library\n" ); } catch( const IO_ERROR& ) { success = false; } return success; } bool PART_LIB::SaveHeader( OUTPUTFORMATTER& aFormatter ) { aFormatter.Print( 0, "%s %d.%d\n", LIBFILE_IDENT, LIB_VERSION_MAJOR, LIB_VERSION_MINOR ); aFormatter.Print( 0, "#encoding utf-8\n"); #if 0 aFormatter.Print( 0, "$HEADER\n" ); aFormatter.Print( 0, "TimeStamp %8.8lX\n", m_TimeStamp ); aFormatter.Print( 0, "Parts %d\n", m_amap.size() ); aFormatter.Print( 0, "$ENDHEADER\n" ) != 1 ); #endif return true; } PART_LIB* PART_LIB::LoadLibrary( const wxString& aFileName ) throw( IO_ERROR, boost::bad_pointer ) { std::unique_ptr lib( new PART_LIB( LIBRARY_TYPE_EESCHEMA, aFileName ) ); wxBusyCursor ShowWait; // Do we want UI elements in PART_LIB? wxString errorMsg; #ifdef KICAD_USE_SCH_IO_MANAGER SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) ); wxArrayString tmp; pi->EnumerateSymbolLib( tmp, aFileName ); pi->TransferCache( *lib.get() ); #else if( !lib->Load( errorMsg ) ) THROW_IO_ERROR( errorMsg ); if( USE_OLD_DOC_FILE_FORMAT( lib->versionMajor, lib->versionMinor ) ) { #if 1 // not fatal if error here. lib->LoadDocs( errorMsg ); #else if( !lib->LoadDocs( errorMsg ) ) THROW_IO_ERROR( errorMsg ); #endif } #endif PART_LIB* ret = lib.release(); return ret; } PART_LIB* PART_LIBS::AddLibrary( const wxString& aFileName ) throw( IO_ERROR, boost::bad_pointer ) { PART_LIB* lib; wxFileName fn = aFileName; // Don't reload the library if it is already loaded. lib = FindLibrary( fn.GetName() ); if( lib ) return lib; lib = PART_LIB::LoadLibrary( aFileName ); push_back( lib ); return lib; } PART_LIB* PART_LIBS::AddLibrary( const wxString& aFileName, PART_LIBS::iterator& aIterator ) throw( IO_ERROR, boost::bad_pointer ) { // Don't reload the library if it is already loaded. wxFileName fn( aFileName ); PART_LIB* lib = FindLibrary( fn.GetName() ); if( lib ) return lib; lib = PART_LIB::LoadLibrary( aFileName ); if( aIterator >= begin() && aIterator < end() ) insert( aIterator, lib ); else push_back( lib ); return lib; } PART_LIB* PART_LIBS::FindLibrary( const wxString& aName ) { for( PART_LIBS::iterator it = begin(); it!=end(); ++it ) { if( it->GetName() == aName ) return &*it; } return NULL; } wxArrayString PART_LIBS::GetLibraryNames( bool aSorted ) { wxArrayString cacheNames; wxArrayString names; for( PART_LIB& lib : *this ) { if( lib.IsCache() && aSorted ) cacheNames.Add( lib.GetName() ); else names.Add( lib.GetName() ); } // Even sorted, the cache library is always at the end of the list. if( aSorted ) names.Sort(); for( unsigned int i = 0; i& aCandidates, const wxString& aEntryName, const wxString& aLibraryName ) { for( PART_LIB& lib : *this ) { if( !!aLibraryName && lib.GetName() != aLibraryName ) continue; wxArrayString aliasNames; lib.GetAliasNames( aliasNames ); if( aliasNames.IsEmpty() ) continue; for( size_t i = 0; i < aliasNames.size(); i++ ) { if( aliasNames[i].CmpNoCase( aEntryName ) == 0 ) aCandidates.push_back( lib.FindAlias( aliasNames[i] ) ); } } } int PART_LIBS::s_modify_generation = 1; // starts at 1 and goes up int PART_LIBS::GetModifyHash() { int hash = 0; for( PART_LIBS::const_iterator it = begin(); it != end(); ++it ) { hash += it->m_mod_hash; } return hash; } void PART_LIBS::LibNamesAndPaths( PROJECT* aProject, bool doSave, wxString* aPaths, wxArrayString* aNames ) throw( IO_ERROR, boost::bad_pointer ) { wxString pro = aProject->GetProjectFullName(); PARAM_CFG_ARRAY ca; if( aPaths ) ca.push_back( new PARAM_CFG_FILENAME( "LibDir", aPaths ) ); if( aNames ) ca.push_back( new PARAM_CFG_LIBNAME_LIST( wxT( "LibName" ), aNames, GROUP_SCH_LIBS ) ); if( doSave ) { aProject->ConfigSave( Kiface().KifaceSearch(), GROUP_SCH, ca ); /* { wxString msg = wxString::Format( _( "Unable save project's '%s' file" ), GetChars( pro ) ); THROW_IO_ERROR( msg ); } */ } else { if( !aProject->ConfigLoad( Kiface().KifaceSearch(), GROUP_SCH, ca ) ) { wxString msg = wxString::Format( _( "Unable to load project's '%s' file" ), GetChars( pro ) ); THROW_IO_ERROR( msg ); } } } const wxString PART_LIBS::CacheName( const wxString& aFullProjectFilename ) { /* until apr 2009 the project cache lib was named: .cache.lib, * and after: -cache.lib. So if the -cache.lib is not found, * the old file will be renamed and returned. */ wxFileName new_name = aFullProjectFilename; new_name.SetName( new_name.GetName() + "-cache" ); new_name.SetExt( SchematicLibraryFileExtension ); if( new_name.FileExists() ) return new_name.GetFullPath(); else { wxFileName old_name = aFullProjectFilename; old_name.SetExt( "cache.lib" ); if( old_name.FileExists() ) { wxRenameFile( old_name.GetFullPath(), new_name.GetFullPath() ); return new_name.GetFullPath(); } } return wxEmptyString; } void PART_LIBS::LoadAllLibraries( PROJECT* aProject ) throw( IO_ERROR, boost::bad_pointer ) { wxString filename; wxString libs_not_found; SEARCH_STACK* lib_search = aProject->SchSearchS(); #if defined(DEBUG) && 1 lib_search->Show( __func__ ); #endif wxArrayString lib_names; LibNamesAndPaths( aProject, false, NULL, &lib_names ); // If the list is empty, force loading the standard power symbol library. if( !lib_names.GetCount() ) lib_names.Add( "power" ); wxASSERT( !size() ); // expect to load into "this" empty container. for( unsigned i = 0; i < lib_names.GetCount(); ++i ) { wxFileName fn = lib_names[i]; // lib_names[] does not store the file extension. Set it: fn.SetExt( SchematicLibraryFileExtension ); // Skip if the file name is not valid.. if( !fn.IsOk() ) continue; if( !fn.FileExists() ) { filename = lib_search->FindValidPath( fn.GetFullPath() ); if( !filename ) { libs_not_found += fn.GetFullPath(); libs_not_found += '\n'; continue; } } else { // ensure the lib filename has a absolute path. // If the lib has no absolute path, and is found in the cwd by fn.FileExists(), // make a full absolute path, to avoid issues with load library functions which // expects an absolute path. if( !fn.IsAbsolute() ) fn.MakeAbsolute(); filename = fn.GetFullPath(); } try { AddLibrary( filename ); } catch( const IO_ERROR& ioe ) { wxString msg = wxString::Format( _( "Part library '%s' failed to load. Error:\n %s" ), GetChars( filename ), GetChars( ioe.What() ) ); wxLogError( msg ); } } // add the special cache library. wxString cache_name = CacheName( aProject->GetProjectFullName() ); PART_LIB* cache_lib; if( !!cache_name ) { try { cache_lib = AddLibrary( cache_name ); if( cache_lib ) cache_lib->SetCache(); } catch( const IO_ERROR& ioe ) { wxString msg = wxString::Format( _( "Part library '%s' failed to load.\nError: %s" ), GetChars( cache_name ), GetChars( ioe.What() ) ); THROW_IO_ERROR( msg ); } } // Print the libraries not found if( !!libs_not_found ) { // Use a different exception type so catch()er can route to proper use // of the HTML_MESSAGE_BOX. THROW_PARSE_ERROR( wxEmptyString, UTF8( __func__ ), UTF8( libs_not_found ), 0, 0 ); } #if defined(DEBUG) && 1 printf( "%s: lib_names:\n", __func__ ); for( PART_LIBS::const_iterator it = begin(); it < end(); ++it ) printf( " %s\n", TO_UTF8( it->GetName() ) ); #endif }