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.
447 lines
13 KiB
447 lines
13 KiB
/* /////////////////////////////////////////////////////////////////////////
|
|
* File: stlsoft/filesystem/read_line.hpp
|
|
*
|
|
* Purpose: Definition of stlsoft::read_line() function template.
|
|
*
|
|
* Created: 2nd January 2007
|
|
* Updated: 28th December 2010
|
|
*
|
|
* Home: http://stlsoft.org/
|
|
*
|
|
* Copyright (c) 2007-2010, Matthew Wilson and Synesis Software
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of
|
|
* any contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* ////////////////////////////////////////////////////////////////////// */
|
|
|
|
|
|
/** \file stlsoft/filesystem/read_line.hpp
|
|
*
|
|
* \brief [C++ only] Definition of the stlsoft::read_line() function
|
|
* template
|
|
* (\ref group__library__filesystem "File System" Library).
|
|
*/
|
|
|
|
#ifndef STLSOFT_INCL_STLSOFT_FILESYSTEM_HPP_READ_LINE
|
|
#define STLSOFT_INCL_STLSOFT_FILESYSTEM_HPP_READ_LINE
|
|
|
|
#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
|
|
# define STLSOFT_VER_STLSOFT_FILESYSTEM_HPP_READ_LINE_MAJOR 2
|
|
# define STLSOFT_VER_STLSOFT_FILESYSTEM_HPP_READ_LINE_MINOR 1
|
|
# define STLSOFT_VER_STLSOFT_FILESYSTEM_HPP_READ_LINE_REVISION 2
|
|
# define STLSOFT_VER_STLSOFT_FILESYSTEM_HPP_READ_LINE_EDIT 15
|
|
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
|
|
|
|
/* /////////////////////////////////////////////////////////////////////////
|
|
* Includes
|
|
*/
|
|
|
|
#ifndef STLSOFT_INCL_STLSOFT_H_STLSOFT
|
|
# include <stlsoft/stlsoft.h>
|
|
#endif /* !STLSOFT_INCL_STLSOFT_H_STLSOFT */
|
|
|
|
#ifndef STLSOFT_INCL_STDEXCEPT
|
|
# define STLSOFT_INCL_STDEXCEPT
|
|
# include <stdexcept>
|
|
#endif /* !STLSOFT_INCL_STDEXCEPT */
|
|
|
|
#ifndef STLSOFT_INCL_H_STDIO
|
|
# define STLSOFT_INCL_H_STDIO
|
|
# include <stdio.h>
|
|
#endif /* !STLSOFT_INCL_H_STDIO */
|
|
|
|
/* /////////////////////////////////////////////////////////////////////////
|
|
* Namespace
|
|
*/
|
|
|
|
#ifndef _STLSOFT_NO_NAMESPACE
|
|
namespace stlsoft
|
|
{
|
|
#endif /* _STLSOFT_NO_NAMESPACE */
|
|
|
|
/* /////////////////////////////////////////////////////////////////////////
|
|
* Typedefs
|
|
*/
|
|
|
|
/** Flags that moderate the behaviour of the stlsoft::read_line() function
|
|
*
|
|
* Each flag causes the stlsoft::read_line() function to recognise exactly
|
|
* one character sequence as a new line. To recognise combinations, they
|
|
* must be combined. For example, to recognise both '\n' and "\r\n" as
|
|
* end-of-line sequences, both recogniseLfAsEOL and recogniseCrLfAsEOL
|
|
* must be specified.
|
|
*/
|
|
struct read_line_flags
|
|
{
|
|
enum flags_t
|
|
{
|
|
/** Recognises a sole carriage return (<code>'\\r'</code>) character as the end-of-line marker */
|
|
recogniseCrAsEOL = 0x0001,
|
|
/** Recognises a sole line feed (<code>'\\r'</code>) character as the end-of-line marker */
|
|
recogniseLfAsEOL = 0x0002,
|
|
/** Recognises the carriage return + line feed sequence (<code>"\r\n"</code>) as the end-of-line marker */
|
|
recogniseCrLfAsEOL = 0x0004,
|
|
/** Recognises a sole carriage return (<code>'\\r'</code>) character, or a sole line feed (<code>'\\r'</code>) character or the carriage return + line feed sequence (<code>"\r\n"</code>) as the end-of-line marker */
|
|
recogniseAll = (recogniseCrAsEOL | recogniseLfAsEOL | recogniseCrLfAsEOL),
|
|
|
|
/** Flags mask */
|
|
mask = 0x0007
|
|
};
|
|
};
|
|
|
|
inline read_line_flags::flags_t operator |(read_line_flags::flags_t const& lhs, read_line_flags::flags_t const& rhs)
|
|
{
|
|
return static_cast<read_line_flags::flags_t>(int(lhs) | int(rhs));
|
|
}
|
|
|
|
/* /////////////////////////////////////////////////////////////////////////
|
|
* Implementation
|
|
*/
|
|
|
|
#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
|
|
|
|
namespace readers
|
|
{
|
|
class read_from_FILE
|
|
{
|
|
public: // Member Types
|
|
typedef read_from_FILE class_type;
|
|
|
|
public:
|
|
ss_explicit_k read_from_FILE(FILE* stm)
|
|
: m_stm(stm)
|
|
{}
|
|
read_from_FILE(class_type const& rhs)
|
|
: m_stm(rhs.m_stm)
|
|
{}
|
|
private:
|
|
class_type& operator =(class_type const&);
|
|
|
|
public: // Operations
|
|
int read_char()
|
|
{
|
|
return ::fgetc(m_stm);
|
|
}
|
|
|
|
int peek_next_char()
|
|
{
|
|
int ch = ::fgetc(m_stm);
|
|
|
|
if(EOF != ch)
|
|
{
|
|
::ungetc(ch, m_stm);
|
|
}
|
|
|
|
return ch;
|
|
}
|
|
|
|
private:
|
|
FILE* const m_stm;
|
|
};
|
|
|
|
template <ss_typename_param_k I>
|
|
class read_from_iterator_range
|
|
{
|
|
private: // Member Types
|
|
typedef I iterator_type_;
|
|
public:
|
|
typedef read_from_iterator_range<iterator_type_> class_type;
|
|
|
|
public:
|
|
ss_explicit_k read_from_iterator_range(iterator_type_ from, iterator_type_ to)
|
|
: m_from(from)
|
|
, m_to(to)
|
|
{}
|
|
read_from_iterator_range(class_type const& rhs)
|
|
: m_from(rhs.m_from)
|
|
, m_to(rhs.m_to)
|
|
{}
|
|
private:
|
|
class_type& operator =(class_type const&);
|
|
|
|
public: // Operations
|
|
int read_char()
|
|
{
|
|
if(m_from == m_to)
|
|
{
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
return *m_from++;
|
|
}
|
|
}
|
|
|
|
int peek_next_char()
|
|
{
|
|
if(m_from == m_to)
|
|
{
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
return *m_from;
|
|
}
|
|
}
|
|
|
|
private:
|
|
iterator_type_ m_from;
|
|
iterator_type_ const m_to;
|
|
};
|
|
|
|
class read_from_char_buffer
|
|
{
|
|
public: // Member Types
|
|
typedef read_from_char_buffer class_type;
|
|
|
|
public:
|
|
ss_explicit_k read_from_char_buffer(char const* buffer, int size)
|
|
: m_buffer(buffer)
|
|
, m_size(calc_length_(buffer, size))
|
|
, m_current(0)
|
|
{}
|
|
read_from_char_buffer(class_type const& rhs)
|
|
: m_buffer(rhs.m_buffer)
|
|
, m_size(rhs.m_size)
|
|
, m_current(rhs.m_current)
|
|
{}
|
|
private:
|
|
static ss_size_t calc_length_(char const* buffer, int size)
|
|
{
|
|
if(size < 0)
|
|
{
|
|
size_t len = 0;
|
|
|
|
if(NULL != buffer)
|
|
{
|
|
for(; '\0' != *buffer; ++len, ++buffer)
|
|
{}
|
|
}
|
|
|
|
return len;
|
|
}
|
|
else
|
|
{
|
|
return static_cast<ss_size_t>(size);
|
|
}
|
|
}
|
|
class_type& operator =(class_type const&);
|
|
|
|
public: // Operations
|
|
int read_char()
|
|
{
|
|
if(m_current == m_size)
|
|
{
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
return m_buffer[m_current++];
|
|
}
|
|
}
|
|
|
|
int peek_next_char()
|
|
{
|
|
if(m_current == m_size)
|
|
{
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
return m_buffer[m_current];
|
|
}
|
|
}
|
|
|
|
private:
|
|
char const* const m_buffer;
|
|
const ss_size_t m_size;
|
|
ss_size_t m_current;
|
|
};
|
|
|
|
} /* namespace readers */
|
|
|
|
namespace read_line_impl
|
|
{
|
|
template< ss_typename_param_k S
|
|
, ss_typename_param_k P
|
|
>
|
|
static ss_bool_t read_line(P& policy, S& line, read_line_flags::flags_t flags)
|
|
{
|
|
ss_size_t numCr = 0;
|
|
|
|
if(0 == (read_line_flags::mask & flags))
|
|
{
|
|
flags = read_line_flags::recogniseAll;
|
|
}
|
|
|
|
S().swap(line);
|
|
|
|
{ for (int ch; EOF != (ch = policy.read_char()); )
|
|
{
|
|
switch(ch)
|
|
{
|
|
case '\r':
|
|
// Options:
|
|
//
|
|
// - recognising CR - handle
|
|
// - recognising CRLF - handle
|
|
|
|
// If we're recognising either
|
|
|
|
if(0 != ((read_line_flags::recogniseCrAsEOL | read_line_flags::recogniseCrLfAsEOL) & flags))
|
|
{
|
|
if(read_line_flags::recogniseCrLfAsEOL & flags)
|
|
{
|
|
// We need to look ahead in order to work out whether
|
|
// this might be the start of a \r\n pair
|
|
int ch2 = policy.peek_next_char();
|
|
|
|
if('\n' == ch2)
|
|
{
|
|
policy.read_char();
|
|
|
|
line.append(numCr, '\r');
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if(read_line_flags::recogniseCrAsEOL & flags)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
++numCr;
|
|
continue;
|
|
case '\n':
|
|
// Options:
|
|
//
|
|
// - recognising CR - ignore
|
|
// - recognising LF - handle
|
|
// - recognising CRLF - handle
|
|
//
|
|
// We check CRLF first
|
|
|
|
if( numCr > 0 &&
|
|
(read_line_flags::recogniseCrLfAsEOL & flags))
|
|
{
|
|
// Here we will digest any excess CRs as literal
|
|
// characters in the line, and then return the
|
|
// line
|
|
|
|
line.append(numCr - 1, '\r');
|
|
|
|
return true;
|
|
}
|
|
else if(read_line_flags::recogniseLfAsEOL & flags)
|
|
{
|
|
line.append(numCr, '\r');
|
|
|
|
return true;
|
|
}
|
|
break;
|
|
default:
|
|
if(numCr > 0)
|
|
{
|
|
line.append(numCr, '\r');
|
|
numCr = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
line.append(1, static_cast<char>(ch));
|
|
}}
|
|
|
|
return !line.empty();
|
|
}
|
|
|
|
} /* namespace read_line_impl */
|
|
|
|
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
|
|
|
|
/* /////////////////////////////////////////////////////////////////////////
|
|
* Functions
|
|
*/
|
|
|
|
/** Reads a line from a C stream
|
|
*
|
|
* \param stm The stream to read from
|
|
* \param line The line to read into. Must be an instance of a type that
|
|
* is structurally conformant to std::string in the following ways: it
|
|
* has a default constructor, it has a swap() method, and it has a block
|
|
* append method (taking repeat count and character to add)
|
|
* \param flags The flags that control what line-termination sequences are
|
|
* recognised
|
|
*
|
|
* \return An indication of whether there is more to parse
|
|
* \retval true The parsing is not complete
|
|
* \retval false The parsing is complete
|
|
*
|
|
* \remarks The function can recognise any or all of the following as
|
|
* line-termination sequences: carriage-return ('\r'), line-feed ('\n'), or
|
|
* carriage-return+line-feed ("\r\n").
|
|
*/
|
|
template <ss_typename_param_k S>
|
|
ss_bool_t read_line(
|
|
FILE* stm
|
|
, S& line
|
|
, read_line_flags::flags_t flags = read_line_flags::recogniseAll
|
|
)
|
|
{
|
|
readers::read_from_FILE policy(stm);
|
|
|
|
return read_line_impl::read_line(policy, line, flags);
|
|
}
|
|
|
|
/** Reads a line from a pair of iterators
|
|
*/
|
|
template< ss_typename_param_k I
|
|
, ss_typename_param_k S
|
|
>
|
|
ss_bool_t read_line(
|
|
I from
|
|
, I to
|
|
, S& line
|
|
, read_line_flags::flags_t flags = read_line_flags::recogniseAll
|
|
)
|
|
{
|
|
readers::read_from_iterator_range<I> policy(from, to);
|
|
|
|
return read_line_impl::read_line(policy, line, flags);
|
|
}
|
|
|
|
/* ////////////////////////////////////////////////////////////////////// */
|
|
|
|
#ifndef _STLSOFT_NO_NAMESPACE
|
|
} /* namespace stlsoft */
|
|
#endif /* _STLSOFT_NO_NAMESPACE */
|
|
|
|
/* ////////////////////////////////////////////////////////////////////// */
|
|
|
|
#endif /* !STLSOFT_INCL_STLSOFT_FILESYSTEM_HPP_READ_LINE */
|
|
|
|
/* ///////////////////////////// end of file //////////////////////////// */
|