1 /**
2  * \file texstream.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Enrico Forestieri
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10 
11 #include <config.h>
12 
13 #include "texstream.h"
14 
15 #include "TexRow.h"
16 
17 #include "support/lstrings.h"
18 #include "support/unicode.h"
19 
20 #include <algorithm>
21 #include <cerrno>
22 #include <cstdio>
23 #include <cstring>
24 #include <iconv.h>
25 #include <locale>
26 
27 using namespace std;
28 
29 using lyx::support::contains;
30 using lyx::support::split;
31 
32 
33 namespace lyx {
34 
35 
otexrowstream(odocstream & os)36 otexrowstream::otexrowstream(odocstream & os)
37 	: os_(os), texrow_(make_unique<TexRow>())
38 {}
39 
40 
41 otexrowstream::~otexrowstream() = default;
42 
43 
releaseTexRow()44 unique_ptr<TexRow> otexrowstream::releaseTexRow()
45 {
46 	auto p = make_unique<TexRow>();
47 	swap(texrow_, p);
48 	return p;
49 }
50 
51 
put(char_type const & c)52 void otexrowstream::put(char_type const & c)
53 {
54 	os_.put(c);
55 	if (c == '\n')
56 		texrow_->newline();
57 }
58 
59 
put(char_type const & c)60 void otexstream::put(char_type const & c)
61 {
62 	bool isprotected = false;
63 	if (protectspace_) {
64 		if (!canbreakline_ && c == ' ') {
65 			os() << "{}";
66 			isprotected = true;
67 		}
68 		protectspace_ = false;
69 	}
70 	if (terminate_command_) {
71 		if ((c == ' ' || c == '\0' || c == '\n') && !isprotected)
72 			// A space or line break follows. Terminate with brackets.
73 			os() << "{}";
74 		else if (c != '\\' && c != '{' && c != '}')
75 			// Non-terminating character follows. Terminate with space.
76 			os() << " ";
77 		terminate_command_ = false;
78 	}
79 	otexrowstream::put(c);
80 	lastChar(c);
81 }
82 
83 
length()84 size_t otexstringstream::length()
85 {
86 	auto pos = ods_.tellp();
87 	return (pos >= 0) ? size_t(pos) : 0;
88 }
89 
90 
release()91 TexString otexstringstream::release()
92 {
93 	TexString ts(ods_.str(), move(texrow()));
94 	// reset this
95 	texrow() = TexRow();
96 	ods_.clear();
97 	ods_.str(docstring());
98 	return ts;
99 }
100 
101 
102 BreakLine breakln;
103 SafeBreakLine safebreakln;
104 TerminateCommand termcmd;
105 
106 
operator <<(otexstream & ots,BreakLine)107 otexstream & operator<<(otexstream & ots, BreakLine)
108 {
109 	if (ots.canBreakLine()) {
110 		if (ots.terminateCommand())
111 			ots << "{}";
112 		ots.otexrowstream::put('\n');
113 		ots.lastChar('\n');
114 	}
115 	ots.protectSpace(false);
116 	ots.terminateCommand(false);
117 	return ots;
118 }
119 
120 
operator <<(otexstream & ots,SafeBreakLine)121 otexstream & operator<<(otexstream & ots, SafeBreakLine)
122 {
123 	otexrowstream & otrs = ots;
124 	if (ots.canBreakLine()) {
125 	    if (ots.terminateCommand())
126 			otrs << "{}";
127 		otrs << "%\n";
128 		ots.lastChar('\n');
129 	}
130 	ots.protectSpace(false);
131 	ots.terminateCommand(false);
132 	return ots;
133 }
134 
135 
operator <<(otexstream & ots,TerminateCommand)136 otexstream & operator<<(otexstream & ots, TerminateCommand)
137 {
138 	ots.terminateCommand(true);
139 	return ots;
140 }
141 
142 
operator <<(otexrowstream & ots,odocstream_manip pf)143 otexrowstream & operator<<(otexrowstream & ots, odocstream_manip pf)
144 {
145 	ots.os() << pf;
146 	if (pf == static_cast<odocstream_manip>(endl)) {
147 		ots.texrow().newline();
148 	}
149 	return ots;
150 }
151 
152 
operator <<(otexstream & ots,odocstream_manip pf)153 otexstream & operator<<(otexstream & ots, odocstream_manip pf)
154 {
155 	otexrowstream & otrs = ots;
156 	otrs << pf;
157 	if (pf == static_cast<odocstream_manip>(endl)) {
158 		ots.lastChar('\n');
159 	}
160 	return ots;
161 }
162 
163 
operator <<(otexrowstream & ots,TexString ts)164 otexrowstream & operator<<(otexrowstream & ots, TexString ts)
165 {
166 	ts.validate();
167 	ots.os() << move(ts.str);
168 	ots.texrow().append(move(ts.texrow));
169 	return ots;
170 }
171 
172 
operator <<(otexstream & ots,TexString ts)173 otexstream & operator<<(otexstream & ots, TexString ts)
174 {
175 	size_t const len = ts.str.length();
176 	// Check whether there is something to output
177 	if (len == 0)
178 		return ots;
179 
180 	otexrowstream & otrs = ots;
181 	bool isprotected = false;
182 	char_type const c = ts.str[0];
183 	if (ots.protectSpace()) {
184 		if (!ots.canBreakLine() && c == ' ') {
185 			otrs << "{}";
186 			isprotected = true;
187 		}
188 		ots.protectSpace(false);
189 	}
190 	if (ots.terminateCommand()) {
191 		if ((c == ' ' || c == '\0' || c == '\n') && !isprotected)
192 			// A space or line break follows. Terminate with brackets.
193 			otrs << "{}";
194 		else if (c != '\\' && c != '{' && c != '}')
195 			// Non-terminating character follows. Terminate with space.
196 			otrs << " ";
197 		ots.terminateCommand(false);
198 	}
199 
200 	if (len > 1)
201 		ots.canBreakLine(ts.str[len - 2] != '\n');
202 	ots.lastChar(ts.str[len - 1]);
203 
204 	otrs << move(ts);
205 	return ots;
206 }
207 
208 
operator <<(otexrowstream & ots,docstring const & s)209 otexrowstream & operator<<(otexrowstream & ots, docstring const & s)
210 {
211 	ots.os() << s;
212 	ots.texrow().newlines(count(s.begin(), s.end(), '\n'));
213 	return ots;
214 }
215 
216 
operator <<(otexstream & ots,docstring const & s)217 otexstream & operator<<(otexstream & ots, docstring const & s)
218 {
219 	size_t const len = s.length();
220 
221 	// Check whether there's something to output
222 	if (len == 0)
223 		return ots;
224 	otexrowstream & otrs = ots;
225 	bool isprotected = false;
226 	char_type const c = s[0];
227 	if (ots.protectSpace()) {
228 		if (!ots.canBreakLine() && c == ' ') {
229 			otrs << "{}";
230 			isprotected = true;
231 		}
232 		ots.protectSpace(false);
233 	}
234 	if (ots.terminateCommand()) {
235 		if ((c == ' ' || c == '\0' || c == '\n') && !isprotected)
236 			// A space or line break follows. Terminate with brackets.
237 			otrs << "{}";
238 		else if (c != '\\' && c != '{' && c != '}')
239 			// Non-terminating character follows. Terminate with space.
240 			otrs << " ";
241 		ots.terminateCommand(false);
242 	}
243 
244 	if (contains(s, 0xF0000)) {
245 		// Some encoding changes for the underlying stream are embedded
246 		// in the docstring. The encoding names to be used are enclosed
247 		// between the code points 0xF0000 and 0xF0001, the first two
248 		// characters of plane 15, which is a Private Use Area whose
249 		// codepoints don't have any associated glyph.
250 		docstring s1;
251 		docstring s2 = split(s, s1, 0xF0000);
252 		while (true) {
253 			if (!s1.empty())
254 				otrs << s1;
255 			if (s2.empty())
256 				break;
257 			docstring enc;
258 			docstring const s3 = split(s2, enc, 0xF0001);
259 			if (!contains(s2, 0xF0001))
260 				s2 = split(enc, s1, 0xF0000);
261 			else {
262 				otrs << setEncoding(to_ascii(enc));
263 				s2 = split(s3, s1, 0xF0000);
264 			}
265 		}
266 	} else
267 		otrs << s;
268 
269 	if (len > 1)
270 		ots.canBreakLine(s[len - 2] != '\n');
271 	ots.lastChar(s[len - 1]);
272 	return ots;
273 }
274 
275 
operator <<(otexrowstream & ots,string const & s)276 otexrowstream & operator<<(otexrowstream & ots, string const & s)
277 {
278 	ots << from_utf8(s);
279 	return ots;
280 }
281 
282 
operator <<(otexstream & ots,string const & s)283 otexstream & operator<<(otexstream & ots, string const & s)
284 {
285 	ots << from_utf8(s);
286 	return ots;
287 }
288 
289 
operator <<(otexrowstream & ots,char const * s)290 otexrowstream & operator<<(otexrowstream & ots, char const * s)
291 {
292 	ots << from_utf8(s);
293 	return ots;
294 }
295 
296 
operator <<(otexstream & ots,char const * s)297 otexstream & operator<<(otexstream & ots, char const * s)
298 {
299 	ots << from_utf8(s);
300 	return ots;
301 }
302 
303 
operator <<(otexrowstream & ots,char c)304 otexrowstream & operator<<(otexrowstream & ots, char c)
305 {
306 	ots.put(c);
307 	return ots;
308 }
309 
310 
operator <<(otexstream & ots,char c)311 otexstream & operator<<(otexstream & ots, char c)
312 {
313 	ots.put(c);
314 	return ots;
315 }
316 
317 
318 template <typename Type>
operator <<(otexrowstream & ots,Type value)319 otexrowstream & operator<<(otexrowstream & ots, Type value)
320 {
321 	ots.os() << value;
322 	return ots;
323 }
324 
325 template otexrowstream & operator<< <SetEnc>(otexrowstream & os, SetEnc);
326 template otexrowstream & operator<< <double>(otexrowstream &, double);
327 template otexrowstream & operator<< <int>(otexrowstream &, int);
328 template otexrowstream & operator<< <unsigned int>(otexrowstream &,
329 												   unsigned int);
330 template otexrowstream & operator<< <unsigned long>(otexrowstream &,
331 													unsigned long);
332 
333 #ifdef LYX_USE_LONG_LONG
334 template otexrowstream & operator<< <unsigned long long>(otexrowstream &,
335                                                          unsigned long long);
336 #endif
337 
338 
339 template <typename Type>
340 otexstream & operator<<(otexstream & ots, Type value)
341 {
342 	ots.os() << value;
343 	ots.lastChar(0);
344 	ots.protectSpace(false);
345 	ots.terminateCommand(false);
346 	return ots;
347 }
348 
349 template otexstream & operator<< <SetEnc>(otexstream & os, SetEnc);
350 template otexstream & operator<< <double>(otexstream &, double);
351 template otexstream & operator<< <int>(otexstream &, int);
352 template otexstream & operator<< <unsigned int>(otexstream &, unsigned int);
353 template otexstream & operator<< <unsigned long>(otexstream &, unsigned long);
354 #ifdef LYX_USE_LONG_LONG
355 template otexstream & operator<< <unsigned long long>(otexstream &, unsigned long long);
356 #endif
357 
358 } // namespace lyx
359