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