325 lines
7.2 KiB
C++
325 lines
7.2 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2014 Cirilo Bernardo
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, you may find one here:
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <cctype>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
#include <idf_common.h>
|
|
#include <idf_helpers.h>
|
|
|
|
using namespace std;
|
|
using namespace IDF3;
|
|
|
|
// fetch a line from the given input file and trim the ends
|
|
bool IDF3::FetchIDFLine( std::ifstream& aModel, std::string& aLine, bool& isComment, std::streampos& aFilePos )
|
|
{
|
|
aLine = "";
|
|
aFilePos = aModel.tellg();
|
|
|
|
if( aModel.fail() )
|
|
return false;
|
|
|
|
std::getline( aModel, aLine );
|
|
|
|
isComment = false;
|
|
|
|
// A comment begins with a '#' and must be the first character on the line
|
|
if( aLine[0] == '#' )
|
|
{
|
|
// opening '#' is stripped
|
|
isComment = true;
|
|
aLine.erase( aLine.begin() );
|
|
}
|
|
|
|
// strip leading and trailing spaces
|
|
while( !aLine.empty() && isspace( *aLine.begin() ) )
|
|
aLine.erase( aLine.begin() );
|
|
|
|
while( !aLine.empty() && isspace( *aLine.rbegin() ) )
|
|
aLine.erase( --aLine.end() );
|
|
|
|
// a comment line may be empty to improve human readability
|
|
if( aLine.empty() && !isComment )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// extract an IDF string and move the index to point to the character after the substring
|
|
bool IDF3::GetIDFString( const std::string& aLine, std::string& aIDFString,
|
|
bool& hasQuotes, int& aIndex )
|
|
{
|
|
// 1. drop all leading spaces
|
|
// 2. if the first character is '"', read until the next '"',
|
|
// otherwise read until the next space or EOL.
|
|
|
|
std::ostringstream ostr;
|
|
|
|
int len = aLine.length();
|
|
int idx = aIndex;
|
|
|
|
if( idx < 0 || idx >= len )
|
|
return false;
|
|
|
|
while( idx < len && isspace( aLine[idx] ) )
|
|
++idx;
|
|
|
|
if( idx == len )
|
|
{
|
|
aIndex = idx;
|
|
return false;
|
|
}
|
|
|
|
if( aLine[idx] == '"' )
|
|
{
|
|
hasQuotes = true;
|
|
++idx;
|
|
while( idx < len && aLine[idx] != '"' )
|
|
ostr << aLine[idx++];
|
|
|
|
if( idx == len )
|
|
{
|
|
ERROR_IDF << "unterminated quote mark in line:\n";
|
|
std::cerr << "LINE: " << aLine << "\n";
|
|
aIndex = idx;
|
|
return false;
|
|
}
|
|
|
|
++idx;
|
|
}
|
|
else
|
|
{
|
|
hasQuotes = false;
|
|
|
|
while( idx < len && !isspace( aLine[idx] ) )
|
|
ostr << aLine[idx++];
|
|
|
|
}
|
|
|
|
aIDFString = ostr.str();
|
|
aIndex = idx;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// perform a comparison between a fixed token string and an input string.
|
|
// the token is assumed to be an upper case IDF token and the input string
|
|
// is data from an IDF file. Since IDF tokens are case-insensitive, we cannot
|
|
// assume anything about the case of the input string.
|
|
bool IDF3::CompareToken( const char* aTokenString, const std::string& aInputString )
|
|
{
|
|
std::string::size_type i, j;
|
|
std::string bigToken = aInputString;
|
|
j = aInputString.length();
|
|
|
|
for( i = 0; i < j; ++i )
|
|
bigToken[i] = std::toupper( bigToken[i] );
|
|
|
|
if( !bigToken.compare( aTokenString ) )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// parse a string for an IDF3::KEY_OWNER
|
|
bool IDF3::ParseOwner( const std::string& aToken, IDF3::KEY_OWNER& aOwner )
|
|
{
|
|
if( CompareToken( "UNOWNED", aToken ) )
|
|
{
|
|
aOwner = UNOWNED;
|
|
return true;
|
|
}
|
|
else if( CompareToken( "ECAD", aToken ) )
|
|
{
|
|
aOwner = ECAD;
|
|
return true;
|
|
}
|
|
else if( CompareToken( "MCAD", aToken ) )
|
|
{
|
|
aOwner = MCAD;
|
|
return true;
|
|
}
|
|
|
|
ERROR_IDF << "unrecognized IDF OWNER: '" << aToken << "'\n";
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool IDF3::ParseIDFLayer( const std::string& aToken, IDF3::IDF_LAYER& aLayer )
|
|
{
|
|
if( CompareToken( "TOP", aToken ) )
|
|
{
|
|
aLayer = LYR_TOP;
|
|
return true;
|
|
}
|
|
else if( CompareToken( "BOTTOM", aToken ) )
|
|
{
|
|
aLayer = LYR_BOTTOM;
|
|
return true;
|
|
}
|
|
else if( CompareToken( "BOTH", aToken ) )
|
|
{
|
|
aLayer = LYR_BOTH;
|
|
return true;
|
|
}
|
|
else if( CompareToken( "INNER", aToken ) )
|
|
{
|
|
aLayer = LYR_INNER;
|
|
return true;
|
|
}
|
|
else if( CompareToken( "ALL", aToken ) )
|
|
{
|
|
aLayer = LYR_ALL;
|
|
return true;
|
|
}
|
|
|
|
ERROR_IDF << "unrecognized IDF LAYER: '" << aToken << "'\n";
|
|
|
|
aLayer = LYR_INVALID;
|
|
return false;
|
|
}
|
|
|
|
|
|
bool IDF3::WriteLayersText( std::ofstream& aBoardFile, IDF3::IDF_LAYER aLayer )
|
|
{
|
|
switch( aLayer )
|
|
{
|
|
case LYR_TOP:
|
|
aBoardFile << "TOP";
|
|
break;
|
|
|
|
case LYR_BOTTOM:
|
|
aBoardFile << "BOTTOM";
|
|
break;
|
|
|
|
case LYR_BOTH:
|
|
aBoardFile << "BOTH";
|
|
break;
|
|
|
|
case LYR_INNER:
|
|
aBoardFile << "INNER";
|
|
break;
|
|
|
|
case LYR_ALL:
|
|
aBoardFile << "ALL";
|
|
break;
|
|
|
|
default:
|
|
do{
|
|
std::ostringstream ostr;
|
|
ostr << "invalid IDF layer: " << aLayer;
|
|
|
|
throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
|
|
} while( 0 );
|
|
|
|
break;
|
|
}
|
|
|
|
return !aBoardFile.fail();
|
|
}
|
|
|
|
|
|
std::string IDF3::GetPlacementString( IDF3::IDF_PLACEMENT aPlacement )
|
|
{
|
|
switch( aPlacement )
|
|
{
|
|
case PS_UNPLACED:
|
|
return "UNPLACED";
|
|
|
|
case PS_PLACED:
|
|
return "PLACED";
|
|
|
|
case PS_MCAD:
|
|
return "MCAD";
|
|
|
|
case PS_ECAD:
|
|
return "ECAD";
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
std::ostringstream ostr;
|
|
ostr << "[INVALID PLACEMENT VALUE]:" << aPlacement;
|
|
|
|
return ostr.str();
|
|
}
|
|
|
|
|
|
std::string IDF3::GetLayerString( IDF3::IDF_LAYER aLayer )
|
|
{
|
|
switch( aLayer )
|
|
{
|
|
case LYR_TOP:
|
|
return "TOP";
|
|
|
|
case LYR_BOTTOM:
|
|
return "BOTTOM";
|
|
|
|
case LYR_BOTH:
|
|
return "BOTH";
|
|
|
|
case LYR_INNER:
|
|
return "INNER";
|
|
|
|
case LYR_ALL:
|
|
return "ALL";
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
std::ostringstream ostr;
|
|
ostr << "[INVALID LAYER VALUE]:" << aLayer;
|
|
|
|
return ostr.str();
|
|
}
|
|
|
|
std::string IDF3::GetOwnerString( IDF3::KEY_OWNER aOwner )
|
|
{
|
|
switch( aOwner )
|
|
{
|
|
case IDF3::UNOWNED:
|
|
return "UNOWNED";
|
|
|
|
case IDF3::MCAD:
|
|
return "MCAD";
|
|
|
|
case IDF3::ECAD:
|
|
return "ECAD";
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ostringstream ostr;
|
|
ostr << "UNKNOWN: " << aOwner;
|
|
|
|
return ostr.str();
|
|
}
|