You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

728 lines
20 KiB

15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2010 SoftPLC Corporation, <dick@softplc.com>
  5. * Copyright (C) 2010 KiCad Developers, see change_log.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. /* Note: this LIB_SOURCE implementation relies on the posix specified opendir() and
  25. related functions rather than wx functions which might do the same thing. This
  26. is because I did not want to become very dependent on wxWidgets at such a low
  27. level as this, in case someday this code needs to be used on kde or whatever.
  28. Mingw and unix, linux, & osx will all have these posix functions.
  29. MS Visual Studio may need the posix compatible opendir() functions brought in
  30. http://www.softagalleria.net/dirent.php
  31. wx has these but they are based on wxString which can be wchar_t based and wx should
  32. not be introduced at a level this low.
  33. Part files: have the general form partname.part[.revN...]
  34. Categories: are any subdirectories immediately below the sourceURI, one level only.
  35. Part names: [category/]partname[/revN...]
  36. */
  37. #include <dirent.h>
  38. #include <sys/stat.h>
  39. #include <cstring>
  40. #include <cstdio>
  41. #include <sys/types.h>
  42. #include <sys/stat.h>
  43. #include <unistd.h>
  44. #include <fcntl.h>
  45. #include <errno.h>
  46. #include <assert.h>
  47. #include <sch_dir_lib_source.h>
  48. using namespace SCH;
  49. /**
  50. * DIR_WRAP
  51. * provides a destructor which is invoked if an exception is thrown.
  52. */
  53. class DIR_WRAP
  54. {
  55. DIR* dir;
  56. public:
  57. DIR_WRAP( DIR* aDir ) : dir( aDir ) {}
  58. ~DIR_WRAP()
  59. {
  60. if( dir )
  61. closedir( dir );
  62. }
  63. DIR* operator->() { return dir; }
  64. DIR* operator*() { return dir; }
  65. operator bool () { return dir!=0; }
  66. };
  67. /**
  68. * FILE_WRAP
  69. * provides a destructor which is invoked if an exception is thrown.
  70. */
  71. class FILE_WRAP
  72. {
  73. int fh;
  74. public:
  75. FILE_WRAP( int aFileHandle ) : fh( aFileHandle ) {}
  76. ~FILE_WRAP()
  77. {
  78. if( fh != -1 )
  79. close( fh );
  80. }
  81. operator int () { return fh; }
  82. };
  83. /**
  84. * Function strrstr
  85. * finds the last instance of needle in haystack, if any.
  86. */
  87. static const char* strrstr( const char* haystack, const char* needle )
  88. {
  89. const char* ret = 0;
  90. const char* next = haystack;
  91. // find last instance of haystack
  92. while( (next = strstr( next, needle )) != 0 )
  93. {
  94. ret = next;
  95. ++next; // don't keep finding the same one.
  96. }
  97. return ret;
  98. }
  99. #if 1 // @todo switch over to EndsWithRev() global
  100. static inline bool isDigit( char c )
  101. {
  102. return c >= '0' && c <= '9';
  103. }
  104. /**
  105. * Function endsWithRev
  106. * returns a pointer to the final string segment: "revN[N..]" or NULL if none.
  107. * @param start is the beginning of string segment to test, the partname or
  108. * any middle portion of it.
  109. * @param tail is a pointer to the terminating nul, or one past inclusive end of
  110. * segment, i.e. the string segment of interest is [start,tail)
  111. * @param separator is the separating byte, expected: '.' or '/', depending on context.
  112. */
  113. static const char* endsWithRev( const char* start, const char* tail, char separator = '/' )
  114. {
  115. bool sawDigit = false;
  116. while( tail > start && isDigit( *--tail ) )
  117. {
  118. sawDigit = true;
  119. }
  120. // if sawDigit, tail points to the 'v' here.
  121. if( sawDigit && tail-3 >= start )
  122. {
  123. tail -= 3;
  124. if( tail[0]==separator && tail[1]=='r' && tail[2]=='e' && tail[3]=='v' )
  125. {
  126. return tail+1; // omit separator, return "revN[N..]"
  127. }
  128. }
  129. return 0;
  130. }
  131. static inline const char* endsWithRev( const STRING& aPartName, char separator = '/' )
  132. {
  133. return endsWithRev( aPartName.c_str(), aPartName.c_str()+aPartName.size(), separator );
  134. }
  135. #endif
  136. // see struct BY_REV
  137. bool BY_REV::operator() ( const STRING& s1, const STRING& s2 ) const
  138. {
  139. // avoid instantiating new STRINGs, and thank goodness that c_str() is const.
  140. const char* rev1 = endsWithRev( s1 );
  141. const char* rev2 = endsWithRev( s2 );
  142. int rootLen1 = rev1 ? rev1 - s1.c_str() : s1.size();
  143. int rootLen2 = rev2 ? rev2 - s2.c_str() : s2.size();
  144. int r = memcmp( s1.c_str(), s2.c_str(), min( rootLen1, rootLen2 ) );
  145. if( r )
  146. {
  147. return r < 0;
  148. }
  149. if( rootLen1 != rootLen2 )
  150. {
  151. return rootLen1 < rootLen2;
  152. }
  153. // root strings match at this point, compare the revision number numerically,
  154. // and chose the higher numbered version as "less", according to std::set lingo.
  155. if( bool(rev1) != bool(rev2) )
  156. {
  157. return bool(rev1) < bool(rev2);
  158. }
  159. if( rev1 && rev2 )
  160. {
  161. int rnum1 = atoi( rev1+3 );
  162. int rnum2 = atoi( rev2+3 );
  163. // higher numbered revs are "less" so that they bubble to top.
  164. return rnum1 > rnum2;
  165. }
  166. return false; // strings are equal, and they don't have a rev
  167. }
  168. bool DIR_LIB_SOURCE::makePartName( STRING* aPartName, const char* aEntry,
  169. const STRING& aCategory )
  170. {
  171. const char* cp = strrstr( aEntry, SWEET_EXT );
  172. // if base name is not empty, contains SWEET_EXT, && cp is not NULL
  173. if( cp > aEntry )
  174. {
  175. const char* limit = cp + strlen( cp );
  176. // If versioning, then must find a trailing "revN.." type of string.
  177. if( useVersioning )
  178. {
  179. const char* rev = endsWithRev( cp + SWEET_EXTZ, limit, '.' );
  180. if( rev )
  181. {
  182. if( aCategory.size() )
  183. *aPartName = aCategory + "/";
  184. else
  185. aPartName->clear();
  186. aPartName->append( aEntry, cp - aEntry );
  187. aPartName->append( "/" );
  188. aPartName->append( rev );
  189. return true;
  190. }
  191. }
  192. // If using versioning, then all valid partnames must have a rev string,
  193. // so we don't even bother to try and load any other partfile down here.
  194. else
  195. {
  196. // if file extension is exactly SWEET_EXT, and no rev
  197. if( cp==limit-5 )
  198. {
  199. if( aCategory.size() )
  200. *aPartName = aCategory + "/";
  201. else
  202. aPartName->clear();
  203. aPartName->append( aEntry, cp - aEntry );
  204. return true;
  205. }
  206. }
  207. }
  208. return false;
  209. }
  210. STRING DIR_LIB_SOURCE::makeFileName( const STRING& aPartName )
  211. {
  212. // create a fileName for the sweet string, using a reversible
  213. // partname <-> fileName conversion protocol:
  214. STRING fileName = sourceURI + "/";
  215. const char* rev = endsWithRev( aPartName );
  216. if( rev )
  217. {
  218. int basePartLen = rev - aPartName.c_str() - 1; // omit '/' separator
  219. fileName.append( aPartName, 0, basePartLen );
  220. fileName += SWEET_EXT;
  221. fileName += '.';
  222. fileName += rev;
  223. }
  224. else
  225. {
  226. fileName += aPartName;
  227. fileName += SWEET_EXT;
  228. }
  229. return fileName;
  230. }
  231. void DIR_LIB_SOURCE::readString( STRING* aResult, const STRING& aFileName )
  232. {
  233. FILE_WRAP fw = open( aFileName.c_str(), O_RDONLY );
  234. if( fw == -1 )
  235. {
  236. STRING msg = strerror( errno );
  237. msg += "; cannot open(O_RDONLY) file " + aFileName;
  238. THROW_IO_ERROR( msg );
  239. }
  240. struct stat fs;
  241. fstat( fw, &fs );
  242. // sanity check on file size
  243. if( fs.st_size > (1*1024*1024) )
  244. {
  245. STRING msg = aFileName;
  246. msg += " seems too big. ( > 1 mbyte )";
  247. THROW_IO_ERROR( msg );
  248. }
  249. #if 0
  250. // I read somewhere on the Internet that std::string chars are not guaranteed
  251. // (over time) to be contiguous in future implementations of C++, so this
  252. // strategy is here for that eventuality. We buffer through readBuffer here.
  253. // reuse same readBuffer, which is not thread safe, but the API
  254. // is not advertising thread safe (yet, if ever).
  255. if( (int) fs.st_size > (int) readBuffer.size() )
  256. readBuffer.resize( fs.st_size + 1000 );
  257. int count = read( fw, &readBuffer[0], fs.st_size );
  258. if( count != (int) fs.st_size )
  259. {
  260. STRING msg = strerror( errno );
  261. msg += "; cannot read file " + aFileName;
  262. THROW_IO_ERROR( msg );
  263. }
  264. aResult->assign( &readBuffer[0], count );
  265. #else
  266. // read into the string directly
  267. aResult->resize( fs.st_size );
  268. int count = read( fw, &(*aResult)[0], fs.st_size );
  269. if( count != (int) fs.st_size )
  270. {
  271. STRING msg = strerror( errno );
  272. msg += "; cannot read file " + aFileName;
  273. THROW_IO_ERROR( msg );
  274. }
  275. // test trailing nul is there, which should have been put there with resize() above
  276. // printf( "'%s'\n", aResult->c_str() ); // checked OK.
  277. #endif
  278. }
  279. void DIR_LIB_SOURCE::cache()
  280. {
  281. partnames.clear();
  282. categories.clear();
  283. cacheOneDir( "" );
  284. }
  285. DIR_LIB_SOURCE::DIR_LIB_SOURCE( const STRING& aDirectoryPath,
  286. const STRING& aOptions ) :
  287. useVersioning( strstr( aOptions.c_str(), "useVersioning" ) )
  288. {
  289. sourceURI = aDirectoryPath;
  290. sourceType = "dir";
  291. if( sourceURI.size() == 0 )
  292. {
  293. THROW_IO_ERROR( STRING("aDirectoryPath cannot be empty") );
  294. }
  295. // remove any trailing separator, so we can add it back later without ambiguity
  296. if( strchr( "/\\", sourceURI[sourceURI.size()-1] ) )
  297. sourceURI.erase( sourceURI.size()-1 );
  298. cache();
  299. }
  300. DIR_LIB_SOURCE::~DIR_LIB_SOURCE()
  301. {
  302. }
  303. void DIR_LIB_SOURCE::GetCategoricalPartNames( STRINGS* aResults, const STRING& aCategory )
  304. {
  305. PN_ITER end = aCategory.size() ?
  306. partnames.lower_bound( aCategory + char( '/' + 1 ) ) :
  307. partnames.end();
  308. PN_ITER it = aCategory.size() ?
  309. partnames.upper_bound( aCategory + "/" ) :
  310. partnames.begin();
  311. aResults->clear();
  312. if( useVersioning )
  313. {
  314. STRING partName;
  315. while( it != end )
  316. {
  317. const char* rev = endsWithRev( *it );
  318. // all cached partnames have a rev string in useVersioning mode
  319. assert( rev );
  320. // partName is substring which omits the rev AND the rev separator
  321. partName.assign( *it, 0, rev - it->c_str() - 1 );
  322. aResults->push_back( partName );
  323. // skip over all other versions of the same partName.
  324. it = partnames.lower_bound( partName + char( '/' + 1 ) );
  325. }
  326. }
  327. else
  328. {
  329. while( it != end )
  330. aResults->push_back( *it++ );
  331. }
  332. }
  333. void DIR_LIB_SOURCE::GetRevisions( STRINGS* aResults, const STRING& aPartName )
  334. {
  335. aResults->clear();
  336. if( useVersioning )
  337. {
  338. STRING partName;
  339. const char* rev = endsWithRev( aPartName );
  340. if( rev )
  341. // partName is substring which omits the rev and the separator
  342. partName.assign( aPartName, 0, rev - aPartName.c_str() - 1 );
  343. else
  344. partName = aPartName;
  345. PN_ITER it = partnames.upper_bound( partName +'/' );
  346. PN_ITER end = partnames.lower_bound( partName + char( '/' +1 ) );
  347. for( ; it != end; ++it )
  348. {
  349. const char* rev = endsWithRev( *it );
  350. assert( rev );
  351. aResults->push_back( it->substr( rev - it->c_str() ) );
  352. }
  353. }
  354. else
  355. {
  356. // In non-version mode, there were no revisions read in, only part
  357. // files without a revision. But clients higher up expect to see
  358. // at least one revision in order for the API to work, so we return
  359. // a revision ""
  360. aResults->push_back( "" );
  361. }
  362. }
  363. void DIR_LIB_SOURCE::ReadPart( STRING* aResult, const STRING& aPartName, const STRING& aRev )
  364. {
  365. STRING partName = aPartName;
  366. const char* hasRev = endsWithRev( partName );
  367. if( useVersioning )
  368. {
  369. if( aRev.size() )
  370. {
  371. // a supplied rev replaces any in aPartName
  372. if( hasRev )
  373. partName.resize( hasRev - partName.c_str() - 1 );
  374. partName += '/';
  375. partName + aRev;
  376. // find this exact revision
  377. PN_ITER it = partnames.find( partName );
  378. if( it == partnames.end() ) // part not found
  379. {
  380. partName += " not found.";
  381. THROW_IO_ERROR( partName );
  382. }
  383. readString( aResult, makeFileName( partName ) );
  384. }
  385. else
  386. {
  387. // There's no rev on partName string. Find the most recent rev, i.e. highest,
  388. // which will be first because of the BY_REV compare method, which treats
  389. // higher numbered revs as first.
  390. STRING search = partName + '/';
  391. // There's no rev on partName string. Find the most recent rev, i.e. highest,
  392. // which will be first because of the BY_REV compare method, which treats
  393. // higher numbered revs as first.
  394. PN_ITER it = partnames.upper_bound( search );
  395. // verify that this one that is greater than partName is a match and not
  396. // some unrelated name that is larger.
  397. if( it == partnames.end() ||
  398. it->compare( 0, search.size(), search ) != 0 )
  399. {
  400. partName += " is not present without a revision.";
  401. THROW_IO_ERROR( partName );
  402. }
  403. readString( aResult, makeFileName( *it ) );
  404. }
  405. }
  406. else // !useVersioning
  407. {
  408. #if 1
  409. if( hasRev || aRev.size() )
  410. {
  411. STRING msg = "this type 'dir' LIB_SOURCE not using 'useVersioning' option, cannot ask for a revision";
  412. THROW_IO_ERROR( msg );
  413. }
  414. #else
  415. // no revisions allowed, strip it
  416. if( hasRev )
  417. partName.resize( hasRev - partName.c_str() - 1 );
  418. #endif
  419. // find the part name without any revision
  420. PN_ITER it = partnames.find( partName );
  421. if( it == partnames.end() ) // part not found
  422. {
  423. partName += " not found.";
  424. THROW_IO_ERROR( partName );
  425. }
  426. readString( aResult, makeFileName( partName ) );
  427. }
  428. }
  429. void DIR_LIB_SOURCE::ReadParts( STRINGS* aResults, const STRINGS& aPartNames )
  430. {
  431. aResults->clear();
  432. for( STRINGS::const_iterator n = aPartNames.begin(); n!=aPartNames.end(); ++n )
  433. {
  434. aResults->push_back( STRING() );
  435. ReadPart( &aResults->back(), *n );
  436. }
  437. }
  438. void DIR_LIB_SOURCE::GetCategories( STRINGS* aResults )
  439. {
  440. aResults->clear();
  441. // caller fetches them sorted.
  442. for( NAME_CACHE::const_iterator it = categories.begin(); it!=categories.end(); ++it )
  443. {
  444. aResults->push_back( *it );
  445. }
  446. }
  447. #if defined(DEBUG)
  448. void DIR_LIB_SOURCE::Show()
  449. {
  450. printf( "Show categories:\n" );
  451. for( NAME_CACHE::const_iterator it = categories.begin(); it!=categories.end(); ++it )
  452. printf( " '%s'\n", it->c_str() );
  453. printf( "\n" );
  454. printf( "Show parts:\n" );
  455. for( PART_CACHE::const_iterator it = partnames.begin(); it != partnames.end(); ++it )
  456. {
  457. printf( " '%s'\n", it->c_str() );
  458. }
  459. }
  460. #endif
  461. void DIR_LIB_SOURCE::cacheOneDir( const STRING& aCategory )
  462. {
  463. STRING curDir = sourceURI;
  464. if( aCategory.size() )
  465. curDir += "/" + aCategory;
  466. DIR_WRAP dir = opendir( curDir.c_str() );
  467. if( !dir )
  468. {
  469. STRING msg = strerror( errno );
  470. msg += "; scanning directory " + curDir;
  471. THROW_IO_ERROR( msg );
  472. }
  473. struct stat fs;
  474. STRING partName;
  475. STRING fileName;
  476. dirent* entry;
  477. while( (entry = readdir( *dir )) != NULL )
  478. {
  479. if( !strcmp( ".", entry->d_name ) || !strcmp( "..", entry->d_name ) )
  480. continue;
  481. fileName = curDir + "/" + entry->d_name;
  482. if( !stat( fileName.c_str(), &fs ) )
  483. {
  484. // is this a valid part name?
  485. if( S_ISREG( fs.st_mode ) && makePartName( &partName, entry->d_name, aCategory ) )
  486. {
  487. std::pair<NAME_CACHE::iterator, bool> pair = partnames.insert( partName );
  488. if( !pair.second )
  489. {
  490. STRING msg = partName;
  491. msg += " has already been encountered";
  492. THROW_IO_ERROR( msg );
  493. }
  494. }
  495. // is this an acceptable category name?
  496. else if( S_ISDIR( fs.st_mode ) && !aCategory.size() && isCategoryName( entry->d_name ) )
  497. {
  498. // only one level of recursion is used, controlled by the
  499. // emptiness of aCategory.
  500. categories.insert( entry->d_name );
  501. // somebody needs to test Windows (mingw), make sure it can
  502. // handle opendir() recursively
  503. cacheOneDir( entry->d_name );
  504. }
  505. else
  506. {
  507. //D( printf( "ignoring %s\n", entry->d_name );)
  508. }
  509. }
  510. }
  511. }
  512. #if 0 && defined(DEBUG)
  513. void DIR_LIB_SOURCE::Test( int argc, char** argv )
  514. {
  515. STRINGS partnames;
  516. STRINGS sweets;
  517. try
  518. {
  519. STRINGS::const_iterator pn;
  520. // DIR_LIB_SOURCE uut( argv[1] ? argv[1] : "", "" );
  521. DIR_LIB_SOURCE uut( argv[1] ? argv[1] : "", "useVersioning" );
  522. // show the cached content, only the directory information is cached,
  523. // parts are cached in class LIB, not down here.
  524. uut.Show();
  525. uut.GetCategoricalPartNames( &partnames, "lions" );
  526. printf( "\nGetCategoricalPartNames( aCatagory = 'lions' ):\n" );
  527. for( STRINGS::const_iterator it = partnames.begin(); it!=partnames.end(); ++it )
  528. {
  529. printf( " '%s'\n", it->c_str() );
  530. }
  531. uut.ReadParts( &sweets, partnames );
  532. printf( "\nSweets for Category = 'lions' parts:\n" );
  533. pn = partnames.begin();
  534. for( STRINGS::const_iterator it = sweets.begin(); it!=sweets.end(); ++it, ++pn )
  535. {
  536. printf( " %s: %s", pn->c_str(), it->c_str() );
  537. }
  538. // fetch the part names for ALL categories.
  539. uut.GetCategoricalPartNames( &partnames );
  540. printf( "\nGetCategoricalPartNames( aCategory = '' i.e. ALL):\n" );
  541. for( STRINGS::const_iterator it = partnames.begin(); it!=partnames.end(); ++it )
  542. {
  543. printf( " '%s'\n", it->c_str() );
  544. }
  545. uut.ReadParts( &sweets, partnames );
  546. printf( "\nSweets for ALL parts:\n" );
  547. pn = partnames.begin();
  548. for( STRINGS::const_iterator it = sweets.begin(); it!=sweets.end(); ++it, ++pn )
  549. {
  550. printf( " %s: %s", pn->c_str(), it->c_str() );
  551. }
  552. }
  553. catch( std::exception& ex )
  554. {
  555. printf( "std::exception\n" );
  556. }
  557. catch( const IO_ERROR& ioe )
  558. {
  559. printf( "exception: %s\n", (const char*) ioe.errorText.ToUTF8() ) );
  560. }
  561. }
  562. int main( int argc, char** argv )
  563. {
  564. DIR_LIB_SOURCE::Test( argc, argv );
  565. return 0;
  566. }
  567. #endif