kicad-developers team mailing list archive
-
kicad-developers team
-
Mailing list archive
-
Message #24418
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