← Back to team overview

kicad-developers team mailing list archive

Re: New pcbnew features and versioning

 

Followed up on this question in a separate mail.

Patch is attached. Please have a good look over it, if it's going to stable I
don't want to be the only one who's had a look :)

On Sat, Apr 30, 2016 at 07:57:36PM -0400, Chris Pavlina wrote:
> I'm very close to finished - I'll take some time to fully test and review the
> patch to ensure it's ready for a commit to stable, though - will submit
> tomorrow.
> 
> I have a question. Currently, when loading a full library as opposed to a
> single footprint, we silently ignore errors and just do not load footprints
> that have syntax issues. This of course means that format versioning won't
> really work for these. The user will never see the hint about their version
> being old.
> 
> Should I:
> 1) Leave it as is. (please say no please say no please say no)
> 2) Make an exception for the version-too-new case.
> 3) Change this and *do* display these errors, in all cases.
> 
> On Fri, Apr 29, 2016 at 09:24:19AM -0400, Wayne Stambaugh wrote:
> > Thanks for the update.  I've been holding off on releasing 4.0.3 for
> > this.  I apologize for my absence over the last week or so.  I've been
> > really busy at work and got sick on top of that so my motivation to
> > spend what little free time I had working on KiCad was low.
> > 
> > Cheers,
> > 
> > Wayne
> > 
> > On 4/28/2016 2:38 PM, Chris Pavlina wrote:
> > > Just a quick ping to reassure y'all I'm still working on this - been caught
> > > up in other things a bit the last couple weeks. I've got a nearly working
> > > implementation here.
> > > 
> > > On Tue, Apr 12, 2016 at 12:22:48PM -0400, Wayne Stambaugh wrote:
> > >> I doubt this going to be a big issue.  Since the new board file format
> > >> was implemented over fours years ago there have been a handful of
> > >> changes.  I think we're going to be OK with just the date code.
> > >>
> > >> On 4/12/2016 12:06 PM, Chris Pavlina wrote:
> > >>> Let's just not do more than one format change in a single day... I think that
> > >>> would be a beneficial decision for project stability as well...
> > >>>
> > >>> On Tue, Apr 12, 2016 at 05:26:27PM +0200, Timofonic wrote:
> > >>>> Despite my very limited knowledge, I like the simple approach. 
> > >>>>
> > >>>> What about using letters if more than one format change is done? 
> > >>>>
> > >>>> 20160412A, 20160412B, 20160412C...
> > >>>>
> > >>>> On April 12, 2016 2:30:23 PM CEST, Chris Pavlina <pavlina.chris@xxxxxxxxx> wrote:
> > >>>>> Honestly I don't see the advantage to using Semantic Versioning for an
> > >>>>> internal file format version... and using 2016.04.12 instead of
> > >>>>> 20160412
> > >>>>> just seems like an exercise in making the parser more complicated.
> > >>>>> Could
> > >>>>> you explain *why* this would be a good thing?
> > >>>>> On Apr 12, 2016 1:51 AM, "David Cary" <d.cary+2012@xxxxxxxx> wrote:
> > >>>>>
> > >>>>>> Please at least consider Semantic Versioning ( http://semver.org/ ).
> > >>>>>> And I recommend that if you figure out any way to improve on SemVer,
> > >>>>>> please speak up so maybe the next version of SemVer can incorporate
> > >>>>>> those improvements.
> > >>>>>>
> > >>>>>> I have enjoyed the discussion of new features and various ideas for
> > >>>>>> versioning, and I encourage you to discuss them further.
> > >>>>>>
> > >>>>>> I am happy that the native KiCad file formats already avoid many
> > >>>>>> problems mentioned in
> > >>>>>> "Designing File Formats" http://www.fadden.com/tech/file-formats.html
> > >>>>>> .
> > >>>>>> Are there any remaining recommendations in that essay that maybe we
> > >>>>>> should include in future versions of KiCad file formats?
> > >>>>>>
> > >>>>>> If hypothetically we did use Semantic Versioning,
> > >>>>>> would it be better to do (a) or (b)?:
> > >>>>>> (a) have a single KiCad version number that KiCad writes into every
> > >>>>>> new file it creates, or
> > >>>>>> (b) have a separate and independent version number for each part of
> > >>>>>> KiCad -- the Eeschema version number written into new schematic
> > >>>>> files,
> > >>>>>> a separate Pcbnew version number written into new footprint and PCB
> > >>>>>> layout files, etc.
> > >>>>>>
> > >>>>>> (How many independent version numbers could option (b) require?)
> > >>>>>>
> > >>>>>> On Thu, Apr 7, 2016 at 1:04 PM, Chris Pavlina
> > >>>>> <pavlina.chris@xxxxxxxxx>
> > >>>>>> wrote:
> > >>>>>>> What about using the date the change was made as a "version
> > >>>>> number"? Can
> > >>>>>>> integerize it like 20160407 for example. This allows easy
> > >>>>>> cross-referencing of
> > >>>>>>> a format version with the revision that it was made in, and is
> > >>>>>> guaranteed to
> > >>>>>>> increase monotonically if you use a YMD format :)
> > >>>>>>
> > >>>>>> I agree with Wayne that it's more meaningful than most version
> > >>>>> strings.
> > >>>>>>
> > >>>>>> My understanding is that "integerized date" without punctuation is
> > >>>>>> more commonly known as the "ISO 8601 date basic format".
> > >>>>>>
> > >>>>>> Recently I've been putting a date like that on the silkscreen of my
> > >>>>>> PCBs. (I use the "ISO 8601 date extended format" like 2016-04-07, the
> > >>>>>> format produced by the KiCad "%D" format symbol).
> > >>>>>>
> > >>>>>> Is it possible to combine that with Semantic versioning, getting
> > >>>>>> something like 2016.04.07 ?
> > >>>>>> (This assumes we won't make a breaking change in the file format more
> > >>>>>> than once a year -- optimistic? :-)
> > >>>>>>
> > >>>>>> --
> > >>>>>> David Cary
> > >>>>>> +1(918)813-2279
> > >>>>>> http://OpenCircuits.com/
> > >>>>>> http://david.carybros.com/
> > >>>>>>
> > >>>>>
> > >>>>>
> > >>>>> ------------------------------------------------------------------------
> > >>>>>
> > >>>>> _______________________________________________
> > >>>>> Mailing list: https://launchpad.net/~kicad-developers
> > >>>>> Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> > >>>>> Unsubscribe : https://launchpad.net/~kicad-developers
> > >>>>> More help   : https://help.launchpad.net/ListHelp
> > >>>>
> > >>>> -- 
> > >>>> Enviado desde mi dispositivo Android con K-9 Mail. Por favor disculpa mi brevedad.
> > >>>
> > >>> _______________________________________________
> > >>> Mailing list: https://launchpad.net/~kicad-developers
> > >>> Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> > >>> Unsubscribe : https://launchpad.net/~kicad-developers
> > >>> More help   : https://help.launchpad.net/ListHelp
> > >>>
> > >>
> > >> _______________________________________________
> > >> Mailing list: https://launchpad.net/~kicad-developers
> > >> Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> > >> Unsubscribe : https://launchpad.net/~kicad-developers
> > >> More help   : https://help.launchpad.net/ListHelp
> > 
>From 4a1c297eb639fc65fe5616540293a8310947b3c1 Mon Sep 17 00:00:00 2001
From: Chris Pavlina <pavlina.chris@xxxxxxxxx>
Date: Sun, 1 May 2016 18:50:21 -0400
Subject: [PATCH] Add format version checking for PCB and footprints

1. Add a (version %d) entry to footprints matching that used in PCB files.

2. When a user loads a file and the version entry is encountered, store the
version for later comparison.

3. If a syntax error is encountered, check whether a version was encountered.
If the file is more recent than KiCad, explain to the user that they might be
able to open the file if they upgrade KiCad.

Also, update a few copyright lines that were in the general area where I was
working.

Relevant bug: https://bugs.launchpad.net/kicad/+bug/1416736

Users will still not see the error message when loading an entire library,
as the library loading/caching system suppresses them.
---
 common/richio.cpp               |   3 +-
 include/richio.h                |  33 ++++-
 pcbnew/eagle_plugin.cpp         |   3 +-
 pcbnew/eagle_plugin.h           |   2 +-
 pcbnew/files.cpp                |   2 +-
 pcbnew/github/github_plugin.cpp |   2 +-
 pcbnew/github/github_plugin.h   |   2 +-
 pcbnew/gpcb_plugin.cpp          |   3 +-
 pcbnew/gpcb_plugin.h            |   2 +-
 pcbnew/io_mgr.cpp               |   5 +-
 pcbnew/io_mgr.h                 |   4 +-
 pcbnew/kicad_plugin.cpp         |  41 +++++-
 pcbnew/kicad_plugin.h           |   3 +-
 pcbnew/legacy_plugin.cpp        |   2 +-
 pcbnew/legacy_plugin.h          |   5 +-
 pcbnew/librairi.cpp             | 300 +++++++++++++++++++++++-----------------
 pcbnew/pcb_parser.cpp           | 145 ++++++++++++++++---
 pcbnew/pcb_parser.h             |  43 +++++-
 pcbnew/plugin.cpp               |   2 +-
 19 files changed, 428 insertions(+), 174 deletions(-)

diff --git a/common/richio.cpp b/common/richio.cpp
index 339684e..37c138c 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 <dick@xxxxxxxxxxx>
- * Copyright (C) 2015 KiCad Developers, see change_log.txt for contributors.
+ * Copyright (C) 2015 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/include/richio.h b/include/richio.h
index 78e95d3..3ab6d69 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 <dick@xxxxxxxxxxx>
- * 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
@@ -193,6 +193,9 @@ struct PARSE_ERROR : public IO_ERROR
                int aLineNumber, int aByteIndex );
 
     ~PARSE_ERROR() throw ( /*none*/ ){}
+
+protected:
+    PARSE_ERROR(): IO_ERROR() {}
 };
 
 
@@ -200,6 +203,34 @@ 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 newer 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;
+    }
+};
+
+
 /** @} exception_types */
 
 
diff --git a/pcbnew/eagle_plugin.cpp b/pcbnew/eagle_plugin.cpp
index 6297da4..27d0d6c 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 <dick@xxxxxxxxxxx>
- * 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
diff --git a/pcbnew/eagle_plugin.h b/pcbnew/eagle_plugin.h
index e9bde95..078bc8b 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 <dick@xxxxxxxxxxx>
- * 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
diff --git a/pcbnew/files.cpp b/pcbnew/files.cpp
index 379a1a2..650e7bd 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 <stambaughw@xxxxxxxxxxx>
- * 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
diff --git a/pcbnew/github/github_plugin.cpp b/pcbnew/github/github_plugin.cpp
index 75d4e7b..e43c2fb 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 <dick@xxxxxxxxxxx>
- * 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 3dc10a6..92f2c13 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 <dick@xxxxxxxxxxx>
- * 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 16432fb..94815f0 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 <stambaughw@xxxxxxxxxxx>
- * 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 4ae6556..8fb1618 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 <stambaughw@xxxxxxxxxxx>
- * 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
diff --git a/pcbnew/io_mgr.cpp b/pcbnew/io_mgr.cpp
index c733d30..8d3981b 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 <dick@xxxxxxxxxxx>
- * 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 e2b0135..80b4821 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 <dick@xxxxxxxxxxx>
- * 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 09abbcd..c495e00 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,21 @@ 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 FUTURE_FORMAT_ERROR& err )
+    {
+        throw;
+    }
+    catch( const PARSE_ERROR& err )
+    {
+        if( m_parser->IsTooRecent() )
+            throw FUTURE_FORMAT_ERROR( err, m_parser->GetRequiredVersion() );
+        else
+            throw;
+    }
 }
 
 
@@ -1721,7 +1735,24 @@ BOARD* PCB_IO::Load( const wxString& aFileName, BOARD* aAppendToMe, const PROPER
     m_parser->SetLineReader( &reader );
     m_parser->SetBoard( aAppendToMe );
 
-    BOARD* board = dyn_cast<BOARD*>( m_parser->Parse() );
+    BOARD* board = NULL;
+
+    try
+    {
+        board = dynamic_cast<BOARD*>( m_parser->Parse() );
+    }
+    catch( const FUTURE_FORMAT_ERROR& err )
+    {
+        throw;
+    }
+    catch( const PARSE_ERROR& err )
+    {
+        if( m_parser->IsTooRecent() )
+            throw FUTURE_FORMAT_ERROR( err, 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 1655293..5b8f415 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
@@ -153,7 +154,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 c621c71..012800c 100644
--- a/pcbnew/legacy_plugin.cpp
+++ b/pcbnew/legacy_plugin.cpp
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2007-2012 SoftPLC Corporation, Dick Hollenbeck <dick@xxxxxxxxxxx>
  * Copyright (C) 2004 Jean-Pierre Charras, jp.charras@xxxxxxxxxx
- * 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/legacy_plugin.h b/pcbnew/legacy_plugin.h
index 2216438..c604705 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 <dick@xxxxxxxxxxx>
- * 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,6 +30,9 @@
 #include <string>
 #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_CNT   18
 
diff --git a/pcbnew/librairi.cpp b/pcbnew/librairi.cpp
index 86210c8..f0f6aba 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,180 +98,223 @@ 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;
+    wxFileName fn;
 
     wildCard << wxGetTranslation( KiCadFootprintLibFileWildcard ) << wxChar( '|' )
              << wxGetTranslation( ModLegacyExportFileWildcard ) << wxChar( '|' )
              << 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;
+    if( dlg.ShowModal() != wxID_CANCEL )
+    {
+        lastFilterIndex = dlg.GetFilterIndex();
+        fn = wxFileName( dlg.GetPath() );
+    }
+
+    return fn;
+}
 
-    lastFilterIndex = dlg.GetFilterIndex();
 
-    FILE* fp = wxFopen( dlg.GetPath(), wxT( "rt" ) );
+/**
+ * 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;
 
-    if( !fp )
+    wxASSERT( aName );
+
+    reader.ReadLine();
+    char* line = reader.Line();
+
+    if( !strnicmp( line, "(module", strlen( "(module" ) ) )
     {
-        wxString msg = wxString::Format( FMT_FILE_NOT_FOUND, GetChars( dlg.GetPath() ) );
-        DisplayError( this, msg );
-        return NULL;
+        file_type = IO_MGR::KICAD;
+        *aName = aFileName.GetName();
     }
-
-    if( config )    // Save file path
+    else if( !strnicmp( line, FOOTPRINT_LIBRARY_HEADER, FOOTPRINT_LIBRARY_HEADER_CNT ) )
     {
-        lastOpenedPathForLoading = wxPathOnly( dlg.GetPath() );
-        config->Write( EXPORT_IMPORT_LASTPATH_KEY, lastOpenedPathForLoading );
+        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;
     }
 
-    wxString    moduleName;
+    return file_type;
+}
 
-    bool        isGeda   = false;
-    bool        isLegacy = false;
 
+/**
+ * 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 )
     {
-        FILE_LINE_READER         freader( fp, dlg.GetPath() );   // I own fp, and will close it.
-        WHITESPACE_FILTER_READER reader( freader );              // skip blank lines
+    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" ) );
+    }
 
-        reader.ReadLine();
-        char* line = reader.Line();
+    PLUGIN::RELEASER pi( IO_MGR::PluginFind( aFileType ) );
 
-        if( !strnicmp( line, "(module", 7 ) )
-        {
-            // isKicad = true;
-        }
-        else if( !strnicmp( line, FOOTPRINT_LIBRARY_HEADER, FOOTPRINT_LIBRARY_HEADER_CNT ) )
-        {
-            isLegacy = true;
+    return pi->FootprintLoad( path, aName );
+}
 
-            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()
+/**
+ * Parse a KICAD footprint.
+ * @param aFileName - file name to parse
+ */
+static MODULE* parse_module_kicad( const wxFileName& aFileName )
+{
+    wxFFile  f( aFileName.GetFullPath() );
+    MODULE*  mod = NULL;
+
+    if( f.IsOpened() )
+    {
+        PCB_IO   pcb_io;
+        wxString fcontents;
+
+        f.ReadAll( &fcontents );
+        mod = dynamic_cast<MODULE*>( pcb_io.Parse( fcontents ) );
     }
 
-    MODULE*   module;
+    return mod;
+}
 
-    if( isGeda )
-    {
-        try
-        {
-            wxFileName fn = dlg.GetPath();
-            PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::GEDA_PCB ) );
 
-            moduleName = fn.GetName();
-            module = pi->FootprintLoad( fn.GetPath(), moduleName );
+/**
+ * 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 = NULL;
 
-            if( !module )
-            {
-                wxString msg = wxString::Format(
-                    FMT_MOD_NOT_FOUND, GetChars( moduleName ), GetChars( fn.GetPath() ) );
+    switch( aFileType )
+    {
+    case IO_MGR::GEDA_PCB:
+    case IO_MGR::LEGACY:
+        module = parse_module_with_plugin( aFileName, aFileType, aName );
+        break;
 
-                DisplayError( this, msg );
-                return NULL;
-            }
-        }
-        catch( const IO_ERROR& ioe )
-        {
-            DisplayError( this, ioe.errorText );
-            return NULL;
-        }
+    case IO_MGR::KICAD:
+        module = parse_module_kicad( aFileName );
+        break;
+
+    default:
+        wxFAIL_MSG( wxT( "unexpected IO_MGR::PCB_FILE_T" ) );
     }
-    else if( isLegacy )
-    {
-        try
-        {
-            PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::LEGACY ) );
 
-            module = pi->FootprintLoad( dlg.GetPath(), moduleName );
+    return module;
+}
 
-            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.
+MODULE* FOOTPRINT_EDIT_FRAME::Import_Module()
+{
+    wxString        lastOpenedPathForLoading = m_mruPath;
+    wxConfigBase*   config = Kiface().KifaceSettings();
+
+    if( config )
+        config->Read( EXPORT_IMPORT_LASTPATH_KEY, &lastOpenedPathForLoading );
 
-            wxString    fcontents;
-            PCB_IO      pcb_io;
-            wxFFile     f( dlg.GetPath() );
+    wxFileName fn = prompt_for_module( this, lastOpenedPathForLoading );
 
-            if( !f.IsOpened() )
-            {
-                wxString msg = wxString::Format( FMT_BAD_PATH, GetChars( dlg.GetPath() ) );
+    if( !fn.IsOk() )
+        return NULL;
 
-                DisplayError( this, msg );
-                return NULL;
-            }
+    FILE* fp = wxFopen( fn.GetFullPath(), wxT( "rt" ) );
 
-            f.ReadAll( &fcontents );
+    if( !fp )
+    {
+        wxString msg = wxString::Format( FMT_FILE_NOT_FOUND, GetChars( fn.GetFullPath() ) );
+        DisplayError( this, msg );
+        return NULL;
+    }
 
-            module = dyn_cast<MODULE*>( pcb_io.Parse( fcontents ) );
+    if( config )    // Save file path
+    {
+        lastOpenedPathForLoading = fn.GetPath();
+        config->Write( EXPORT_IMPORT_LASTPATH_KEY, lastOpenedPathForLoading );
+    }
 
-            if( !module )
-            {
-                wxString msg = wxString::Format( FMT_BAD_PATH, GetChars( dlg.GetPath() ) );
+    wxString    moduleName;
+    IO_MGR::PCB_FILE_T fileType = detect_file_type( fp, fn.GetFullPath(), &moduleName );
 
-                DisplayError( this, msg );
-                return NULL;
-            }
-        }
-        catch( const IO_ERROR& ioe )
+    if( fileType == IO_MGR::FILE_TYPE_NONE )
+    {
+        DisplayError( this, FMT_NOT_MODULE );
+        return NULL;
+    }
+
+    MODULE*    module;
+    wxString   errMessage;
+
+    try
+    {
+        module = try_load_footprint( fn, fileType, moduleName );
+
+        if( !module )
         {
-            DisplayError( this, ioe.errorText );
+            wxString msg = wxString::Format(
+                    FMT_MOD_NOT_FOUND, GetChars( moduleName ), GetChars( fn.GetFullPath() ) );
+            DisplayError( this, msg );
             return NULL;
         }
     }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( this, ioe.errorText );
+        return NULL;
+    }
 
     // Insert footprint in list
     GetBoard()->Add( module );
diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp
index a6a373f..965bd61 100644
--- a/pcbnew/pcb_parser.cpp
+++ b/pcbnew/pcb_parser.cpp
@@ -2,8 +2,8 @@
  * 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 <stambaughw@xxxxxxxxxxx>
+ * Copyright (C) 2013 Wayne Stambaugh <stambaughw@xxxxxxxxxxx>
+ * 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 +60,8 @@ using namespace PCB_KEYS_T;
 
 void PCB_PARSER::init()
 {
+    m_tooRecent = false;
+    m_requiredVersion = 0;
     m_layerIndices.clear();
     m_layerMasks.clear();
 
@@ -169,6 +171,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 +450,27 @@ 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 FUTURE_FORMAT_ERROR& err )
+    {
+        throw;
+    }
+    catch( const PARSE_ERROR& err )
+    {
+        if( m_tooRecent )
+            throw FUTURE_FORMAT_ERROR( err, GetRequiredVersion() );
+        else
+            throw;
+    }
+}
+
+
+BOARD* PCB_PARSER::parseBOARD_unchecked() throw( IO_ERROR, PARSE_ERROR )
 {
     T token;
 
@@ -506,24 +565,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 +1720,29 @@ 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 FUTURE_FORMAT_ERROR& err )
+    {
+        throw;
+    }
+    catch( const PARSE_ERROR& err )
+    {
+        if( m_tooRecent )
+            throw FUTURE_FORMAT_ERROR( err, 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 +1756,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 +1778,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 9350b4e..cd815ad 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<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,
     ///> 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 22e060b..9b5f5ab 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 <dick@xxxxxxxxxxx>
- * 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
-- 
2.7.4


Follow ups

References