Commit 80f21358 authored by Dick Hollenbeck's avatar Dick Hollenbeck

work on DIR_LIB_SOURCE

parent c02ca2ed
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2010 SoftPLC Corporation, <dick@softplc.com>
* Copyright (C) 2010 Kicad Developers, see change_log.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
* 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
*/
#ifndef KICAD_EXCEPTIONS_H_
#define KICAD_EXCEPTIONS_H_
/* Just exceptions
*/
#include <wx/string.h>
/**
* Struct IO_ERROR
* is a class used to hold an error message and may be used to throw exceptions
* containing meaningful error messages.
* @author Dick Hollenbeck
*/
struct IO_ERROR
{
wxString errorText;
IO_ERROR( const wxChar* aMsg ) :
errorText( aMsg )
{
}
IO_ERROR( const wxString& aMsg ) :
errorText( aMsg )
{
}
IO_ERROR( const char* aMsg ) :
errorText( wxConvertMB2WX( aMsg ) )
{
}
};
/**
* Class PARSE_ERROR
* contains a filename or source description, a line number, a character offset,
* and an error message.
* @author Dick Hollenbeck
*/
struct PARSE_ERROR : public IO_ERROR
{
wxString source; ///< filename typically, unless from RAM
int lineNumber;
int byteIndex; ///< char offset, starting from 1, into the problem line.
PARSE_ERROR( const wxString& aMsg, const wxString& aSource,
int aLineNumber, int aByteIndex ) :
IO_ERROR( aMsg ),
source( aSource ),
lineNumber( aLineNumber )
{
}
};
#endif // KICAD_EXCEPTIONS_H_
...@@ -6,9 +6,10 @@ ...@@ -6,9 +6,10 @@
*/ */
#ifndef __INCLUDE__KICAD_STRING_H__ #ifndef KICAD_STRING_H_
#define __INCLUDE__KICAD_STRING_H__ 1 #define KICAD_STRING_H_
#include <wx/string.h>
char* strupper( char* Text ); char* strupper( char* Text );
char* strlower( char* Text ); char* strlower( char* Text );
...@@ -102,4 +103,4 @@ int SplitString( wxString strToSplit, ...@@ -102,4 +103,4 @@ int SplitString( wxString strToSplit,
wxString* strDigits, wxString* strDigits,
wxString* strEnd ); wxString* strEnd );
#endif // __INCLUDE__KICAD_STRING_H__ #endif // KICAD_STRING_H_
...@@ -37,30 +37,10 @@ ...@@ -37,30 +37,10 @@
// I really did not want to be dependent on wxWidgets in richio // I really did not want to be dependent on wxWidgets in richio
// but the errorText needs to be wide char so wxString rules. // but the errorText needs to be wide char so wxString rules.
#include <wx/wx.h> #include <wx/wx.h>
#include <cstdio> // FILE #include <cstdio>
#include <kicad_exceptions.h>
/**
* Struct IO_ERROR
* is a class used to hold an error message and may be used to throw exceptions
* containing meaningful error messages.
*/
struct IO_ERROR
{
wxString errorText;
IO_ERROR( const wxChar* aMsg ) :
errorText( aMsg )
{
}
IO_ERROR( const wxString& aMsg ) :
errorText( aMsg )
{
}
};
#define LINE_READER_LINE_DEFAULT_MAX 100000 #define LINE_READER_LINE_DEFAULT_MAX 100000
#define LINE_READER_LINE_INITIAL_SIZE 5000 #define LINE_READER_LINE_INITIAL_SIZE 5000
......
set( STAND_ALONE true )
if( STAND_ALONE )
project(kicad-new)
cmake_minimum_required(VERSION 2.6.4 FATAL_ERROR)
set( PROJECT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../ )
# Path to local CMake modules.
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules)
include(CheckFindPackageResult)
find_package(Doxygen)
# On Apple only wxwidgets 2.9 or higher doesn't need to find aui part of base
if(APPLE)
find_package(wxWidgets COMPONENTS gl adv html core net base xml QUIET)
else()
find_package(wxWidgets COMPONENTS gl aui adv html core net base xml QUIET)
endif()
check_find_package_result( wxWidgets_FOUND "wxWidgets" )
# Include wxWidgets macros.
include( ${wxWidgets_USE_FILE} )
include_directories( ${PROJECT_SOURCE_DIR}/include )
if(CMAKE_COMPILER_IS_GNUCXX)
# Set default flags for Release build.
set(CMAKE_C_FLAGS_RELEASE "-Wall -O2 -DNDEBUG ")
set(CMAKE_CXX_FLAGS_RELEASE "-Wall -O2 -DNDEBUG")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-s -static-libgcc")
# Set default flags for Debug build.
set(CMAKE_C_FLAGS_DEBUG "-Wall -g3 -ggdb3 -DDEBUG")
set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g3 -ggdb3 -DDEBUG")
endif(CMAKE_COMPILER_IS_GNUCXX)
endif()
#================================================
# Doxygen Output
#================================================
if(DOXYGEN_FOUND)
add_custom_target( new-docs ${DOXYGEN_EXECUTABLE}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS Doxyfile )
else(DOXYGEN_FOUND)
message( STATUS "WARNING: Doxygen not found - new-docs (Source Docs) target not created" )
endif()
include_directories( ${CMAKE_SOURCE_DIR} )
add_executable( test_dir_lib_source sch_dir_lib_source.cpp )
#add_executable( test_dir_lib_source EXCLUDE_FROM_ALL sch_dir_lib_source.cpp )
target_link_libraries( test_dir_lib_source ${wxWidgets_LIBRARIES} )
...@@ -382,14 +382,6 @@ const STRING StrEmpty = ""; ...@@ -382,14 +382,6 @@ const STRING StrEmpty = "";
* @{ * @{
*/ */
/**
* Class PARSE_ERROR
* contains a filename or source description, a line number, a character offset,
* and an error message.
*/
struct PARSE_ERROR : public IO_ERROR
{
};
/** @} exception_types Exception Types */ /** @} exception_types Exception Types */
...@@ -624,91 +616,6 @@ public: ...@@ -624,91 +616,6 @@ public:
}; };
/**
* Class LIB_SOURCE
* is an abstract class from which implementation specific LIB_SOURCEs
* may be derived, one for each kind of library type allowed in the library table.
* The class name stems from the fact that this interface only provides READ ONLY
* functions.
*/
class LIB_SOURCE
{
friend class LIBS; ///< the LIB factory is LIBS::GetLibrary()
friend class LIB; ///< the LIB uses these functions.
protected: ///< derived classes must implement
/**
* Function GetSourceType
* retuns type library table entry's type for library source.
*/
const STRING& GetSourceType() { return sourceType ; }
/**
* Function ReadPart
* fetches @a aPartName's s-expression into @a aResult after clear()ing aResult.
*/
virtual void ReadPart( STRING* aResult, const STRING& aPartName, const STRING& aRev=StrEmpty ) throw( IO_ERROR ) = 0;
/**
* Function ReadParts
* fetches the s-expressions for each part given in @a aPartNames, into @a aResults,
* honoring the array indices respectfully.
* @param aPartNames is a list of part names, one name per list element.
* @param aResults receives the s-expressions
*/
virtual void ReadParts( STRING_TOKS* aResults, const STRINGS& aPartNames ) throw( IO_ERROR ) = 0;
/**
* Function GetCategories
* fetches all categories present in the library source into @a aResults
*/
virtual void GetCategories( STRING_TOKS* aResults ) throw( IO_ERROR ) = 0;
/**
* Function GetCategoricalPartNames
* fetches all the part names for @a aCategory, which was returned by GetCategories().
*
* @param aCategory is a subdividing navigator within the library source,
* but may default to empty which will be taken to mean all categories.
*
* @param aResults is a place to put the fetched result, one category per STRING.
*/
virtual void GetCategoricalPartNames( STRING_TOKS* aResults,
const STRING& aCategory=StrEmpty ) throw( IO_ERROR ) = 0;
/**
* Function GetRevisions
* fetches all revisions for @a aPartName into @a aResults. Revisions are strings
* like "rev12", "rev279", and are library source agnostic. These
*/
virtual void GetRevisions( STRING_TOKS* aResults,
const STRING& aPartName ) throw( IO_ERROR ) = 0;
/**
* Function FindParts
* fetches part names for all parts matching the criteria given in @a
* aQuery, into @a aResults. The query string is designed to be easily marshalled,
* i.e. serialized, so that long distance queries can be made with minimal overhead.
* The library source needs to have an intelligent friend on the other end if
* the actual library data is remotely located, otherwise it will be too slow
* to honor this portion of the API contract.
*
* @param aQuery is a string holding a domain specific query language expression. One candidate
* here is an s-expression that uses (and ..) and (or ..) operators and uses them as RPN. For example
* "(and (footprint 0805)(value 33ohm)(category passives))".
* The UI can shield the user from this if it wants.
*
* @param aResults is a place to put the fetched part names, one part per STRING.
*/
virtual void FindParts( STRING_TOKS* aResults,
const STRING& aQuery ) throw( IO_ERROR ) = 0;
protected:
STRING sourceType;
STRING sourceURI;
};
/** /**
...@@ -759,38 +666,6 @@ protected: ...@@ -759,38 +666,6 @@ protected:
}; };
/**
* Class LIB_SINK
* is an abstract class from which implementation specific LIB_SINKs
* may be derived, one for each kind of library type in the library table that
* supports writing. The class name stems from the fact that this interface
* only provides WRITE functions.
*/
class LIB_SINK
{
friend class LIB; ///< only the LIB uses these functions.
protected: ///< derived classes must implement
/**
* Function WritePart
* saves the part to non-volatile storage. @a aPartName may have the revision
* portion present. If it is not present, and a overwrite of an existhing
* part is done, then LIB::ReloadPart() must be called on this same part
* and all parts that inherit it must be reparsed.
* @return STRING - if the LIB_SINK support revision numbering, then return a
* evision name that was next in the sequence, e.g. "rev22", else StrEmpty.
*/
virtual STRING WritePart( const STRING& aPartName,
const STRING& aSExpression ) throw( IO_ERROR ) = 0;
protected:
STRING sinkType;
STRING sinkURI;
};
/** /**
* Class LIBS * Class LIBS
* houses a handful of functions that manage all the RAM resident LIBs, and * houses a handful of functions that manage all the RAM resident LIBs, and
...@@ -841,150 +716,6 @@ private: ...@@ -841,150 +716,6 @@ private:
}; };
/**
* Class LIB
* is a cache of parts, and because the LIB_SOURCE is abstracted, there
* should be no need to extend from this class in any case except for the
* PARTS_LIST.
*/
class LIB
{
friend class LIBS; ///< the LIB factory is LIBS::GetLibrary()
protected: // constructor is not public, called from LIBS only.
/**
* Constructor LIB
* is not public and is only called from LIBS::GetLib()
*
* @param aLogicalLibrary is the name of a well know logical library, and is
* known because it already exists in the library table.
*
* @param aSource is an open LIB_SOURCE whose ownership is
* given over to this LIB.
*
* @param aSink is an open LIB_SINK whose ownership is given over
* to this LIB, and it is normally NULL.
*/
LIB( const STRING& aLogicalLibrary, LIB_SOURCE* aSource, LIB_SINK* aSink=NULL ) :
name( aLogicalLibrary ),
source( aSource ),
sink( aSink )
{
}
~LIB()
{
delete source;
delete sink;
}
public:
/**
* Function HasSink
* returns true if this library has write/save capability. Most LIBs
* are read only.
*/
bool HasSink() { return sink != NULL; }
/**
* Function LogicalName
* returns the logical name of this LIB.
*/
STRING LogicalName();
//-----<use delegates: source and sink>---------------------------------
/**
* Function GetPart
* returns a PART given @a aPartName, such as "passives/R".
* @param aPartName is local to this LIB and does not have the logical
* library name prefixed.
*/
const PART* GetPart( const STRING& aPartName ) throw( IO_ERROR );
/**
* Function ReloadPart
* will reload the part assuming the library source has a changed content
* for it.
*/
void ReloadPart( PART* aPart ) throw( IO_ERROR );
/**
* Function GetCategories
* returns all categories of parts within this LIB into @a aResults.
*/
STRINGS GetCategories() throw( IO_ERROR );
/**
* Function GetCategoricalPartNames
* returns the part names for @a aCategory, and at the same time
* creates cache entries for the very same parts if they do not already exist
* in this LIB (i.e. cache).
*/
STRINGS GetCategoricalPartNames( const STRING& aCategory=StrEmpty ) throw( IO_ERROR );
//-----<.use delegates: source and sink>--------------------------------
/**
* Function WritePart
* saves the part to non-volatile storage and returns the next new revision
* name in the sequence established by the LIB_SINK.
*/
virtual STRING WritePart( PART* aPart ) throw( IO_ERROR );
virtual void SetPartBody( PART* aPart, const STRING& aSExpression ) throw( IO_ERROR );
/**
* Function GetRevisions
* returns the revisions of @a aPartName that are present in this LIB.
* The returned STRINGS will look like "rev1", "rev2", etc.
*/
STRINGS GetRevisions( const STRING& aPartName ) throw( IO_ERROR );
/**
* Function FindParts
* returns part names for all parts matching the criteria given in @a
* aQuery, into @a aResults. The query string is designed to be easily marshalled,
* i.e. serialized, so that long distance queries can be made with minimal overhead.
* The library source needs to have an intelligent friend on the other end if
* the actual library data is remotely located, otherwise it will be too slow
* to honor this portion of the API contract.
*
* @param aQuery is a string holding a domain specific language expression. One candidate
* here is an RPN s-expression that uses (and ..) and (or ..) operators. For example
* "(and (footprint 0805)(value 33ohm)(category passives))"
*/
STRINGS FindParts( const STRING& aQuery ) throw( IO_ERROR );
{
// run the query on the cached data first for any PARTS which are fully
// parsed (i.e. cached), then on the LIB_SOURCE to find any that
// are not fully parsed, then unify the results.
}
private:
STRING fetch; // scratch, used to fetch things, grows to worst case size.
STRINGS vfetch; // scratch, used to fetch things.
LIB_SOURCE* source;
LIB_SINK* sink;
STRING name;
STRING libraryType;
STRING libraryURI;
STRINGS categories;
PARTS parts;
};
/** /**
* Class PARTS_LIST * Class PARTS_LIST
* is a LIB which resides in a SCHEMATIC, and it is a table model for a * is a LIB which resides in a SCHEMATIC, and it is a table model for a
......
/* /*
* 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.
* *
...@@ -23,110 +22,307 @@ ...@@ -23,110 +22,307 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/ */
#ifndef DIR_LIB_SOURCE_H_
#define DIR_LIB_SOURCE_H_
/* Note: this LIB_SOURCE implementation relies on the posix specified opendir() and /* Note: this LIB_SOURCE implementation relies on the posix specified opendir() and
related functions. Mingw and unix, linux, & osx will all have these posix functions. related functions rather than wx functions which might do the same thing. This
is because I did not want to become very dependent on wxWidgets at such a low
level as this, in case someday this code needs to be used on kde or whatever.
Mingw and unix, linux, & osx will all have these posix functions.
MS Visual Studio may need the posix compatible opendir() functions brought in MS Visual Studio may need the posix compatible opendir() functions brought in
http://www.softagalleria.net/dirent.php http://www.softagalleria.net/dirent.php
wx has these but they are based on wxString and wx should not be introduced wx has these but they are based on wxString which can be wchar_t based and wx should
at a level this low. not be introduced at a level this low.
*/ */
#include <sch_dir_lib_source.h>
using namespace SCH;
#include <kicad_exceptions.h>
#include <dirent.h>
#include <sys/stat.h>
#include <cstring>
#include <cstdio>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <vector>
using namespace std;
namespace SCH {
/** /**
* Class DIR_LIB_SOURCE * Class DIR_WRAP
* implements a LIB_SOURCE in a file system directory. * provides a destructor which may be invoked if an exception is thrown.
*
* @author Dick Hollenbeck
*/ */
class DIR_LIB_SOURCE : public LIB_SOURCE class DIR_WRAP
{ {
friend class LIBS; ///< LIBS::GetLib() can construct one. DIR* dir;
STRING path; ///< base directory path of LIB_SOURCE public:
DIR_WRAP( DIR* aDir ) : dir( aDir ) {}
~DIR_WRAP()
{
if( dir )
closedir( dir );
}
protected: DIR* operator->() { return dir; }
DIR* operator*() { return dir; }
};
/**
* Constructor DIR_LIB_SOURCE( const STRING& aDirectoryPath )
* sets up a LIB_SOURCE using aDirectoryPath in a file system.
* @see LIBS::GetLibrary().
*
* @param aDirectoryPath is a full pathname of a directory which contains
* the library source of part files. Examples might be "C:\kicad_data\mylib" or
* "/home/designer/mylibdir".
*/
DIR_LIB_SOURCE( const STRING& aDirectoryPath ) throws( IO_ERROR, PARSE_ERROR );
/**
* Class FILE_WRAP
* provides a destructor which may be invoked if an exception is thrown.
*/
class FILE_WRAP
{
int fh;
public:
FILE_WRAP( int aFileHandle ) : fh( aFileHandle ) {}
~FILE_WRAP()
{
if( fh != -1 )
close( fh );
}
operator int () { return fh; }
}; };
} // namespace SCH
#endif // DIR_LIB_SOURCE_H_ /**
* Function strrstr
* finds the last instance of needle in haystack, if any.
*/
static const char* strrstr( const char* haystack, const char* needle )
{
const char* ret = 0;
const char* next = haystack;
// find last instance of haystack
while( (next = strstr( next, needle )) != 0 )
{
ret = next;
++next; // don't keep finding the same one.
}
return ret;
}
#include <dirent.h>
#include <cstring>
#include <ki_exceptions.h>
#include <sys/types.h> static const char* endsWithRev( const char* cp, const char* limit )
{
// find last instance of ".rev"
cp = strrstr( cp, ".rev" );
if( cp )
{
const char* rev = cp + 1;
cp += sizeof( ".rev" )-1;
/** while( isdigit( *cp ) )
* Class DIR_WRAP ++cp;
* provides a destructor which may be invoked if an exception is thrown,
* thereby closing the DIR.
*/
class DIR_WRAP
{
DIR* dir;
public: if( cp != limit ) // there is garbage after "revN.."
DIR_WRAP( DIR* aDir ) : dir( aDir ) {} rev = 0;
~DIR_WRAP() return rev;
}
return 0;
}
bool DIR_LIB_SOURCE::makePartFileName( const char* aEntry,
const STRING& aCategory, STRING* aPartName )
{
const char* cp = strrstr( aEntry, ".part" );
// if base name is not empty, contains ".part", && cp is not NULL
if( cp > aEntry )
{ {
if( dir ) const char* limit = cp + strlen( cp );
closedir( dir );
// if file extension is exactly ".part", and no rev
if( cp==limit-5 )
{
if( aCategory.size() )
*aPartName = aCategory + "/";
else
aPartName->clear();
aPartName->append( aEntry, cp - aEntry );
return true;
}
// if versioning, test for a trailing "revN.." type of string
if( useVersioning )
{
const char* rev = endsWithRev( cp + sizeof(".part") - 1, limit );
if( rev )
{
if( aCategory.size() )
*aPartName = aCategory + "/";
else
aPartName->clear();
aPartName->append( aEntry, cp - aEntry );
aPartName->append( "/" );
aPartName->append( rev );
return true;
}
}
} }
DIR* operator->() { return dir; } return false;
}; }
static bool isCategoryName( const char* aName )
{
return true;
}
#define MAX_PART_FILE_SIZE (1*1024*1024) // sanity check
DIR_LIB_SOURCE::DIR_LIB_SOURCE( const STRING& aDirectoryPath ) throws( IO_ERROR, PARSE_ERROR ) DIR_LIB_SOURCE::DIR_LIB_SOURCE( const STRING& aDirectoryPath, bool doUseVersioning )
throw( IO_ERROR )
{ {
DIR_WRAP* dir = opendir( aDirectoryPath.c_str() ); useVersioning = doUseVersioning;
sourceURI = aDirectoryPath;
sourceType = "dir";
if( !dir ) if( sourceURI.size() == 0 )
{ {
char buf[256]; throw( IO_ERROR( "aDirectoryPath cannot be empty" ) );
}
// remove any trailing separator, so we can add it back later without ambiguity
if( strchr( "/\\", sourceURI[sourceURI.size()-1] ) )
sourceURI.erase( sourceURI.size()-1 );
doOneDir( "" );
}
strerror_r( errno, buf, sizeof(buf) ); DIR_LIB_SOURCE::~DIR_LIB_SOURCE()
throw( IO_ERROR( buf ) ); {
// delete the sweet STRINGS, which "sweets" owns by pointer.
for( DIR_CACHE::iterator it = sweets.begin(); it != sweets.end(); ++it )
{
delete it->second;
} }
}
path = aDirectoryPath;
void DIR_LIB_SOURCE::Show()
{
printf( "categories:\n" );
for( STRINGS::const_iterator it = categories.begin(); it!=categories.end(); ++it )
printf( " '%s'\n", it->c_str() );
printf( "\n" );
printf( "parts:\n" );
for( DIR_CACHE::const_iterator it = sweets.begin(); it != sweets.end(); ++it )
{
printf( " '%s'\n", it->first.c_str() );
}
} }
void DIR_LIB_SOURCE::doOneDir( const STRING& aCategory ) throw( IO_ERROR )
{
STRING curDir = sourceURI;
if( aCategory.size() )
curDir += "/" + aCategory;
DIR_WRAP dir = opendir( curDir.c_str() );
if( !*dir )
{
STRING msg = strerror( errno );
msg += "; scanning directory " + curDir;
throw( IO_ERROR( msg.c_str() ) );
}
struct stat fs;
STRING partName;
STRING fileName;
dirent* entry;
while( (entry = readdir( *dir )) != NULL )
{
if( !strcmp( ".", entry->d_name ) || !strcmp( "..", entry->d_name ) )
continue;
fileName = curDir + "/" + entry->d_name;
//D( printf("name: '%s'\n", fileName.c_str() );)
if( !stat( fileName.c_str(), &fs ) )
{
if( S_ISREG( fs.st_mode ) && makePartFileName( entry->d_name, aCategory, &partName ) )
{
/*
if( sweets.find( partName ) != sweets.end() )
{
STRING msg = partName;
msg += " has already been encountered";
throw IO_ERROR( msg.c_str() );
}
*/
sweets[partName] = NULL; // NULL for now, load the sweet later.
//D( printf("part: %s\n", partName.c_str() );)
}
else if( S_ISDIR( fs.st_mode ) && !aCategory.size() && isCategoryName( entry->d_name ) )
{
// only one level of recursion is used, controlled by the
// emptiness of aCategory.
//D( printf("category: %s\n", entry->d_name );)
categories.push_back( entry->d_name );
doOneDir( entry->d_name );
}
else
{
//D( printf( "ignoring %s\n", entry->d_name );)
}
}
}
}
#if 1 || defined( TEST_DIR_LIB_SOURCE ) #if 1 || defined( TEST_DIR_LIB_SOURCE )
int main( int argv, char** argv ) int main( int argc, char** argv )
{ {
try
{
DIR_LIB_SOURCE uut( argv[1] ? argv[1] : "", true );
uut.Show();
}
catch( IO_ERROR ioe )
{
printf( "exception: %s\n", (const char*) wxConvertWX2MB( ioe.errorText ) );
}
return 0;
} }
#endif #endif
......
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2010 Kicad Developers, see change_log.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
* 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
*/
#ifndef DIR_LIB_SOURCE_H_
#define DIR_LIB_SOURCE_H_
#include <sch_lib.h>
#include <map>
/**
* Type DIR_CACHE
* is a tuple, where the key is partname (prefixed with the category if any),
* and value is pointer to Sweet string which is loaded lazily, so can be NULL
* until loaded.
*/
typedef std::map< STRING, STRING* > DIR_CACHE;
namespace SCH {
/**
* Class DIR_LIB_SOURCE
* implements a LIB_SOURCE in a file system directory.
*
* @author Dick Hollenbeck
*/
class DIR_LIB_SOURCE : public LIB_SOURCE
{
friend class LIBS; ///< LIBS::GetLib() can construct one.
bool useVersioning; ///< use files with extension ".revNNN..", else not
DIR_CACHE sweets;
STRINGS categories;
/**
* Function isPartFileName
* returns true iff aName is a valid part file name.
*/
bool isPartFileName( const char* aName );
/**
* Function makePartFileName
* returns true iff aEntry holds a valid part filename, in the form of
* "someroot.part[.revNNNN]" where NNN are number characters [0-9]
* @param aEntry is the raw directory entry without path information.
* @param aCategory is the last portion of the directory path.
* @param aPartName is where to put a part name, assuming aEntry is legal.
* @return bool - true only if aEntry is a legal part file name.
*/
bool makePartFileName( const char* aEntry,
const STRING& aCategory, STRING* aPartName );
/**
* Function doOneDir
* loads part names [and categories] from a directory given by
* "sourceURI + '/' + category"
* Categories are only loaded if processing the top most directory because
* only one level of categories are supported. We know we are in the
* top most directory if aCategory is empty.
*/
void doOneDir( const STRING& aCategory ) throw( IO_ERROR );
//protected:
public:
/**
* Constructor DIR_LIB_SOURCE( const STRING& aDirectoryPath )
* sets up a LIB_SOURCE using aDirectoryPath in a file system.
* @see LIBS::GetLibrary().
*
* @param aDirectoryPath is a full file pathname of a directory which contains
* the library source of part files. Examples might be "C:\kicad_data\mylib" or
* "/home/designer/mylibdir". This is not a URI, but an OS specific path that
* can be given to opendir().
*
* @param doUseVersioning if true means support versioning in the directory tree, otherwise
* only a single version of each part is recognized.
*/
DIR_LIB_SOURCE( const STRING& aDirectoryPath, bool doUseVersioning = false )
throw( IO_ERROR );
~DIR_LIB_SOURCE();
//-----<LIB_SOURCE implementation functions >------------------------------
void ReadPart( STRING* aResult, const STRING& aPartName, const STRING& aRev=StrEmpty )
throw( IO_ERROR )
{
}
void ReadParts( STRING_TOKS* aResults, const STRINGS& aPartNames )
throw( IO_ERROR )
{
}
void GetCategories( STRING_TOKS* aResults ) throw( IO_ERROR )
{
}
void GetCategoricalPartNames( STRING_TOKS* aResults,
const STRING& aCategory=StrEmpty ) throw( IO_ERROR )
{
}
void GetRevisions( STRING_TOKS* aResults, const STRING& aPartName ) throw( IO_ERROR )
{
}
void FindParts( STRING_TOKS* aResults, const STRING& aQuery ) throw( IO_ERROR )
{
}
//-----</LIB_SOURCE implementation functions >------------------------------
/**
* Function Show
* will output a debug dump of contents.
*/
void Show();
};
} // namespace SCH
#endif // DIR_LIB_SOURCE_H_
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2010 Kicad Developers, see change_log.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
* 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
*/
#ifndef SCH_LIB_H_
#define SCH_LIB_H_
#include <string>
#include <deque>
#include <kicad_exceptions.h>
#ifdef DEBUG
#define D(x) x
#else
#define D(x) // nothing
#endif
typedef std::string STRING;
typedef std::deque<STRING> STRINGS;
typedef STRINGS STRING_TOKS;
extern const STRING StrEmpty;
namespace SCH {
class PART;
/**
* Class LIB_SOURCE
* is an abstract class from which implementation specific LIB_SOURCEs
* may be derived, one for each kind of library type allowed in the library table.
* The class name stems from the fact that this interface only provides READ ONLY
* functions.
*
* @author Dick Hollenbeck
*/
class LIB_SOURCE
{
friend class LIBS; ///< the LIB factory is LIBS::GetLibrary()
friend class LIB; ///< the LIB uses these functions.
protected: ///< derived classes must implement
/**
* Function GetSourceType
* returns the library table entry's type for this library source.
*/
const STRING& GetSourceType() { return sourceType; }
/**
* Function GetSourceURI
* returns absolute location of the library source.
*/
const STRING& GetSourceURI() { return sourceURI; }
//-----<abstract for implementors>---------------------------------------
/**
* Function ReadPart
* fetches @a aPartName's s-expression into @a aResult after clear()ing aResult.
*/
virtual void ReadPart( STRING* aResult, const STRING& aPartName, const STRING& aRev=StrEmpty )
throw( IO_ERROR ) = 0;
/**
* Function ReadParts
* fetches the s-expressions for each part given in @a aPartNames, into @a aResults,
* honoring the array indices respectfully.
* @param aPartNames is a list of part names, one name per list element.
* @param aResults receives the s-expressions
*/
virtual void ReadParts( STRING_TOKS* aResults, const STRINGS& aPartNames )
throw( IO_ERROR ) = 0;
/**
* Function GetCategories
* fetches all categories present in the library source into @a aResults
*/
virtual void GetCategories( STRING_TOKS* aResults )
throw( IO_ERROR ) = 0;
/**
* Function GetCategoricalPartNames
* fetches all the part names for @a aCategory, which was returned by GetCategories().
*
* @param aCategory is a subdividing navigator within the library source,
* but may default to empty which will be taken to mean all categories.
*
* @param aResults is a place to put the fetched result, one category per STRING.
*/
virtual void GetCategoricalPartNames( STRING_TOKS* aResults, const STRING& aCategory=StrEmpty )
throw( IO_ERROR ) = 0;
/**
* Function GetRevisions
* fetches all revisions for @a aPartName into @a aResults. Revisions are strings
* like "rev12", "rev279", and are library source agnostic. These
*/
virtual void GetRevisions( STRING_TOKS* aResults, const STRING& aPartName )
throw( IO_ERROR ) = 0;
/**
* Function FindParts
* fetches part names for all parts matching the criteria given in @a
* aQuery, into @a aResults. The query string is designed to be easily marshalled,
* i.e. serialized, so that long distance queries can be made with minimal overhead.
* The library source needs to have an intelligent friend on the other end if
* the actual library data is remotely located, otherwise it will be too slow
* to honor this portion of the API contract.
*
* @param aQuery is a string holding a domain specific query language expression.
* One candidate here is an s-expression that uses (and ..) and (or ..) operators
* and uses them as RPN. For example "(and (footprint 0805)(value 33ohm)(category passives))".
* The UI can shield the user from this if it wants.
*
* @param aResults is a place to put the fetched part names, one part per STRING.
*/
virtual void FindParts( STRING_TOKS* aResults, const STRING& aQuery )
throw( IO_ERROR ) = 0;
//-----</abstract for implementors>--------------------------------------
protected:
STRING sourceType;
STRING sourceURI;
};
/**
* Class LIB_SINK
* is an abstract class from which implementation specific LIB_SINKs
* may be derived, one for each kind of library type in the library table that
* supports writing. The class name stems from the fact that this interface
* only provides WRITE functions.
*
* @author Dick Hollenbeck
*/
class LIB_SINK
{
friend class LIB; ///< only the LIB uses these functions.
protected: ///< derived classes must implement
/**
* Function GetSinkType
* returns the library table entry's type for this library sink.
*/
const STRING& GetSinkType() { return sinkType; }
/**
* Function GetSinkURI
* returns absolute location of the library sink.
*/
const STRING& GetSinkURI() { return sinkURI; }
/**
* Function WritePart
* saves the part to non-volatile storage. @a aPartName may have the revision
* portion present. If it is not present, and a overwrite of an existhing
* part is done, then LIB::ReloadPart() must be called on this same part
* and all parts that inherit it must be reparsed.
* @return STRING - if the LIB_SINK support revision numbering, then return a
* evision name that was next in the sequence, e.g. "rev22", else StrEmpty.
*/
virtual STRING WritePart( const STRING& aPartName, const STRING& aSExpression )
throw( IO_ERROR ) = 0;
protected:
STRING sinkType;
STRING sinkURI;
};
/**
* Class LIB
* is a cache of parts, and because the LIB_SOURCE is abstracted, there
* should be no need to extend from this class in any case except for the
* PARTS_LIST.
*
* @author Dick Hollenbeck
*/
class LIB
{
friend class LIBS; ///< the LIB factory is LIBS::GetLibrary()
protected: // constructor is not public, called from LIBS only.
/**
* Constructor LIB
* is not public and is only called from LIBS::GetLib()
*
* @param aLogicalLibrary is the name of a well know logical library, and is
* known because it already exists in the library table.
*
* @param aSource is an open LIB_SOURCE whose ownership is
* given over to this LIB.
*
* @param aSink is an open LIB_SINK whose ownership is given over
* to this LIB, and it is normally NULL.
*/
LIB( const STRING& aLogicalLibrary, LIB_SOURCE* aSource, LIB_SINK* aSink=NULL ) :
name( aLogicalLibrary ),
source( aSource ),
sink( aSink )
{
}
~LIB()
{
delete source;
delete sink;
}
public:
/**
* Function HasSink
* returns true if this library has write/save capability. Most LIBs
* are read only.
*/
bool HasSink() { return sink != NULL; }
/**
* Function LogicalName
* returns the logical name of this LIB.
*/
STRING LogicalName();
//-----<use delegates: source and sink>---------------------------------
/**
* Function GetPart
* returns a PART given @a aPartName, such as "passives/R".
* @param aPartName is local to this LIB and does not have the logical
* library name prefixed.
*/
const PART* GetPart( const STRING& aPartName ) throw( IO_ERROR );
/**
* Function ReloadPart
* will reload the part assuming the library source has a changed content
* for it.
*/
void ReloadPart( PART* aPart ) throw( IO_ERROR );
/**
* Function GetCategories
* returns all categories of parts within this LIB into @a aResults.
*/
STRINGS GetCategories() throw( IO_ERROR );
/**
* Function GetCategoricalPartNames
* returns the part names for @a aCategory, and at the same time
* creates cache entries for the very same parts if they do not already exist
* in this LIB (i.e. cache).
*/
STRINGS GetCategoricalPartNames( const STRING& aCategory=StrEmpty ) throw( IO_ERROR );
//-----<.use delegates: source and sink>--------------------------------
/**
* Function WritePart
* saves the part to non-volatile storage and returns the next new revision
* name in the sequence established by the LIB_SINK.
*/
virtual STRING WritePart( PART* aPart ) throw( IO_ERROR );
virtual void SetPartBody( PART* aPart, const STRING& aSExpression ) throw( IO_ERROR );
/**
* Function GetRevisions
* returns the revisions of @a aPartName that are present in this LIB.
* The returned STRINGS will look like "rev1", "rev2", etc.
*/
STRINGS GetRevisions( const STRING& aPartName ) throw( IO_ERROR );
/**
* Function FindParts
* returns part names for all parts matching the criteria given in @a
* aQuery, into @a aResults. The query string is designed to be easily marshalled,
* i.e. serialized, so that long distance queries can be made with minimal overhead.
* The library source needs to have an intelligent friend on the other end if
* the actual library data is remotely located, otherwise it will be too slow
* to honor this portion of the API contract.
*
* @param aQuery is a string holding a domain specific language expression. One candidate
* here is an RPN s-expression that uses (and ..) and (or ..) operators. For example
* "(and (footprint 0805)(value 33ohm)(category passives))"
*/
STRINGS FindParts( const STRING& aQuery ) throw( IO_ERROR )
{
// run the query on the cached data first for any PARTS which are fully
// parsed (i.e. cached), then on the LIB_SOURCE to find any that
// are not fully parsed, then unify the results.
return STRINGS();
}
protected:
STRING fetch; // scratch, used to fetch things, grows to worst case size.
STRINGS vfetch; // scratch, used to fetch things.
STRING name;
LIB_SOURCE* source;
LIB_SINK* sink;
STRING libraryURI;
STRINGS categories;
// PARTS parts;
};
} // namespace SCH
const STRING StrEmpty = "";
#endif // SCH_LIB_H_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment