diff --git a/common/richio.cpp b/common/richio.cpp index 339684ee3c..1096ddc0f6 100644 --- a/common/richio.cpp +++ b/common/richio.cpp @@ -1,9 +1,8 @@ - /* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2007-2011 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2015 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 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 @@ -36,11 +35,6 @@ #endif -// This file defines 3 classes and some functions useful for working with text files -// and is named "richio" after its author, Richard Hollenbeck, aka Dick Hollenbeck. - - - static int vprint( std::string* result, const char* format, va_list ap ) { char msg[512]; diff --git a/include/richio.h b/include/richio.h index 78e95d3551..86d72b2d9a 100644 --- a/include/richio.h +++ b/include/richio.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2007-2010 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2007 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 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 @@ -192,7 +192,10 @@ struct PARSE_ERROR : public IO_ERROR const char* aInputLine, int aLineNumber, int aByteIndex ); - ~PARSE_ERROR() throw ( /*none*/ ){} + virtual ~PARSE_ERROR() throw ( /*none*/ ){} + +protected: + PARSE_ERROR(): IO_ERROR() {} }; @@ -200,6 +203,36 @@ struct PARSE_ERROR : public IO_ERROR throw PARSE_ERROR( __FILE__, __LOC__, aMsg, aSource, aInputLine, aLineNumber, aByteIndex ) +/** + * Struct FUTURE_FORMAT_ERROR + * variant of PARSE_ERROR indicating that a syntax or related error was likely caused + * by a file generated by a newer version of KiCad than this. Can be used to generate + * more informative error messages. + */ +struct FUTURE_FORMAT_ERROR : public PARSE_ERROR +{ + wxString requiredVersion; ///< version or date of KiCad required to open file + + FUTURE_FORMAT_ERROR( const PARSE_ERROR& aParseError, const wxString& aRequiredVersion ) : + PARSE_ERROR(), requiredVersion( aRequiredVersion ) + { + errorText.Printf( _( + "KiCad was unable to open this file, as it was created with a more " + "recent version than the one you are running. To open it, you'll need " + "to upgrade KiCad to a more recent version.\n\n" + "Date of KiCad version required (or newer): %s\n\n" + "Full error text:\n%s" ), + requiredVersion, aParseError.errorText ); + + lineNumber = aParseError.lineNumber; + byteIndex = aParseError.byteIndex; + inputLine = aParseError.inputLine; + } + + ~FUTURE_FORMAT_ERROR() throw () {} +}; + + /** @} exception_types */ diff --git a/pcbnew/eagle_plugin.cpp b/pcbnew/eagle_plugin.cpp index 6297da4329..4040161283 100644 --- a/pcbnew/eagle_plugin.cpp +++ b/pcbnew/eagle_plugin.cpp @@ -3,8 +3,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2012-2015 KiCad Developers, see change_log.txt for contributors. - + * Copyright (C) 2012-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 @@ -3121,7 +3120,8 @@ wxArrayString EAGLE_PLUGIN::FootprintEnumerate( const wxString& aLibraryPath, co } -MODULE* EAGLE_PLUGIN::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, const PROPERTIES* aProperties ) +MODULE* EAGLE_PLUGIN::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, + const PROPERTIES* aProperties ) { init( aProperties ); diff --git a/pcbnew/eagle_plugin.h b/pcbnew/eagle_plugin.h index e9bde95969..d12738fced 100644 --- a/pcbnew/eagle_plugin.h +++ b/pcbnew/eagle_plugin.h @@ -5,7 +5,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2012-2015 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2012-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 @@ -87,7 +87,8 @@ public: wxArrayString FootprintEnumerate( const wxString& aLibraryPath, const PROPERTIES* aProperties = NULL); - MODULE* FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, const PROPERTIES* aProperties = NULL ); + MODULE* FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, + const PROPERTIES* aProperties = NULL ); bool IsFootprintLibWritable( const wxString& aLibraryPath ) { diff --git a/pcbnew/files.cpp b/pcbnew/files.cpp index 379a1a25d9..e7202700d5 100644 --- a/pcbnew/files.cpp +++ b/pcbnew/files.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2004-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2011-2015 Wayne Stambaugh - * Copyright (C) 2015 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 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 @@ -518,7 +518,6 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in GetChars( ioe.errorText ) ); DisplayError( this, msg ); - return false; } diff --git a/pcbnew/github/github_plugin.cpp b/pcbnew/github/github_plugin.cpp index 75d4e7bfb0..e43c2fbb0b 100644 --- a/pcbnew/github/github_plugin.cpp +++ b/pcbnew/github/github_plugin.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2016 KiCad Developers, see CHANGELOG.TXT for contributors. + * Copyright (C) 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 diff --git a/pcbnew/github/github_plugin.h b/pcbnew/github/github_plugin.h index 3dc10a69af..92f2c1372d 100644 --- a/pcbnew/github/github_plugin.h +++ b/pcbnew/github/github_plugin.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2013 KiCad Developers, see CHANGELOG.TXT for contributors. + * Copyright (C) 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 diff --git a/pcbnew/gpcb_plugin.cpp b/pcbnew/gpcb_plugin.cpp index 16432fb4ea..94815f0430 100644 --- a/pcbnew/gpcb_plugin.cpp +++ b/pcbnew/gpcb_plugin.cpp @@ -2,8 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 Wayne Stambaugh - * Copyright (C) 1992-2015 KiCad Developers, see change_log.txt for contributors. - * + * Copyright (C) 1992-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 diff --git a/pcbnew/gpcb_plugin.h b/pcbnew/gpcb_plugin.h index 4ae65563f9..20951cdf38 100644 --- a/pcbnew/gpcb_plugin.h +++ b/pcbnew/gpcb_plugin.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 Wayne Stambaugh - * Copyright (C) 1992-2012 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 1992-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 @@ -66,10 +66,10 @@ public: const PROPERTIES* aProperties = NULL); MODULE* FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, - const PROPERTIES* aProperties = NULL ); + const PROPERTIES* aProperties = NULL ); void FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName, - const PROPERTIES* aProperties = NULL ); + const PROPERTIES* aProperties = NULL ); bool FootprintLibDelete( const wxString& aLibraryPath, const PROPERTIES* aProperties = NULL ); diff --git a/pcbnew/io_mgr.cpp b/pcbnew/io_mgr.cpp index c733d30441..8d3981bd5f 100644 --- a/pcbnew/io_mgr.cpp +++ b/pcbnew/io_mgr.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2011-2012 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2011 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 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 @@ -98,6 +98,9 @@ PLUGIN* IO_MGR::PluginFind( PCB_FILE_T aFileType ) #else THROW_IO_ERROR( "BUILD_GITHUB_PLUGIN not enabled in cmake build environment" ); #endif + + case FILE_TYPE_NONE: + return NULL; } return NULL; diff --git a/pcbnew/io_mgr.h b/pcbnew/io_mgr.h index e2b0135227..80b4821888 100644 --- a/pcbnew/io_mgr.h +++ b/pcbnew/io_mgr.h @@ -5,7 +5,7 @@ * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 2011-2012 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2011 Kicad Developers, see change_log.txt for contributors. + * Copyright (C) 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 @@ -83,6 +83,8 @@ public: // ALTIUM, // etc. + + FILE_TYPE_NONE }; /** diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index 09abbcd22a..be662c2a73 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -2,8 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 CERN - * Copyright (C) 1992-2011 KiCad Developers, see change_log.txt for contributors. - * + * Copyright (C) 1992-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 @@ -418,7 +417,8 @@ void PCB_IO::Save( const wxString& aFileName, BOARD* aBoard, const PROPERTIES* a } -BOARD_ITEM* PCB_IO::Parse( const wxString& aClipboardSourceInput ) throw( PARSE_ERROR, IO_ERROR ) +BOARD_ITEM* PCB_IO::Parse( const wxString& aClipboardSourceInput ) + throw( FUTURE_FORMAT_ERROR, PARSE_ERROR, IO_ERROR ) { std::string input = TO_UTF8( aClipboardSourceInput ); @@ -426,7 +426,17 @@ BOARD_ITEM* PCB_IO::Parse( const wxString& aClipboardSourceInput ) throw( PARSE_ m_parser->SetLineReader( &reader ); - return m_parser->Parse(); + try + { + return m_parser->Parse(); + } + catch( const PARSE_ERROR& parse_error ) + { + if( m_parser->IsTooRecent() ) + throw FUTURE_FORMAT_ERROR( parse_error, m_parser->GetRequiredVersion() ); + else + throw; + } } @@ -1721,7 +1731,20 @@ BOARD* PCB_IO::Load( const wxString& aFileName, BOARD* aAppendToMe, const PROPER m_parser->SetLineReader( &reader ); m_parser->SetBoard( aAppendToMe ); - BOARD* board = dyn_cast( m_parser->Parse() ); + BOARD* board; + + try + { + board = dynamic_cast( m_parser->Parse() ); + } + catch( const PARSE_ERROR& parse_error ) + { + if( m_parser->IsTooRecent() ) + throw FUTURE_FORMAT_ERROR( parse_error, m_parser->GetRequiredVersion() ); + else + throw; + } + wxASSERT( board ); // Give the filename to the board if it's new diff --git a/pcbnew/kicad_plugin.h b/pcbnew/kicad_plugin.h index 1655293617..f81555f2a3 100644 --- a/pcbnew/kicad_plugin.h +++ b/pcbnew/kicad_plugin.h @@ -2,6 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 CERN. + * Copyright (C) 1992-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 @@ -108,10 +109,11 @@ public: BOARD* Load( const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties = NULL ); - wxArrayString FootprintEnumerate( const wxString& aLibraryPath, const PROPERTIES* aProperties = NULL); + wxArrayString FootprintEnumerate( const wxString& aLibraryPath, + const PROPERTIES* aProperties = NULL ); MODULE* FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, - const PROPERTIES* aProperties = NULL ); + const PROPERTIES* aProperties = NULL ); void FootprintSave( const wxString& aLibraryPath, const MODULE* aFootprint, const PROPERTIES* aProperties = NULL ); @@ -153,7 +155,7 @@ public: void SetOutputFormatter( OUTPUTFORMATTER* aFormatter ) { m_out = aFormatter; } BOARD_ITEM* Parse( const wxString& aClipboardSourceInput ) - throw( PARSE_ERROR, IO_ERROR ); + throw( FUTURE_FORMAT_ERROR, PARSE_ERROR, IO_ERROR ); protected: diff --git a/pcbnew/legacy_plugin.cpp b/pcbnew/legacy_plugin.cpp index c621c716b4..54ccdc880b 100644 --- a/pcbnew/legacy_plugin.cpp +++ b/pcbnew/legacy_plugin.cpp @@ -4,7 +4,7 @@ * * Copyright (C) 2007-2012 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2004 Jean-Pierre Charras, jp.charras@wanadoo.fr - * Copyright (C) 1992-2015 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 1992-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 @@ -387,7 +387,8 @@ static inline long hexParse( const char* next, const char** out = NULL ) } -BOARD* LEGACY_PLUGIN::Load( const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties ) +BOARD* LEGACY_PLUGIN::Load( const wxString& aFileName, BOARD* aAppendToMe, + const PROPERTIES* aProperties ) { LOCALE_IO toggle; // toggles on, then off, the C locale. diff --git a/pcbnew/legacy_plugin.h b/pcbnew/legacy_plugin.h index 2216438f9a..ddf59714f9 100644 --- a/pcbnew/legacy_plugin.h +++ b/pcbnew/legacy_plugin.h @@ -5,7 +5,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2012 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 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 @@ -30,9 +30,11 @@ #include #include +// FOOTPRINT_LIBRARY_HEADER_CNT gives the number of characters to compare to detect +// a footprint library. A few variants may have been used, and so we can only be +// sure that the header contains "PCBNEW-LibModule-V", not "PCBNEW-LibModule-V1". #define FOOTPRINT_LIBRARY_HEADER "PCBNEW-LibModule-V1" -#define FOOTPRINT_LIBRARY_HEADER_CNT 18 - +#define FOOTPRINT_LIBRARY_HEADER_CNT 18 class PCB_TARGET; class MODULE; @@ -77,12 +79,14 @@ public: return wxT( "brd" ); } - BOARD* Load( const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties = NULL ); + BOARD* Load( const wxString& aFileName, BOARD* aAppendToMe, + const PROPERTIES* aProperties = NULL ); - wxArrayString FootprintEnumerate( const wxString& aLibraryPath, const PROPERTIES* aProperties = NULL); + wxArrayString FootprintEnumerate( const wxString& aLibraryPath, + const PROPERTIES* aProperties = NULL ); MODULE* FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, - const PROPERTIES* aProperties = NULL ); + const PROPERTIES* aProperties = NULL ); bool FootprintLibDelete( const wxString& aLibraryPath, const PROPERTIES* aProperties = NULL ); diff --git a/pcbnew/librairi.cpp b/pcbnew/librairi.cpp index 86210c8259..744a3d4e4b 100644 --- a/pcbnew/librairi.cpp +++ b/pcbnew/librairi.cpp @@ -1,8 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 1992-2015 KiCad Developers, see change_log.txt for contributors. - * + * Copyright (C) 1992-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 @@ -99,19 +98,14 @@ static const wxString ModImportFileWildcard( _( "GPcb foot print files (*)|*" ) #define EXPORT_IMPORT_LASTPATH_KEY wxT( "import_last_path" ) -MODULE* FOOTPRINT_EDIT_FRAME::Import_Module() +/** + * Prompt the user for a module file to open. + * @param aParent - parent window for the dialog + * @param aLastPath - last opened path + */ +static wxFileName prompt_for_module( wxWindow* aParent, const wxString& aLastPath ) { - // use the clipboard for this in the future? - - // Some day it might be useful save the last library type selected along with the path. static int lastFilterIndex = 0; - - wxString lastOpenedPathForLoading = m_mruPath; - wxConfigBase* config = Kiface().KifaceSettings(); - - if( config ) - config->Read( EXPORT_IMPORT_LASTPATH_KEY, &lastOpenedPathForLoading ); - wxString wildCard; wildCard << wxGetTranslation( KiCadFootprintLibFileWildcard ) << wxChar( '|' ) @@ -119,159 +113,204 @@ MODULE* FOOTPRINT_EDIT_FRAME::Import_Module() << wxGetTranslation( ModImportFileWildcard ) << wxChar( '|' ) << wxGetTranslation( GedaPcbFootprintLibFileWildcard ); - wxFileDialog dlg( this, FMT_IMPORT_MODULE, - lastOpenedPathForLoading, wxEmptyString, - wildCard, wxFD_OPEN | wxFD_FILE_MUST_EXIST ); + wxFileDialog dlg( aParent, FMT_IMPORT_MODULE, aLastPath, wxEmptyString, wildCard, + wxFD_OPEN | wxFD_FILE_MUST_EXIST ); + dlg.SetFilterIndex( lastFilterIndex ); if( dlg.ShowModal() == wxID_CANCEL ) - return NULL; - + return wxFileName(); + lastFilterIndex = dlg.GetFilterIndex(); - FILE* fp = wxFopen( dlg.GetPath(), wxT( "rt" ) ); + return wxFileName( dlg.GetPath() ); +} + + +/** + * Read a file to detect the type. + * @param aFile - open file to be read. File pointer will be closed. + * @param aFileName - file name to be read + * @param aName - wxString to receive the module name iff type is LEGACY + */ +static IO_MGR::PCB_FILE_T detect_file_type( FILE* aFile, const wxFileName& aFileName, wxString* aName ) +{ + FILE_LINE_READER freader( aFile, aFileName.GetFullPath() ); + WHITESPACE_FILTER_READER reader( freader ); + IO_MGR::PCB_FILE_T file_type; + + wxASSERT( aName ); + + reader.ReadLine(); + char* line = reader.Line(); + + if( !strnicmp( line, "(module", strlen( "(module" ) ) ) + { + file_type = IO_MGR::KICAD; + *aName = aFileName.GetName(); + } + else if( !strnicmp( line, FOOTPRINT_LIBRARY_HEADER, FOOTPRINT_LIBRARY_HEADER_CNT ) ) + { + file_type = IO_MGR::LEGACY; + while( reader.ReadLine() ) + { + if( !strnicmp( line, "$MODULE", strlen( "$MODULE" ) ) ) + { + *aName = FROM_UTF8( StrPurge( line + strlen( "$MODULE" ) ) ); + break; + } + } + } + else if( !strnicmp( line, "Element", strlen( "Element" ) ) ) + { + file_type = IO_MGR::GEDA_PCB; + *aName = aFileName.GetName(); + } + else + { + file_type = IO_MGR::FILE_TYPE_NONE; + } + + return file_type; +} + + +/** + * Parse a footprint using a PLUGIN. + * @param aFileName - file name to parse + * @param aFileType - type of the file + * @param aName - name of the footprint + */ +static MODULE* parse_module_with_plugin( + const wxFileName& aFileName, IO_MGR::PCB_FILE_T aFileType, + const wxString& aName ) +{ + wxString path; + + switch( aFileType ) + { + case IO_MGR::GEDA_PCB: + path = aFileName.GetPath(); + break; + case IO_MGR::LEGACY: + path = aFileName.GetFullPath(); + break; + default: + wxFAIL_MSG( wxT( "unexpected IO_MGR::PCB_FILE_T" ) ); + } + + PLUGIN::RELEASER pi( IO_MGR::PluginFind( aFileType ) ); + + return pi->FootprintLoad( path, aName ); +} + + +/** + * Parse a KICAD footprint. + * @param aFileName - file name to parse + */ +static MODULE* parse_module_kicad( const wxFileName& aFileName ) +{ + wxString fcontents; + PCB_IO pcb_io; + wxFFile f( aFileName.GetFullPath() ); + + if( !f.IsOpened() ) + return NULL; + + f.ReadAll( &fcontents ); + + return dynamic_cast( pcb_io.Parse( fcontents ) ); +} + + +/** + * Try to load a footprint, returning NULL if the file couldn't be accessed. + * @param aFileName - file name to load + * @param aFileType - type of the file to load + * @param aName - footprint name + */ +MODULE* try_load_footprint( const wxFileName& aFileName, IO_MGR::PCB_FILE_T aFileType, + const wxString& aName ) +{ + MODULE* module; + + switch( aFileType ) + { + case IO_MGR::GEDA_PCB: + case IO_MGR::LEGACY: + module = parse_module_with_plugin( aFileName, aFileType, aName ); + break; + + case IO_MGR::KICAD: + module = parse_module_kicad( aFileName ); + break; + + default: + wxFAIL_MSG( wxT( "unexpected IO_MGR::PCB_FILE_T" ) ); + module = NULL; + } + + return module; +} + + +MODULE* FOOTPRINT_EDIT_FRAME::Import_Module() +{ + wxString lastOpenedPathForLoading = m_mruPath; + wxConfigBase* config = Kiface().KifaceSettings(); + + if( config ) + config->Read( EXPORT_IMPORT_LASTPATH_KEY, &lastOpenedPathForLoading ); + + wxFileName fn = prompt_for_module( this, lastOpenedPathForLoading ); + + if( !fn.IsOk() ) + return NULL; + + FILE* fp = wxFopen( fn.GetFullPath(), wxT( "rt" ) ); if( !fp ) { - wxString msg = wxString::Format( FMT_FILE_NOT_FOUND, GetChars( dlg.GetPath() ) ); + wxString msg = wxString::Format( FMT_FILE_NOT_FOUND, GetChars( fn.GetFullPath() ) ); DisplayError( this, msg ); return NULL; } if( config ) // Save file path { - lastOpenedPathForLoading = wxPathOnly( dlg.GetPath() ); + lastOpenedPathForLoading = fn.GetPath(); config->Write( EXPORT_IMPORT_LASTPATH_KEY, lastOpenedPathForLoading ); } wxString moduleName; + IO_MGR::PCB_FILE_T fileType = detect_file_type( fp, fn.GetFullPath(), &moduleName ); - bool isGeda = false; - bool isLegacy = false; - + if( fileType == IO_MGR::FILE_TYPE_NONE ) { - FILE_LINE_READER freader( fp, dlg.GetPath() ); // I own fp, and will close it. - WHITESPACE_FILTER_READER reader( freader ); // skip blank lines - - reader.ReadLine(); - char* line = reader.Line(); - - if( !strnicmp( line, "(module", 7 ) ) - { - // isKicad = true; - } - else if( !strnicmp( line, FOOTPRINT_LIBRARY_HEADER, FOOTPRINT_LIBRARY_HEADER_CNT ) ) - { - isLegacy = true; - - while( reader.ReadLine() ) - { - if( !strnicmp( line, "$MODULE", 7 ) ) - { - moduleName = FROM_UTF8( StrPurge( line + sizeof( "$MODULE" ) -1 ) ); - break; - } - } - } - else if( !strnicmp( line, "Element", 7 ) ) - { - isGeda = true; - } - else - { - DisplayError( this, FMT_NOT_MODULE ); - return NULL; - } - - // fp is closed here by ~FILE_LINE_READER() + DisplayError( this, FMT_NOT_MODULE ); + return NULL; } - MODULE* module; + MODULE* module; + wxString errMessage; - if( isGeda ) + try { - try + module = try_load_footprint( fn, fileType, moduleName ); + + if( !module ) { - wxFileName fn = dlg.GetPath(); - PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::GEDA_PCB ) ); - - moduleName = fn.GetName(); - module = pi->FootprintLoad( fn.GetPath(), moduleName ); - - if( !module ) - { - wxString msg = wxString::Format( - FMT_MOD_NOT_FOUND, GetChars( moduleName ), GetChars( fn.GetPath() ) ); - - DisplayError( this, msg ); - return NULL; - } - } - catch( const IO_ERROR& ioe ) - { - DisplayError( this, ioe.errorText ); + wxString msg = wxString::Format( + FMT_MOD_NOT_FOUND, GetChars( moduleName ), GetChars( fn.GetFullPath() ) ); + DisplayError( this, msg ); return NULL; } } - else if( isLegacy ) + catch( const IO_ERROR& ioe ) { - try - { - PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::LEGACY ) ); - - module = pi->FootprintLoad( dlg.GetPath(), moduleName ); - - if( !module ) - { - wxString msg = wxString::Format( - FMT_MOD_NOT_FOUND, GetChars( moduleName ), GetChars( dlg.GetPath() ) ); - - DisplayError( this, msg ); - return NULL; - } - } - catch( const IO_ERROR& ioe ) - { - DisplayError( this, ioe.errorText ); - return NULL; - } - } - else // if( isKicad ) - { - try - { - // This technique was chosen to create an example of how reading - // the s-expression format from clipboard could be done. - - wxString fcontents; - PCB_IO pcb_io; - wxFFile f( dlg.GetPath() ); - - if( !f.IsOpened() ) - { - wxString msg = wxString::Format( FMT_BAD_PATH, GetChars( dlg.GetPath() ) ); - - DisplayError( this, msg ); - return NULL; - } - - f.ReadAll( &fcontents ); - - module = dyn_cast( pcb_io.Parse( fcontents ) ); - - if( !module ) - { - wxString msg = wxString::Format( FMT_BAD_PATH, GetChars( dlg.GetPath() ) ); - - DisplayError( this, msg ); - return NULL; - } - } - catch( const IO_ERROR& ioe ) - { - DisplayError( this, ioe.errorText ); - return NULL; - } + DisplayError( this, ioe.errorText ); + return NULL; } // Insert footprint in list diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp index a6a373fef7..09240bf0e8 100644 --- a/pcbnew/pcb_parser.cpp +++ b/pcbnew/pcb_parser.cpp @@ -2,8 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 CERN - * Copyright (C) 2012-2015 KiCad Developers, see change_log.txt for contributors. - * @author Wayne Stambaugh + * Copyright (C) 2012-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 @@ -60,6 +59,8 @@ using namespace PCB_KEYS_T; void PCB_PARSER::init() { + m_tooRecent = false; + m_requiredVersion = 0; m_layerIndices.clear(); m_layerMasks.clear(); @@ -169,6 +170,43 @@ bool PCB_PARSER::parseBool() throw( PARSE_ERROR ) } +int PCB_PARSER::parseVersion() throw( IO_ERROR, PARSE_ERROR ) +{ + if( NextTok() != T_version ) + Expecting( GetTokenText( T_version ) ); + + int pcb_version = parseInt( FromUTF8() ); + + NeedRIGHT(); + + return pcb_version; +} + + +wxString PCB_PARSER::GetRequiredVersion() +{ + int year, month, day; + + year = m_requiredVersion / 10000; + month = ( m_requiredVersion / 100 ) - ( year * 100 ); + day = m_requiredVersion - ( year * 10000 ) - ( month * 100 ); + + // wx throws an assertion, not a catchable exception, when the date is invalid. + // User input shouldn't give wx asserts, so check manually and throw a proper + // error instead + if( day <= 0 || month <= 0 || month > 12 || + day > wxDateTime::GetNumberOfDays( (wxDateTime::Month)( month - 1 ), year ) ) + { + wxString err; + err.Printf( _( "cannot interpret date code %d" ), m_requiredVersion ); + THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() ); + } + + wxDateTime date( day, (wxDateTime::Month)( month - 1 ), year, 0, 0, 0, 0 ); + return date.FormatDate(); +} + + wxPoint PCB_PARSER::parseXY() throw( PARSE_ERROR, IO_ERROR ) { if( CurTok() != T_LEFT ) @@ -411,7 +449,23 @@ BOARD_ITEM* PCB_PARSER::Parse() throw( IO_ERROR, PARSE_ERROR ) } -BOARD* PCB_PARSER::parseBOARD() throw( IO_ERROR, PARSE_ERROR ) +BOARD* PCB_PARSER::parseBOARD() throw( IO_ERROR, PARSE_ERROR, FUTURE_FORMAT_ERROR ) +{ + try + { + return parseBOARD_unchecked(); + } + catch( const PARSE_ERROR& parse_error ) + { + if( m_tooRecent ) + throw FUTURE_FORMAT_ERROR( parse_error, GetRequiredVersion() ); + else + throw; + } +} + + +BOARD* PCB_PARSER::parseBOARD_unchecked() throw( IO_ERROR, PARSE_ERROR ) { T token; @@ -506,24 +560,34 @@ void PCB_PARSER::parseHeader() throw( IO_ERROR, PARSE_ERROR ) wxCHECK_RET( CurTok() == T_kicad_pcb, wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a header." ) ); - T token; - NeedLEFT(); - token = NextTok(); - if( token != T_version ) - Expecting( GetTokenText( T_version ) ); + T tok = NextTok(); + if( tok == T_version ) + { + m_requiredVersion = parseInt( FromUTF8() ); + m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION ); + NeedRIGHT(); - // Get the file version. - m_board->SetFileFormatVersionAtLoad( parseInt( GetTokenText( T_version ) ) ); + // Skip the host name and host build version information. + NeedLEFT(); + NeedSYMBOL(); + NeedSYMBOL(); + NeedSYMBOL(); + NeedRIGHT(); + } + else + { + m_requiredVersion = SEXPR_BOARD_FILE_VERSION; + m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION ); - // Skip the host name and host build version information. - NeedRIGHT(); - NeedLEFT(); - NeedSYMBOL(); - NeedSYMBOL(); - NeedSYMBOL(); - NeedRIGHT(); + // Skip the host name and host build version information. + NeedSYMBOL(); + NeedSYMBOL(); + NeedRIGHT(); + } + + m_board->SetFileFormatVersionAtLoad( m_requiredVersion ); } @@ -1651,7 +1715,25 @@ DIMENSION* PCB_PARSER::parseDIMENSION() throw( IO_ERROR, PARSE_ERROR ) } -MODULE* PCB_PARSER::parseMODULE( wxArrayString* aInitialComments ) throw( IO_ERROR, PARSE_ERROR ) +MODULE* PCB_PARSER::parseMODULE( wxArrayString* aInitialComments ) + throw( IO_ERROR, PARSE_ERROR, FUTURE_FORMAT_ERROR ) +{ + try + { + return parseMODULE_unchecked( aInitialComments ); + } + catch( const PARSE_ERROR& parse_error ) + { + if( m_tooRecent ) + throw FUTURE_FORMAT_ERROR( parse_error, GetRequiredVersion() ); + else + throw; + } +} + + +MODULE* PCB_PARSER::parseMODULE_unchecked( wxArrayString* aInitialComments ) + throw( IO_ERROR, PARSE_ERROR ) { wxCHECK_MSG( CurTok() == T_module, NULL, wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as MODULE." ) ); @@ -1665,7 +1747,11 @@ MODULE* PCB_PARSER::parseMODULE( wxArrayString* aInitialComments ) throw( IO_ERR module->SetInitialComments( aInitialComments ); - NeedSYMBOLorNUMBER(); + token = NextTok(); + + if( !IsSymbol( token ) && token != T_NUMBER ) + Expecting( "symbol|number" ); + name = FromUTF8(); if( !name.IsEmpty() && fpid.Parse( FromUTF8() ) >= 0 ) @@ -1683,6 +1769,18 @@ MODULE* PCB_PARSER::parseMODULE( wxArrayString* aInitialComments ) throw( IO_ERR switch( token ) { + case T_version: + { + // Theoretically a module nested in a PCB could declare its own version, though + // as of writing this comment we don't do that. Just in case, take the greater + // version. + int this_version = parseInt( FromUTF8() ); + NeedRIGHT(); + m_requiredVersion = std::max( m_requiredVersion, this_version ); + m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION ); + break; + } + case T_locked: module->SetLocked( true ); break; diff --git a/pcbnew/pcb_parser.h b/pcbnew/pcb_parser.h index 9350b4e461..cd815ad22f 100644 --- a/pcbnew/pcb_parser.h +++ b/pcbnew/pcb_parser.h @@ -2,6 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 CERN + * Copyright (C) 2012-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 @@ -68,6 +69,8 @@ class PCB_PARSER : public PCB_LEXER LAYER_ID_MAP m_layerIndices; ///< map layer name to it's index LSET_MAP m_layerMasks; ///< map layer names to their masks std::vector m_netCodes; ///< net codes mapping for boards being loaded + bool m_tooRecent; ///< true if version parses as later than supported + int m_requiredVersion; ///< set to the KiCad format version this board requires ///> Converts net code using the mapping table if available, ///> otherwise returns unchanged net code if < 0 or if is is out of range @@ -113,12 +116,20 @@ class PCB_PARSER : public PCB_LEXER DIMENSION* parseDIMENSION() throw( IO_ERROR, PARSE_ERROR ); /** - * Function parseModule + * Function parseMODULE * @param aInitialComments may be a pointer to a heap allocated initial comment block * or NULL. If not NULL, then caller has given ownership of a wxArrayString to * this function and care must be taken to delete it even on exception. */ - MODULE* parseMODULE( wxArrayString* aInitialComments = 0 ) throw( IO_ERROR, PARSE_ERROR ); + MODULE* parseMODULE( wxArrayString* aInitialComments = 0 ) + throw( IO_ERROR, PARSE_ERROR, FUTURE_FORMAT_ERROR ); + + /** + * Function parseMODULE_unchecked + * Parse a module, but do not replace PARSE_ERROR with FUTURE_FORMAT_ERROR automatically. + */ + MODULE* parseMODULE_unchecked( wxArrayString* aInitialComments = 0 ) + throw( IO_ERROR, PARSE_ERROR ); TEXTE_MODULE* parseTEXTE_MODULE() throw( IO_ERROR, PARSE_ERROR ); EDGE_MODULE* parseEDGE_MODULE() throw( IO_ERROR, PARSE_ERROR ); D_PAD* parseD_PAD( MODULE* aParent = NULL ) throw( IO_ERROR, PARSE_ERROR ); @@ -126,7 +137,13 @@ class PCB_PARSER : public PCB_LEXER VIA* parseVIA() throw( IO_ERROR, PARSE_ERROR ); ZONE_CONTAINER* parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR ); PCB_TARGET* parsePCB_TARGET() throw( IO_ERROR, PARSE_ERROR ); - BOARD* parseBOARD() throw( IO_ERROR, PARSE_ERROR ); + BOARD* parseBOARD() throw( IO_ERROR, PARSE_ERROR, FUTURE_FORMAT_ERROR ); + + /** + * Function parseBOARD_unchecked + * Parse a module, but do not replace PARSE_ERROR with FUTURE_FORMAT_ERROR automatically. + */ + BOARD* parseBOARD_unchecked() throw( IO_ERROR, PARSE_ERROR ); /** @@ -252,6 +269,11 @@ class PCB_PARSER : public PCB_LEXER bool parseBool() throw( PARSE_ERROR ); + /** + * Parse a format version tag like (version 20160417) return the version. + * Expects to start on 'version', and eats the closing paren. + */ + int parseVersion() throw( IO_ERROR, PARSE_ERROR ); public: @@ -284,6 +306,21 @@ public: } BOARD_ITEM* Parse() throw( IO_ERROR, PARSE_ERROR ); + + /** + * Return whether a version number, if any was parsed, was too recent + */ + bool IsTooRecent() + { + return m_tooRecent; + } + + /** + * Return a string representing the version of kicad required to open this + * file. Not particularly meaningful if IsTooRecent() returns false. + */ + wxString GetRequiredVersion(); + }; diff --git a/pcbnew/plugin.cpp b/pcbnew/plugin.cpp index 22e060be09..9b5f5ab8a0 100644 --- a/pcbnew/plugin.cpp +++ b/pcbnew/plugin.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2011-2012 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2011 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 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