Add support for PCB and footprint format versioning

Currently, kicad_pcb files have a (version ...) tag, but it is ignored. This
commit does the following:

1. Parse the version. If it's later than the last supported version, present an
alternative error message suggesting an upgrade if the file does not load
correctly. The version will be interpreted as an integer YYYYMMDD to suggest
a KiCad release date.

2. Accept a (version ...) tag also in kicad_mod files, but do not write them
yet. If no version tag is present in these files, assume the version to be that
of the current format version at the time of this commit.

This is meant to be merged to the 4.x stable series, and preps for KiCad 5
which will start emitting version tags also in footprints - users with what
will then be 'old stable' will not get a parse error when we introduce this for
footprints, and we can safely increment the format version later.
This commit is contained in:
Chris Pavlina 2016-05-10 15:07:35 -04:00
parent 35fe82739b
commit f9386fcbc0
19 changed files with 436 additions and 201 deletions

View File

@ -1,9 +1,8 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2007-2011 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> * Copyright (C) 2007-2011 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -36,11 +35,6 @@
#endif #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 ) static int vprint( std::string* result, const char* format, va_list ap )
{ {
char msg[512]; char msg[512];

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2007-2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> * Copyright (C) 2007-2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -192,7 +192,10 @@ struct PARSE_ERROR : public IO_ERROR
const char* aInputLine, const char* aInputLine,
int aLineNumber, int aByteIndex ); 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 ) 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 */ /** @} exception_types */

View File

@ -3,8 +3,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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 ); init( aProperties );

View File

@ -5,7 +5,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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); 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 ) bool IsFootprintLibWritable( const wxString& aLibraryPath )
{ {

View File

@ -3,7 +3,7 @@
* *
* Copyright (C) 2004-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2004-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2011-2015 Wayne Stambaugh <stambaughw@verizon.net> * Copyright (C) 2011-2015 Wayne Stambaugh <stambaughw@verizon.net>
* 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -518,7 +518,6 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
GetChars( ioe.errorText ) GetChars( ioe.errorText )
); );
DisplayError( this, msg ); DisplayError( this, msg );
return false; return false;
} }

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> * Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License

View File

@ -2,8 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2012 Wayne Stambaugh <stambaughw@verizon.net> * Copyright (C) 2012 Wayne Stambaugh <stambaughw@verizon.net>
* 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2012 Wayne Stambaugh <stambaughw@verizon.net> * Copyright (C) 2012 Wayne Stambaugh <stambaughw@verizon.net>
* 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2011-2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> * Copyright (C) 2011-2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -98,6 +98,9 @@ PLUGIN* IO_MGR::PluginFind( PCB_FILE_T aFileType )
#else #else
THROW_IO_ERROR( "BUILD_GITHUB_PLUGIN not enabled in cmake build environment" ); THROW_IO_ERROR( "BUILD_GITHUB_PLUGIN not enabled in cmake build environment" );
#endif #endif
case FILE_TYPE_NONE:
return NULL;
} }
return NULL; return NULL;

View File

@ -5,7 +5,7 @@
* This program source code file is part of KICAD, a free EDA CAD application. * This program source code file is part of KICAD, a free EDA CAD application.
* *
* Copyright (C) 2011-2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> * Copyright (C) 2011-2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -83,6 +83,8 @@ public:
// ALTIUM, // ALTIUM,
// etc. // etc.
FILE_TYPE_NONE
}; };
/** /**

View File

@ -2,8 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2012 CERN * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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 ); std::string input = TO_UTF8( aClipboardSourceInput );
@ -426,7 +426,17 @@ BOARD_ITEM* PCB_IO::Parse( const wxString& aClipboardSourceInput ) throw( PARSE_
m_parser->SetLineReader( &reader ); m_parser->SetLineReader( &reader );
try
{
return m_parser->Parse(); 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->SetLineReader( &reader );
m_parser->SetBoard( aAppendToMe ); m_parser->SetBoard( aAppendToMe );
BOARD* board = dyn_cast<BOARD*>( m_parser->Parse() ); BOARD* board;
try
{
board = dynamic_cast<BOARD*>( 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 ); wxASSERT( board );
// Give the filename to the board if it's new // Give the filename to the board if it's new

View File

@ -2,6 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2012 CERN. * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -108,7 +109,8 @@ public:
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, MODULE* FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
const PROPERTIES* aProperties = NULL ); const PROPERTIES* aProperties = NULL );
@ -153,7 +155,7 @@ public:
void SetOutputFormatter( OUTPUTFORMATTER* aFormatter ) { m_out = aFormatter; } void SetOutputFormatter( OUTPUTFORMATTER* aFormatter ) { m_out = aFormatter; }
BOARD_ITEM* Parse( const wxString& aClipboardSourceInput ) BOARD_ITEM* Parse( const wxString& aClipboardSourceInput )
throw( PARSE_ERROR, IO_ERROR ); throw( FUTURE_FORMAT_ERROR, PARSE_ERROR, IO_ERROR );
protected: protected:

View File

@ -4,7 +4,7 @@
* *
* Copyright (C) 2007-2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> * Copyright (C) 2007-2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2004 Jean-Pierre Charras, jp.charras@wanadoo.fr * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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. LOCALE_IO toggle; // toggles on, then off, the C locale.

View File

@ -5,7 +5,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -30,10 +30,12 @@
#include <string> #include <string>
#include <layers_id_colors_and_visibility.h> #include <layers_id_colors_and_visibility.h>
// 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 "PCBNEW-LibModule-V1"
#define FOOTPRINT_LIBRARY_HEADER_CNT 18 #define FOOTPRINT_LIBRARY_HEADER_CNT 18
class PCB_TARGET; class PCB_TARGET;
class MODULE; class MODULE;
class DRAWSEGMENT; class DRAWSEGMENT;
@ -77,9 +79,11 @@ public:
return wxT( "brd" ); 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, MODULE* FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
const PROPERTIES* aProperties = NULL ); const PROPERTIES* aProperties = NULL );

View File

@ -1,8 +1,7 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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" ) #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; static int lastFilterIndex = 0;
wxString lastOpenedPathForLoading = m_mruPath;
wxConfigBase* config = Kiface().KifaceSettings();
if( config )
config->Read( EXPORT_IMPORT_LASTPATH_KEY, &lastOpenedPathForLoading );
wxString wildCard; wxString wildCard;
wildCard << wxGetTranslation( KiCadFootprintLibFileWildcard ) << wxChar( '|' ) wildCard << wxGetTranslation( KiCadFootprintLibFileWildcard ) << wxChar( '|' )
@ -119,90 +113,196 @@ MODULE* FOOTPRINT_EDIT_FRAME::Import_Module()
<< wxGetTranslation( ModImportFileWildcard ) << wxChar( '|' ) << wxGetTranslation( ModImportFileWildcard ) << wxChar( '|' )
<< wxGetTranslation( GedaPcbFootprintLibFileWildcard ); << wxGetTranslation( GedaPcbFootprintLibFileWildcard );
wxFileDialog dlg( this, FMT_IMPORT_MODULE, wxFileDialog dlg( aParent, FMT_IMPORT_MODULE, aLastPath, wxEmptyString, wildCard,
lastOpenedPathForLoading, wxEmptyString, wxFD_OPEN | wxFD_FILE_MUST_EXIST );
wildCard, wxFD_OPEN | wxFD_FILE_MUST_EXIST );
dlg.SetFilterIndex( lastFilterIndex ); dlg.SetFilterIndex( lastFilterIndex );
if( dlg.ShowModal() == wxID_CANCEL ) if( dlg.ShowModal() == wxID_CANCEL )
return NULL; return wxFileName();
lastFilterIndex = dlg.GetFilterIndex(); 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<MODULE*>( 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 ) 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 ); DisplayError( this, msg );
return NULL; return NULL;
} }
if( config ) // Save file path if( config ) // Save file path
{ {
lastOpenedPathForLoading = wxPathOnly( dlg.GetPath() ); lastOpenedPathForLoading = fn.GetPath();
config->Write( EXPORT_IMPORT_LASTPATH_KEY, lastOpenedPathForLoading ); config->Write( EXPORT_IMPORT_LASTPATH_KEY, lastOpenedPathForLoading );
} }
wxString moduleName; wxString moduleName;
IO_MGR::PCB_FILE_T fileType = detect_file_type( fp, fn.GetFullPath(), &moduleName );
bool isGeda = false; if( fileType == IO_MGR::FILE_TYPE_NONE )
bool isLegacy = false;
{
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 ); DisplayError( this, FMT_NOT_MODULE );
return NULL; return NULL;
} }
// fp is closed here by ~FILE_LINE_READER()
}
MODULE* module; MODULE* module;
wxString errMessage;
if( isGeda )
{
try try
{ {
wxFileName fn = dlg.GetPath(); module = try_load_footprint( fn, fileType, moduleName );
PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::GEDA_PCB ) );
moduleName = fn.GetName();
module = pi->FootprintLoad( fn.GetPath(), moduleName );
if( !module ) if( !module )
{ {
wxString msg = wxString::Format( wxString msg = wxString::Format(
FMT_MOD_NOT_FOUND, GetChars( moduleName ), GetChars( fn.GetPath() ) ); FMT_MOD_NOT_FOUND, GetChars( moduleName ), GetChars( fn.GetFullPath() ) );
DisplayError( this, msg ); DisplayError( this, msg );
return NULL; return NULL;
} }
@ -212,67 +312,6 @@ MODULE* FOOTPRINT_EDIT_FRAME::Import_Module()
DisplayError( this, ioe.errorText ); DisplayError( this, ioe.errorText );
return NULL; return NULL;
} }
}
else if( isLegacy )
{
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<MODULE*>( 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;
}
}
// Insert footprint in list // Insert footprint in list
GetBoard()->Add( module ); GetBoard()->Add( module );

View File

@ -2,8 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2012 CERN * Copyright (C) 2012 CERN
* Copyright (C) 2012-2015 KiCad Developers, see change_log.txt for contributors. * Copyright (C) 2012-2016 KiCad Developers, see AUTHORS.txt for contributors.
* @author Wayne Stambaugh <stambaughw@verizon.net>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -60,6 +59,8 @@ using namespace PCB_KEYS_T;
void PCB_PARSER::init() void PCB_PARSER::init()
{ {
m_tooRecent = false;
m_requiredVersion = 0;
m_layerIndices.clear(); m_layerIndices.clear();
m_layerMasks.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 ) wxPoint PCB_PARSER::parseXY() throw( PARSE_ERROR, IO_ERROR )
{ {
if( CurTok() != T_LEFT ) 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; T token;
@ -506,24 +560,34 @@ void PCB_PARSER::parseHeader() throw( IO_ERROR, PARSE_ERROR )
wxCHECK_RET( CurTok() == T_kicad_pcb, wxCHECK_RET( CurTok() == T_kicad_pcb,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a header." ) ); wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a header." ) );
T token;
NeedLEFT(); NeedLEFT();
token = NextTok();
if( token != T_version ) T tok = NextTok();
Expecting( GetTokenText( T_version ) ); if( tok == T_version )
{
// Get the file version. m_requiredVersion = parseInt( FromUTF8() );
m_board->SetFileFormatVersionAtLoad( parseInt( GetTokenText( T_version ) ) ); m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION );
NeedRIGHT();
// Skip the host name and host build version information. // Skip the host name and host build version information.
NeedRIGHT();
NeedLEFT(); NeedLEFT();
NeedSYMBOL(); NeedSYMBOL();
NeedSYMBOL(); NeedSYMBOL();
NeedSYMBOL(); NeedSYMBOL();
NeedRIGHT(); 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.
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, wxCHECK_MSG( CurTok() == T_module, NULL,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as MODULE." ) ); wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as MODULE." ) );
@ -1665,7 +1747,11 @@ MODULE* PCB_PARSER::parseMODULE( wxArrayString* aInitialComments ) throw( IO_ERR
module->SetInitialComments( aInitialComments ); module->SetInitialComments( aInitialComments );
NeedSYMBOLorNUMBER(); token = NextTok();
if( !IsSymbol( token ) && token != T_NUMBER )
Expecting( "symbol|number" );
name = FromUTF8(); name = FromUTF8();
if( !name.IsEmpty() && fpid.Parse( FromUTF8() ) >= 0 ) if( !name.IsEmpty() && fpid.Parse( FromUTF8() ) >= 0 )
@ -1683,6 +1769,18 @@ MODULE* PCB_PARSER::parseMODULE( wxArrayString* aInitialComments ) throw( IO_ERR
switch( token ) 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: case T_locked:
module->SetLocked( true ); module->SetLocked( true );
break; break;

View File

@ -2,6 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2012 CERN * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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 LAYER_ID_MAP m_layerIndices; ///< map layer name to it's index
LSET_MAP m_layerMasks; ///< map layer names to their masks LSET_MAP m_layerMasks; ///< map layer names to their masks
std::vector<int> m_netCodes; ///< net codes mapping for boards being loaded std::vector<int> 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, ///> Converts net code using the mapping table if available,
///> otherwise returns unchanged net code if < 0 or if is is out of range ///> 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 ); DIMENSION* parseDIMENSION() throw( IO_ERROR, PARSE_ERROR );
/** /**
* Function parseModule * Function parseMODULE
* @param aInitialComments may be a pointer to a heap allocated initial comment block * @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 * 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. * 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 ); TEXTE_MODULE* parseTEXTE_MODULE() throw( IO_ERROR, PARSE_ERROR );
EDGE_MODULE* parseEDGE_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 ); 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 ); VIA* parseVIA() throw( IO_ERROR, PARSE_ERROR );
ZONE_CONTAINER* parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR ); ZONE_CONTAINER* parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR );
PCB_TARGET* parsePCB_TARGET() 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 ); 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: public:
@ -284,6 +306,21 @@ public:
} }
BOARD_ITEM* Parse() throw( IO_ERROR, PARSE_ERROR ); 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();
}; };

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2011-2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> * Copyright (C) 2011-2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License