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.

544 lines
14 KiB

15 years ago
15 years ago
16 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) 2007-2011 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  5. * Copyright (C) 2007 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. #include <cstdarg>
  25. #include <richio.h>
  26. // Fall back to getc() when getc_unlocked() is not available on the target platform.
  27. #if !defined( HAVE_FGETC_NOLOCK )
  28. #define getc_unlocked getc
  29. #endif
  30. // This file defines 3 classes useful for working with DSN text files and is named
  31. // "richio" after its author, Richard Hollenbeck, aka Dick Hollenbeck.
  32. void IO_ERROR::init( const char* aThrowersFile, const char* aThrowersLoc, const wxString& aMsg )
  33. {
  34. errorText.Printf( IO_FORMAT, aMsg.GetData(),
  35. wxString::FromUTF8( aThrowersFile ).GetData(),
  36. wxString::FromUTF8( aThrowersLoc ).GetData() );
  37. }
  38. void PARSE_ERROR::init( const char* aThrowersFile, const char* aThrowersLoc,
  39. const wxString& aMsg, const wxString& aSource,
  40. const char* aInputLine,
  41. int aLineNumber, int aByteIndex )
  42. {
  43. // save inpuLine, lineNumber, and offset for UI (.e.g. Sweet text editor)
  44. inputLine = aInputLine;
  45. lineNumber = aLineNumber;
  46. byteIndex = aByteIndex;
  47. errorText.Printf( PARSE_FORMAT, aMsg.GetData(), aSource.GetData(),
  48. aLineNumber, aByteIndex,
  49. wxString::FromUTF8( aThrowersFile ).GetData(),
  50. wxString::FromUTF8( aThrowersLoc ).GetData() );
  51. }
  52. //-----<LINE_READER>------------------------------------------------------
  53. LINE_READER::LINE_READER( unsigned aMaxLineLength )
  54. {
  55. lineNum = 0;
  56. if( aMaxLineLength == 0 )
  57. {
  58. line = 0;
  59. }
  60. else
  61. {
  62. maxLineLength = aMaxLineLength;
  63. // start at the INITIAL size, expand as needed up to the MAX size in maxLineLength
  64. capacity = LINE_READER_LINE_INITIAL_SIZE;
  65. // but never go above user's aMaxLineLength, and leave space for trailing nul
  66. if( capacity > aMaxLineLength+1 )
  67. capacity = aMaxLineLength+1;
  68. line = new char[capacity];
  69. line[0] = '\0';
  70. }
  71. length = 0;
  72. }
  73. LINE_READER::~LINE_READER()
  74. {
  75. delete[] line;
  76. }
  77. void LINE_READER::expandCapacity( unsigned newsize )
  78. {
  79. // length can equal maxLineLength and nothing breaks, there's room for
  80. // the terminating nul. cannot go over this.
  81. if( newsize > maxLineLength+1 )
  82. newsize = maxLineLength+1;
  83. if( newsize > capacity )
  84. {
  85. capacity = newsize;
  86. // resize the buffer, and copy the original data
  87. char* bigger = new char[capacity];
  88. wxASSERT( capacity >= length+1 );
  89. memcpy( bigger, line, length );
  90. bigger[length] = 0;
  91. delete[] line;
  92. line = bigger;
  93. }
  94. }
  95. FILE_LINE_READER::FILE_LINE_READER( const wxString& aFileName,
  96. unsigned aStartingLineNumber,
  97. unsigned aMaxLineLength ) throw( IO_ERROR ) :
  98. LINE_READER( aMaxLineLength ),
  99. iOwn( true )
  100. {
  101. fp = wxFopen( aFileName, wxT( "rt" ) );
  102. if( !fp )
  103. {
  104. wxString msg = wxString::Format(
  105. _( "Unable to open filename '%s' for reading" ), aFileName.GetData() );
  106. THROW_IO_ERROR( msg );
  107. }
  108. setvbuf( fp, NULL, _IOFBF, BUFSIZ * 8 );
  109. source = aFileName;
  110. lineNum = aStartingLineNumber;
  111. }
  112. FILE_LINE_READER::FILE_LINE_READER( FILE* aFile, const wxString& aFileName,
  113. bool doOwn,
  114. unsigned aStartingLineNumber,
  115. unsigned aMaxLineLength ) :
  116. LINE_READER( aMaxLineLength ),
  117. iOwn( doOwn ),
  118. fp( aFile )
  119. {
  120. if( doOwn && ftell( aFile ) == 0L )
  121. {
  122. #ifndef __WXMAC__
  123. setvbuf( fp, NULL, _IOFBF, BUFSIZ * 8 );
  124. #endif
  125. }
  126. source = aFileName;
  127. lineNum = aStartingLineNumber;
  128. }
  129. FILE_LINE_READER::~FILE_LINE_READER()
  130. {
  131. if( iOwn && fp )
  132. fclose( fp );
  133. }
  134. char* FILE_LINE_READER::ReadLine() throw( IO_ERROR )
  135. {
  136. length = 0;
  137. for(;;)
  138. {
  139. if( length >= maxLineLength )
  140. THROW_IO_ERROR( _( "Maximum line length exceeded" ) );
  141. if( length >= capacity )
  142. expandCapacity( capacity * 2 );
  143. // faster, POSIX compatible fgetc(), no locking.
  144. int cc = getc_unlocked( fp );
  145. if( cc == EOF )
  146. break;
  147. line[ length++ ] = (char) cc;
  148. if( cc == '\n' )
  149. break;
  150. }
  151. line[ length ] = 0;
  152. // lineNum is incremented even if there was no line read, because this
  153. // leads to better error reporting when we hit an end of file.
  154. ++lineNum;
  155. return length ? line : NULL;
  156. }
  157. STRING_LINE_READER::STRING_LINE_READER( const std::string& aString, const wxString& aSource ) :
  158. LINE_READER( LINE_READER_LINE_DEFAULT_MAX ),
  159. lines( aString ),
  160. ndx( 0 )
  161. {
  162. // Clipboard text should be nice and _use multiple lines_ so that
  163. // we can report _line number_ oriented error messages when parsing.
  164. source = aSource;
  165. }
  166. STRING_LINE_READER::STRING_LINE_READER( const STRING_LINE_READER& aStartingPoint ) :
  167. LINE_READER( LINE_READER_LINE_DEFAULT_MAX ),
  168. lines( aStartingPoint.lines ),
  169. ndx( aStartingPoint.ndx )
  170. {
  171. // since we are keeping the same "source" name, for error reporting purposes
  172. // we need to have the same notion of line number and offset.
  173. source = aStartingPoint.source;
  174. lineNum = aStartingPoint.lineNum;
  175. }
  176. char* STRING_LINE_READER::ReadLine() throw( IO_ERROR )
  177. {
  178. size_t nlOffset = lines.find( '\n', ndx );
  179. if( nlOffset == std::string::npos )
  180. length = lines.length() - ndx;
  181. else
  182. length = nlOffset - ndx + 1; // include the newline, so +1
  183. if( length )
  184. {
  185. if( length >= maxLineLength )
  186. THROW_IO_ERROR( _("Line length exceeded") );
  187. if( length+1 > capacity ) // +1 for terminating nul
  188. expandCapacity( length+1 );
  189. wxASSERT( ndx + length <= lines.length() );
  190. memcpy( line, &lines[ndx], length );
  191. ndx += length;
  192. }
  193. ++lineNum; // this gets incremented even if no bytes were read
  194. line[length] = 0;
  195. return length ? line : NULL;
  196. }
  197. INPUTSTREAM_LINE_READER::INPUTSTREAM_LINE_READER( wxInputStream* aStream ) :
  198. LINE_READER( LINE_READER_LINE_DEFAULT_MAX ),
  199. m_stream( aStream )
  200. {
  201. }
  202. char* INPUTSTREAM_LINE_READER::ReadLine() throw( IO_ERROR )
  203. {
  204. length = 0;
  205. for(;;)
  206. {
  207. if( length >= maxLineLength )
  208. THROW_IO_ERROR( _( "Maximum line length exceeded" ) );
  209. if( length + 1 > capacity )
  210. expandCapacity( capacity * 2 );
  211. // this read may fail, docs say to test LastRead() before trusting cc.
  212. char cc = m_stream->GetC();
  213. if( !m_stream->LastRead() )
  214. break;
  215. line[ length++ ] = cc;
  216. if( cc == '\n' )
  217. break;
  218. }
  219. line[ length ] = 0;
  220. // lineNum is incremented even if there was no line read, because this
  221. // leads to better error reporting when we hit an end of file.
  222. ++lineNum;
  223. return length ? line : NULL;
  224. }
  225. //-----<OUTPUTFORMATTER>----------------------------------------------------
  226. // factor out a common GetQuoteChar
  227. const char* OUTPUTFORMATTER::GetQuoteChar( const char* wrapee, const char* quote_char )
  228. {
  229. // Include '#' so a symbol is not confused with a comment. We intend
  230. // to wrap any symbol starting with a '#'.
  231. // Our LEXER class handles comments, and comments appear to be an extension
  232. // to the SPECCTRA DSN specification.
  233. if( *wrapee == '#' )
  234. return quote_char;
  235. if( strlen( wrapee ) == 0 )
  236. return quote_char;
  237. bool isFirst = true;
  238. for( ; *wrapee; ++wrapee, isFirst = false )
  239. {
  240. static const char quoteThese[] = "\t ()"
  241. "%" // per Alfons of freerouting.net, he does not like this unquoted as of 1-Feb-2008
  242. "{}" // guessing that these are problems too
  243. ;
  244. // if the string to be wrapped (wrapee) has a delimiter in it,
  245. // return the quote_char so caller wraps the wrapee.
  246. if( strchr( quoteThese, *wrapee ) )
  247. return quote_char;
  248. if( !isFirst && '-' == *wrapee )
  249. return quote_char;
  250. }
  251. return ""; // caller does not need to wrap, can use an unwrapped string.
  252. }
  253. const char* OUTPUTFORMATTER::GetQuoteChar( const char* wrapee )
  254. {
  255. return GetQuoteChar( wrapee, quoteChar );
  256. }
  257. int OUTPUTFORMATTER::vprint( const char* fmt, va_list ap ) throw( IO_ERROR )
  258. {
  259. int ret = vsnprintf( &buffer[0], buffer.size(), fmt, ap );
  260. if( ret >= (int) buffer.size() )
  261. {
  262. buffer.resize( ret + 2000 );
  263. ret = vsnprintf( &buffer[0], buffer.size(), fmt, ap );
  264. }
  265. if( ret > 0 )
  266. write( &buffer[0], ret );
  267. return ret;
  268. }
  269. int OUTPUTFORMATTER::sprint( const char* fmt, ... ) throw( IO_ERROR )
  270. {
  271. va_list args;
  272. va_start( args, fmt );
  273. int ret = vprint( fmt, args);
  274. va_end( args );
  275. return ret;
  276. }
  277. int OUTPUTFORMATTER::Print( int nestLevel, const char* fmt, ... ) throw( IO_ERROR )
  278. {
  279. #define NESTWIDTH 2 ///< how many spaces per nestLevel
  280. va_list args;
  281. va_start( args, fmt );
  282. int result = 0;
  283. int total = 0;
  284. for( int i=0; i<nestLevel; ++i )
  285. {
  286. // no error checking needed, an exception indicates an error.
  287. result = sprint( "%*c", NESTWIDTH, ' ' );
  288. total += result;
  289. }
  290. // no error checking needed, an exception indicates an error.
  291. result = vprint( fmt, args );
  292. va_end( args );
  293. total += result;
  294. return total;
  295. }
  296. std::string OUTPUTFORMATTER::Quotes( const std::string& aWrapee ) throw( IO_ERROR )
  297. {
  298. static const char quoteThese[] = "\t ()\n\r";
  299. if( !aWrapee.size() || // quote null string as ""
  300. aWrapee[0]=='#' || // quote a potential s-expression comment, so it is not a comment
  301. aWrapee[0]=='"' || // NextTok() will travel through DSN_STRING path anyway, then must apply escapes
  302. aWrapee.find_first_of( quoteThese ) != std::string::npos )
  303. {
  304. std::string ret;
  305. ret.reserve( aWrapee.size()*2 + 2 );
  306. ret += '"';
  307. for( std::string::const_iterator it = aWrapee.begin(); it!=aWrapee.end(); ++it )
  308. {
  309. switch( *it )
  310. {
  311. case '\n':
  312. ret += '\\';
  313. ret += 'n';
  314. break;
  315. case '\r':
  316. ret += '\\';
  317. ret += 'r';
  318. break;
  319. case '\\':
  320. ret += '\\';
  321. ret += '\\';
  322. break;
  323. case '"':
  324. ret += '\\';
  325. ret += '"';
  326. break;
  327. default:
  328. ret += *it;
  329. }
  330. }
  331. ret += '"';
  332. return ret;
  333. }
  334. return aWrapee;
  335. }
  336. std::string OUTPUTFORMATTER::Quotew( const wxString& aWrapee ) throw( IO_ERROR )
  337. {
  338. // wxStrings are always encoded as UTF-8 as we convert to a byte sequence.
  339. // The non-virutal function calls the virtual workhorse function, and if
  340. // a different quoting or escaping strategy is desired from the standard,
  341. // a derived class can overload Quotes() above, but
  342. // should never be a reason to overload this Quotew() here.
  343. return Quotes( (const char*) aWrapee.utf8_str() );
  344. }
  345. //-----<STRING_FORMATTER>----------------------------------------------------
  346. void STRING_FORMATTER::write( const char* aOutBuf, int aCount ) throw( IO_ERROR )
  347. {
  348. mystring.append( aOutBuf, aCount );
  349. }
  350. void STRING_FORMATTER::StripUseless()
  351. {
  352. std::string copy = mystring;
  353. mystring.clear();
  354. for( std::string::iterator i=copy.begin(); i!=copy.end(); ++i )
  355. {
  356. if( !isspace( *i ) && *i!=')' && *i!='(' && *i!='"' )
  357. {
  358. mystring += *i;
  359. }
  360. }
  361. }
  362. //-----<FILE_OUTPUTFORMATTER>----------------------------------------
  363. FILE_OUTPUTFORMATTER::FILE_OUTPUTFORMATTER( const wxString& aFileName,
  364. const wxChar* aMode, char aQuoteChar ) throw( IO_ERROR ) :
  365. OUTPUTFORMATTER( OUTPUTFMTBUFZ, aQuoteChar ),
  366. m_filename( aFileName )
  367. {
  368. m_fp = wxFopen( aFileName, aMode );
  369. if( !m_fp )
  370. {
  371. wxString msg = wxString::Format(
  372. _( "cannot open or save file '%s'" ),
  373. m_filename.GetData() );
  374. THROW_IO_ERROR( msg );
  375. }
  376. }
  377. FILE_OUTPUTFORMATTER::~FILE_OUTPUTFORMATTER()
  378. {
  379. if( m_fp )
  380. fclose( m_fp );
  381. }
  382. void FILE_OUTPUTFORMATTER::write( const char* aOutBuf, int aCount ) throw( IO_ERROR )
  383. {
  384. if( 1 != fwrite( aOutBuf, aCount, 1, m_fp ) )
  385. {
  386. wxString msg = wxString::Format(
  387. _( "error writing to file '%s'" ),
  388. m_filename.GetData() );
  389. THROW_IO_ERROR( msg );
  390. }
  391. }
  392. //-----<STREAM_OUTPUTFORMATTER>--------------------------------------
  393. void STREAM_OUTPUTFORMATTER::write( const char* aOutBuf, int aCount ) throw( IO_ERROR )
  394. {
  395. int lastWrite;
  396. // This might delay awhile if you were writing to say a socket, but for
  397. // a file it should only go through the loop once.
  398. for( int total = 0; total<aCount; total += lastWrite )
  399. {
  400. lastWrite = os.Write( aOutBuf, aCount ).LastWrite();
  401. if( !os.IsOk() )
  402. {
  403. THROW_IO_ERROR( _( "OUTPUTSTREAM_OUTPUTFORMATTER write error" ) );
  404. }
  405. }
  406. }