Merge pull request #423 from KLayout/issue-415

Fixed #415 (text mode for XML reader and stream writer to avoid macro format confusion)
This commit is contained in:
Matthias Köfferlein 2019-11-22 23:12:31 +01:00 committed by GitHub
commit c5dc7c9fa8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 121 additions and 9 deletions

View File

@ -196,7 +196,7 @@ void Macro::save_to (const std::string &path)
tl::log << "Saving macro to " << path;
}
tl::OutputStream os (path, tl::OutputStream::OM_Plain);
tl::OutputStream os (path, tl::OutputStream::OM_Plain, true /*as text*/);
if (m_format == MacroFormat) {
xml_struct.write (os, *this);

View File

@ -84,6 +84,10 @@ TL_PUBLIC void file_utils_force_windows () { s_mode = OS_Windows; }
TL_PUBLIC void file_utils_force_linux () { s_mode = OS_Linux; }
TL_PUBLIC void file_utils_force_reset () { s_mode = OS_Auto; }
const char *line_separator ()
{
return is_win () ? "\r\n" : "\n";
}
static bool is_drive (const std::string &part)
{

View File

@ -189,6 +189,11 @@ std::string TL_PUBLIC get_inst_path ();
*/
std::string TL_PUBLIC get_module_path (void *addr);
/**
* @brief Gets the line separator (CRLF on windows, LF on linux)
*/
TL_PUBLIC const char *line_separator ();
}
#endif

View File

@ -658,8 +658,8 @@ InputZLibFile::filename () const
// ---------------------------------------------------------------
// OutputStream implementation
OutputStream::OutputStream (OutputStreamBase &delegate)
: m_pos (0), mp_delegate (&delegate), m_owns_delegate (false)
OutputStream::OutputStream (OutputStreamBase &delegate, bool as_text)
: m_pos (0), mp_delegate (&delegate), m_owns_delegate (false), m_as_text (as_text)
{
m_buffer_capacity = 16384;
m_buffer_pos = 0;
@ -690,8 +690,8 @@ OutputStreamBase *create_file_stream (const std::string &path, OutputStream::Out
}
}
OutputStream::OutputStream (const std::string &abstract_path, OutputStreamMode om)
: m_pos (0), mp_delegate (0), m_owns_delegate (false)
OutputStream::OutputStream (const std::string &abstract_path, OutputStreamMode om, bool as_text)
: m_pos (0), mp_delegate (0), m_owns_delegate (false), m_as_text (as_text)
{
// Determine output mode
om = output_mode_from_filename (abstract_path, om);
@ -761,6 +761,36 @@ OutputStream::flush ()
void
OutputStream::put (const char *b, size_t n)
{
if (m_as_text) {
// skip CR, but replace LF by CRLF -> this will normalize the line terminators to CRLF
while (n > 0) {
if (*b == '\r') {
++b;
--n;
} else if (*b == '\n') {
const char *ls = line_separator ();
while (*ls) {
put_raw (ls++, 1);
}
++b;
--n;
} else {
const char *b0 = b;
while (n > 0 && *b != '\r' && *b != '\n') {
++b;
--n;
}
put_raw (b0, b - b0);
}
}
} else {
put_raw (b, n);
}
}
void
OutputStream::put_raw (const char *b, size_t n)
{
m_pos += n;

View File

@ -1016,14 +1016,15 @@ public:
*
* This constructor takes a delegate object.
*/
OutputStream (OutputStreamBase &delegate);
OutputStream (OutputStreamBase &delegate, bool as_text = false);
/**
* @brief Open an output stream with the given path and stream mode
*
* This will automatically create a delegate object and delete it later.
* If "as_text" is true, the output will be formatted with the system's line separator.
*/
OutputStream (const std::string &abstract_path, OutputStreamMode om = OM_Auto);
OutputStream (const std::string &abstract_path, OutputStreamMode om = OM_Auto, bool as_text = false);
/**
* @brief Destructor
@ -1141,9 +1142,12 @@ private:
size_t m_pos;
OutputStreamBase *mp_delegate;
bool m_owns_delegate;
bool m_as_text;
char *mp_buffer;
size_t m_buffer_capacity, m_buffer_pos;
void put_raw (const char *b, size_t n);
// No copying currently
OutputStream (const OutputStream &);
OutputStream &operator= (const OutputStream &);

View File

@ -639,8 +639,12 @@ public:
}
qint64 n0 = n;
for (const char *rd = 0; n > 0 && (rd = mp_stream->get (1)) != 0; --n) {
*data++ = *rd;
for (const char *rd = 0; n > 0 && (rd = mp_stream->get (1)) != 0; ) {
// NOTE: we skip CR to compensate for Windows CRLF line terminators (issue #419).
if (*rd != '\r') {
*data++ = *rd;
--n;
}
}
if (n0 == n) {

View File

@ -22,6 +22,15 @@
#include "tlStream.h"
#include "tlUnitTest.h"
#include "tlFileUtils.h"
// Secret mode switchers for testing
namespace tl
{
TL_PUBLIC void file_utils_force_windows ();
TL_PUBLIC void file_utils_force_linux ();
TL_PUBLIC void file_utils_force_reset ();
}
TEST(InputPipe1)
{
@ -42,3 +51,59 @@ TEST(InputPipe2)
tl::info << "Process exit code: " << ret;
EXPECT_NE (ret, 0);
}
TEST(TextOutputStream)
{
std::string fn = tmp_file ("test.txt");
{
tl::OutputStream os (fn, tl::OutputStream::OM_Auto, false);
os << "Hello, world!\nWith another line\n\r\r\nseparated by a LFCR and CRLF.";
}
{
tl::InputStream is (fn);
std::string s = is.read_all ();
EXPECT_EQ (s, "Hello, world!\nWith another line\n\r\r\nseparated by a LFCR and CRLF.");
}
try {
tl::file_utils_force_linux ();
{
tl::OutputStream os (fn, tl::OutputStream::OM_Auto, true);
os << "Hello, world!\nWith another line\n\r\r\nseparated by a LFCR and CRLF.";
}
tl::InputStream is (fn);
std::string s = is.read_all ();
EXPECT_EQ (s, "Hello, world!\nWith another line\n\nseparated by a LFCR and CRLF.");
tl::file_utils_force_reset ();
} catch (...) {
tl::file_utils_force_reset ();
throw;
}
try {
tl::file_utils_force_windows ();
{
tl::OutputStream os (fn, tl::OutputStream::OM_Auto, true);
os << "Hello, world!\nWith another line\n\r\r\nseparated by a LFCR and CRLF.";
}
tl::InputStream is (fn);
std::string s = is.read_all ();
EXPECT_EQ (s, "Hello, world!\r\nWith another line\r\n\r\nseparated by a LFCR and CRLF.");
tl::file_utils_force_reset ();
} catch (...) {
tl::file_utils_force_reset ();
throw;
}
}