/src/qpdf/libqpdf/QUtil.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Include qpdf-config.h first so off_t is guaranteed to have the right size. |
2 | | #include <qpdf/qpdf-config.h> |
3 | | |
4 | | #include <qpdf/QUtil.hh> |
5 | | |
6 | | #include <qpdf/CryptoRandomDataProvider.hh> |
7 | | #include <qpdf/Pipeline.hh> |
8 | | #include <qpdf/QIntC.hh> |
9 | | #include <qpdf/QPDFSystemError.hh> |
10 | | #include <qpdf/QTC.hh> |
11 | | #include <qpdf/Util.hh> |
12 | | |
13 | | #include <cerrno> |
14 | | #include <cstdlib> |
15 | | #include <cstring> |
16 | | #include <fcntl.h> |
17 | | #include <fstream> |
18 | | #include <iomanip> |
19 | | #include <map> |
20 | | #include <memory> |
21 | | #include <regex> |
22 | | #include <set> |
23 | | #include <sstream> |
24 | | #include <stdexcept> |
25 | | #ifndef QPDF_NO_WCHAR_T |
26 | | # include <cwchar> |
27 | | #endif |
28 | | #ifdef _WIN32 |
29 | | # define WIN32_LEAN_AND_MEAN |
30 | | # include <direct.h> |
31 | | # include <io.h> |
32 | | # include <windows.h> |
33 | | #else |
34 | | # include <sys/stat.h> |
35 | | # include <unistd.h> |
36 | | #endif |
37 | | #ifdef HAVE_MALLOC_INFO |
38 | | # include <malloc.h> |
39 | | #endif |
40 | | |
41 | | using namespace qpdf; |
42 | | |
43 | | // First element is 24 |
44 | | static unsigned short pdf_doc_low_to_unicode[] = { |
45 | | 0x02d8, // 0x18 BREVE |
46 | | 0x02c7, // 0x19 CARON |
47 | | 0x02c6, // 0x1a MODIFIER LETTER CIRCUMFLEX ACCENT |
48 | | 0x02d9, // 0x1b DOT ABOVE |
49 | | 0x02dd, // 0x1c DOUBLE ACUTE ACCENT |
50 | | 0x02db, // 0x1d OGONEK |
51 | | 0x02da, // 0x1e RING ABOVE |
52 | | 0x02dc, // 0x1f SMALL TILDE |
53 | | }; |
54 | | // First element is 127 |
55 | | static unsigned short pdf_doc_to_unicode[] = { |
56 | | 0xfffd, // 0x7f UNDEFINED |
57 | | 0x2022, // 0x80 BULLET |
58 | | 0x2020, // 0x81 DAGGER |
59 | | 0x2021, // 0x82 DOUBLE DAGGER |
60 | | 0x2026, // 0x83 HORIZONTAL ELLIPSIS |
61 | | 0x2014, // 0x84 EM DASH |
62 | | 0x2013, // 0x85 EN DASH |
63 | | 0x0192, // 0x86 SMALL LETTER F WITH HOOK |
64 | | 0x2044, // 0x87 FRACTION SLASH (solidus) |
65 | | 0x2039, // 0x88 SINGLE LEFT-POINTING ANGLE QUOTATION MARK |
66 | | 0x203a, // 0x89 SINGLE RIGHT-POINTING ANGLE QUOTATION MARK |
67 | | 0x2212, // 0x8a MINUS SIGN |
68 | | 0x2030, // 0x8b PER MILLE SIGN |
69 | | 0x201e, // 0x8c DOUBLE LOW-9 QUOTATION MARK (quotedblbase) |
70 | | 0x201c, // 0x8d LEFT DOUBLE QUOTATION MARK (double quote left) |
71 | | 0x201d, // 0x8e RIGHT DOUBLE QUOTATION MARK (quotedblright) |
72 | | 0x2018, // 0x8f LEFT SINGLE QUOTATION MARK (quoteleft) |
73 | | 0x2019, // 0x90 RIGHT SINGLE QUOTATION MARK (quoteright) |
74 | | 0x201a, // 0x91 SINGLE LOW-9 QUOTATION MARK (quotesinglbase) |
75 | | 0x2122, // 0x92 TRADE MARK SIGN |
76 | | 0xfb01, // 0x93 LATIN SMALL LIGATURE FI |
77 | | 0xfb02, // 0x94 LATIN SMALL LIGATURE FL |
78 | | 0x0141, // 0x95 LATIN CAPITAL LETTER L WITH STROKE |
79 | | 0x0152, // 0x96 LATIN CAPITAL LIGATURE OE |
80 | | 0x0160, // 0x97 LATIN CAPITAL LETTER S WITH CARON |
81 | | 0x0178, // 0x98 LATIN CAPITAL LETTER Y WITH DIAERESIS |
82 | | 0x017d, // 0x99 LATIN CAPITAL LETTER Z WITH CARON |
83 | | 0x0131, // 0x9a LATIN SMALL LETTER DOTLESS I |
84 | | 0x0142, // 0x9b LATIN SMALL LETTER L WITH STROKE |
85 | | 0x0153, // 0x9c LATIN SMALL LIGATURE OE |
86 | | 0x0161, // 0x9d LATIN SMALL LETTER S WITH CARON |
87 | | 0x017e, // 0x9e LATIN SMALL LETTER Z WITH CARON |
88 | | 0xfffd, // 0x9f UNDEFINED |
89 | | 0x20ac, // 0xa0 EURO SIGN |
90 | | }; |
91 | | static unsigned short win_ansi_to_unicode[] = { |
92 | | 0x20ac, // 0x80 |
93 | | 0xfffd, // 0x81 |
94 | | 0x201a, // 0x82 |
95 | | 0x0192, // 0x83 |
96 | | 0x201e, // 0x84 |
97 | | 0x2026, // 0x85 |
98 | | 0x2020, // 0x86 |
99 | | 0x2021, // 0x87 |
100 | | 0x02c6, // 0x88 |
101 | | 0x2030, // 0x89 |
102 | | 0x0160, // 0x8a |
103 | | 0x2039, // 0x8b |
104 | | 0x0152, // 0x8c |
105 | | 0xfffd, // 0x8d |
106 | | 0x017d, // 0x8e |
107 | | 0xfffd, // 0x8f |
108 | | 0xfffd, // 0x90 |
109 | | 0x2018, // 0x91 |
110 | | 0x2019, // 0x92 |
111 | | 0x201c, // 0x93 |
112 | | 0x201d, // 0x94 |
113 | | 0x2022, // 0x95 |
114 | | 0x2013, // 0x96 |
115 | | 0x2014, // 0x97 |
116 | | 0x0303, // 0x98 |
117 | | 0x2122, // 0x99 |
118 | | 0x0161, // 0x9a |
119 | | 0x203a, // 0x9b |
120 | | 0x0153, // 0x9c |
121 | | 0xfffd, // 0x9d |
122 | | 0x017e, // 0x9e |
123 | | 0x0178, // 0x9f |
124 | | 0x00a0, // 0xa0 |
125 | | }; |
126 | | static unsigned short mac_roman_to_unicode[] = { |
127 | | 0x00c4, // 0x80 |
128 | | 0x00c5, // 0x81 |
129 | | 0x00c7, // 0x82 |
130 | | 0x00c9, // 0x83 |
131 | | 0x00d1, // 0x84 |
132 | | 0x00d6, // 0x85 |
133 | | 0x00dc, // 0x86 |
134 | | 0x00e1, // 0x87 |
135 | | 0x00e0, // 0x88 |
136 | | 0x00e2, // 0x89 |
137 | | 0x00e4, // 0x8a |
138 | | 0x00e3, // 0x8b |
139 | | 0x00e5, // 0x8c |
140 | | 0x00e7, // 0x8d |
141 | | 0x00e9, // 0x8e |
142 | | 0x00e8, // 0x8f |
143 | | 0x00ea, // 0x90 |
144 | | 0x00eb, // 0x91 |
145 | | 0x00ed, // 0x92 |
146 | | 0x00ec, // 0x93 |
147 | | 0x00ee, // 0x94 |
148 | | 0x00ef, // 0x95 |
149 | | 0x00f1, // 0x96 |
150 | | 0x00f3, // 0x97 |
151 | | 0x00f2, // 0x98 |
152 | | 0x00f4, // 0x99 |
153 | | 0x00f6, // 0x9a |
154 | | 0x00f5, // 0x9b |
155 | | 0x00fa, // 0x9c |
156 | | 0x00f9, // 0x9d |
157 | | 0x00fb, // 0x9e |
158 | | 0x00fc, // 0x9f |
159 | | 0x2020, // 0xa0 |
160 | | 0x00b0, // 0xa1 |
161 | | 0x00a2, // 0xa2 |
162 | | 0x00a3, // 0xa3 |
163 | | 0x00a7, // 0xa4 |
164 | | 0x2022, // 0xa5 |
165 | | 0x00b6, // 0xa6 |
166 | | 0x00df, // 0xa7 |
167 | | 0x00ae, // 0xa8 |
168 | | 0x00a9, // 0xa9 |
169 | | 0x2122, // 0xaa |
170 | | 0x0301, // 0xab |
171 | | 0x0308, // 0xac |
172 | | 0xfffd, // 0xad |
173 | | 0x00c6, // 0xae |
174 | | 0x00d8, // 0xaf |
175 | | 0xfffd, // 0xb0 |
176 | | 0x00b1, // 0xb1 |
177 | | 0xfffd, // 0xb2 |
178 | | 0xfffd, // 0xb3 |
179 | | 0x00a5, // 0xb4 |
180 | | 0x03bc, // 0xb5 |
181 | | 0xfffd, // 0xb6 |
182 | | 0xfffd, // 0xb7 |
183 | | 0xfffd, // 0xb8 |
184 | | 0xfffd, // 0xb9 |
185 | | 0xfffd, // 0xba |
186 | | 0x1d43, // 0xbb |
187 | | 0x1d52, // 0xbc |
188 | | 0xfffd, // 0xbd |
189 | | 0x00e6, // 0xbe |
190 | | 0x00f8, // 0xbf |
191 | | 0x00bf, // 0xc0 |
192 | | 0x00a1, // 0xc1 |
193 | | 0x00ac, // 0xc2 |
194 | | 0xfffd, // 0xc3 |
195 | | 0x0192, // 0xc4 |
196 | | 0xfffd, // 0xc5 |
197 | | 0xfffd, // 0xc6 |
198 | | 0x00ab, // 0xc7 |
199 | | 0x00bb, // 0xc8 |
200 | | 0x2026, // 0xc9 |
201 | | 0xfffd, // 0xca |
202 | | 0x00c0, // 0xcb |
203 | | 0x00c3, // 0xcc |
204 | | 0x00d5, // 0xcd |
205 | | 0x0152, // 0xce |
206 | | 0x0153, // 0xcf |
207 | | 0x2013, // 0xd0 |
208 | | 0x2014, // 0xd1 |
209 | | 0x201c, // 0xd2 |
210 | | 0x201d, // 0xd3 |
211 | | 0x2018, // 0xd4 |
212 | | 0x2019, // 0xd5 |
213 | | 0x00f7, // 0xd6 |
214 | | 0xfffd, // 0xd7 |
215 | | 0x00ff, // 0xd8 |
216 | | 0x0178, // 0xd9 |
217 | | 0x2044, // 0xda |
218 | | 0x00a4, // 0xdb |
219 | | 0x2039, // 0xdc |
220 | | 0x203a, // 0xdd |
221 | | 0xfb01, // 0xde |
222 | | 0xfb02, // 0xdf |
223 | | 0x2021, // 0xe0 |
224 | | 0x00b7, // 0xe1 |
225 | | 0x201a, // 0xe2 |
226 | | 0x201e, // 0xe3 |
227 | | 0x2030, // 0xe4 |
228 | | 0x00c2, // 0xe5 |
229 | | 0x00ca, // 0xe6 |
230 | | 0x00c1, // 0xe7 |
231 | | 0x00cb, // 0xe8 |
232 | | 0x00c8, // 0xe9 |
233 | | 0x00cd, // 0xea |
234 | | 0x00ce, // 0xeb |
235 | | 0x00cf, // 0xec |
236 | | 0x00cc, // 0xed |
237 | | 0x00d3, // 0xee |
238 | | 0x00d4, // 0xef |
239 | | 0xfffd, // 0xf0 |
240 | | 0x00d2, // 0xf1 |
241 | | 0x00da, // 0xf2 |
242 | | 0x00db, // 0xf3 |
243 | | 0x00d9, // 0xf4 |
244 | | 0x0131, // 0xf5 |
245 | | 0x02c6, // 0xf6 |
246 | | 0x0303, // 0xf7 |
247 | | 0x0304, // 0xf8 |
248 | | 0x0306, // 0xf9 |
249 | | 0x0307, // 0xfa |
250 | | 0x030a, // 0xfb |
251 | | 0x0327, // 0xfc |
252 | | 0x030b, // 0xfd |
253 | | 0x0328, // 0xfe |
254 | | 0x02c7, // 0xff |
255 | | }; |
256 | | |
257 | | static std::map<unsigned long, unsigned char> unicode_to_win_ansi = { |
258 | | {0x20ac, 0x80}, {0x201a, 0x82}, {0x192, 0x83}, {0x201e, 0x84}, {0x2026, 0x85}, {0x2020, 0x86}, |
259 | | {0x2021, 0x87}, {0x2c6, 0x88}, {0x2030, 0x89}, {0x160, 0x8a}, {0x2039, 0x8b}, {0x152, 0x8c}, |
260 | | {0x17d, 0x8e}, {0x2018, 0x91}, {0x2019, 0x92}, {0x201c, 0x93}, {0x201d, 0x94}, {0x2022, 0x95}, |
261 | | {0x2013, 0x96}, {0x2014, 0x97}, {0x303, 0x98}, {0x2122, 0x99}, {0x161, 0x9a}, {0x203a, 0x9b}, |
262 | | {0x153, 0x9c}, {0x17e, 0x9e}, {0x178, 0x9f}, {0xa0, 0xa0}, |
263 | | }; |
264 | | static std::map<unsigned long, unsigned char> unicode_to_mac_roman = { |
265 | | {0xc4, 0x80}, {0xc5, 0x81}, {0xc7, 0x82}, {0xc9, 0x83}, {0xd1, 0x84}, {0xd6, 0x85}, |
266 | | {0xdc, 0x86}, {0xe1, 0x87}, {0xe0, 0x88}, {0xe2, 0x89}, {0xe4, 0x8a}, {0xe3, 0x8b}, |
267 | | {0xe5, 0x8c}, {0xe7, 0x8d}, {0xe9, 0x8e}, {0xe8, 0x8f}, {0xea, 0x90}, {0xeb, 0x91}, |
268 | | {0xed, 0x92}, {0xec, 0x93}, {0xee, 0x94}, {0xef, 0x95}, {0xf1, 0x96}, {0xf3, 0x97}, |
269 | | {0xf2, 0x98}, {0xf4, 0x99}, {0xf6, 0x9a}, {0xf5, 0x9b}, {0xfa, 0x9c}, {0xf9, 0x9d}, |
270 | | {0xfb, 0x9e}, {0xfc, 0x9f}, {0x2020, 0xa0}, {0xb0, 0xa1}, {0xa2, 0xa2}, {0xa3, 0xa3}, |
271 | | {0xa7, 0xa4}, {0x2022, 0xa5}, {0xb6, 0xa6}, {0xdf, 0xa7}, {0xae, 0xa8}, {0xa9, 0xa9}, |
272 | | {0x2122, 0xaa}, {0x301, 0xab}, {0x308, 0xac}, {0xc6, 0xae}, {0xd8, 0xaf}, {0xb1, 0xb1}, |
273 | | {0xa5, 0xb4}, {0x3bc, 0xb5}, {0x1d43, 0xbb}, {0x1d52, 0xbc}, {0xe6, 0xbe}, {0xf8, 0xbf}, |
274 | | {0xbf, 0xc0}, {0xa1, 0xc1}, {0xac, 0xc2}, {0x192, 0xc4}, {0xab, 0xc7}, {0xbb, 0xc8}, |
275 | | {0x2026, 0xc9}, {0xc0, 0xcb}, {0xc3, 0xcc}, {0xd5, 0xcd}, {0x152, 0xce}, {0x153, 0xcf}, |
276 | | {0x2013, 0xd0}, {0x2014, 0xd1}, {0x201c, 0xd2}, {0x201d, 0xd3}, {0x2018, 0xd4}, {0x2019, 0xd5}, |
277 | | {0xf7, 0xd6}, {0xff, 0xd8}, {0x178, 0xd9}, {0x2044, 0xda}, {0xa4, 0xdb}, {0x2039, 0xdc}, |
278 | | {0x203a, 0xdd}, {0xfb01, 0xde}, {0xfb02, 0xdf}, {0x2021, 0xe0}, {0xb7, 0xe1}, {0x201a, 0xe2}, |
279 | | {0x201e, 0xe3}, {0x2030, 0xe4}, {0xc2, 0xe5}, {0xca, 0xe6}, {0xc1, 0xe7}, {0xcb, 0xe8}, |
280 | | {0xc8, 0xe9}, {0xcd, 0xea}, {0xce, 0xeb}, {0xcf, 0xec}, {0xcc, 0xed}, {0xd3, 0xee}, |
281 | | {0xd4, 0xef}, {0xd2, 0xf1}, {0xda, 0xf2}, {0xdb, 0xf3}, {0xd9, 0xf4}, {0x131, 0xf5}, |
282 | | {0x2c6, 0xf6}, {0x303, 0xf7}, {0x304, 0xf8}, {0x306, 0xf9}, {0x307, 0xfa}, {0x30a, 0xfb}, |
283 | | {0x327, 0xfc}, {0x30b, 0xfd}, {0x328, 0xfe}, {0x2c7, 0xff}, |
284 | | }; |
285 | | static std::map<unsigned long, unsigned char> unicode_to_pdf_doc = { |
286 | | {0x02d8, 0x18}, {0x02c7, 0x19}, {0x02c6, 0x1a}, {0x02d9, 0x1b}, {0x02dd, 0x1c}, {0x02db, 0x1d}, |
287 | | {0x02da, 0x1e}, {0x02dc, 0x1f}, {0x2022, 0x80}, {0x2020, 0x81}, {0x2021, 0x82}, {0x2026, 0x83}, |
288 | | {0x2014, 0x84}, {0x2013, 0x85}, {0x0192, 0x86}, {0x2044, 0x87}, {0x2039, 0x88}, {0x203a, 0x89}, |
289 | | {0x2212, 0x8a}, {0x2030, 0x8b}, {0x201e, 0x8c}, {0x201c, 0x8d}, {0x201d, 0x8e}, {0x2018, 0x8f}, |
290 | | {0x2019, 0x90}, {0x201a, 0x91}, {0x2122, 0x92}, {0xfb01, 0x93}, {0xfb02, 0x94}, {0x0141, 0x95}, |
291 | | {0x0152, 0x96}, {0x0160, 0x97}, {0x0178, 0x98}, {0x017d, 0x99}, {0x0131, 0x9a}, {0x0142, 0x9b}, |
292 | | {0x0153, 0x9c}, {0x0161, 0x9d}, {0x017e, 0x9e}, {0xfffd, 0x9f}, {0x20ac, 0xa0}, |
293 | | }; |
294 | | |
295 | | template <typename T> |
296 | | static std::string |
297 | | int_to_string_base_internal(T num, int base, int length) |
298 | 0 | { |
299 | | // Backward compatibility -- int_to_string, which calls this function, used to use sprintf with |
300 | | // %0*d, so we interpret length such that a negative value appends spaces and a positive value |
301 | | // prepends zeroes. |
302 | 0 | if (!((base == 8) || (base == 10) || (base == 16))) { |
303 | 0 | throw std::logic_error("int_to_string_base called with unsupported base"); |
304 | 0 | } |
305 | 0 | std::string cvt; |
306 | 0 | if (base == 10) { |
307 | | // Use the more efficient std::to_string when possible |
308 | 0 | cvt = std::to_string(num); |
309 | 0 | } else { |
310 | 0 | std::ostringstream buf; |
311 | 0 | buf.imbue(std::locale::classic()); |
312 | 0 | buf << std::setbase(base) << std::nouppercase << num; |
313 | 0 | cvt = buf.str(); |
314 | 0 | } |
315 | 0 | std::string result; |
316 | 0 | int str_length = QIntC::to_int(cvt.length()); |
317 | 0 | if ((length > 0) && (str_length < length)) { |
318 | 0 | result.append(QIntC::to_size(length - str_length), '0'); |
319 | 0 | } |
320 | 0 | result += cvt; |
321 | 0 | if ((length < 0) && (str_length < -length)) { |
322 | 0 | result.append(QIntC::to_size(-length - str_length), ' '); |
323 | 0 | } |
324 | 0 | return result; |
325 | 0 | } Unexecuted instantiation: QUtil.cc:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > int_to_string_base_internal<long long>(long long, int, int) Unexecuted instantiation: QUtil.cc:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > int_to_string_base_internal<unsigned long long>(unsigned long long, int, int) |
326 | | |
327 | | std::string |
328 | | QUtil::int_to_string(long long num, int length) |
329 | 0 | { |
330 | 0 | return int_to_string_base(num, 10, length); |
331 | 0 | } |
332 | | |
333 | | std::string |
334 | | QUtil::uint_to_string(unsigned long long num, int length) |
335 | 0 | { |
336 | 0 | return uint_to_string_base(num, 10, length); |
337 | 0 | } |
338 | | |
339 | | std::string |
340 | | QUtil::int_to_string_base(long long num, int base, int length) |
341 | 0 | { |
342 | 0 | return int_to_string_base_internal(num, base, length); |
343 | 0 | } |
344 | | |
345 | | std::string |
346 | | QUtil::uint_to_string_base(unsigned long long num, int base, int length) |
347 | 0 | { |
348 | 0 | return int_to_string_base_internal(num, base, length); |
349 | 0 | } |
350 | | |
351 | | std::string |
352 | | QUtil::double_to_string(double num, int decimal_places, bool trim_trailing_zeroes) |
353 | 0 | { |
354 | | // Backward compatibility -- this code used to use sprintf and treated decimal_places <= 0 to |
355 | | // mean to use the default, which was six decimal places. Starting in 10.2, we trim trailing |
356 | | // zeroes by default. |
357 | 0 | if (decimal_places <= 0) { |
358 | 0 | decimal_places = 6; |
359 | 0 | } |
360 | 0 | std::ostringstream buf; |
361 | 0 | buf.imbue(std::locale::classic()); |
362 | 0 | buf << std::setprecision(decimal_places) << std::fixed << num; |
363 | 0 | std::string result = buf.str(); |
364 | 0 | if (trim_trailing_zeroes) { |
365 | 0 | while ((result.length() > 1) && (result.back() == '0')) { |
366 | 0 | result.pop_back(); |
367 | 0 | } |
368 | 0 | if ((result.length() > 1) && (result.back() == '.')) { |
369 | 0 | result.pop_back(); |
370 | 0 | } |
371 | 0 | } |
372 | 0 | return result; |
373 | 0 | } |
374 | | |
375 | | long long |
376 | | QUtil::string_to_ll(char const* str) |
377 | 0 | { |
378 | 0 | errno = 0; |
379 | | #ifdef _MSC_VER |
380 | | long long result = _strtoi64(str, 0, 10); |
381 | | #else |
382 | 0 | long long result = strtoll(str, nullptr, 10); |
383 | 0 | #endif |
384 | 0 | if (errno == ERANGE) { |
385 | 0 | throw std::range_error( |
386 | 0 | std::string("overflow/underflow converting ") + str + " to 64-bit integer"); |
387 | 0 | } |
388 | 0 | return result; |
389 | 0 | } |
390 | | |
391 | | int |
392 | | QUtil::string_to_int(char const* str) |
393 | 0 | { |
394 | | // QIntC::to_int does range checking |
395 | 0 | return QIntC::to_int(string_to_ll(str)); |
396 | 0 | } |
397 | | |
398 | | unsigned long long |
399 | | QUtil::string_to_ull(char const* str) |
400 | 0 | { |
401 | 0 | char const* p = str; |
402 | 0 | while (*p && util::is_space(*p)) { |
403 | 0 | ++p; |
404 | 0 | } |
405 | 0 | if (*p == '-') { |
406 | 0 | throw std::runtime_error( |
407 | 0 | std::string("underflow converting ") + str + " to 64-bit unsigned integer"); |
408 | 0 | } |
409 | | |
410 | 0 | errno = 0; |
411 | | #ifdef _MSC_VER |
412 | | unsigned long long result = _strtoui64(str, 0, 10); |
413 | | #else |
414 | 0 | unsigned long long result = strtoull(str, nullptr, 10); |
415 | 0 | #endif |
416 | 0 | if (errno == ERANGE) { |
417 | 0 | throw std::runtime_error( |
418 | 0 | std::string("overflow converting ") + str + " to 64-bit unsigned integer"); |
419 | 0 | } |
420 | 0 | return result; |
421 | 0 | } |
422 | | |
423 | | unsigned int |
424 | | QUtil::string_to_uint(char const* str) |
425 | 0 | { |
426 | | // QIntC::to_uint does range checking |
427 | 0 | return QIntC::to_uint(string_to_ull(str)); |
428 | 0 | } |
429 | | |
430 | | bool |
431 | | QUtil::is_long_long(char const* str) |
432 | 0 | { |
433 | 0 | try { |
434 | 0 | auto i1 = string_to_ll(str); |
435 | 0 | std::string s1 = int_to_string(i1); |
436 | 0 | return str == s1; |
437 | 0 | } catch (std::exception&) { |
438 | | // overflow or other error |
439 | 0 | } |
440 | 0 | return false; |
441 | 0 | } |
442 | | |
443 | | unsigned char* |
444 | | QUtil::unsigned_char_pointer(std::string const& str) |
445 | 0 | { |
446 | 0 | return reinterpret_cast<unsigned char*>(const_cast<char*>(str.c_str())); |
447 | 0 | } |
448 | | |
449 | | unsigned char* |
450 | | QUtil::unsigned_char_pointer(char const* str) |
451 | 0 | { |
452 | 0 | return reinterpret_cast<unsigned char*>(const_cast<char*>(str)); |
453 | 0 | } |
454 | | |
455 | | void |
456 | | QUtil::throw_system_error(std::string const& description) |
457 | 0 | { |
458 | 0 | throw QPDFSystemError(description, errno); |
459 | 0 | } |
460 | | |
461 | | int |
462 | | QUtil::os_wrapper(std::string const& description, int status) |
463 | 0 | { |
464 | 0 | if (status == -1) { |
465 | 0 | throw_system_error(description); |
466 | 0 | } |
467 | 0 | return status; |
468 | 0 | } |
469 | | |
470 | | #ifdef _WIN32 |
471 | | static std::shared_ptr<wchar_t> |
472 | | win_convert_filename(char const* filename) |
473 | | { |
474 | | // Convert the utf-8 encoded filename argument to wchar_t*. First, |
475 | | // convert to utf16, then to wchar_t*. Note that u16 will start |
476 | | // with the UTF16 marker, which we skip. |
477 | | std::string u16 = QUtil::utf8_to_utf16(filename); |
478 | | size_t len = u16.length(); |
479 | | size_t wlen = (len / 2) - 1; |
480 | | auto wfilenamep = QUtil::make_shared_array<wchar_t>(wlen + 1); |
481 | | wchar_t* wfilename = wfilenamep.get(); |
482 | | wfilename[wlen] = 0; |
483 | | for (unsigned int i = 2; i < len; i += 2) { |
484 | | wfilename[(i / 2) - 1] = static_cast<wchar_t>( |
485 | | (static_cast<unsigned char>(u16.at(i)) << 8) + |
486 | | static_cast<unsigned char>(u16.at(i + 1))); |
487 | | } |
488 | | return wfilenamep; |
489 | | } |
490 | | #endif |
491 | | |
492 | | FILE* |
493 | | QUtil::safe_fopen(char const* filename, char const* mode) |
494 | 0 | { |
495 | 0 | FILE* f = nullptr; |
496 | | #ifdef _WIN32 |
497 | | std::shared_ptr<wchar_t> wfilenamep = win_convert_filename(filename); |
498 | | wchar_t* wfilename = wfilenamep.get(); |
499 | | auto wmodep = QUtil::make_shared_array<wchar_t>(strlen(mode) + 1); |
500 | | wchar_t* wmode = wmodep.get(); |
501 | | wmode[strlen(mode)] = 0; |
502 | | for (size_t i = 0; i < strlen(mode); ++i) { |
503 | | wmode[i] = static_cast<wchar_t>(mode[i]); |
504 | | } |
505 | | |
506 | | # ifdef _MSC_VER |
507 | | errno_t err = _wfopen_s(&f, wfilename, wmode); |
508 | | if (err != 0) { |
509 | | errno = err; |
510 | | } |
511 | | # else |
512 | | f = _wfopen(wfilename, wmode); |
513 | | # endif |
514 | | if (f == 0) { |
515 | | throw_system_error(std::string("open ") + filename); |
516 | | } |
517 | | #else |
518 | 0 | f = fopen_wrapper(std::string("open ") + filename, fopen(filename, mode)); |
519 | 0 | #endif |
520 | 0 | return f; |
521 | 0 | } |
522 | | |
523 | | FILE* |
524 | | QUtil::fopen_wrapper(std::string const& description, FILE* f) |
525 | 0 | { |
526 | 0 | if (f == nullptr) { |
527 | 0 | throw_system_error(description); |
528 | 0 | } |
529 | 0 | return f; |
530 | 0 | } |
531 | | |
532 | | bool |
533 | | QUtil::file_can_be_opened(char const* filename) |
534 | 0 | { |
535 | 0 | try { |
536 | 0 | fclose(safe_fopen(filename, "rb")); |
537 | 0 | return true; |
538 | 0 | } catch (std::runtime_error&) { |
539 | | // can't open the file |
540 | 0 | } |
541 | 0 | return false; |
542 | 0 | } |
543 | | |
544 | | int |
545 | | QUtil::seek(FILE* stream, qpdf_offset_t offset, int whence) |
546 | 0 | { |
547 | 0 | #if HAVE_FSEEKO |
548 | 0 | return fseeko(stream, QIntC::IntConverter<qpdf_offset_t, off_t>::convert(offset), whence); |
549 | | #elif HAVE_FSEEKO64 |
550 | | return fseeko64(stream, offset, whence); |
551 | | #else |
552 | | # if defined _MSC_VER || defined __BORLANDC__ |
553 | | return _fseeki64(stream, offset, whence); |
554 | | # else |
555 | | return fseek(stream, QIntC::to_long(offset), whence); |
556 | | # endif |
557 | | #endif |
558 | 0 | } |
559 | | |
560 | | qpdf_offset_t |
561 | | QUtil::tell(FILE* stream) |
562 | 0 | { |
563 | 0 | #if HAVE_FSEEKO |
564 | 0 | return QIntC::to_offset(ftello(stream)); |
565 | | #elif HAVE_FSEEKO64 |
566 | | return QIntC::to_offset(ftello64(stream)); |
567 | | #else |
568 | | # if defined _MSC_VER || defined __BORLANDC__ |
569 | | return _ftelli64(stream); |
570 | | # else |
571 | | return QIntC::to_offset(ftell(stream)); |
572 | | # endif |
573 | | #endif |
574 | 0 | } |
575 | | |
576 | | bool |
577 | | QUtil::same_file(char const* name1, char const* name2) |
578 | 0 | { |
579 | 0 | if ((name1 == nullptr) || (strlen(name1) == 0) || (name2 == nullptr) || (strlen(name2) == 0)) { |
580 | 0 | return false; |
581 | 0 | } |
582 | | #ifdef _WIN32 |
583 | | bool same = false; |
584 | | # ifndef AVOID_WINDOWS_HANDLE |
585 | | HANDLE fh1 = CreateFile( |
586 | | name1, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
587 | | HANDLE fh2 = CreateFile( |
588 | | name2, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
589 | | BY_HANDLE_FILE_INFORMATION fi1; |
590 | | BY_HANDLE_FILE_INFORMATION fi2; |
591 | | if ((fh1 != INVALID_HANDLE_VALUE) && (fh2 != INVALID_HANDLE_VALUE) && |
592 | | GetFileInformationByHandle(fh1, &fi1) && GetFileInformationByHandle(fh2, &fi2) && |
593 | | (fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber) && |
594 | | (fi1.nFileIndexLow == fi2.nFileIndexLow) && (fi1.nFileIndexHigh == fi2.nFileIndexHigh)) { |
595 | | same = true; |
596 | | } |
597 | | if (fh1 != INVALID_HANDLE_VALUE) { |
598 | | CloseHandle(fh1); |
599 | | } |
600 | | if (fh2 != INVALID_HANDLE_VALUE) { |
601 | | CloseHandle(fh2); |
602 | | } |
603 | | # endif |
604 | | return same; |
605 | | #else |
606 | 0 | struct stat st1; |
607 | 0 | struct stat st2; |
608 | 0 | if ((stat(name1, &st1) == 0) && (stat(name2, &st2) == 0) && (st1.st_ino == st2.st_ino) && |
609 | 0 | (st1.st_dev == st2.st_dev)) { |
610 | 0 | return true; |
611 | 0 | } |
612 | 0 | #endif |
613 | 0 | return false; |
614 | 0 | } |
615 | | |
616 | | void |
617 | | QUtil::remove_file(char const* path) |
618 | 0 | { |
619 | | #ifdef _WIN32 |
620 | | std::shared_ptr<wchar_t> wpath = win_convert_filename(path); |
621 | | os_wrapper(std::string("remove ") + path, _wunlink(wpath.get())); |
622 | | #else |
623 | 0 | os_wrapper(std::string("remove ") + path, unlink(path)); |
624 | 0 | #endif |
625 | 0 | } |
626 | | |
627 | | void |
628 | | QUtil::rename_file(char const* oldname, char const* newname) |
629 | 0 | { |
630 | | #ifdef _WIN32 |
631 | | try { |
632 | | remove_file(newname); |
633 | | } catch (QPDFSystemError&) { |
634 | | // ignore |
635 | | } |
636 | | std::shared_ptr<wchar_t> wold = win_convert_filename(oldname); |
637 | | std::shared_ptr<wchar_t> wnew = win_convert_filename(newname); |
638 | | os_wrapper(std::string("rename ") + oldname + " " + newname, _wrename(wold.get(), wnew.get())); |
639 | | #else |
640 | 0 | os_wrapper(std::string("rename ") + oldname + " " + newname, rename(oldname, newname)); |
641 | 0 | #endif |
642 | 0 | } |
643 | | |
644 | | void |
645 | | QUtil::pipe_file(char const* filename, Pipeline* p) |
646 | 0 | { |
647 | | // Exercised in test suite by testing file_provider. |
648 | 0 | FILE* f = safe_fopen(filename, "rb"); |
649 | 0 | FileCloser fc(f); |
650 | 0 | size_t len = 0; |
651 | 0 | int constexpr size = 8192; |
652 | 0 | unsigned char buf[size]; |
653 | 0 | while ((len = fread(buf, 1, size, f)) > 0) { |
654 | 0 | p->write(buf, len); |
655 | 0 | } |
656 | 0 | p->finish(); |
657 | 0 | if (ferror(f)) { |
658 | 0 | throw std::runtime_error(std::string("failure reading file ") + filename); |
659 | 0 | } |
660 | 0 | } |
661 | | |
662 | | std::function<void(Pipeline*)> |
663 | | QUtil::file_provider(std::string const& filename) |
664 | 0 | { |
665 | 0 | return [filename](Pipeline* p) { pipe_file(filename.c_str(), p); }; |
666 | 0 | } |
667 | | |
668 | | std::string |
669 | | QUtil::path_basename(std::string const& filename) |
670 | 0 | { |
671 | | #ifdef _WIN32 |
672 | | char const* pathsep = "/\\"; |
673 | | #else |
674 | 0 | char const* pathsep = "/"; |
675 | 0 | #endif |
676 | 0 | std::string last = filename; |
677 | 0 | auto len = last.length(); |
678 | 0 | while (len > 1) { |
679 | 0 | auto pos = last.find_last_of(pathsep); |
680 | 0 | if (pos == len - 1) { |
681 | 0 | last.pop_back(); |
682 | 0 | --len; |
683 | 0 | } else if (pos == std::string::npos) { |
684 | 0 | break; |
685 | 0 | } else { |
686 | 0 | last = last.substr(pos + 1); |
687 | 0 | break; |
688 | 0 | } |
689 | 0 | } |
690 | 0 | return last; |
691 | 0 | } |
692 | | |
693 | | char* |
694 | | QUtil::copy_string(std::string const& str) |
695 | 0 | { |
696 | 0 | char* result = new char[str.length() + 1]; |
697 | | // Use memcpy in case string contains nulls |
698 | 0 | result[str.length()] = '\0'; |
699 | 0 | memcpy(result, str.c_str(), str.length()); |
700 | 0 | return result; |
701 | 0 | } |
702 | | |
703 | | std::shared_ptr<char> |
704 | | QUtil::make_shared_cstr(std::string const& str) |
705 | 0 | { |
706 | 0 | auto result = QUtil::make_shared_array<char>(str.length() + 1); |
707 | | // Use memcpy in case string contains nulls |
708 | 0 | result.get()[str.length()] = '\0'; |
709 | 0 | memcpy(result.get(), str.c_str(), str.length()); |
710 | 0 | return result; |
711 | 0 | } |
712 | | |
713 | | std::unique_ptr<char[]> |
714 | | QUtil::make_unique_cstr(std::string const& str) |
715 | 0 | { |
716 | 0 | auto result = std::make_unique<char[]>(str.length() + 1); |
717 | | // Use memcpy in case string contains nulls |
718 | 0 | result.get()[str.length()] = '\0'; |
719 | 0 | memcpy(result.get(), str.c_str(), str.length()); |
720 | 0 | return result; |
721 | 0 | } |
722 | | |
723 | | std::string |
724 | | QUtil::hex_encode(std::string const& input) |
725 | 0 | { |
726 | 0 | static auto constexpr hexchars = "0123456789abcdef"; |
727 | 0 | std::string result; |
728 | 0 | result.reserve(2 * input.length()); |
729 | 0 | for (const char c: input) { |
730 | 0 | result += hexchars[static_cast<unsigned char>(c) >> 4]; |
731 | 0 | result += hexchars[c & 0x0f]; |
732 | 0 | } |
733 | 0 | return result; |
734 | 0 | } |
735 | | |
736 | | std::string |
737 | | QUtil::hex_decode(std::string const& input) |
738 | 0 | { |
739 | 0 | std::string result; |
740 | | // We know result.size() <= 0.5 * input.size() + 1. However, reserving string space for this |
741 | | // upper bound has a negative impact. |
742 | 0 | bool first = true; |
743 | 0 | char decoded; |
744 | 0 | for (auto ch: input) { |
745 | 0 | ch = util::hex_decode_char(ch); |
746 | 0 | if (ch < '\20') { |
747 | 0 | if (first) { |
748 | 0 | decoded = static_cast<char>(ch << 4); |
749 | 0 | first = false; |
750 | 0 | } else { |
751 | 0 | result.push_back(decoded | ch); |
752 | 0 | first = true; |
753 | 0 | } |
754 | 0 | } |
755 | 0 | } |
756 | 0 | if (!first) { |
757 | 0 | result.push_back(decoded); |
758 | 0 | } |
759 | 0 | return result; |
760 | 0 | } |
761 | | |
762 | | void |
763 | | QUtil::binary_stdout() |
764 | 0 | { |
765 | | #if defined(_WIN32) && defined(__BORLANDC__) |
766 | | setmode(_fileno(stdout), _O_BINARY); |
767 | | #elif defined(_WIN32) |
768 | | _setmode(_fileno(stdout), _O_BINARY); |
769 | | #endif |
770 | 0 | } |
771 | | |
772 | | void |
773 | | QUtil::binary_stdin() |
774 | 0 | { |
775 | | #if defined(_WIN32) && defined(__BORLANDC__) |
776 | | setmode(_fileno(stdin), _O_BINARY); |
777 | | #elif defined(_WIN32) |
778 | | _setmode(_fileno(stdin), _O_BINARY); |
779 | | #endif |
780 | 0 | } |
781 | | |
782 | | void |
783 | | QUtil::setLineBuf(FILE* f) |
784 | 0 | { |
785 | 0 | #ifndef _WIN32 |
786 | 0 | setvbuf(f, reinterpret_cast<char*>(0), _IOLBF, 0); |
787 | 0 | #endif |
788 | 0 | } |
789 | | |
790 | | char* |
791 | | QUtil::getWhoami(char* argv0) |
792 | 0 | { |
793 | 0 | char* whoami = nullptr; |
794 | 0 | if (((whoami = strrchr(argv0, '/')) == nullptr) && |
795 | 0 | ((whoami = strrchr(argv0, '\\')) == nullptr)) { |
796 | 0 | whoami = argv0; |
797 | 0 | } else { |
798 | 0 | ++whoami; |
799 | 0 | } |
800 | |
|
801 | 0 | if ((strlen(whoami) > 4) && (strcmp(whoami + strlen(whoami) - 4, ".exe") == 0)) { |
802 | 0 | whoami[strlen(whoami) - 4] = '\0'; |
803 | 0 | } |
804 | |
|
805 | 0 | return whoami; |
806 | 0 | } |
807 | | |
808 | | bool |
809 | | QUtil::get_env(std::string const& var, std::string* value) |
810 | 0 | { |
811 | | // This was basically ripped out of wxWindows. |
812 | | #ifdef _WIN32 |
813 | | # ifdef NO_GET_ENVIRONMENT |
814 | | return false; |
815 | | # else |
816 | | // first get the size of the buffer |
817 | | DWORD len = ::GetEnvironmentVariable(var.c_str(), NULL, 0); |
818 | | if (len == 0) { |
819 | | // this means that there is no such variable |
820 | | return false; |
821 | | } |
822 | | |
823 | | if (value) { |
824 | | auto t = QUtil::make_shared_array<char>(len + 1); |
825 | | ::GetEnvironmentVariable(var.c_str(), t.get(), len); |
826 | | *value = t.get(); |
827 | | } |
828 | | |
829 | | return true; |
830 | | # endif |
831 | | #else |
832 | 0 | char* p = getenv(var.c_str()); |
833 | 0 | if (p == nullptr) { |
834 | 0 | return false; |
835 | 0 | } |
836 | 0 | if (value) { |
837 | 0 | *value = p; |
838 | 0 | } |
839 | |
|
840 | 0 | return true; |
841 | 0 | #endif |
842 | 0 | } |
843 | | |
844 | | time_t |
845 | | QUtil::get_current_time() |
846 | 0 | { |
847 | | #ifdef _WIN32 |
848 | | // The procedure to get local time at this resolution comes from |
849 | | // the Microsoft documentation. It says to convert a SYSTEMTIME |
850 | | // to a FILETIME, and to copy the FILETIME to a ULARGE_INTEGER. |
851 | | // The resulting number is the number of 100-nanosecond intervals |
852 | | // between January 1, 1601 and now. POSIX threads wants a time |
853 | | // based on January 1, 1970, so we adjust by subtracting the |
854 | | // number of seconds in that time period from the result we get |
855 | | // here. |
856 | | SYSTEMTIME sysnow; |
857 | | GetSystemTime(&sysnow); |
858 | | FILETIME filenow; |
859 | | SystemTimeToFileTime(&sysnow, &filenow); |
860 | | ULARGE_INTEGER uinow; |
861 | | uinow.LowPart = filenow.dwLowDateTime; |
862 | | uinow.HighPart = filenow.dwHighDateTime; |
863 | | ULONGLONG now = uinow.QuadPart; |
864 | | return static_cast<time_t>((now / 10000000ULL) - 11644473600ULL); |
865 | | #else |
866 | 0 | return time(nullptr); |
867 | 0 | #endif |
868 | 0 | } |
869 | | |
870 | | QUtil::QPDFTime |
871 | | QUtil::get_current_qpdf_time() |
872 | 0 | { |
873 | | #ifdef _WIN32 |
874 | | SYSTEMTIME ltime; |
875 | | GetLocalTime(<ime); |
876 | | TIME_ZONE_INFORMATION tzinfo; |
877 | | GetTimeZoneInformation(&tzinfo); |
878 | | return QPDFTime( |
879 | | static_cast<int>(ltime.wYear), |
880 | | static_cast<int>(ltime.wMonth), |
881 | | static_cast<int>(ltime.wDay), |
882 | | static_cast<int>(ltime.wHour), |
883 | | static_cast<int>(ltime.wMinute), |
884 | | static_cast<int>(ltime.wSecond), |
885 | | // tzinfo.Bias is minutes before UTC |
886 | | static_cast<int>(tzinfo.Bias)); |
887 | | #else |
888 | 0 | struct tm ltime; |
889 | 0 | time_t now = time(nullptr); |
890 | 0 | tzset(); |
891 | 0 | # ifdef HAVE_LOCALTIME_R |
892 | 0 | localtime_r(&now, <ime); |
893 | | # else |
894 | | ltime = *localtime(&now); |
895 | | # endif |
896 | | # if HAVE_TM_GMTOFF |
897 | | // tm_gmtoff is seconds after UTC |
898 | | int tzoff = -static_cast<int>(ltime.tm_gmtoff / 60); |
899 | | # elif HAVE_EXTERN_LONG_TIMEZONE |
900 | | // timezone is seconds before UTC, not adjusted for daylight saving time |
901 | 0 | int tzoff = static_cast<int>(timezone / 60); |
902 | | # else |
903 | | // Don't know how to get timezone on this platform |
904 | | int tzoff = 0; |
905 | | # endif |
906 | 0 | return { |
907 | 0 | static_cast<int>(ltime.tm_year + 1900), |
908 | 0 | static_cast<int>(ltime.tm_mon + 1), |
909 | 0 | static_cast<int>(ltime.tm_mday), |
910 | 0 | static_cast<int>(ltime.tm_hour), |
911 | 0 | static_cast<int>(ltime.tm_min), |
912 | 0 | static_cast<int>(ltime.tm_sec), |
913 | 0 | tzoff}; |
914 | 0 | #endif |
915 | 0 | } |
916 | | |
917 | | std::string |
918 | | QUtil::qpdf_time_to_pdf_time(QPDFTime const& qtm) |
919 | 0 | { |
920 | 0 | std::string tz_offset; |
921 | 0 | int t = qtm.tz_delta; |
922 | 0 | if (t == 0) { |
923 | 0 | tz_offset = "Z"; |
924 | 0 | } else { |
925 | 0 | if (t < 0) { |
926 | 0 | t = -t; |
927 | 0 | tz_offset += "+"; |
928 | 0 | } else { |
929 | 0 | tz_offset += "-"; |
930 | 0 | } |
931 | 0 | tz_offset += QUtil::int_to_string(t / 60, 2) + "'" + QUtil::int_to_string(t % 60, 2) + "'"; |
932 | 0 | } |
933 | 0 | return ( |
934 | 0 | "D:" + QUtil::int_to_string(qtm.year, 4) + QUtil::int_to_string(qtm.month, 2) + |
935 | 0 | QUtil::int_to_string(qtm.day, 2) + QUtil::int_to_string(qtm.hour, 2) + |
936 | 0 | QUtil::int_to_string(qtm.minute, 2) + QUtil::int_to_string(qtm.second, 2) + tz_offset); |
937 | 0 | } |
938 | | |
939 | | std::string |
940 | | QUtil::qpdf_time_to_iso8601(QPDFTime const& qtm) |
941 | 0 | { |
942 | 0 | std::string tz_offset; |
943 | 0 | int t = qtm.tz_delta; |
944 | 0 | if (t == 0) { |
945 | 0 | tz_offset = "Z"; |
946 | 0 | } else { |
947 | 0 | if (t < 0) { |
948 | 0 | t = -t; |
949 | 0 | tz_offset += "+"; |
950 | 0 | } else { |
951 | 0 | tz_offset += "-"; |
952 | 0 | } |
953 | 0 | tz_offset += QUtil::int_to_string(t / 60, 2) + ":" + QUtil::int_to_string(t % 60, 2); |
954 | 0 | } |
955 | 0 | return ( |
956 | 0 | QUtil::int_to_string(qtm.year, 4) + "-" + QUtil::int_to_string(qtm.month, 2) + "-" + |
957 | 0 | QUtil::int_to_string(qtm.day, 2) + "T" + QUtil::int_to_string(qtm.hour, 2) + ":" + |
958 | 0 | QUtil::int_to_string(qtm.minute, 2) + ":" + QUtil::int_to_string(qtm.second, 2) + |
959 | 0 | tz_offset); |
960 | 0 | } |
961 | | |
962 | | bool |
963 | | QUtil::pdf_time_to_qpdf_time(std::string const& str, QPDFTime* qtm) |
964 | 0 | { |
965 | 0 | static std::regex pdf_date( |
966 | 0 | "^D:([0-9]{4})([0-9]{2})([0-9]{2})" |
967 | 0 | "([0-9]{2})([0-9]{2})([0-9]{2})" |
968 | 0 | "(?:(Z?)|([\\+\\-])([0-9]{2})'([0-9]{2})')$"); |
969 | 0 | std::smatch m; |
970 | 0 | if (!std::regex_match(str, m, pdf_date)) { |
971 | 0 | return false; |
972 | 0 | } |
973 | 0 | int tz_delta = 0; |
974 | 0 | auto to_i = [](std::string const& s) { return QUtil::string_to_int(s.c_str()); }; |
975 | |
|
976 | 0 | if (m[8] != "") { |
977 | 0 | tz_delta = ((to_i(m[9]) * 60) + to_i(m[10])); |
978 | 0 | if (m[8] == "+") { |
979 | 0 | tz_delta = -tz_delta; |
980 | 0 | } |
981 | 0 | } |
982 | 0 | if (qtm) { |
983 | 0 | *qtm = QPDFTime( |
984 | 0 | to_i(m[1]), to_i(m[2]), to_i(m[3]), to_i(m[4]), to_i(m[5]), to_i(m[6]), tz_delta); |
985 | 0 | } |
986 | 0 | return true; |
987 | 0 | } |
988 | | |
989 | | bool |
990 | | QUtil::pdf_time_to_iso8601(std::string const& pdf_time, std::string& iso8601) |
991 | 0 | { |
992 | 0 | QPDFTime qtm; |
993 | 0 | if (pdf_time_to_qpdf_time(pdf_time, &qtm)) { |
994 | 0 | iso8601 = qpdf_time_to_iso8601(qtm); |
995 | 0 | return true; |
996 | 0 | } |
997 | 0 | return false; |
998 | 0 | } |
999 | | |
1000 | | std::string |
1001 | | QUtil::toUTF8(unsigned long uval) |
1002 | 0 | { |
1003 | 0 | std::string result; |
1004 | | |
1005 | | // A UTF-8 encoding of a Unicode value is a single byte for Unicode values <= 127. For larger |
1006 | | // values, the first byte of the UTF-8 encoding has '1' as each of its n highest bits and '0' |
1007 | | // for its (n+1)th highest bit where n is the total number of bytes required. Subsequent bytes |
1008 | | // start with '10' and have the remaining 6 bits free for encoding. For example, an 11-bit |
1009 | | // Unicode value can be stored in two bytes where the first is 110zzzzz, the second is 10zzzzzz, |
1010 | | // and the z's represent the remaining bits. |
1011 | |
|
1012 | 0 | if (uval > 0x7fffffff) { |
1013 | 0 | throw std::runtime_error("bounds error in QUtil::toUTF8"); |
1014 | 0 | } else if (uval < 128) { |
1015 | 0 | result += static_cast<char>(uval); |
1016 | 0 | } else { |
1017 | 0 | unsigned char bytes[7]; |
1018 | 0 | bytes[6] = '\0'; |
1019 | 0 | unsigned char* cur_byte = &bytes[5]; |
1020 | | |
1021 | | // maximum value that will fit in the current number of bytes |
1022 | 0 | unsigned char maxval = 0x3f; // six bits |
1023 | |
|
1024 | 0 | while (uval > QIntC::to_ulong(maxval)) { |
1025 | | // Assign low six bits plus 10000000 to lowest unused byte position, then shift |
1026 | 0 | *cur_byte = static_cast<unsigned char>(0x80 + (uval & 0x3f)); |
1027 | 0 | uval >>= 6; |
1028 | | // Maximum that will fit in high byte now shrinks by one bit |
1029 | 0 | maxval = static_cast<unsigned char>(maxval >> 1); |
1030 | | // Slide to the left one byte |
1031 | 0 | if (cur_byte <= bytes) { |
1032 | 0 | throw std::logic_error("QUtil::toUTF8: overflow error"); |
1033 | 0 | } |
1034 | 0 | --cur_byte; |
1035 | 0 | } |
1036 | | // If maxval is k bits long, the high (7 - k) bits of the resulting byte must be high. |
1037 | 0 | *cur_byte = static_cast<unsigned char>(QIntC::to_ulong(0xff - (1 + (maxval << 1))) + uval); |
1038 | |
|
1039 | 0 | result += reinterpret_cast<char*>(cur_byte); |
1040 | 0 | } |
1041 | | |
1042 | 0 | return result; |
1043 | 0 | } |
1044 | | |
1045 | | std::string |
1046 | | QUtil::toUTF16(unsigned long uval) |
1047 | 0 | { |
1048 | 0 | std::string result; |
1049 | 0 | if ((uval >= 0xd800) && (uval <= 0xdfff)) { |
1050 | 0 | result = "\xff\xfd"; |
1051 | 0 | } else if (uval <= 0xffff) { |
1052 | 0 | char out[2]; |
1053 | 0 | out[0] = static_cast<char>((uval & 0xff00) >> 8); |
1054 | 0 | out[1] = static_cast<char>(uval & 0xff); |
1055 | 0 | result = std::string(out, 2); |
1056 | 0 | } else if (uval <= 0x10ffff) { |
1057 | 0 | char out[4]; |
1058 | 0 | uval -= 0x10000; |
1059 | 0 | unsigned short high = static_cast<unsigned short>(((uval & 0xffc00) >> 10) + 0xd800); |
1060 | 0 | unsigned short low = static_cast<unsigned short>((uval & 0x3ff) + 0xdc00); |
1061 | 0 | out[0] = static_cast<char>((high & 0xff00) >> 8); |
1062 | 0 | out[1] = static_cast<char>(high & 0xff); |
1063 | 0 | out[2] = static_cast<char>((low & 0xff00) >> 8); |
1064 | 0 | out[3] = static_cast<char>(low & 0xff); |
1065 | 0 | result = std::string(out, 4); |
1066 | 0 | } else { |
1067 | 0 | result = "\xff\xfd"; |
1068 | 0 | } |
1069 | |
|
1070 | 0 | return result; |
1071 | 0 | } |
1072 | | |
1073 | | // Random data support |
1074 | | |
1075 | | namespace |
1076 | | { |
1077 | | class RandomDataProviderProvider |
1078 | | { |
1079 | | public: |
1080 | | RandomDataProviderProvider(); |
1081 | | void setProvider(RandomDataProvider*); |
1082 | | RandomDataProvider* getProvider(); |
1083 | | |
1084 | | private: |
1085 | | RandomDataProvider* default_provider; |
1086 | | RandomDataProvider* current_provider{nullptr}; |
1087 | | }; |
1088 | | } // namespace |
1089 | | |
1090 | | RandomDataProviderProvider::RandomDataProviderProvider() : |
1091 | 0 | default_provider(CryptoRandomDataProvider::getInstance()) |
1092 | 0 | { |
1093 | 0 | this->current_provider = default_provider; |
1094 | 0 | } |
1095 | | |
1096 | | RandomDataProvider* |
1097 | | RandomDataProviderProvider::getProvider() |
1098 | 0 | { |
1099 | 0 | return this->current_provider; |
1100 | 0 | } |
1101 | | |
1102 | | void |
1103 | | RandomDataProviderProvider::setProvider(RandomDataProvider* p) |
1104 | 0 | { |
1105 | 0 | this->current_provider = p ? p : this->default_provider; |
1106 | 0 | } |
1107 | | |
1108 | | static RandomDataProviderProvider* |
1109 | | getRandomDataProviderProvider() |
1110 | 0 | { |
1111 | | // Thread-safe static initializer |
1112 | 0 | static RandomDataProviderProvider rdpp; |
1113 | 0 | return &rdpp; |
1114 | 0 | } |
1115 | | |
1116 | | void |
1117 | | QUtil::setRandomDataProvider(RandomDataProvider* p) |
1118 | 0 | { |
1119 | 0 | getRandomDataProviderProvider()->setProvider(p); |
1120 | 0 | } |
1121 | | |
1122 | | RandomDataProvider* |
1123 | | QUtil::getRandomDataProvider() |
1124 | 0 | { |
1125 | 0 | return getRandomDataProviderProvider()->getProvider(); |
1126 | 0 | } |
1127 | | |
1128 | | void |
1129 | | QUtil::initializeWithRandomBytes(unsigned char* data, size_t len) |
1130 | 0 | { |
1131 | 0 | getRandomDataProvider()->provideRandomData(data, len); |
1132 | 0 | } |
1133 | | |
1134 | | long |
1135 | | QUtil::random() |
1136 | 0 | { |
1137 | 0 | long result = 0L; |
1138 | 0 | initializeWithRandomBytes(reinterpret_cast<unsigned char*>(&result), sizeof(result)); |
1139 | 0 | return result; |
1140 | 0 | } |
1141 | | |
1142 | | void |
1143 | | QUtil::read_file_into_memory(char const* filename, std::shared_ptr<char>& file_buf, size_t& size) |
1144 | 0 | { |
1145 | 0 | FILE* f = safe_fopen(filename, "rb"); |
1146 | 0 | FileCloser fc(f); |
1147 | 0 | fseek(f, 0, SEEK_END); |
1148 | 0 | size = QIntC::to_size(QUtil::tell(f)); |
1149 | 0 | fseek(f, 0, SEEK_SET); |
1150 | 0 | file_buf = QUtil::make_shared_array<char>(size); |
1151 | 0 | char* buf_p = file_buf.get(); |
1152 | 0 | size_t bytes_read = 0; |
1153 | 0 | size_t len = 0; |
1154 | 0 | while ((len = fread(buf_p + bytes_read, 1, size - bytes_read, f)) > 0) { |
1155 | 0 | bytes_read += len; |
1156 | 0 | } |
1157 | 0 | if (bytes_read != size) { |
1158 | 0 | if (ferror(f)) { |
1159 | 0 | throw std::runtime_error( |
1160 | 0 | std::string("failure reading file ") + filename + " into memory: read " + |
1161 | 0 | uint_to_string(bytes_read) + "; wanted " + uint_to_string(size)); |
1162 | 0 | } else { |
1163 | 0 | throw std::runtime_error( |
1164 | 0 | std::string("premature eof reading file ") + filename + " into memory: read " + |
1165 | 0 | uint_to_string(bytes_read) + "; wanted " + uint_to_string(size)); |
1166 | 0 | } |
1167 | 0 | } |
1168 | 0 | } |
1169 | | |
1170 | | std::string |
1171 | | QUtil::read_file_into_string(char const* filename) |
1172 | 0 | { |
1173 | 0 | FILE* f = safe_fopen(filename, "rb"); |
1174 | 0 | FileCloser fc(f); |
1175 | 0 | return read_file_into_string(f, filename); |
1176 | 0 | } |
1177 | | |
1178 | | std::string |
1179 | | QUtil::read_file_into_string(FILE* f, std::string_view filename) |
1180 | 0 | { |
1181 | 0 | fseek(f, 0, SEEK_END); |
1182 | 0 | auto o_size = QUtil::tell(f); |
1183 | 0 | if (o_size >= 0) { |
1184 | | // Seekable file |
1185 | 0 | auto size = QIntC::to_size(o_size); |
1186 | 0 | fseek(f, 0, SEEK_SET); |
1187 | 0 | std::string result(size, '\0'); |
1188 | 0 | if (auto n_read = fread(result.data(), 1, size, f); n_read != size) { |
1189 | 0 | if (ferror(f)) { |
1190 | 0 | throw std::runtime_error( |
1191 | 0 | std::string("failure reading file ") + std::string(filename) + |
1192 | 0 | " into memory: read " + uint_to_string(n_read) + "; wanted " + |
1193 | 0 | uint_to_string(size)); |
1194 | 0 | } else { |
1195 | 0 | throw std::runtime_error( |
1196 | 0 | std::string("premature eof reading file ") + std::string(filename) + |
1197 | 0 | " into memory: read " + uint_to_string(n_read) + "; wanted " + |
1198 | 0 | uint_to_string(size)); |
1199 | 0 | } |
1200 | 0 | } |
1201 | 0 | return result; |
1202 | 0 | } else { |
1203 | | // Pipe or other non-seekable file |
1204 | 0 | size_t buf_size = 8192; |
1205 | 0 | auto n_read = buf_size; |
1206 | 0 | std::string buffer(buf_size, '\0'); |
1207 | 0 | std::string result; |
1208 | 0 | while (n_read == buf_size) { |
1209 | 0 | n_read = fread(buffer.data(), 1, buf_size, f); |
1210 | 0 | buffer.erase(n_read); |
1211 | 0 | result.append(buffer); |
1212 | 0 | } |
1213 | 0 | if (ferror(f)) { |
1214 | 0 | throw std::runtime_error( |
1215 | 0 | std::string("failure reading file ") + std::string(filename) + " into memory"); |
1216 | 0 | } |
1217 | 0 | return result; |
1218 | 0 | } |
1219 | 0 | } |
1220 | | |
1221 | | static bool |
1222 | | read_char_from_FILE(char& ch, FILE* f) |
1223 | 0 | { |
1224 | 0 | auto len = fread(&ch, 1, 1, f); |
1225 | 0 | if (len == 0) { |
1226 | 0 | if (ferror(f)) { |
1227 | 0 | throw std::runtime_error("failure reading character from file"); |
1228 | 0 | } |
1229 | 0 | return false; |
1230 | 0 | } |
1231 | 0 | return true; |
1232 | 0 | } |
1233 | | |
1234 | | std::list<std::string> |
1235 | | QUtil::read_lines_from_file(char const* filename, bool preserve_eol) |
1236 | 0 | { |
1237 | 0 | std::list<std::string> lines; |
1238 | 0 | FILE* f = safe_fopen(filename, "rb"); |
1239 | 0 | FileCloser fc(f); |
1240 | 0 | auto next_char = [&f](char& ch) { return read_char_from_FILE(ch, f); }; |
1241 | 0 | read_lines_from_file(next_char, lines, preserve_eol); |
1242 | 0 | return lines; |
1243 | 0 | } |
1244 | | |
1245 | | std::list<std::string> |
1246 | | QUtil::read_lines_from_file(std::istream& in, bool preserve_eol) |
1247 | 0 | { |
1248 | 0 | std::list<std::string> lines; |
1249 | 0 | auto next_char = [&in](char& ch) { return (in.get(ch)) ? true : false; }; |
1250 | 0 | read_lines_from_file(next_char, lines, preserve_eol); |
1251 | 0 | return lines; |
1252 | 0 | } |
1253 | | |
1254 | | std::list<std::string> |
1255 | | QUtil::read_lines_from_file(FILE* f, bool preserve_eol) |
1256 | 0 | { |
1257 | 0 | std::list<std::string> lines; |
1258 | 0 | auto next_char = [&f](char& ch) { return read_char_from_FILE(ch, f); }; |
1259 | 0 | read_lines_from_file(next_char, lines, preserve_eol); |
1260 | 0 | return lines; |
1261 | 0 | } |
1262 | | |
1263 | | void |
1264 | | QUtil::read_lines_from_file( |
1265 | | std::function<bool(char&)> next_char, std::list<std::string>& lines, bool preserve_eol) |
1266 | 0 | { |
1267 | 0 | std::string* buf = nullptr; |
1268 | 0 | char c; |
1269 | 0 | while (next_char(c)) { |
1270 | 0 | if (buf == nullptr) { |
1271 | 0 | lines.emplace_back(""); |
1272 | 0 | buf = &(lines.back()); |
1273 | 0 | buf->reserve(80); |
1274 | 0 | } |
1275 | |
|
1276 | 0 | if (buf->capacity() == buf->size()) { |
1277 | 0 | buf->reserve(buf->capacity() * 2); |
1278 | 0 | } |
1279 | 0 | if (c == '\n') { |
1280 | 0 | if (preserve_eol) { |
1281 | 0 | buf->append(1, c); |
1282 | 0 | } else { |
1283 | | // Remove any carriage return that preceded the newline and discard the newline |
1284 | 0 | if ((!buf->empty()) && ((*(buf->rbegin())) == '\r')) { |
1285 | 0 | buf->erase(buf->length() - 1); |
1286 | 0 | } |
1287 | 0 | } |
1288 | 0 | buf = nullptr; |
1289 | 0 | } else { |
1290 | 0 | buf->append(1, c); |
1291 | 0 | } |
1292 | 0 | } |
1293 | 0 | } |
1294 | | |
1295 | | int |
1296 | | QUtil::str_compare_nocase(char const* s1, char const* s2) |
1297 | 0 | { |
1298 | | #if defined(_WIN32) && defined(__BORLANDC__) |
1299 | | return stricmp(s1, s2); |
1300 | | #elif defined(_WIN32) |
1301 | | return _stricmp(s1, s2); |
1302 | | #else |
1303 | 0 | return strcasecmp(s1, s2); |
1304 | 0 | #endif |
1305 | 0 | } |
1306 | | |
1307 | | std::vector<int> |
1308 | | QUtil::parse_numrange(char const* range, int max) |
1309 | 0 | { |
1310 | | // Performance note: this implementation aims to be straightforward, not efficient. Numeric |
1311 | | // range parsing is used only during argument processing. It is not used during processing of |
1312 | | // PDF files. |
1313 | |
|
1314 | 0 | static std::regex group_re(R"((x)?(z|r?\d+)(?:-(z|r?\d+))?)"); |
1315 | 0 | auto parse_num = [&max](std::string const& s) -> int { |
1316 | 0 | if (s == "z") { |
1317 | 0 | return max; |
1318 | 0 | } |
1319 | 0 | int num; |
1320 | 0 | if (s.at(0) == 'r') { |
1321 | 0 | num = max + 1 - string_to_int(s.substr(1).c_str()); |
1322 | 0 | } else { |
1323 | 0 | num = string_to_int(s.c_str()); |
1324 | 0 | } |
1325 | | // max == 0 means we don't know the max and are just testing for valid syntax. |
1326 | 0 | if ((max > 0) && ((num < 1) || (num > max))) { |
1327 | 0 | throw std::runtime_error("number " + std::to_string(num) + " out of range"); |
1328 | 0 | } |
1329 | 0 | return num; |
1330 | 0 | }; |
1331 | |
|
1332 | 0 | auto populate = [](std::vector<int>& group, int first_num, bool is_span, int last_num) { |
1333 | 0 | group.clear(); |
1334 | 0 | group.emplace_back(first_num); |
1335 | 0 | if (is_span) { |
1336 | 0 | if (first_num > last_num) { |
1337 | 0 | for (auto i = first_num - 1; i >= last_num; --i) { |
1338 | 0 | group.push_back(i); |
1339 | 0 | } |
1340 | 0 | } else { |
1341 | 0 | for (auto i = first_num + 1; i <= last_num; ++i) { |
1342 | 0 | group.push_back(i); |
1343 | 0 | } |
1344 | 0 | } |
1345 | 0 | } |
1346 | 0 | }; |
1347 | |
|
1348 | 0 | char const* p; |
1349 | 0 | try { |
1350 | 0 | char const* range_end = range + strlen(range); |
1351 | 0 | std::vector<int> result; |
1352 | 0 | std::vector<int> last_group; |
1353 | | // See if range ends with :even or :odd. |
1354 | 0 | size_t start_idx = 0; |
1355 | 0 | size_t skip = 1; |
1356 | 0 | p = std::find(range, range_end, ':'); |
1357 | 0 | if (*p == ':') { |
1358 | 0 | if (strcmp(p, ":odd") == 0) { |
1359 | 0 | skip = 2; |
1360 | 0 | } else if (strcmp(p, ":even") == 0) { |
1361 | 0 | skip = 2; |
1362 | 0 | start_idx = 1; |
1363 | 0 | } else { |
1364 | 0 | throw std::runtime_error("expected :even or :odd"); |
1365 | 0 | } |
1366 | 0 | range_end = p; |
1367 | 0 | } |
1368 | | |
1369 | | // Divide the range into groups |
1370 | 0 | p = range; |
1371 | 0 | char const* group_end; |
1372 | 0 | bool first = true; |
1373 | 0 | while (p != range_end) { |
1374 | 0 | group_end = std::find(p, range_end, ','); |
1375 | 0 | std::cmatch m; |
1376 | 0 | if (!std::regex_match(p, group_end, m, group_re)) { |
1377 | 0 | throw std::runtime_error("invalid range syntax"); |
1378 | 0 | } |
1379 | 0 | auto is_exclude = m[1].matched; |
1380 | 0 | if (first && is_exclude) { |
1381 | 0 | throw std::runtime_error("first range group may not be an exclusion"); |
1382 | 0 | } |
1383 | 0 | first = false; |
1384 | 0 | auto first_num = parse_num(m[2].str()); |
1385 | 0 | auto is_span = m[3].matched; |
1386 | 0 | int last_num{0}; |
1387 | 0 | if (is_span) { |
1388 | 0 | last_num = parse_num(m[3].str()); |
1389 | 0 | } |
1390 | 0 | if (is_exclude) { |
1391 | 0 | std::vector<int> work; |
1392 | 0 | populate(work, first_num, is_span, last_num); |
1393 | 0 | std::set<int> exclusions; |
1394 | 0 | exclusions.insert(work.begin(), work.end()); |
1395 | 0 | work = last_group; |
1396 | 0 | last_group.clear(); |
1397 | 0 | for (auto n: work) { |
1398 | 0 | if (!exclusions.contains(n)) { |
1399 | 0 | last_group.emplace_back(n); |
1400 | 0 | } |
1401 | 0 | } |
1402 | 0 | } else { |
1403 | 0 | result.insert(result.end(), last_group.begin(), last_group.end()); |
1404 | 0 | populate(last_group, first_num, is_span, last_num); |
1405 | 0 | } |
1406 | 0 | p = group_end; |
1407 | 0 | if (*p == ',') { |
1408 | 0 | ++p; |
1409 | 0 | if (p == range_end) { |
1410 | 0 | throw std::runtime_error("trailing comma"); |
1411 | 0 | } |
1412 | 0 | } |
1413 | 0 | } |
1414 | 0 | result.insert(result.end(), last_group.begin(), last_group.end()); |
1415 | 0 | if (skip == 1) { |
1416 | 0 | return result; |
1417 | 0 | } |
1418 | 0 | std::vector<int> filtered; |
1419 | 0 | for (auto i = start_idx; i < result.size(); i += skip) { |
1420 | 0 | filtered.emplace_back(result.at(i)); |
1421 | 0 | } |
1422 | 0 | return filtered; |
1423 | 0 | } catch (std::runtime_error const& e) { |
1424 | 0 | std::string message; |
1425 | 0 | if (p) { |
1426 | 0 | message = "error at * in numeric range " + |
1427 | 0 | std::string(range, QIntC::to_size(p - range)) + "*" + p + ": " + e.what(); |
1428 | 0 | } else { |
1429 | 0 | message = "error in numeric range " + std::string(range) + ": " + e.what(); |
1430 | 0 | } |
1431 | 0 | throw std::runtime_error(message); |
1432 | 0 | } |
1433 | 0 | } |
1434 | | |
1435 | | enum encoding_e { e_utf16, e_ascii, e_winansi, e_macroman, e_pdfdoc }; |
1436 | | |
1437 | | static unsigned char |
1438 | | encode_winansi(unsigned long codepoint) |
1439 | 0 | { |
1440 | 0 | auto i = unicode_to_win_ansi.find(codepoint); |
1441 | 0 | if (i != unicode_to_win_ansi.end()) { |
1442 | 0 | return i->second; |
1443 | 0 | } |
1444 | 0 | return '\0'; |
1445 | 0 | } |
1446 | | |
1447 | | static unsigned char |
1448 | | encode_macroman(unsigned long codepoint) |
1449 | 0 | { |
1450 | 0 | auto i = unicode_to_mac_roman.find(codepoint); |
1451 | 0 | if (i != unicode_to_mac_roman.end()) { |
1452 | 0 | return i->second; |
1453 | 0 | } |
1454 | 0 | return '\0'; |
1455 | 0 | } |
1456 | | |
1457 | | static unsigned char |
1458 | | encode_pdfdoc(unsigned long codepoint) |
1459 | 0 | { |
1460 | 0 | auto i = unicode_to_pdf_doc.find(codepoint); |
1461 | 0 | if (i != unicode_to_pdf_doc.end()) { |
1462 | 0 | return i->second; |
1463 | 0 | } |
1464 | 0 | return '\0'; |
1465 | 0 | } |
1466 | | |
1467 | | unsigned long |
1468 | | QUtil::get_next_utf8_codepoint(std::string const& utf8_val, size_t& pos, bool& error) |
1469 | 0 | { |
1470 | 0 | auto o_pos = pos; |
1471 | 0 | size_t len = utf8_val.length(); |
1472 | 0 | unsigned char ch = static_cast<unsigned char>(utf8_val.at(pos++)); |
1473 | 0 | error = false; |
1474 | 0 | if (ch < 128) { |
1475 | 0 | return static_cast<unsigned long>(ch); |
1476 | 0 | } |
1477 | | |
1478 | 0 | size_t bytes_needed = 0; |
1479 | 0 | unsigned bit_check = 0x40; |
1480 | 0 | unsigned char to_clear = 0x80; |
1481 | 0 | while (ch & bit_check) { |
1482 | 0 | ++bytes_needed; |
1483 | 0 | to_clear = static_cast<unsigned char>(to_clear | bit_check); |
1484 | 0 | bit_check >>= 1; |
1485 | 0 | } |
1486 | 0 | if (((bytes_needed > 5) || (bytes_needed < 1)) || ((pos + bytes_needed) > len)) { |
1487 | 0 | error = true; |
1488 | 0 | return 0xfffd; |
1489 | 0 | } |
1490 | | |
1491 | 0 | auto codepoint = static_cast<unsigned long>(ch & ~to_clear); |
1492 | 0 | while (bytes_needed > 0) { |
1493 | 0 | --bytes_needed; |
1494 | 0 | ch = static_cast<unsigned char>(utf8_val.at(pos++)); |
1495 | 0 | if ((ch & 0xc0) != 0x80) { |
1496 | 0 | --pos; |
1497 | 0 | error = true; |
1498 | 0 | return 0xfffd; |
1499 | 0 | } |
1500 | 0 | codepoint <<= 6; |
1501 | 0 | codepoint += (ch & 0x3f); |
1502 | 0 | } |
1503 | 0 | unsigned long lower_bound = 0; |
1504 | 0 | switch (pos - o_pos) { |
1505 | 0 | case 2: |
1506 | 0 | lower_bound = 1 << 7; |
1507 | 0 | break; |
1508 | 0 | case 3: |
1509 | 0 | lower_bound = 1 << 11; |
1510 | 0 | break; |
1511 | 0 | case 4: |
1512 | 0 | lower_bound = 1 << 16; |
1513 | 0 | break; |
1514 | 0 | case 5: |
1515 | 0 | lower_bound = 1 << 12; |
1516 | 0 | break; |
1517 | 0 | case 6: |
1518 | 0 | lower_bound = 1 << 26; |
1519 | 0 | break; |
1520 | 0 | default: |
1521 | 0 | lower_bound = 0; |
1522 | 0 | } |
1523 | | |
1524 | 0 | if (lower_bound > 0 && codepoint < lower_bound) { |
1525 | | // Too many bytes were used, but return whatever character was encoded. |
1526 | 0 | error = true; |
1527 | 0 | } |
1528 | 0 | return codepoint; |
1529 | 0 | } |
1530 | | |
1531 | | static bool |
1532 | | transcode_utf8(std::string const& utf8_val, std::string& result, encoding_e encoding, char unknown) |
1533 | 0 | { |
1534 | 0 | bool okay = true; |
1535 | 0 | result.clear(); |
1536 | 0 | size_t len = utf8_val.length(); |
1537 | 0 | switch (encoding) { |
1538 | 0 | case e_utf16: |
1539 | 0 | result += "\xfe\xff"; |
1540 | 0 | break; |
1541 | 0 | case e_pdfdoc: |
1542 | | // We need to avoid having the result start with something that will be interpreted as |
1543 | | // UTF-16 or UTF-8, meaning we can't end up with a string that starts with "fe ff", |
1544 | | // (UTF-16-BE) "ff fe" (UTF-16-LE, not officially part of the PDF spec, but recognized by |
1545 | | // most readers including qpdf), or "ef bb bf" (UTF-8). It's more efficient to check the |
1546 | | // input string to see if it will map to one of those sequences than to check the output |
1547 | | // string since all cases start with the same starting character. |
1548 | 0 | if ((len >= 4) && (utf8_val[0] == '\xc3')) { |
1549 | 0 | static std::string fe_ff("\xbe\xc3\xbf"); |
1550 | 0 | static std::string ff_fe("\xbf\xc3\xbe"); |
1551 | 0 | static std::string ef_bb_bf("\xaf\xc2\xbb\xc2\xbf"); |
1552 | | // C++-20 has starts_with, but when this was written, qpdf had a minimum supported |
1553 | | // version of C++-17. |
1554 | 0 | if ((utf8_val.compare(1, 3, fe_ff) == 0) || (utf8_val.compare(1, 3, ff_fe) == 0) || |
1555 | 0 | (utf8_val.compare(1, 5, ef_bb_bf) == 0)) { |
1556 | 0 | result += unknown; |
1557 | 0 | okay = false; |
1558 | 0 | } |
1559 | 0 | } |
1560 | 0 | break; |
1561 | 0 | default: |
1562 | 0 | break; |
1563 | 0 | } |
1564 | 0 | size_t pos = 0; |
1565 | 0 | while (pos < len) { |
1566 | 0 | bool error = false; |
1567 | 0 | unsigned long codepoint = QUtil::get_next_utf8_codepoint(utf8_val, pos, error); |
1568 | 0 | if (error) { |
1569 | 0 | okay = false; |
1570 | 0 | if (encoding == e_utf16) { |
1571 | 0 | result += "\xff\xfd"; |
1572 | 0 | } else { |
1573 | 0 | result.append(1, unknown); |
1574 | 0 | } |
1575 | 0 | } else if (codepoint < 128) { |
1576 | 0 | char ch = static_cast<char>(codepoint); |
1577 | 0 | if (encoding == e_utf16) { |
1578 | 0 | result += QUtil::toUTF16(QIntC::to_ulong(ch)); |
1579 | 0 | } else if ((encoding == e_pdfdoc) && (((ch >= 0x18) && (ch <= 0x1f)) || (ch == 127))) { |
1580 | | // PDFDocEncoding maps some low characters to Unicode, so if we encounter those |
1581 | | // invalid UTF-8 code points, map them to unknown so reversing the mapping doesn't |
1582 | | // change them into other characters. |
1583 | 0 | okay = false; |
1584 | 0 | result.append(1, unknown); |
1585 | 0 | } else { |
1586 | 0 | result.append(1, ch); |
1587 | 0 | } |
1588 | 0 | } else if (encoding == e_utf16) { |
1589 | 0 | result += QUtil::toUTF16(codepoint); |
1590 | 0 | } else if ((codepoint == 0xad) && (encoding == e_pdfdoc)) { |
1591 | | // PDFDocEncoding omits 0x00ad (soft hyphen). |
1592 | 0 | okay = false; |
1593 | 0 | result.append(1, unknown); |
1594 | 0 | } else if ( |
1595 | 0 | (codepoint > 160) && (codepoint < 256) && |
1596 | 0 | ((encoding == e_winansi) || (encoding == e_pdfdoc))) { |
1597 | 0 | result.append(1, static_cast<char>(codepoint & 0xff)); |
1598 | 0 | } else { |
1599 | 0 | unsigned char ch = '\0'; |
1600 | 0 | if (encoding == e_winansi) { |
1601 | 0 | ch = encode_winansi(codepoint); |
1602 | 0 | } else if (encoding == e_macroman) { |
1603 | 0 | ch = encode_macroman(codepoint); |
1604 | 0 | } else if (encoding == e_pdfdoc) { |
1605 | 0 | ch = encode_pdfdoc(codepoint); |
1606 | 0 | } |
1607 | 0 | if (ch == '\0') { |
1608 | 0 | okay = false; |
1609 | 0 | ch = static_cast<unsigned char>(unknown); |
1610 | 0 | } |
1611 | 0 | result.append(1, static_cast<char>(ch)); |
1612 | 0 | } |
1613 | 0 | } |
1614 | 0 | return okay; |
1615 | 0 | } |
1616 | | |
1617 | | static std::string |
1618 | | transcode_utf8(std::string const& utf8_val, encoding_e encoding, char unknown) |
1619 | 0 | { |
1620 | 0 | std::string result; |
1621 | 0 | transcode_utf8(utf8_val, result, encoding, unknown); |
1622 | 0 | return result; |
1623 | 0 | } |
1624 | | |
1625 | | std::string |
1626 | | QUtil::utf8_to_utf16(std::string const& utf8) |
1627 | 0 | { |
1628 | 0 | return transcode_utf8(utf8, e_utf16, 0); |
1629 | 0 | } |
1630 | | |
1631 | | std::string |
1632 | | QUtil::utf8_to_ascii(std::string const& utf8, char unknown_char) |
1633 | 0 | { |
1634 | 0 | return transcode_utf8(utf8, e_ascii, unknown_char); |
1635 | 0 | } |
1636 | | |
1637 | | std::string |
1638 | | QUtil::utf8_to_win_ansi(std::string const& utf8, char unknown_char) |
1639 | 0 | { |
1640 | 0 | return transcode_utf8(utf8, e_winansi, unknown_char); |
1641 | 0 | } |
1642 | | |
1643 | | std::string |
1644 | | QUtil::utf8_to_mac_roman(std::string const& utf8, char unknown_char) |
1645 | 0 | { |
1646 | 0 | return transcode_utf8(utf8, e_macroman, unknown_char); |
1647 | 0 | } |
1648 | | |
1649 | | std::string |
1650 | | QUtil::utf8_to_pdf_doc(std::string const& utf8, char unknown_char) |
1651 | 0 | { |
1652 | 0 | return transcode_utf8(utf8, e_pdfdoc, unknown_char); |
1653 | 0 | } |
1654 | | |
1655 | | bool |
1656 | | QUtil::utf8_to_ascii(std::string const& utf8, std::string& ascii, char unknown_char) |
1657 | 0 | { |
1658 | 0 | return transcode_utf8(utf8, ascii, e_ascii, unknown_char); |
1659 | 0 | } |
1660 | | |
1661 | | bool |
1662 | | QUtil::utf8_to_win_ansi(std::string const& utf8, std::string& win, char unknown_char) |
1663 | 0 | { |
1664 | 0 | return transcode_utf8(utf8, win, e_winansi, unknown_char); |
1665 | 0 | } |
1666 | | |
1667 | | bool |
1668 | | QUtil::utf8_to_mac_roman(std::string const& utf8, std::string& mac, char unknown_char) |
1669 | 0 | { |
1670 | 0 | return transcode_utf8(utf8, mac, e_macroman, unknown_char); |
1671 | 0 | } |
1672 | | |
1673 | | bool |
1674 | | QUtil::utf8_to_pdf_doc(std::string const& utf8, std::string& pdfdoc, char unknown_char) |
1675 | 0 | { |
1676 | 0 | return transcode_utf8(utf8, pdfdoc, e_pdfdoc, unknown_char); |
1677 | 0 | } |
1678 | | |
1679 | | bool |
1680 | | QUtil::is_utf16(std::string const& val) |
1681 | 0 | { |
1682 | 0 | return ( |
1683 | 0 | (val.length() >= 2) && |
1684 | 0 | (((val.at(0) == '\xfe') && (val.at(1) == '\xff')) || |
1685 | 0 | ((val.at(0) == '\xff') && (val.at(1) == '\xfe')))); |
1686 | 0 | } |
1687 | | |
1688 | | bool |
1689 | | QUtil::is_explicit_utf8(std::string const& val) |
1690 | 0 | { |
1691 | | // QPDF_String.cc knows that this is a 3-byte sequence. |
1692 | 0 | return ( |
1693 | 0 | (val.length() >= 3) && (val.at(0) == '\xef') && (val.at(1) == '\xbb') && |
1694 | 0 | (val.at(2) == '\xbf')); |
1695 | 0 | } |
1696 | | |
1697 | | std::string |
1698 | | QUtil::utf16_to_utf8(std::string const& val) |
1699 | 0 | { |
1700 | 0 | std::string result; |
1701 | | // This code uses unsigned long and unsigned short to hold codepoint values. It requires |
1702 | | // unsigned long to be at least 32 bits and unsigned short to be at least 16 bits, but it will |
1703 | | // work fine if they are larger. |
1704 | 0 | unsigned long codepoint = 0L; |
1705 | 0 | size_t len = val.length(); |
1706 | 0 | size_t start = 0; |
1707 | 0 | bool is_le = false; |
1708 | 0 | if (is_utf16(val)) { |
1709 | 0 | if (static_cast<unsigned char>(val.at(0)) == 0xff) { |
1710 | 0 | is_le = true; |
1711 | 0 | } |
1712 | 0 | start += 2; |
1713 | 0 | } |
1714 | | // If the string has an odd number of bytes, the last byte is ignored. |
1715 | 0 | for (size_t i = start; i + 1 < len; i += 2) { |
1716 | | // Convert from UTF16-BE. If we get a malformed codepoint, this code will generate |
1717 | | // incorrect output without giving a warning. Specifically, a high codepoint not followed |
1718 | | // by a low codepoint will be discarded, and a low codepoint not preceded by a high |
1719 | | // codepoint will just get its low 10 bits output. |
1720 | 0 | auto msb = is_le ? i + 1 : i; |
1721 | 0 | auto lsb = is_le ? i : i + 1; |
1722 | 0 | unsigned short bits = QIntC::to_ushort( |
1723 | 0 | (static_cast<unsigned char>(val.at(msb)) << 8) + |
1724 | 0 | static_cast<unsigned char>(val.at(lsb))); |
1725 | 0 | if ((bits & 0xFC00) == 0xD800) { |
1726 | 0 | codepoint = 0x10000U + ((bits & 0x3FFU) << 10U); |
1727 | 0 | continue; |
1728 | 0 | } else if ((bits & 0xFC00) == 0xDC00) { |
1729 | 0 | if (codepoint != 0) { |
1730 | 0 | QTC::TC("qpdf", "QUtil non-trivial UTF-16"); |
1731 | 0 | } |
1732 | 0 | codepoint += bits & 0x3FF; |
1733 | 0 | } else { |
1734 | 0 | codepoint = bits; |
1735 | 0 | } |
1736 | | |
1737 | 0 | result += QUtil::toUTF8(codepoint); |
1738 | 0 | codepoint = 0; |
1739 | 0 | } |
1740 | 0 | return result; |
1741 | 0 | } |
1742 | | |
1743 | | std::string |
1744 | | QUtil::win_ansi_to_utf8(std::string const& val) |
1745 | 0 | { |
1746 | 0 | std::string result; |
1747 | 0 | size_t len = val.length(); |
1748 | 0 | for (unsigned int i = 0; i < len; ++i) { |
1749 | 0 | unsigned char ch = static_cast<unsigned char>(val.at(i)); |
1750 | 0 | unsigned short ch_short = ch; |
1751 | 0 | if ((ch >= 128) && (ch <= 160)) { |
1752 | 0 | ch_short = win_ansi_to_unicode[ch - 128]; |
1753 | 0 | } |
1754 | 0 | result += QUtil::toUTF8(ch_short); |
1755 | 0 | } |
1756 | 0 | return result; |
1757 | 0 | } |
1758 | | |
1759 | | std::string |
1760 | | QUtil::mac_roman_to_utf8(std::string const& val) |
1761 | 0 | { |
1762 | 0 | std::string result; |
1763 | 0 | size_t len = val.length(); |
1764 | 0 | for (unsigned int i = 0; i < len; ++i) { |
1765 | 0 | unsigned char ch = static_cast<unsigned char>(val.at(i)); |
1766 | 0 | unsigned short ch_short = ch; |
1767 | 0 | if (ch >= 128) { |
1768 | 0 | ch_short = mac_roman_to_unicode[ch - 128]; |
1769 | 0 | } |
1770 | 0 | result += QUtil::toUTF8(ch_short); |
1771 | 0 | } |
1772 | 0 | return result; |
1773 | 0 | } |
1774 | | |
1775 | | std::string |
1776 | | QUtil::pdf_doc_to_utf8(std::string const& val) |
1777 | 0 | { |
1778 | 0 | std::string result; |
1779 | 0 | size_t len = val.length(); |
1780 | 0 | for (unsigned int i = 0; i < len; ++i) { |
1781 | 0 | unsigned char ch = static_cast<unsigned char>(val.at(i)); |
1782 | 0 | unsigned short ch_short = ch; |
1783 | 0 | if ((ch >= 127) && (ch <= 160)) { |
1784 | 0 | ch_short = pdf_doc_to_unicode[ch - 127]; |
1785 | 0 | } else if ((ch >= 24) && (ch <= 31)) { |
1786 | 0 | ch_short = pdf_doc_low_to_unicode[ch - 24]; |
1787 | 0 | } else if (ch == 173) { |
1788 | 0 | ch_short = 0xfffd; |
1789 | 0 | } |
1790 | 0 | result += QUtil::toUTF8(ch_short); |
1791 | 0 | } |
1792 | 0 | return result; |
1793 | 0 | } |
1794 | | |
1795 | | void |
1796 | | QUtil::analyze_encoding( |
1797 | | std::string const& val, bool& has_8bit_chars, bool& is_valid_utf8, bool& is_utf16) |
1798 | 0 | { |
1799 | 0 | has_8bit_chars = is_utf16 = is_valid_utf8 = false; |
1800 | 0 | if (QUtil::is_utf16(val)) { |
1801 | 0 | has_8bit_chars = true; |
1802 | 0 | is_utf16 = true; |
1803 | 0 | return; |
1804 | 0 | } |
1805 | 0 | size_t len = val.length(); |
1806 | 0 | size_t pos = 0; |
1807 | 0 | bool any_errors = false; |
1808 | 0 | while (pos < len) { |
1809 | 0 | bool error = false; |
1810 | 0 | auto o_pos = pos; |
1811 | 0 | get_next_utf8_codepoint(val, pos, error); |
1812 | 0 | if (error) { |
1813 | 0 | any_errors = true; |
1814 | 0 | } |
1815 | 0 | if (pos - o_pos > 1 || val[o_pos] & 0x80) { |
1816 | 0 | has_8bit_chars = true; |
1817 | 0 | } |
1818 | 0 | } |
1819 | 0 | if (has_8bit_chars && (!any_errors)) { |
1820 | 0 | is_valid_utf8 = true; |
1821 | 0 | } |
1822 | 0 | } |
1823 | | |
1824 | | std::vector<std::string> |
1825 | | QUtil::possible_repaired_encodings(std::string supplied) |
1826 | 0 | { |
1827 | 0 | std::vector<std::string> result; |
1828 | | // Always include the original string |
1829 | 0 | result.push_back(supplied); |
1830 | 0 | bool has_8bit_chars = false; |
1831 | 0 | bool is_valid_utf8 = false; |
1832 | 0 | bool is_utf16 = false; |
1833 | 0 | analyze_encoding(supplied, has_8bit_chars, is_valid_utf8, is_utf16); |
1834 | 0 | if (!has_8bit_chars) { |
1835 | 0 | return result; |
1836 | 0 | } |
1837 | 0 | if (is_utf16) { |
1838 | | // Convert to UTF-8 and pretend we got a UTF-8 string. |
1839 | 0 | is_utf16 = false; |
1840 | 0 | is_valid_utf8 = true; |
1841 | 0 | supplied = utf16_to_utf8(supplied); |
1842 | 0 | } |
1843 | 0 | std::string output; |
1844 | 0 | if (is_valid_utf8) { |
1845 | | // Maybe we were given UTF-8 but wanted one of the single-byte encodings. |
1846 | 0 | if (utf8_to_pdf_doc(supplied, output)) { |
1847 | 0 | result.push_back(output); |
1848 | 0 | } |
1849 | 0 | if (utf8_to_win_ansi(supplied, output)) { |
1850 | 0 | result.push_back(output); |
1851 | 0 | } |
1852 | 0 | if (utf8_to_mac_roman(supplied, output)) { |
1853 | 0 | result.push_back(output); |
1854 | 0 | } |
1855 | 0 | } else { |
1856 | | // Maybe we were given one of the single-byte encodings but wanted UTF-8. |
1857 | 0 | std::string from_pdf_doc(pdf_doc_to_utf8(supplied)); |
1858 | 0 | result.push_back(from_pdf_doc); |
1859 | 0 | std::string from_win_ansi(win_ansi_to_utf8(supplied)); |
1860 | 0 | result.push_back(from_win_ansi); |
1861 | 0 | std::string from_mac_roman(mac_roman_to_utf8(supplied)); |
1862 | 0 | result.push_back(from_mac_roman); |
1863 | | |
1864 | | // Maybe we were given one of the other single-byte encodings but wanted one of the other |
1865 | | // ones. |
1866 | 0 | if (utf8_to_win_ansi(from_pdf_doc, output)) { |
1867 | 0 | result.push_back(output); |
1868 | 0 | } |
1869 | 0 | if (utf8_to_mac_roman(from_pdf_doc, output)) { |
1870 | 0 | result.push_back(output); |
1871 | 0 | } |
1872 | 0 | if (utf8_to_pdf_doc(from_win_ansi, output)) { |
1873 | 0 | result.push_back(output); |
1874 | 0 | } |
1875 | 0 | if (utf8_to_mac_roman(from_win_ansi, output)) { |
1876 | 0 | result.push_back(output); |
1877 | 0 | } |
1878 | 0 | if (utf8_to_pdf_doc(from_mac_roman, output)) { |
1879 | 0 | result.push_back(output); |
1880 | 0 | } |
1881 | 0 | if (utf8_to_win_ansi(from_mac_roman, output)) { |
1882 | 0 | result.push_back(output); |
1883 | 0 | } |
1884 | 0 | } |
1885 | | // De-duplicate |
1886 | 0 | std::vector<std::string> t; |
1887 | 0 | std::set<std::string> seen; |
1888 | 0 | for (auto const& iter: result) { |
1889 | 0 | if (!seen.contains(iter)) { |
1890 | 0 | seen.insert(iter); |
1891 | 0 | t.push_back(iter); |
1892 | 0 | } |
1893 | 0 | } |
1894 | 0 | return t; |
1895 | 0 | } |
1896 | | |
1897 | | #ifndef QPDF_NO_WCHAR_T |
1898 | | static int |
1899 | | call_main_from_wmain( |
1900 | | bool, int argc, wchar_t const* const argv[], std::function<int(int, char*[])> realmain) |
1901 | 0 | { |
1902 | | // argv contains UTF-16-encoded strings with a 16-bit wchar_t. Convert this to UTF-8-encoded |
1903 | | // strings for compatibility with other systems. That way the rest of qpdf.cc can just act like |
1904 | | // arguments are UTF-8. |
1905 | |
|
1906 | 0 | std::vector<std::string> utf8_argv; |
1907 | 0 | utf8_argv.reserve(QIntC::to_size(argc)); |
1908 | 0 | for (int i = 0; i < argc; ++i) { |
1909 | 0 | std::string utf16; |
1910 | 0 | for (size_t j = 0; j < std::wcslen(argv[i]); ++j) { |
1911 | 0 | unsigned short codepoint = static_cast<unsigned short>(argv[i][j]); |
1912 | 0 | utf16.append(1, static_cast<char>(QIntC::to_uchar(codepoint >> 8))); |
1913 | 0 | utf16.append(1, static_cast<char>(QIntC::to_uchar(codepoint & 0xff))); |
1914 | 0 | } |
1915 | 0 | utf8_argv.emplace_back(QUtil::utf16_to_utf8(utf16)); |
1916 | 0 | } |
1917 | 0 | std::vector<char*> new_argv; |
1918 | 0 | new_argv.reserve(utf8_argv.size() + 1U); |
1919 | 0 | for (auto const& arg: utf8_argv) { |
1920 | 0 | new_argv.emplace_back(const_cast<char*>(arg.data())); |
1921 | 0 | } |
1922 | 0 | argc = QIntC::to_int(utf8_argv.size()); |
1923 | 0 | new_argv.emplace_back(nullptr); |
1924 | 0 | return realmain(argc, new_argv.data()); |
1925 | 0 | } |
1926 | | |
1927 | | int |
1928 | | QUtil::call_main_from_wmain(int argc, wchar_t* argv[], std::function<int(int, char*[])> realmain) |
1929 | 0 | { |
1930 | 0 | return ::call_main_from_wmain(true, argc, argv, realmain); |
1931 | 0 | } |
1932 | | |
1933 | | int |
1934 | | QUtil::call_main_from_wmain( |
1935 | | int argc, wchar_t const* const argv[], std::function<int(int, char const* const[])> realmain) |
1936 | 0 | { |
1937 | 0 | return ::call_main_from_wmain(true, argc, argv, [realmain](int new_argc, char* new_argv[]) { |
1938 | 0 | return realmain(new_argc, new_argv); |
1939 | 0 | }); |
1940 | 0 | } |
1941 | | |
1942 | | #endif // QPDF_NO_WCHAR_T |
1943 | | |
1944 | | size_t |
1945 | | QUtil::get_max_memory_usage() |
1946 | 0 | { |
1947 | 0 | #if defined(HAVE_MALLOC_INFO) && defined(HAVE_OPEN_MEMSTREAM) |
1948 | 0 | static std::regex tag_re("<(/?\\w+)([^>]*?)>"); |
1949 | 0 | static std::regex attr_re("(\\w+)=\"(.*?)\""); |
1950 | |
|
1951 | 0 | char* buf; |
1952 | 0 | size_t size; |
1953 | 0 | FILE* f = open_memstream(&buf, &size); |
1954 | 0 | if (f == nullptr) { |
1955 | 0 | return 0; |
1956 | 0 | } |
1957 | 0 | malloc_info(0, f); |
1958 | 0 | fclose(f); |
1959 | 0 | if (QUtil::get_env("QPDF_DEBUG_MEM_USAGE")) { |
1960 | 0 | fprintf(stderr, "%s", buf); |
1961 | 0 | } |
1962 | | |
1963 | | // Warning: this code uses regular expression to extract data from an XML string. This is |
1964 | | // generally a bad idea, but we're going to do it anyway because QUtil.hh warns against using |
1965 | | // this function for other than development/testing, and if this function fails to generate |
1966 | | // reasonable output during performance testing, it will be noticed. |
1967 | | |
1968 | | // This is my best guess at how to interpret malloc_info. Anyway it seems to provide useful |
1969 | | // information for detecting code changes that drastically change memory usage. |
1970 | 0 | size_t result = 0; |
1971 | 0 | try { |
1972 | 0 | std::cregex_iterator m_begin(buf, buf + size, tag_re); |
1973 | 0 | std::cregex_iterator cr_end; |
1974 | 0 | std::sregex_iterator sr_end; |
1975 | |
|
1976 | 0 | int in_heap = 0; |
1977 | 0 | for (auto m = m_begin; m != cr_end; ++m) { |
1978 | 0 | std::string tag(m->str(1)); |
1979 | 0 | if (tag == "heap") { |
1980 | 0 | ++in_heap; |
1981 | 0 | } else if (tag == "/heap") { |
1982 | 0 | --in_heap; |
1983 | 0 | } else if (in_heap == 0) { |
1984 | 0 | std::string rest = m->str(2); |
1985 | 0 | std::map<std::string, std::string> attrs; |
1986 | 0 | std::sregex_iterator a_begin(rest.begin(), rest.end(), attr_re); |
1987 | 0 | for (auto m2 = a_begin; m2 != sr_end; ++m2) { |
1988 | 0 | attrs[m2->str(1)] = m2->str(2); |
1989 | 0 | } |
1990 | 0 | if (tag == "total") { |
1991 | 0 | if (attrs.contains("size")) { |
1992 | 0 | result += QIntC::to_size(QUtil::string_to_ull(attrs["size"].c_str())); |
1993 | 0 | } |
1994 | 0 | } else if (tag == "system" && attrs["type"] == "max") { |
1995 | 0 | result += QIntC::to_size(QUtil::string_to_ull(attrs["size"].c_str())); |
1996 | 0 | } |
1997 | 0 | } |
1998 | 0 | } |
1999 | 0 | } catch (...) { |
2000 | | // ignore -- just return 0 |
2001 | 0 | } |
2002 | 0 | free(buf); |
2003 | 0 | return result; |
2004 | | #else |
2005 | | return 0; |
2006 | | #endif |
2007 | 0 | } |
2008 | | |
2009 | | char |
2010 | | QUtil::hex_decode_char(char digit) |
2011 | 0 | { |
2012 | 0 | return util::hex_decode_char(digit); |
2013 | 0 | } |
2014 | | |
2015 | | std::string |
2016 | | QUtil::hex_encode_char(char c) |
2017 | 0 | { |
2018 | 0 | return util::hex_encode_char(c); |
2019 | 0 | } |
2020 | | |
2021 | | bool |
2022 | | QUtil::is_number(char const* p) |
2023 | 0 | { |
2024 | | // No longer used by qpdf. |
2025 | | |
2026 | | // ^[\+\-]?(\.\d*|\d+(\.\d*)?)$ |
2027 | 0 | if (!*p) { |
2028 | 0 | return false; |
2029 | 0 | } |
2030 | 0 | if ((*p == '-') || (*p == '+')) { |
2031 | 0 | ++p; |
2032 | 0 | } |
2033 | 0 | bool found_dot = false; |
2034 | 0 | bool found_digit = false; |
2035 | 0 | for (; *p; ++p) { |
2036 | 0 | if (*p == '.') { |
2037 | 0 | if (found_dot) { |
2038 | | // only one dot |
2039 | 0 | return false; |
2040 | 0 | } |
2041 | 0 | found_dot = true; |
2042 | 0 | } else if (util::is_digit(*p)) { |
2043 | 0 | found_digit = true; |
2044 | 0 | } else { |
2045 | 0 | return false; |
2046 | 0 | } |
2047 | 0 | } |
2048 | 0 | return found_digit; |
2049 | 0 | } |
2050 | | |
2051 | | bool |
2052 | | QUtil::is_space(char c) |
2053 | 0 | { |
2054 | 0 | return util::is_space(c); |
2055 | 0 | } |
2056 | | |
2057 | | bool |
2058 | | QUtil::is_digit(char c) |
2059 | 0 | { |
2060 | 0 | return util::is_digit(c); |
2061 | 0 | } |
2062 | | |
2063 | | bool |
2064 | | QUtil::is_hex_digit(char c) |
2065 | 0 | { |
2066 | 0 | return util::is_hex_digit(c); |
2067 | 0 | } |