KiCad PCB EDA Suite
Loading...
Searching...
No Matches
richio.cpp
Go to the documentation of this file.
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 <[email protected]>
5 * Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.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
25
26#include <cstdarg>
27#include <config.h> // HAVE_FGETC_NOLOCK
28
29#include <kiplatform/io.h>
30#include <core/ignore.h>
31#include <richio.h>
32#include <errno.h>
34
35#include <wx/file.h>
36#include <wx/translation.h>
37
38
39// Fall back to getc() when getc_unlocked() is not available on the target platform.
40#if !defined( HAVE_FGETC_NOLOCK )
41#ifdef _MSC_VER
42//getc is not a macro on windows and adds a tiny overhead for the indirection to eventually calling fgetc
43#define getc_unlocked _fgetc_nolock
44#else
45#define getc_unlocked getc
46#endif
47#endif
48
49
50static int vprint( std::string* result, const char* format, va_list ap )
51{
52 va_list tmp;
53 va_copy( tmp, ap );
54 size_t len = vsnprintf( nullptr, 0, format, tmp );
55 va_end( tmp );
56
57 // Resize the output to hold the required data
58 size_t size = result->size();
59 result->resize( size + len );
60
61 // Now do the actual printing
62 len = vsnprintf( result->data() + size, len + 1, format, ap );
63
64 return len;
65}
66
67
68int StrPrintf( std::string* result, const char* format, ... )
69{
70 va_list args;
71
72 va_start( args, format );
73 int ret = vprint( result, format, args );
74 va_end( args );
75
76 return ret;
77}
78
79
80std::string StrPrintf( const char* format, ... )
81{
82 std::string ret;
83 va_list args;
84
85 va_start( args, format );
86 ignore_unused( vprint( &ret, format, args ) );
87 va_end( args );
88
89 return ret;
90}
91
92
93wxString SafeReadFile( const wxString& aFilePath, const wxString& aReadType )
94{
95 auto From_UTF8_WINE =
96 []( const char* cstring )
97 {
98 wxString line = wxString::FromUTF8( cstring );
99
100 if( line.IsEmpty() ) // happens when cstring is not a valid UTF8 sequence
101 line = wxConvCurrent->cMB2WC( cstring ); // try to use locale conversion
102
103 // We have trouble here *probably* because Wine-hosted LTSpice writes out MSW
104 // encoded text on a macOS, where it isn't expected. In any case, wxWidgets'
105 // wxSafeConvert() appears to get around it.
106
107 if( line.IsEmpty() )
108 line = wxSafeConvertMB2WX( cstring );
109
110 // I'm not sure what the source of this style of line-endings is, but it can be
111 // found in some Fairchild Semiconductor SPICE files.
112 line.Replace( wxS( "\r\r\n" ), wxS( "\n" ) );
113
114 return line;
115 };
116
117 // Open file
118 FILE* fp = wxFopen( aFilePath, aReadType );
119
120 if( !fp )
121 THROW_IO_ERROR( wxString::Format( _( "Cannot open file '%s'." ), aFilePath ) );
122
123 FILE_LINE_READER fileReader( fp, aFilePath );
124
125 wxString contents;
126
127 while( fileReader.ReadLine() )
128 contents += From_UTF8_WINE( fileReader.Line() );
129
130 return contents;
131}
132
133
134//-----<LINE_READER>------------------------------------------------------
135
136LINE_READER::LINE_READER( unsigned aMaxLineLength ) :
137 m_length( 0 ), m_lineNum( 0 ), m_line( nullptr ),
138 m_capacity( 0 ), m_maxLineLength( aMaxLineLength )
139{
140 if( aMaxLineLength != 0 )
141 {
142 // start at the INITIAL size, expand as needed up to the MAX size in maxLineLength
144
145 // but never go above user's aMaxLineLength, and leave space for trailing nul
146 if( m_capacity > aMaxLineLength+1 )
147 m_capacity = aMaxLineLength+1;
148
149 // Be sure there is room for a null EOL char, so reserve at least capacity+1 bytes
150 // to ensure capacity line length and avoid corner cases
151 // Use capacity+5 to cover and corner case
152 m_line = new char[m_capacity+5];
153
154 m_line[0] = '\0';
155 }
156}
157
158
160{
161 delete[] m_line;
162}
163
164
165void LINE_READER::expandCapacity( unsigned aNewsize )
166{
167 // m_length can equal maxLineLength and nothing breaks, there's room for
168 // the terminating nul. cannot go over this.
169 if( aNewsize > m_maxLineLength+1 )
170 aNewsize = m_maxLineLength+1;
171
172 if( aNewsize > m_capacity )
173 {
174 m_capacity = aNewsize;
175
176 // resize the buffer, and copy the original data
177 // Be sure there is room for the null EOL char, so reserve capacity+1 bytes
178 // to ensure capacity line length. Use capacity+5 to cover and corner case
179 char* bigger = new char[m_capacity+5];
180
181 wxASSERT( m_capacity >= m_length+1 );
182
183 memcpy( bigger, m_line, m_length );
184 bigger[m_length] = 0;
185
186 delete[] m_line;
187 m_line = bigger;
188 }
189}
190
191
192FILE_LINE_READER::FILE_LINE_READER( const wxString& aFileName, unsigned aStartingLineNumber,
193 unsigned aMaxLineLength ):
194 LINE_READER( aMaxLineLength ), m_iOwn( true )
195{
196 m_fp = KIPLATFORM::IO::SeqFOpen( aFileName, wxT( "rt" ) );
197
198 if( !m_fp )
199 {
200 wxString msg = wxString::Format( _( "Unable to open %s for reading." ),
201 aFileName.GetData() );
202 THROW_IO_ERROR( msg );
203 }
204
205 m_source = aFileName;
206 m_lineNum = aStartingLineNumber;
207}
208
209
210FILE_LINE_READER::FILE_LINE_READER( FILE* aFile, const wxString& aFileName,
211 bool doOwn,
212 unsigned aStartingLineNumber,
213 unsigned aMaxLineLength ) :
214 LINE_READER( aMaxLineLength ), m_iOwn( doOwn ), m_fp( aFile )
215{
216 m_source = aFileName;
217 m_lineNum = aStartingLineNumber;
218}
219
220
222{
223 if( m_iOwn && m_fp )
224 fclose( m_fp );
225}
226
227
229{
230 fseek( m_fp, 0, SEEK_END );
231 long int fileLength = ftell( m_fp );
232 rewind( m_fp );
233
234 return fileLength;
235}
236
237
239{
240 return ftell( m_fp );
241}
242
243
245{
246 m_length = 0;
247
248 for( ;; )
249 {
251 THROW_IO_ERROR( _( "Maximum line length exceeded" ) );
252
253 if( m_length >= m_capacity )
255
256 // faster, POSIX compatible fgetc(), no locking.
257 int cc = getc_unlocked( m_fp );
258
259 if( cc == EOF )
260 break;
261
262 m_line[ m_length++ ] = (char) cc;
263
264 if( cc == '\n' )
265 break;
266 }
267
268 m_line[ m_length ] = 0;
269
270 // m_lineNum is incremented even if there was no line read, because this
271 // leads to better error reporting when we hit an end of file.
272 ++m_lineNum;
273
274 return m_length ? m_line : nullptr;
275}
276
277
278STRING_LINE_READER::STRING_LINE_READER( const std::string& aString, const wxString& aSource ):
280 m_lines( aString ), m_ndx( 0 )
281{
282 // Clipboard text should be nice and _use multiple lines_ so that
283 // we can report _line number_ oriented error messages when parsing.
284 m_source = aSource;
285}
286
287
290 m_lines( aStartingPoint.m_lines ),
291 m_ndx( aStartingPoint.m_ndx )
292{
293 // since we are keeping the same "source" name, for error reporting purposes
294 // we need to have the same notion of line number and offset.
295
296 m_source = aStartingPoint.m_source;
297 m_lineNum = aStartingPoint.m_lineNum;
298}
299
300
302{
303 size_t nlOffset = m_lines.find( '\n', m_ndx );
304 unsigned new_length;
305
306 if( nlOffset == std::string::npos )
307 new_length = m_lines.length() - m_ndx;
308 else
309 new_length = nlOffset - m_ndx + 1; // include the newline, so +1
310
311 if( new_length )
312 {
313 if( new_length >= m_maxLineLength )
314 THROW_IO_ERROR( _("Line length exceeded") );
315
316 if( new_length+1 > m_capacity ) // +1 for terminating nul
317 expandCapacity( new_length+1 );
318
319 wxASSERT( m_ndx + new_length <= m_lines.length() );
320
321 memcpy( m_line, &m_lines[m_ndx], new_length );
322 m_ndx += new_length;
323 }
324
325 m_length = new_length;
326 ++m_lineNum; // this gets incremented even if no bytes were read
327 m_line[m_length] = 0;
328
329 return m_length ? m_line : nullptr;
330}
331
332
334 const wxString& aSource ) :
336 m_stream( aStream )
337{
338 m_source = aSource;
339}
340
341
343{
344 m_length = 0;
345
346 for( ;; )
347 {
349 THROW_IO_ERROR( _( "Maximum line length exceeded" ) );
350
351 if( m_length + 1 > m_capacity )
353
354 // this read may fail, docs say to test LastRead() before trusting cc.
355 char cc = m_stream->GetC();
356
357 if( !m_stream->LastRead() )
358 break;
359
360 m_line[ m_length++ ] = cc;
361
362 if( cc == '\n' )
363 break;
364 }
365
366 m_line[ m_length ] = 0;
367
368 // m_lineNum is incremented even if there was no line read, because this
369 // leads to better error reporting when we hit an end of file.
370 ++m_lineNum;
371
372 return m_length ? m_line : nullptr;
373}
374
375
376//-----<OUTPUTFORMATTER>----------------------------------------------------
377
378// factor out a common GetQuoteChar
379
380const char* OUTPUTFORMATTER::GetQuoteChar( const char* wrapee, const char* quote_char )
381{
382 // Include '#' so a symbol is not confused with a comment. We intend
383 // to wrap any symbol starting with a '#'.
384 // Our LEXER class handles comments, and comments appear to be an extension
385 // to the SPECCTRA DSN specification.
386 if( *wrapee == '#' )
387 return quote_char;
388
389 if( strlen( wrapee ) == 0 )
390 return quote_char;
391
392 bool isFirst = true;
393
394 for( ; *wrapee; ++wrapee, isFirst = false )
395 {
396 static const char quoteThese[] = "\t ()"
397 "%" // per Alfons of freerouting.net, he does not like this unquoted as of 1-Feb-2008
398 "{}" // guessing that these are problems too
399 ;
400
401 // if the string to be wrapped (wrapee) has a delimiter in it,
402 // return the quote_char so caller wraps the wrapee.
403 if( strchr( quoteThese, *wrapee ) )
404 return quote_char;
405
406 if( !isFirst && '-' == *wrapee )
407 return quote_char;
408 }
409
410 return ""; // caller does not need to wrap, can use an unwrapped string.
411}
412
413
414const char* OUTPUTFORMATTER::GetQuoteChar( const char* wrapee ) const
415{
416 return GetQuoteChar( wrapee, quoteChar );
417}
418
419
420int OUTPUTFORMATTER::vprint( const char* fmt, va_list ap )
421{
422 // This function can call vsnprintf twice.
423 // But internally, vsnprintf retrieves arguments from the va_list identified by arg as if
424 // va_arg was used on it, and thus the state of the va_list is likely to be altered by the call.
425 // see: www.cplusplus.com/reference/cstdio/vsnprintf
426 // we make a copy of va_list ap for the second call, if happens
427 va_list tmp;
428 va_copy( tmp, ap );
429 int ret = vsnprintf( &m_buffer[0], m_buffer.size(), fmt, ap );
430
431 if( ret >= (int) m_buffer.size() )
432 {
433 m_buffer.resize( ret + 1000 );
434 ret = vsnprintf( &m_buffer[0], m_buffer.size(), fmt, tmp );
435 }
436
437 va_end( tmp ); // Release the temporary va_list, initialised from ap
438
439 if( ret > 0 )
440 write( &m_buffer[0], ret );
441
442 return ret;
443}
444
445
446int OUTPUTFORMATTER::sprint( const char* fmt, ... )
447{
448 va_list args;
449
450 va_start( args, fmt );
451 int ret = vprint( fmt, args);
452 va_end( args );
453
454 return ret;
455}
456
457
458int OUTPUTFORMATTER::Print( int nestLevel, const char* fmt, ... )
459{
460#define NESTWIDTH 2
461
462 va_list args;
463
464 va_start( args, fmt );
465
466 int result = 0;
467 int total = 0;
468
469 for( int i = 0; i < nestLevel; ++i )
470 {
471 // no error checking needed, an exception indicates an error.
472 result = sprint( "%*c", NESTWIDTH, ' ' );
473
474 total += result;
475 }
476
477 // no error checking needed, an exception indicates an error.
478 result = vprint( fmt, args );
479
480 va_end( args );
481
482 total += result;
483 return total;
484}
485
486
487std::string OUTPUTFORMATTER::Quotes( const std::string& aWrapee ) const
488{
489 std::string ret;
490
491 ret.reserve( aWrapee.size() * 2 + 2 );
492
493 ret += '"';
494
495 for( std::string::const_iterator it = aWrapee.begin(); it != aWrapee.end(); ++it )
496 {
497 switch( *it )
498 {
499 case '\n':
500 ret += '\\';
501 ret += 'n';
502 break;
503 case '\r':
504 ret += '\\';
505 ret += 'r';
506 break;
507 case '\\':
508 ret += '\\';
509 ret += '\\';
510 break;
511 case '"':
512 ret += '\\';
513 ret += '"';
514 break;
515 default:
516 ret += *it;
517 }
518 }
519
520 ret += '"';
521
522 return ret;
523}
524
525
526std::string OUTPUTFORMATTER::Quotew( const wxString& aWrapee ) const
527{
528 // wxStrings are always encoded as UTF-8 as we convert to a byte sequence.
529 // The non-virtual function calls the virtual workhorse function, and if
530 // a different quoting or escaping strategy is desired from the standard,
531 // a derived class can overload Quotes() above, but
532 // should never be a reason to overload this Quotew() here.
533 return Quotes( (const char*) aWrapee.utf8_str() );
534}
535
536
537//-----<STRING_FORMATTER>----------------------------------------------------
538
539void STRING_FORMATTER::write( const char* aOutBuf, int aCount )
540{
541 m_mystring.append( aOutBuf, aCount );
542}
543
545{
546 std::string copy = m_mystring;
547
548 m_mystring.clear();
549
550 for( std::string::iterator i = copy.begin(); i != copy.end(); ++i )
551 {
552 if( !isspace( *i ) && *i != ')' && *i != '(' && *i != '"' )
553 {
554 m_mystring += *i;
555 }
556 }
557}
558
559
560FILE_OUTPUTFORMATTER::FILE_OUTPUTFORMATTER( const wxString& aFileName, const wxChar* aMode,
561 char aQuoteChar ):
562 OUTPUTFORMATTER( OUTPUTFMTBUFZ, aQuoteChar ),
563 m_filename( aFileName )
564{
565 m_fp = wxFopen( aFileName, aMode );
566
567 if( !m_fp )
568 THROW_IO_ERROR( strerror( errno ) );
569}
570
571
573{
574 if( m_fp )
575 fclose( m_fp );
576}
577
578
579void FILE_OUTPUTFORMATTER::write( const char* aOutBuf, int aCount )
580{
581 if( fwrite( aOutBuf, (unsigned) aCount, 1, m_fp ) != 1 )
582 THROW_IO_ERROR( strerror( errno ) );
583}
584
585
587 const wxChar* aMode,
588 char aQuoteChar ) :
589 OUTPUTFORMATTER( OUTPUTFMTBUFZ, aQuoteChar )
590{
591 m_fp = wxFopen( aFileName, aMode );
592
593 if( !m_fp )
594 THROW_IO_ERROR( strerror( errno ) );
595}
596
597
599{
600 try
601 {
603 }
604 catch( ... )
605 {}
606}
607
608
610{
611 if( !m_fp )
612 return false;
613
615
616 if( fwrite( m_buf.c_str(), m_buf.length(), 1, m_fp ) != 1 )
617 THROW_IO_ERROR( strerror( errno ) );
618
619 fclose( m_fp );
620 m_fp = nullptr;
621
622 return true;
623}
624
625
626void PRETTIFIED_FILE_OUTPUTFORMATTER::write( const char* aOutBuf, int aCount )
627{
628 m_buf.append( aOutBuf, aCount );
629}
A LINE_READER that reads from an open file.
Definition: richio.h:185
~FILE_LINE_READER()
May or may not close the open file, depending on doOwn in constructor.
Definition: richio.cpp:221
FILE_LINE_READER(const wxString &aFileName, unsigned aStartingLineNumber=0, unsigned aMaxLineLength=LINE_READER_LINE_DEFAULT_MAX)
Take aFileName and the size of the desired line buffer and opens the file and assumes the obligation ...
Definition: richio.cpp:192
FILE * m_fp
I may own this file, but might not.
Definition: richio.h:245
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition: richio.cpp:244
bool m_iOwn
if I own the file, I'll promise to close it, else not.
Definition: richio.h:244
long int FileLength()
Definition: richio.cpp:228
long int CurPos()
Definition: richio.cpp:238
FILE * m_fp
takes ownership
Definition: richio.h:497
FILE_OUTPUTFORMATTER(const wxString &aFileName, const wxChar *aMode=wxT("wt"), char aQuoteChar='"' )
Definition: richio.cpp:560
void write(const char *aOutBuf, int aCount) override
Should be coded in the interface implementation (derived) classes.
Definition: richio.cpp:579
wxInputStream * m_stream
Definition: richio.h:300
INPUTSTREAM_LINE_READER(wxInputStream *aStream, const wxString &aSource)
Construct a LINE_READER from a wxInputStream object.
Definition: richio.cpp:333
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition: richio.cpp:342
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition: richio.h:93
LINE_READER(unsigned aMaxLineLength=LINE_READER_LINE_DEFAULT_MAX)
Build a line reader and fixes the length of the maximum supported line length to aMaxLineLength.
Definition: richio.cpp:136
unsigned m_maxLineLength
maximum allowed capacity using resizing.
Definition: richio.h:173
unsigned m_length
no. bytes in line before trailing nul.
Definition: richio.h:167
unsigned m_capacity
no. bytes allocated for line.
Definition: richio.h:171
void expandCapacity(unsigned aNewsize)
Will expand the capacity of line up to maxLineLength but not greater, so be careful about making assu...
Definition: richio.cpp:165
char * m_line
the read line of UTF8 text
Definition: richio.h:170
unsigned m_lineNum
Definition: richio.h:168
virtual ~LINE_READER()
Definition: richio.cpp:159
wxString m_source
origin of text lines, e.g. filename or "clipboard"
Definition: richio.h:175
char * Line() const
Return a pointer to the last line that was read in.
Definition: richio.h:129
An interface used to output 8 bit text in a convenient way.
Definition: richio.h:322
virtual void write(const char *aOutBuf, int aCount)=0
Should be coded in the interface implementation (derived) classes.
int sprint(const char *fmt,...)
Definition: richio.cpp:446
std::vector< char > m_buffer
Definition: richio.h:421
char quoteChar[2]
Definition: richio.h:422
int vprint(const char *fmt, va_list ap)
Definition: richio.cpp:420
std::string Quotew(const wxString &aWrapee) const
Definition: richio.cpp:526
int PRINTF_FUNC Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition: richio.cpp:458
static const char * GetQuoteChar(const char *wrapee, const char *quote_char)
Perform quote character need determination according to the Specctra DSN specification.
Definition: richio.cpp:380
virtual std::string Quotes(const std::string &aWrapee) const
Check aWrapee input string for a need to be quoted (e.g.
Definition: richio.cpp:487
void write(const char *aOutBuf, int aCount) override
Should be coded in the interface implementation (derived) classes.
Definition: richio.cpp:626
PRETTIFIED_FILE_OUTPUTFORMATTER(const wxString &aFileName, const wxChar *aMode=wxT("wt"), char aQuoteChar='"' )
Definition: richio.cpp:586
bool Finish() override
Performs prettification and writes the stored buffer to the file.
Definition: richio.cpp:609
void write(const char *aOutBuf, int aCount) override
Should be coded in the interface implementation (derived) classes.
Definition: richio.cpp:539
void StripUseless()
Removes whitespace, '(', and ')' from the string.
Definition: richio.cpp:544
std::string m_mystring
Definition: richio.h:468
Is a LINE_READER that reads from a multiline 8 bit wide std::string.
Definition: richio.h:253
std::string m_lines
Definition: richio.h:255
STRING_LINE_READER(const std::string &aString, const wxString &aSource)
Construct a string line reader.
Definition: richio.cpp:278
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition: richio.cpp:301
#define _(s)
void ignore_unused(const T &)
Definition: ignore.h:24
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:39
void Prettify(std::string &aSource, char aQuoteChar)
FILE * SeqFOpen(const wxString &aPath, const wxString &mode)
Opens the file like fopen but sets flags (if available) for sequential read hinting.
Definition: unix/io.cpp:30
static int vprint(std::string *result, const char *format, va_list ap)
Definition: richio.cpp:50
#define NESTWIDTH
#define getc_unlocked
Definition: richio.cpp:45
int StrPrintf(std::string *result, const char *format,...)
This is like sprintf() but the output is appended to a std::string instead of to a character array.
Definition: richio.cpp:68
wxString SafeReadFile(const wxString &aFilePath, const wxString &aReadType)
Nominally opens a file and reads it into a string.
Definition: richio.cpp:93
#define OUTPUTFMTBUFZ
default buffer size for any OUTPUT_FORMATTER
Definition: richio.h:304
#define LINE_READER_LINE_INITIAL_SIZE
Definition: richio.h:86
#define LINE_READER_LINE_DEFAULT_MAX
Definition: richio.h:85