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

  1. /* /////////////////////////////////////////////////////////////////////////
  2. * File: stlsoft/filesystem/read_line.hpp
  3. *
  4. * Purpose: Definition of stlsoft::read_line() function template.
  5. *
  6. * Created: 2nd January 2007
  7. * Updated: 28th December 2010
  8. *
  9. * Home: http://stlsoft.org/
  10. *
  11. * Copyright (c) 2007-2010, Matthew Wilson and Synesis Software
  12. * All rights reserved.
  13. *
  14. * Redistribution and use in source and binary forms, with or without
  15. * modification, are permitted provided that the following conditions are met:
  16. *
  17. * - Redistributions of source code must retain the above copyright notice, this
  18. * list of conditions and the following disclaimer.
  19. * - Redistributions in binary form must reproduce the above copyright notice,
  20. * this list of conditions and the following disclaimer in the documentation
  21. * and/or other materials provided with the distribution.
  22. * - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of
  23. * any contributors may be used to endorse or promote products derived from
  24. * this software without specific prior written permission.
  25. *
  26. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  27. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  30. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  31. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  32. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  33. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  34. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  35. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  36. * POSSIBILITY OF SUCH DAMAGE.
  37. *
  38. * ////////////////////////////////////////////////////////////////////// */
  39. /** \file stlsoft/filesystem/read_line.hpp
  40. *
  41. * \brief [C++ only] Definition of the stlsoft::read_line() function
  42. * template
  43. * (\ref group__library__filesystem "File System" Library).
  44. */
  45. #ifndef STLSOFT_INCL_STLSOFT_FILESYSTEM_HPP_READ_LINE
  46. #define STLSOFT_INCL_STLSOFT_FILESYSTEM_HPP_READ_LINE
  47. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  48. # define STLSOFT_VER_STLSOFT_FILESYSTEM_HPP_READ_LINE_MAJOR 2
  49. # define STLSOFT_VER_STLSOFT_FILESYSTEM_HPP_READ_LINE_MINOR 1
  50. # define STLSOFT_VER_STLSOFT_FILESYSTEM_HPP_READ_LINE_REVISION 2
  51. # define STLSOFT_VER_STLSOFT_FILESYSTEM_HPP_READ_LINE_EDIT 15
  52. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  53. /* /////////////////////////////////////////////////////////////////////////
  54. * Includes
  55. */
  56. #ifndef STLSOFT_INCL_STLSOFT_H_STLSOFT
  57. # include <stlsoft/stlsoft.h>
  58. #endif /* !STLSOFT_INCL_STLSOFT_H_STLSOFT */
  59. #ifndef STLSOFT_INCL_STDEXCEPT
  60. # define STLSOFT_INCL_STDEXCEPT
  61. # include <stdexcept>
  62. #endif /* !STLSOFT_INCL_STDEXCEPT */
  63. #ifndef STLSOFT_INCL_H_STDIO
  64. # define STLSOFT_INCL_H_STDIO
  65. # include <stdio.h>
  66. #endif /* !STLSOFT_INCL_H_STDIO */
  67. /* /////////////////////////////////////////////////////////////////////////
  68. * Namespace
  69. */
  70. #ifndef _STLSOFT_NO_NAMESPACE
  71. namespace stlsoft
  72. {
  73. #endif /* _STLSOFT_NO_NAMESPACE */
  74. /* /////////////////////////////////////////////////////////////////////////
  75. * Typedefs
  76. */
  77. /** Flags that moderate the behaviour of the stlsoft::read_line() function
  78. *
  79. * Each flag causes the stlsoft::read_line() function to recognise exactly
  80. * one character sequence as a new line. To recognise combinations, they
  81. * must be combined. For example, to recognise both '\n' and "\r\n" as
  82. * end-of-line sequences, both recogniseLfAsEOL and recogniseCrLfAsEOL
  83. * must be specified.
  84. */
  85. struct read_line_flags
  86. {
  87. enum flags_t
  88. {
  89. /** Recognises a sole carriage return (<code>'\\r'</code>) character as the end-of-line marker */
  90. recogniseCrAsEOL = 0x0001,
  91. /** Recognises a sole line feed (<code>'\\r'</code>) character as the end-of-line marker */
  92. recogniseLfAsEOL = 0x0002,
  93. /** Recognises the carriage return + line feed sequence (<code>"\r\n"</code>) as the end-of-line marker */
  94. recogniseCrLfAsEOL = 0x0004,
  95. /** 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 */
  96. recogniseAll = (recogniseCrAsEOL | recogniseLfAsEOL | recogniseCrLfAsEOL),
  97. /** Flags mask */
  98. mask = 0x0007
  99. };
  100. };
  101. inline read_line_flags::flags_t operator |(read_line_flags::flags_t const& lhs, read_line_flags::flags_t const& rhs)
  102. {
  103. return static_cast<read_line_flags::flags_t>(int(lhs) | int(rhs));
  104. }
  105. /* /////////////////////////////////////////////////////////////////////////
  106. * Implementation
  107. */
  108. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  109. namespace readers
  110. {
  111. class read_from_FILE
  112. {
  113. public: // Member Types
  114. typedef read_from_FILE class_type;
  115. public:
  116. ss_explicit_k read_from_FILE(FILE* stm)
  117. : m_stm(stm)
  118. {}
  119. read_from_FILE(class_type const& rhs)
  120. : m_stm(rhs.m_stm)
  121. {}
  122. private:
  123. class_type& operator =(class_type const&);
  124. public: // Operations
  125. int read_char()
  126. {
  127. return ::fgetc(m_stm);
  128. }
  129. int peek_next_char()
  130. {
  131. int ch = ::fgetc(m_stm);
  132. if(EOF != ch)
  133. {
  134. ::ungetc(ch, m_stm);
  135. }
  136. return ch;
  137. }
  138. private:
  139. FILE* const m_stm;
  140. };
  141. template <ss_typename_param_k I>
  142. class read_from_iterator_range
  143. {
  144. private: // Member Types
  145. typedef I iterator_type_;
  146. public:
  147. typedef read_from_iterator_range<iterator_type_> class_type;
  148. public:
  149. ss_explicit_k read_from_iterator_range(iterator_type_ from, iterator_type_ to)
  150. : m_from(from)
  151. , m_to(to)
  152. {}
  153. read_from_iterator_range(class_type const& rhs)
  154. : m_from(rhs.m_from)
  155. , m_to(rhs.m_to)
  156. {}
  157. private:
  158. class_type& operator =(class_type const&);
  159. public: // Operations
  160. int read_char()
  161. {
  162. if(m_from == m_to)
  163. {
  164. return -1;
  165. }
  166. else
  167. {
  168. return *m_from++;
  169. }
  170. }
  171. int peek_next_char()
  172. {
  173. if(m_from == m_to)
  174. {
  175. return -1;
  176. }
  177. else
  178. {
  179. return *m_from;
  180. }
  181. }
  182. private:
  183. iterator_type_ m_from;
  184. iterator_type_ const m_to;
  185. };
  186. class read_from_char_buffer
  187. {
  188. public: // Member Types
  189. typedef read_from_char_buffer class_type;
  190. public:
  191. ss_explicit_k read_from_char_buffer(char const* buffer, int size)
  192. : m_buffer(buffer)
  193. , m_size(calc_length_(buffer, size))
  194. , m_current(0)
  195. {}
  196. read_from_char_buffer(class_type const& rhs)
  197. : m_buffer(rhs.m_buffer)
  198. , m_size(rhs.m_size)
  199. , m_current(rhs.m_current)
  200. {}
  201. private:
  202. static ss_size_t calc_length_(char const* buffer, int size)
  203. {
  204. if(size < 0)
  205. {
  206. size_t len = 0;
  207. if(NULL != buffer)
  208. {
  209. for(; '\0' != *buffer; ++len, ++buffer)
  210. {}
  211. }
  212. return len;
  213. }
  214. else
  215. {
  216. return static_cast<ss_size_t>(size);
  217. }
  218. }
  219. class_type& operator =(class_type const&);
  220. public: // Operations
  221. int read_char()
  222. {
  223. if(m_current == m_size)
  224. {
  225. return -1;
  226. }
  227. else
  228. {
  229. return m_buffer[m_current++];
  230. }
  231. }
  232. int peek_next_char()
  233. {
  234. if(m_current == m_size)
  235. {
  236. return -1;
  237. }
  238. else
  239. {
  240. return m_buffer[m_current];
  241. }
  242. }
  243. private:
  244. char const* const m_buffer;
  245. const ss_size_t m_size;
  246. ss_size_t m_current;
  247. };
  248. } /* namespace readers */
  249. namespace read_line_impl
  250. {
  251. template< ss_typename_param_k S
  252. , ss_typename_param_k P
  253. >
  254. static ss_bool_t read_line(P& policy, S& line, read_line_flags::flags_t flags)
  255. {
  256. ss_size_t numCr = 0;
  257. if(0 == (read_line_flags::mask & flags))
  258. {
  259. flags = read_line_flags::recogniseAll;
  260. }
  261. S().swap(line);
  262. { for (int ch; EOF != (ch = policy.read_char()); )
  263. {
  264. switch(ch)
  265. {
  266. case '\r':
  267. // Options:
  268. //
  269. // - recognising CR - handle
  270. // - recognising CRLF - handle
  271. // If we're recognising either
  272. if(0 != ((read_line_flags::recogniseCrAsEOL | read_line_flags::recogniseCrLfAsEOL) & flags))
  273. {
  274. if(read_line_flags::recogniseCrLfAsEOL & flags)
  275. {
  276. // We need to look ahead in order to work out whether
  277. // this might be the start of a \r\n pair
  278. int ch2 = policy.peek_next_char();
  279. if('\n' == ch2)
  280. {
  281. policy.read_char();
  282. line.append(numCr, '\r');
  283. return true;
  284. }
  285. }
  286. if(read_line_flags::recogniseCrAsEOL & flags)
  287. {
  288. return true;
  289. }
  290. }
  291. ++numCr;
  292. continue;
  293. case '\n':
  294. // Options:
  295. //
  296. // - recognising CR - ignore
  297. // - recognising LF - handle
  298. // - recognising CRLF - handle
  299. //
  300. // We check CRLF first
  301. if( numCr > 0 &&
  302. (read_line_flags::recogniseCrLfAsEOL & flags))
  303. {
  304. // Here we will digest any excess CRs as literal
  305. // characters in the line, and then return the
  306. // line
  307. line.append(numCr - 1, '\r');
  308. return true;
  309. }
  310. else if(read_line_flags::recogniseLfAsEOL & flags)
  311. {
  312. line.append(numCr, '\r');
  313. return true;
  314. }
  315. break;
  316. default:
  317. if(numCr > 0)
  318. {
  319. line.append(numCr, '\r');
  320. numCr = 0;
  321. }
  322. break;
  323. }
  324. line.append(1, static_cast<char>(ch));
  325. }}
  326. return !line.empty();
  327. }
  328. } /* namespace read_line_impl */
  329. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  330. /* /////////////////////////////////////////////////////////////////////////
  331. * Functions
  332. */
  333. /** Reads a line from a C stream
  334. *
  335. * \param stm The stream to read from
  336. * \param line The line to read into. Must be an instance of a type that
  337. * is structurally conformant to std::string in the following ways: it
  338. * has a default constructor, it has a swap() method, and it has a block
  339. * append method (taking repeat count and character to add)
  340. * \param flags The flags that control what line-termination sequences are
  341. * recognised
  342. *
  343. * \return An indication of whether there is more to parse
  344. * \retval true The parsing is not complete
  345. * \retval false The parsing is complete
  346. *
  347. * \remarks The function can recognise any or all of the following as
  348. * line-termination sequences: carriage-return ('\r'), line-feed ('\n'), or
  349. * carriage-return+line-feed ("\r\n").
  350. */
  351. template <ss_typename_param_k S>
  352. ss_bool_t read_line(
  353. FILE* stm
  354. , S& line
  355. , read_line_flags::flags_t flags = read_line_flags::recogniseAll
  356. )
  357. {
  358. readers::read_from_FILE policy(stm);
  359. return read_line_impl::read_line(policy, line, flags);
  360. }
  361. /** Reads a line from a pair of iterators
  362. */
  363. template< ss_typename_param_k I
  364. , ss_typename_param_k S
  365. >
  366. ss_bool_t read_line(
  367. I from
  368. , I to
  369. , S& line
  370. , read_line_flags::flags_t flags = read_line_flags::recogniseAll
  371. )
  372. {
  373. readers::read_from_iterator_range<I> policy(from, to);
  374. return read_line_impl::read_line(policy, line, flags);
  375. }
  376. /* ////////////////////////////////////////////////////////////////////// */
  377. #ifndef _STLSOFT_NO_NAMESPACE
  378. } /* namespace stlsoft */
  379. #endif /* _STLSOFT_NO_NAMESPACE */
  380. /* ////////////////////////////////////////////////////////////////////// */
  381. #endif /* !STLSOFT_INCL_STLSOFT_FILESYSTEM_HPP_READ_LINE */
  382. /* ///////////////////////////// end of file //////////////////////////// */