1 // Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
2 //
3 // Permission to use, copy, modify, and distribute this software for any
4 // purpose with or without fee is hereby granted, provided that the above
5 // copyright notice and this permission notice appear in all copies.
6 //
7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 //
15 // Aegisub Project http://www.aegisub.org/
16 
17 #include <libaegisub/fs_fwd.h>
18 
19 #include <boost/interprocess/streams/vectorstream.hpp>
20 #include <boost/io/ios_state.hpp>
21 #include <type_traits>
22 #include <typeinfo> // std::bad_cast
23 
24 class wxString;
25 
26 namespace agi { namespace format_detail {
27 // A static cast which throws at runtime if the cast is invalid rather than
28 // failing to compile, as with format strings we don't know what type to cast
29 // to at compile time.
30 template<typename In, typename Out, bool = std::is_convertible<In, Out>::value>
31 struct runtime_cast_helper {
castruntime_cast_helper32 	static Out cast(In const&) { throw std::bad_cast(); }
33 };
34 
35 template<typename In, typename Out>
36 struct runtime_cast_helper<In, Out, true> {
37 	static Out cast(In const& value) {
38 		return static_cast<Out>(value);
39 	}
40 };
41 
42 template<typename Out, typename In>
43 Out runtime_cast(In const& value) {
44 	return runtime_cast_helper<In, Out>::cast(value);
45 }
46 }
47 
48 template<typename Char, typename T>
49 struct writer {
50 	static void write(std::basic_ostream<Char>& out, int, T const& value) {
51 		out << value;
52 	}
53 };
54 
55 template<typename StreamChar, typename Char>
56 struct writer<StreamChar, const Char *> {
57 	static void write(std::basic_ostream<StreamChar>& out, int max_len, const Char *value);
58 };
59 
60 template<typename StreamChar, typename Char>
61 struct writer<StreamChar, std::basic_string<Char>> {
62 	static void write(std::basic_ostream<StreamChar>& out, int max_len, std::basic_string<Char> const& value);
63 };
64 
65 // Ensure things with specializations don't get implicitly initialized
66 template<> struct writer<char, agi::fs::path>;
67 template<> struct writer<wchar_t, agi::fs::path>;
68 template<> struct writer<char, wxString>;
69 template<> struct writer<wchar_t, wxString>;
70 
71 namespace format_detail {
72 template<typename Char>
73 struct formatter_state {
74 	std::basic_ostream<Char>& out;
75 
76 	const Char *fmt;
77 	const Char *fmt_cur = nullptr;
78 
79 	bool read_width = false;
80 	bool read_precision = false;
81 	bool pending = false;
82 
83 	int width = 0;
84 	int precision = 0;
85 
86 	formatter_state(std::basic_ostream<Char>&out , const Char *fmt)
87 	: out(out), fmt(fmt) { }
88 };
89 
90 template<typename Char>
91 class formatter : formatter_state<Char> {
92 	formatter(const formatter&) = delete;
93 	formatter& operator=(const formatter&) = delete;
94 
95 	boost::io::basic_ios_all_saver<Char> saver;
96 
97 	bool parse_next();
98 
99 public:
100 	formatter(std::basic_ostream<Char>& out, const Char *fmt)
101 	: formatter_state<Char>(out, fmt), saver(out) { }
102 	~formatter();
103 
104 	template<typename T>
105 	void operator()(T&& value) {
106 		if (!this->pending && !parse_next()) return;
107 
108 		if (this->read_width) {
109 			this->width = runtime_cast<int>(value);
110 			this->read_width = false;
111 			return;
112 		}
113 
114 		if (this->read_precision) {
115 			this->precision = runtime_cast<int>(value);
116 			this->read_precision = false;
117 			return;
118 		}
119 		this->pending = false;
120 
121 		if (this->width < 0) {
122 			this->out.fill(' ');
123 			this->out.setf(std::ios::left, std::ios::adjustfield);
124 			this->width = -this->width;
125 		}
126 		this->out.width(this->width);
127 		this->out.precision(this->precision < 0 ? 6 : this->precision);
128 
129 		Char c = *this->fmt_cur ? this->fmt_cur[0] : 's';
130 		if (c >= 'A' && c <= 'Z') {
131 			this->out.setf(std::ios::uppercase);
132 			c += 'a' - 'A';
133 		}
134 
135 		switch (c) {
136 		case 'c':
137 			this->out.setf(std::ios::dec, std::ios::basefield);
138 			this->out << runtime_cast<Char>(value);
139 			break;
140 		case 'd': case 'i':
141 			this->out.setf(std::ios::dec, std::ios::basefield);
142 			this->out << runtime_cast<intmax_t>(value);
143 			break;
144 		case 'o':
145 			this->out.setf(std::ios::oct, std::ios::basefield);
146 			this->out << runtime_cast<intmax_t>(value);
147 			break;
148 		case 'x':
149 			this->out.setf(std::ios::hex, std::ios::basefield);
150 			this->out << runtime_cast<intmax_t>(value);
151 			break;
152 		case 'u':
153 			this->out.setf(std::ios::dec, std::ios::basefield);
154 			this->out << runtime_cast<uintmax_t>(value);
155 			break;
156 		case 'e':
157 			this->out.setf(std::ios::scientific, std::ios::floatfield);
158 			this->out.setf(std::ios::dec, std::ios::basefield);
159 			this->out << runtime_cast<double>(value);
160 			break;
161 		case 'f':
162 			this->out.setf(std::ios::fixed, std::ios::floatfield);
163 			this->out << runtime_cast<double>(value);
164 			break;
165 		case 'g':
166 			this->out.setf(std::ios::dec, std::ios::basefield);
167 			this->out.flags(this->out.flags() & ~std::ios::floatfield);
168 			this->out << runtime_cast<double>(value);
169 			break;
170 		case 'p':
171 			this->out.setf(std::ios::hex, std::ios::basefield);
172 			this->out << runtime_cast<const void *>(value);
173 			break;
174 		default: // s and other
175 			this->out.setf(std::ios::boolalpha);
176 			writer<Char, typename std::decay<T>::type>::write(this->out, this->precision, value);
177 			break;
178 		}
179 
180 		this->fmt = *this->fmt_cur ? this->fmt_cur + 1 : this->fmt_cur;
181 	}
182 };
183 
184 // Base case for variadic template recursion
185 template<typename Char>
186 inline void format(formatter<Char>&&) { }
187 
188 template<typename Char, typename T, typename... Args>
189 void format(formatter<Char>&& fmt, T&& first, Args&&... rest) {
190 	fmt(first);
191 	format(std::move(fmt), std::forward<Args>(rest)...);
192 }
193 } // namespace format_detail
194 
195 template<typename Char, typename... Args>
196 void format(std::basic_ostream<Char>& out, const Char *fmt, Args&&... args) {
197 	format(format_detail::formatter<Char>(out, fmt), std::forward<Args>(args)...);
198 }
199 
200 template<typename Char, typename... Args>
201 std::basic_string<Char> format(const Char *fmt, Args&&... args) {
202 	boost::interprocess::basic_vectorstream<std::basic_string<Char>> out;
203 	format(out, fmt, std::forward<Args>(args)...);
204 	return out.vector();
205 }
206 }
207