From 80f21358a567f6998c82266bc9eb06c11a35e90b Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Sun, 19 Dec 2010 15:42:55 -0600 Subject: [PATCH] work on DIR_LIB_SOURCE --- include/kicad_exceptions.h | 82 +++++++++ include/kicad_string.h | 7 +- include/richio.h | 24 +-- new/CMakeLists.txt | 65 +++++++ new/design.h | 269 ---------------------------- new/sch_dir_lib_source.cpp | 310 ++++++++++++++++++++++++++------ new/sch_dir_lib_source.h | 149 ++++++++++++++++ new/sch_lib.h | 351 +++++++++++++++++++++++++++++++++++++ 8 files changed, 906 insertions(+), 351 deletions(-) create mode 100644 include/kicad_exceptions.h create mode 100644 new/CMakeLists.txt create mode 100644 new/sch_dir_lib_source.h create mode 100644 new/sch_lib.h diff --git a/include/kicad_exceptions.h b/include/kicad_exceptions.h new file mode 100644 index 0000000000..e2a873f375 --- /dev/null +++ b/include/kicad_exceptions.h @@ -0,0 +1,82 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2010 SoftPLC Corporation, + * 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 + +/** + * 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_ diff --git a/include/kicad_string.h b/include/kicad_string.h index acc9f82a99..37df8665bb 100644 --- a/include/kicad_string.h +++ b/include/kicad_string.h @@ -6,9 +6,10 @@ */ -#ifndef __INCLUDE__KICAD_STRING_H__ -#define __INCLUDE__KICAD_STRING_H__ 1 +#ifndef KICAD_STRING_H_ +#define KICAD_STRING_H_ +#include char* strupper( char* Text ); char* strlower( char* Text ); @@ -102,4 +103,4 @@ int SplitString( wxString strToSplit, wxString* strDigits, wxString* strEnd ); -#endif // __INCLUDE__KICAD_STRING_H__ +#endif // KICAD_STRING_H_ diff --git a/include/richio.h b/include/richio.h index a4d04770af..84be5e25fa 100644 --- a/include/richio.h +++ b/include/richio.h @@ -37,30 +37,10 @@ // I really did not want to be dependent on wxWidgets in richio // but the errorText needs to be wide char so wxString rules. #include -#include // FILE +#include +#include - -/** - * 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_INITIAL_SIZE 5000 diff --git a/new/CMakeLists.txt b/new/CMakeLists.txt new file mode 100644 index 0000000000..65298136e3 --- /dev/null +++ b/new/CMakeLists.txt @@ -0,0 +1,65 @@ + +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} ) + diff --git a/new/design.h b/new/design.h index ef5f9fb870..de6089da59 100644 --- a/new/design.h +++ b/new/design.h @@ -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 */ @@ -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: }; -/** - * 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 * houses a handful of functions that manage all the RAM resident LIBs, and @@ -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(); - - //-------------------------------------- - - /** - * 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 * is a LIB which resides in a SCHEMATIC, and it is a table model for a diff --git a/new/sch_dir_lib_source.cpp b/new/sch_dir_lib_source.cpp index ad88bac9b5..cd92d7b900 100644 --- a/new/sch_dir_lib_source.cpp +++ b/new/sch_dir_lib_source.cpp @@ -1,4 +1,3 @@ - /* * This program source code file is part of KICAD, a free EDA CAD application. * @@ -23,69 +22,45 @@ * 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 - 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 http://www.softagalleria.net/dirent.php - wx has these but they are based on wxString and wx should not be introduced - at a level this low. + wx has these but they are based on wxString which can be wchar_t based and wx should + not be introduced at a level this low. */ +#include +using namespace SCH; - -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. - - STRING path; ///< base directory path of LIB_SOURCE - - -protected: - - /** - * 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 ); - - -}; - -} // namespace SCH - -#endif // DIR_LIB_SOURCE_H_ - - +#include #include +#include #include -#include +#include +#include #include +#include +#include +#include +#include + +#include +using namespace std; + /** * Class DIR_WRAP - * provides a destructor which may be invoked if an exception is thrown, - * thereby closing the DIR. + * provides a destructor which may be invoked if an exception is thrown. */ class DIR_WRAP { @@ -100,33 +75,254 @@ public: closedir( dir ); } - DIR* operator->() { return dir; } + DIR* operator->() { return dir; } + DIR* operator*() { return dir; } }; -DIR_LIB_SOURCE::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 { - DIR_WRAP* dir = opendir( aDirectoryPath.c_str() ); + int fh; - if( !dir ) +public: + FILE_WRAP( int aFileHandle ) : fh( aFileHandle ) {} + ~FILE_WRAP() { - char buf[256]; - - strerror_r( errno, buf, sizeof(buf) ); - throw( IO_ERROR( buf ) ); + if( fh != -1 ) + close( fh ); } - path = aDirectoryPath; + operator int () { return fh; } +}; +/** + * 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; } +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 ) ) + ++cp; + + if( cp != limit ) // there is garbage after "revN.." + rev = 0; + + 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 ) + { + const char* limit = cp + strlen( cp ); + + // 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; + } + } + } + + 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, bool doUseVersioning ) + throw( IO_ERROR ) +{ + useVersioning = doUseVersioning; + sourceURI = aDirectoryPath; + sourceType = "dir"; + + if( sourceURI.size() == 0 ) + { + 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( "" ); +} + + +DIR_LIB_SOURCE::~DIR_LIB_SOURCE() +{ + // delete the sweet STRINGS, which "sweets" owns by pointer. + for( DIR_CACHE::iterator it = sweets.begin(); it != sweets.end(); ++it ) + { + delete it->second; + } +} + + +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 ) -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 diff --git a/new/sch_dir_lib_source.h b/new/sch_dir_lib_source.h new file mode 100644 index 0000000000..9712c91b0c --- /dev/null +++ b/new/sch_dir_lib_source.h @@ -0,0 +1,149 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck + * 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 + +#include + + +/** + * 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(); + + //----------------------------------- + + 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 ) + { + } + + //----------------------------------- + + /** + * Function Show + * will output a debug dump of contents. + */ + void Show(); +}; + +} // namespace SCH + +#endif // DIR_LIB_SOURCE_H_ diff --git a/new/sch_lib.h b/new/sch_lib.h new file mode 100644 index 0000000000..a7d5d018b0 --- /dev/null +++ b/new/sch_lib.h @@ -0,0 +1,351 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck + * 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 +#include + +#include + + +#ifdef DEBUG +#define D(x) x +#else +#define D(x) // nothing +#endif + + +typedef std::string STRING; +typedef std::deque 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; } + + //-------------------------------------------- + + /** + * 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; +}; + + +/** + * 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(); + + //-------------------------------------- + + /** + * 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_ +