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.

697 lines
16 KiB

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) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. /**
  24. * @file string.cpp
  25. * @brief Some useful functions to handle strings.
  26. */
  27. #include <fctsys.h>
  28. #include <macros.h>
  29. #include <richio.h> // StrPrintf
  30. #include <kicad_string.h>
  31. /**
  32. * Illegal file name characters used to insure file names will be valid on all supported
  33. * platforms. This is the list of illegal file name characters for Windows which includes
  34. * the illegal file name characters for Linux and OSX.
  35. */
  36. static const char illegalFileNameChars[] = "\\/:\"<>|";
  37. wxString EscapeString( const wxString& aSource )
  38. {
  39. #if 1
  40. return aSource;
  41. #else
  42. wxString converted;
  43. for( wxUniChar c: aSource )
  44. {
  45. if( c == '\"' )
  46. converted += "&quot;";
  47. else if( c == '\'' )
  48. converted += "&apos;";
  49. else if( c == '&' )
  50. converted += "&amp;";
  51. else if( c == '<' )
  52. converted += "&lt;";
  53. else if( c == '>' )
  54. converted += "&gt;";
  55. else if( c == '\\' )
  56. converted += "&Backslash;";
  57. else if( c == '/' )
  58. converted += "&frasl;";
  59. else if( c == '|' )
  60. converted += "&verbar;";
  61. else if( c == ':' )
  62. converted += "&colon;";
  63. else if( c == ' ' )
  64. converted += "&nbsp;";
  65. else if( c == '%' )
  66. converted += "&percnt;";
  67. else if( c == '$' )
  68. converted += "&dollar;";
  69. else if( c == '\t' )
  70. converted += "&tab;";
  71. else if( c == '\n' || c == '\r' )
  72. converted += "&Newline;";
  73. else
  74. converted += c;
  75. }
  76. return converted;
  77. #endif
  78. }
  79. wxString UnescapeString( const wxString& aSource )
  80. {
  81. #if 1
  82. return aSource;
  83. #else
  84. wxString converted = aSource;
  85. converted.Replace( "&quot;", "\"" );
  86. converted.Replace( "&apos;", "'" );
  87. converted.Replace( "&lt;", "<" );
  88. converted.Replace( "&gt;", ">" );
  89. converted.Replace( "&Backslash;", "\\" );
  90. converted.Replace( "&frasl;", "/" );
  91. converted.Replace( "&verbar;", "|" );
  92. converted.Replace( "&colon;", ":" );
  93. converted.Replace( "&nbsp;", " " );
  94. converted.Replace( "&percnt;", "%" );
  95. converted.Replace( "&dollar;", "$" );
  96. converted.Replace( "&tab;", "\t" );
  97. converted.Replace( "&Newline;", "\n" );
  98. // must be done last
  99. converted.Replace( "&amp;", "&" );
  100. return converted;
  101. #endif
  102. }
  103. int ReadDelimitedText( wxString* aDest, const char* aSource )
  104. {
  105. std::string utf8; // utf8 but without escapes and quotes.
  106. bool inside = false;
  107. const char* start = aSource;
  108. char cc;
  109. while( (cc = *aSource++) != 0 )
  110. {
  111. if( cc == '"' )
  112. {
  113. if( inside )
  114. break; // 2nd double quote is end of delimited text
  115. inside = true; // first delimiter found, make note, do not copy
  116. }
  117. else if( inside )
  118. {
  119. if( cc == '\\' )
  120. {
  121. cc = *aSource++;
  122. if( !cc )
  123. break;
  124. // do no copy the escape byte if it is followed by \ or "
  125. if( cc != '"' && cc != '\\' )
  126. utf8 += '\\';
  127. utf8 += cc;
  128. }
  129. else
  130. {
  131. utf8 += cc;
  132. }
  133. }
  134. }
  135. *aDest = FROM_UTF8( utf8.c_str() );
  136. return aSource - start;
  137. }
  138. int ReadDelimitedText( char* aDest, const char* aSource, int aDestSize )
  139. {
  140. if( aDestSize <= 0 )
  141. return 0;
  142. bool inside = false;
  143. const char* start = aSource;
  144. char* limit = aDest + aDestSize - 1;
  145. char cc;
  146. while( (cc = *aSource++) != 0 && aDest < limit )
  147. {
  148. if( cc == '"' )
  149. {
  150. if( inside )
  151. break; // 2nd double quote is end of delimited text
  152. inside = true; // first delimiter found, make note, do not copy
  153. }
  154. else if( inside )
  155. {
  156. if( cc == '\\' )
  157. {
  158. cc = *aSource++;
  159. if( !cc )
  160. break;
  161. // do no copy the escape byte if it is followed by \ or "
  162. if( cc != '"' && cc != '\\' )
  163. *aDest++ = '\\';
  164. if( aDest < limit )
  165. *aDest++ = cc;
  166. }
  167. else
  168. {
  169. *aDest++ = cc;
  170. }
  171. }
  172. }
  173. *aDest = 0;
  174. return aSource - start;
  175. }
  176. std::string EscapedUTF8( const wxString& aString )
  177. {
  178. std::string utf8 = TO_UTF8( aString );
  179. std::string ret;
  180. ret += '"';
  181. for( std::string::const_iterator it = utf8.begin(); it!=utf8.end(); ++it )
  182. {
  183. // this escaping strategy is designed to be compatible with ReadDelimitedText():
  184. if( *it == '"' )
  185. {
  186. ret += '\\';
  187. ret += '"';
  188. }
  189. else if( *it == '\\' )
  190. {
  191. ret += '\\'; // double it up
  192. ret += '\\';
  193. }
  194. else
  195. {
  196. ret += *it;
  197. }
  198. }
  199. ret += '"';
  200. return ret;
  201. }
  202. wxString EscapedHTML( const wxString& aString )
  203. {
  204. wxString converted;
  205. for( wxUniChar c: aString )
  206. {
  207. if( c == '\"' )
  208. converted += "&quot;";
  209. else if( c == '\'' )
  210. converted += "&apos;";
  211. else if( c == '&' )
  212. converted += "&amp;";
  213. else if( c == '<' )
  214. converted += "&lt;";
  215. else if( c == '>' )
  216. converted += "&gt;";
  217. else
  218. converted += c;
  219. }
  220. return converted;
  221. }
  222. char* StrPurge( char* text )
  223. {
  224. static const char whitespace[] = " \t\n\r\f\v";
  225. if( text )
  226. {
  227. while( *text && strchr( whitespace, *text ) )
  228. ++text;
  229. char* cp = text + strlen( text ) - 1;
  230. while( cp >= text && strchr( whitespace, *cp ) )
  231. *cp-- = '\0';
  232. }
  233. return text;
  234. }
  235. char* GetLine( FILE* File, char* Line, int* LineNum, int SizeLine )
  236. {
  237. do {
  238. if( fgets( Line, SizeLine, File ) == NULL )
  239. return NULL;
  240. if( LineNum )
  241. *LineNum += 1;
  242. } while( Line[0] == '#' || Line[0] == '\n' || Line[0] == '\r' || Line[0] == 0 );
  243. strtok( Line, "\n\r" );
  244. return Line;
  245. }
  246. wxString DateAndTime()
  247. {
  248. wxDateTime datetime = wxDateTime::Now();
  249. datetime.SetCountry( wxDateTime::Country_Default );
  250. return datetime.Format( wxDefaultDateTimeFormat, wxDateTime::Local );
  251. }
  252. int StrNumCmp( const wxString& aString1, const wxString& aString2, bool aIgnoreCase )
  253. {
  254. int nb1 = 0, nb2 = 0;
  255. auto str1 = aString1.begin();
  256. auto str2 = aString2.begin();
  257. while( str1 != aString1.end() && str2 != aString2.end() )
  258. {
  259. wxUniChar c1 = *str1;
  260. wxUniChar c2 = *str2;
  261. if( wxIsdigit( c1 ) && wxIsdigit( c2 ) ) // Both characters are digits, do numeric compare.
  262. {
  263. nb1 = 0;
  264. nb2 = 0;
  265. do
  266. {
  267. c1 = *str1;
  268. nb1 = nb1 * 10 + (int) c1 - '0';
  269. ++str1;
  270. } while( str1 != aString1.end() && wxIsdigit( *str1 ) );
  271. do
  272. {
  273. c2 = *str2;
  274. nb2 = nb2 * 10 + (int) c2 - '0';
  275. ++str2;
  276. } while( str2 != aString2.end() && wxIsdigit( *str2 ) );
  277. if( nb1 < nb2 )
  278. return -1;
  279. if( nb1 > nb2 )
  280. return 1;
  281. c1 = ( str1 != aString1.end() ) ? *str1 : wxUniChar( 0 );
  282. c2 = ( str2 != aString2.end() ) ? *str2 : wxUniChar( 0 );
  283. }
  284. // Any numerical comparisons to here are identical.
  285. if( aIgnoreCase )
  286. {
  287. if( wxToupper( c1 ) < wxToupper( c2 ) )
  288. return -1;
  289. if( wxToupper( c1 ) > wxToupper( c2 ) )
  290. return 1;
  291. }
  292. else
  293. {
  294. if( c1 < c2 )
  295. return -1;
  296. if( c1 > c2 )
  297. return 1;
  298. }
  299. if( str1 != aString1.end() )
  300. ++str1;
  301. if( str2 != aString2.end() )
  302. ++str2;
  303. }
  304. if( str1 == aString1.end() && str2 != aString2.end() )
  305. {
  306. return -1; // Identical to here but aString1 is longer.
  307. }
  308. else if( str1 != aString1.end() && str2 == aString2.end() )
  309. {
  310. return 1; // Identical to here but aString2 is longer.
  311. }
  312. return 0;
  313. }
  314. bool WildCompareString( const wxString& pattern, const wxString& string_to_tst,
  315. bool case_sensitive )
  316. {
  317. const wxChar* cp = NULL, * mp = NULL;
  318. const wxChar* wild, * string;
  319. wxString _pattern, _string_to_tst;
  320. if( case_sensitive )
  321. {
  322. wild = pattern.GetData();
  323. string = string_to_tst.GetData();
  324. }
  325. else
  326. {
  327. _pattern = pattern;
  328. _pattern.MakeUpper();
  329. _string_to_tst = string_to_tst;
  330. _string_to_tst.MakeUpper();
  331. wild = _pattern.GetData();
  332. string = _string_to_tst.GetData();
  333. }
  334. while( ( *string ) && ( *wild != '*' ) )
  335. {
  336. if( ( *wild != *string ) && ( *wild != '?' ) )
  337. return false;
  338. wild++; string++;
  339. }
  340. while( *string )
  341. {
  342. if( *wild == '*' )
  343. {
  344. if( !*++wild )
  345. return 1;
  346. mp = wild;
  347. cp = string + 1;
  348. }
  349. else if( ( *wild == *string ) || ( *wild == '?' ) )
  350. {
  351. wild++;
  352. string++;
  353. }
  354. else
  355. {
  356. wild = mp;
  357. string = cp++;
  358. }
  359. }
  360. while( *wild == '*' )
  361. {
  362. wild++;
  363. }
  364. return !*wild;
  365. }
  366. bool ApplyModifier( double& value, const wxString& aString )
  367. {
  368. static const wxString modifiers( wxT( "pnumkKM" ) );
  369. if( !aString.length() )
  370. return false;
  371. wxChar modifier;
  372. wxString units;
  373. if( modifiers.Find( aString[ 0 ] ) >= 0 )
  374. {
  375. modifier = aString[ 0 ];
  376. units = aString.Mid( 1 ).Trim();
  377. }
  378. else
  379. {
  380. modifier = ' ';
  381. units = aString.Mid( 0 ).Trim();
  382. }
  383. if( units.length()
  384. && !units.CmpNoCase( wxT( "F" ) )
  385. && !units.CmpNoCase( wxT( "hz" ) )
  386. && !units.CmpNoCase( wxT( "W" ) )
  387. && !units.CmpNoCase( wxT( "V" ) )
  388. && !units.CmpNoCase( wxT( "H" ) ) )
  389. return false;
  390. if( modifier == 'p' )
  391. value *= 1.0e-12;
  392. if( modifier == 'n' )
  393. value *= 1.0e-9;
  394. else if( modifier == 'u' )
  395. value *= 1.0e-6;
  396. else if( modifier == 'm' )
  397. value *= 1.0e-3;
  398. else if( modifier == 'k' || modifier == 'K' )
  399. value *= 1.0e3;
  400. else if( modifier == 'M' )
  401. value *= 1.0e6;
  402. else if( modifier == 'G' )
  403. value *= 1.0e9;
  404. return true;
  405. }
  406. int ValueStringCompare( wxString strFWord, wxString strSWord )
  407. {
  408. // Compare unescaped text
  409. strFWord = UnescapeString( strFWord );
  410. strSWord = UnescapeString( strSWord );
  411. // The different sections of the two strings
  412. wxString strFWordBeg, strFWordMid, strFWordEnd;
  413. wxString strSWordBeg, strSWordMid, strSWordEnd;
  414. // Split the two strings into separate parts
  415. SplitString( strFWord, &strFWordBeg, &strFWordMid, &strFWordEnd );
  416. SplitString( strSWord, &strSWordBeg, &strSWordMid, &strSWordEnd );
  417. // Compare the Beginning section of the strings
  418. int isEqual = strFWordBeg.CmpNoCase( strSWordBeg );
  419. if( isEqual > 0 )
  420. return 1;
  421. else if( isEqual < 0 )
  422. return -1;
  423. else
  424. {
  425. // If the first sections are equal compare their digits
  426. double lFirstNumber = 0;
  427. double lSecondNumber = 0;
  428. bool endingIsModifier = false;
  429. strFWordMid.ToDouble( &lFirstNumber );
  430. strSWordMid.ToDouble( &lSecondNumber );
  431. endingIsModifier |= ApplyModifier( lFirstNumber, strFWordEnd );
  432. endingIsModifier |= ApplyModifier( lSecondNumber, strSWordEnd );
  433. if( lFirstNumber > lSecondNumber )
  434. return 1;
  435. else if( lFirstNumber < lSecondNumber )
  436. return -1;
  437. // If the first two sections are equal and the endings are modifiers then compare them
  438. else if( !endingIsModifier )
  439. return strFWordEnd.CmpNoCase( strSWordEnd );
  440. // Ran out of things to compare; they must match
  441. else
  442. return 0;
  443. }
  444. }
  445. int SplitString( wxString strToSplit,
  446. wxString* strBeginning,
  447. wxString* strDigits,
  448. wxString* strEnd )
  449. {
  450. static const wxString separators( wxT( ".," ) );
  451. // Clear all the return strings
  452. strBeginning->Empty();
  453. strDigits->Empty();
  454. strEnd->Empty();
  455. // There no need to do anything if the string is empty
  456. if( strToSplit.length() == 0 )
  457. return 0;
  458. // Starting at the end of the string look for the first digit
  459. int ii;
  460. for( ii = (strToSplit.length() - 1); ii >= 0; ii-- )
  461. {
  462. if( wxIsdigit( strToSplit[ii] ) )
  463. break;
  464. }
  465. // If there were no digits then just set the single string
  466. if( ii < 0 )
  467. {
  468. *strBeginning = strToSplit;
  469. }
  470. else
  471. {
  472. // Since there is at least one digit this is the trailing string
  473. *strEnd = strToSplit.substr( ii + 1 );
  474. // Go to the end of the digits
  475. int position = ii + 1;
  476. for( ; ii >= 0; ii-- )
  477. {
  478. if( !wxIsdigit( strToSplit[ii] ) && separators.Find( strToSplit[ii] ) < 0 )
  479. break;
  480. }
  481. // If all that was left was digits, then just set the digits string
  482. if( ii < 0 )
  483. *strDigits = strToSplit.substr( 0, position );
  484. /* We were only looking for the last set of digits everything else is
  485. * part of the preamble */
  486. else
  487. {
  488. *strDigits = strToSplit.substr( ii + 1, position - ii - 1 );
  489. *strBeginning = strToSplit.substr( 0, ii + 1 );
  490. }
  491. }
  492. return 0;
  493. }
  494. int GetTrailingInt( const wxString& aStr )
  495. {
  496. int number = 0;
  497. int base = 1;
  498. // Trim and extract the trailing numeric part
  499. int index = aStr.Len() - 1;
  500. while( index >= 0 )
  501. {
  502. const char chr = aStr.GetChar( index );
  503. if( chr < '0' || chr > '9' )
  504. break;
  505. number += ( chr - '0' ) * base;
  506. base *= 10;
  507. index--;
  508. }
  509. return number;
  510. }
  511. wxString GetIllegalFileNameWxChars()
  512. {
  513. return FROM_UTF8( illegalFileNameChars );
  514. }
  515. bool ReplaceIllegalFileNameChars( std::string* aName, int aReplaceChar )
  516. {
  517. bool changed = false;
  518. std::string result;
  519. result.reserve( aName->length() );
  520. for( std::string::iterator it = aName->begin(); it != aName->end(); ++it )
  521. {
  522. if( strchr( illegalFileNameChars, *it ) )
  523. {
  524. if( aReplaceChar )
  525. StrPrintf( &result, "%c", aReplaceChar );
  526. else
  527. StrPrintf( &result, "%%%02x", *it );
  528. changed = true;
  529. }
  530. else
  531. {
  532. result += *it;
  533. }
  534. }
  535. if( changed )
  536. *aName = result;
  537. return changed;
  538. }
  539. bool ReplaceIllegalFileNameChars( wxString& aName, int aReplaceChar )
  540. {
  541. bool changed = false;
  542. wxString result;
  543. result.reserve( aName.Length() );
  544. wxString illWChars = GetIllegalFileNameWxChars();
  545. for( wxString::iterator it = aName.begin(); it != aName.end(); ++it )
  546. {
  547. if( illWChars.Find( *it ) != wxNOT_FOUND )
  548. {
  549. if( aReplaceChar )
  550. result += aReplaceChar;
  551. else
  552. result += wxString::Format( "%%%02x", *it );
  553. changed = true;
  554. }
  555. else
  556. {
  557. result += *it;
  558. }
  559. }
  560. if( changed )
  561. aName = result;
  562. return changed;
  563. }