| 1 | /* |
| 2 | Copyright 2010 Beman Dawes |
| 3 | |
| 4 | Copyright 2019-2020 Glen Joseph Fernandes |
| 5 | (glenjofe@gmail.com) |
| 6 | |
| 7 | Distributed under the Boost Software License, Version 1.0. |
| 8 | (http://www.boost.org/LICENSE_1_0.txt) |
| 9 | */ |
| 10 | #ifndef BOOST_IO_QUOTED_HPP |
| 11 | #define BOOST_IO_QUOTED_HPP |
| 12 | |
| 13 | #include <boost/io/detail/buffer_fill.hpp> |
| 14 | #include <boost/io/detail/ostream_guard.hpp> |
| 15 | #include <boost/io/ios_state.hpp> |
| 16 | |
| 17 | namespace boost { |
| 18 | namespace io { |
| 19 | namespace detail { |
| 20 | |
| 21 | template<class String, class Char> |
| 22 | struct quoted_proxy { |
| 23 | String string; |
| 24 | Char escape; |
| 25 | Char delim; |
| 26 | }; |
| 27 | |
| 28 | template<class Char> |
| 29 | struct quoted_state { |
| 30 | const Char* string; |
| 31 | std::size_t size; |
| 32 | std::size_t count; |
| 33 | }; |
| 34 | |
| 35 | template<class Char> |
| 36 | inline quoted_state<Char> |
| 37 | quoted_start(const Char* string, Char escape, Char delim) |
| 38 | { |
| 39 | const Char* end = string; |
| 40 | std::size_t count = 2; |
| 41 | for (Char ch; (ch = *end) != 0; ++end) { |
| 42 | count += 1 + (ch == escape || ch == delim); |
| 43 | } |
| 44 | quoted_state<Char> state = { string, |
| 45 | static_cast<std::size_t>(end - string), count }; |
| 46 | return state; |
| 47 | } |
| 48 | |
| 49 | template<class Char, class String> |
| 50 | inline quoted_state<Char> |
| 51 | quoted_start(const String* string, Char escape, Char delim) |
| 52 | { |
| 53 | const Char* begin = string->data(); |
| 54 | std::size_t size = string->size(); |
| 55 | std::size_t count = 2; |
| 56 | for (const Char *it = begin, *end = begin + size; it != end; ++it) { |
| 57 | Char ch = *it; |
| 58 | count += 1 + (ch == escape || ch == delim); |
| 59 | } |
| 60 | quoted_state<Char> state = { begin, size, count }; |
| 61 | return state; |
| 62 | } |
| 63 | |
| 64 | template<class Char, class Traits> |
| 65 | inline bool |
| 66 | quoted_put(std::basic_streambuf<Char, Traits>& buf, const Char* string, |
| 67 | std::size_t size, std::size_t count, Char escape, Char delim) |
| 68 | { |
| 69 | if (buf.sputc(delim) == Traits::eof()) { |
| 70 | return false; |
| 71 | } |
| 72 | if (size == count) { |
| 73 | if (static_cast<std::size_t>(buf.sputn(string, size)) != size) { |
| 74 | return false; |
| 75 | } |
| 76 | } else { |
| 77 | for (const Char* end = string + size; string != end; ++string) { |
| 78 | Char ch = *string; |
| 79 | if ((ch == escape || ch == delim) && |
| 80 | buf.sputc(escape) == Traits::eof()) { |
| 81 | return false; |
| 82 | } |
| 83 | if (buf.sputc(ch) == Traits::eof()) { |
| 84 | return false; |
| 85 | } |
| 86 | } |
| 87 | } |
| 88 | return buf.sputc(delim) != Traits::eof(); |
| 89 | } |
| 90 | |
| 91 | template<class Char, class Traits, class String> |
| 92 | inline std::basic_ostream<Char, Traits>& |
| 93 | quoted_out(std::basic_ostream<Char, Traits>& os, String* string, Char escape, |
| 94 | Char delim) |
| 95 | { |
| 96 | typedef std::basic_ostream<Char, Traits> stream; |
| 97 | ostream_guard<Char, Traits> guard(os); |
| 98 | typename stream::sentry entry(os); |
| 99 | if (entry) { |
| 100 | quoted_state<Char> state = boost::io::detail::quoted_start(string, |
| 101 | escape, delim); |
| 102 | std::basic_streambuf<Char, Traits>& buf = *os.rdbuf(); |
| 103 | std::size_t width = static_cast<std::size_t>(os.width()); |
| 104 | if (width <= state.count) { |
| 105 | if (!boost::io::detail::quoted_put(buf, state.string, state.size, |
| 106 | state.count, escape, delim)) { |
| 107 | return os; |
| 108 | } |
| 109 | } else if ((os.flags() & stream::adjustfield) == stream::left) { |
| 110 | if (!boost::io::detail::quoted_put(buf, state.string, state.size, |
| 111 | state.count, escape, delim) || |
| 112 | !boost::io::detail::buffer_fill(buf, os.fill(), |
| 113 | width - state.count)) { |
| 114 | return os; |
| 115 | } |
| 116 | } else if (!boost::io::detail::buffer_fill(buf, os.fill(), |
| 117 | width - state.count) || |
| 118 | !boost::io::detail::quoted_put(buf, state.string, state.size, |
| 119 | state.count, escape, delim)) { |
| 120 | return os; |
| 121 | } |
| 122 | os.width(0); |
| 123 | } |
| 124 | guard.release(); |
| 125 | return os; |
| 126 | } |
| 127 | |
| 128 | template<class Char, class Traits> |
| 129 | inline std::basic_ostream<Char, Traits>& |
| 130 | operator<<(std::basic_ostream<Char, Traits>& os, |
| 131 | const quoted_proxy<const Char*, Char>& proxy) |
| 132 | { |
| 133 | return boost::io::detail::quoted_out(os, proxy.string, proxy.escape, |
| 134 | proxy.delim); |
| 135 | } |
| 136 | |
| 137 | template <class Char, class Traits, class Alloc> |
| 138 | inline std::basic_ostream<Char, Traits>& |
| 139 | operator<<(std::basic_ostream<Char, Traits>& os, |
| 140 | const quoted_proxy<const std::basic_string<Char, Traits, Alloc>*, |
| 141 | Char>& proxy) |
| 142 | { |
| 143 | return boost::io::detail::quoted_out(os, proxy.string, proxy.escape, |
| 144 | proxy.delim); |
| 145 | } |
| 146 | |
| 147 | template<class Char, class Traits, class Alloc> |
| 148 | inline std::basic_ostream<Char, Traits>& |
| 149 | operator<<(std::basic_ostream<Char, Traits>& os, |
| 150 | const quoted_proxy<std::basic_string<Char, Traits, Alloc>*, Char>& proxy) |
| 151 | { |
| 152 | return boost::io::detail::quoted_out(os, proxy.string, proxy.escape, |
| 153 | proxy.delim); |
| 154 | } |
| 155 | |
| 156 | template<class Char, class Traits, class Alloc> |
| 157 | inline std::basic_istream<Char, Traits>& |
| 158 | operator>>(std::basic_istream<Char, Traits>& is, |
| 159 | const quoted_proxy<std::basic_string<Char, Traits, Alloc>*, Char>& proxy) |
| 160 | { |
| 161 | Char ch; |
| 162 | if (!(is >> ch)) { |
| 163 | return is; |
| 164 | } |
| 165 | if (ch != proxy.delim) { |
| 166 | is.unget(); |
| 167 | return is >> *proxy.string; |
| 168 | } |
| 169 | { |
| 170 | boost::io::ios_flags_saver ifs(is); |
| 171 | std::noskipws(base&: is); |
| 172 | proxy.string->clear(); |
| 173 | while ((is >> ch) && ch != proxy.delim) { |
| 174 | if (ch == proxy.escape && !(is >> ch)) { |
| 175 | break; |
| 176 | } |
| 177 | proxy.string->push_back(ch); |
| 178 | } |
| 179 | } |
| 180 | return is; |
| 181 | } |
| 182 | |
| 183 | } /* detail */ |
| 184 | |
| 185 | template<class Char, class Traits, class Alloc> |
| 186 | inline detail::quoted_proxy<const std::basic_string<Char, Traits, Alloc>*, |
| 187 | Char> |
| 188 | quoted(const std::basic_string<Char, Traits, Alloc>& s, Char escape='\\', |
| 189 | Char delim='\"') |
| 190 | { |
| 191 | detail::quoted_proxy<const std::basic_string<Char, Traits, Alloc>*, |
| 192 | Char> proxy = { &s, escape, delim }; |
| 193 | return proxy; |
| 194 | } |
| 195 | |
| 196 | template<class Char, class Traits, class Alloc> |
| 197 | inline detail::quoted_proxy<std::basic_string<Char, Traits, Alloc>*, Char> |
| 198 | quoted(std::basic_string<Char, Traits, Alloc>& s, Char escape='\\', |
| 199 | Char delim='\"') |
| 200 | { |
| 201 | detail::quoted_proxy<std::basic_string<Char, Traits, Alloc>*, |
| 202 | Char> proxy = { &s, escape, delim }; |
| 203 | return proxy; |
| 204 | } |
| 205 | |
| 206 | template<class Char> |
| 207 | inline detail::quoted_proxy<const Char*, Char> |
| 208 | quoted(const Char* s, Char escape='\\', Char delim='\"') |
| 209 | { |
| 210 | detail::quoted_proxy<const Char*, Char> proxy = { s, escape, delim }; |
| 211 | return proxy; |
| 212 | } |
| 213 | |
| 214 | } /* io */ |
| 215 | } /* boost */ |
| 216 | |
| 217 | #endif |
| 218 | |