1 /******************************************************************************
2 * Project: PROJ
3 * Purpose: File manager
4 * Author: Even Rouault, <even.rouault at spatialys.com>
5 *
6 ******************************************************************************
7 * Copyright (c) 2019, Even Rouault, <even.rouault at spatialys.com>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
26 *****************************************************************************/
27
28 #ifndef FROM_PROJ_CPP
29 #define FROM_PROJ_CPP
30 #endif
31 #define LRU11_DO_NOT_DEFINE_OUT_OF_CLASS_METHODS
32
33 #include <errno.h>
34 #include <stdlib.h>
35
36 #include <algorithm>
37 #include <limits>
38 #include <string>
39
40 #include "filemanager.hpp"
41 #include "proj.h"
42 #include "proj/internal/internal.hpp"
43 #include "proj/internal/io_internal.hpp"
44 #include "proj/io.hpp"
45 #include "proj_internal.h"
46
47 #include <sys/stat.h>
48
49 #include "proj_config.h"
50
51 #ifdef _WIN32
52 #include <shlobj.h>
53 #include <windows.h>
54 #else
55 #ifdef HAVE_LIBDL
56 #include <dlfcn.h>
57 #endif
58 #include <sys/types.h>
59 #include <unistd.h>
60 #endif
61
62 //! @cond Doxygen_Suppress
63
64 #define STR_HELPER(x) #x
65 #define STR(x) STR_HELPER(x)
66
67 using namespace NS_PROJ::internal;
68
69 NS_PROJ_START
70
71 // ---------------------------------------------------------------------------
72
File(const std::string & filename)73 File::File(const std::string &filename) : name_(filename) {}
74
75 // ---------------------------------------------------------------------------
76
77 File::~File() = default;
78
79 // ---------------------------------------------------------------------------
80
read_line(size_t maxLen,bool & maxLenReached,bool & eofReached)81 std::string File::read_line(size_t maxLen, bool &maxLenReached,
82 bool &eofReached) {
83 constexpr size_t MAX_MAXLEN = 1024 * 1024;
84 maxLen = std::min(maxLen, MAX_MAXLEN);
85 while (true) {
86 // Consume existing lines in buffer
87 size_t pos = readLineBuffer_.find_first_of("\r\n");
88 if (pos != std::string::npos) {
89 if (pos > maxLen) {
90 std::string ret(readLineBuffer_.substr(0, maxLen));
91 readLineBuffer_ = readLineBuffer_.substr(maxLen);
92 maxLenReached = true;
93 eofReached = false;
94 return ret;
95 }
96 std::string ret(readLineBuffer_.substr(0, pos));
97 if (readLineBuffer_[pos] == '\r' &&
98 readLineBuffer_[pos + 1] == '\n') {
99 pos += 1;
100 }
101 readLineBuffer_ = readLineBuffer_.substr(pos + 1);
102 maxLenReached = false;
103 eofReached = false;
104 return ret;
105 }
106
107 const size_t prevSize = readLineBuffer_.size();
108 if (maxLen <= prevSize) {
109 std::string ret(readLineBuffer_.substr(0, maxLen));
110 readLineBuffer_ = readLineBuffer_.substr(maxLen);
111 maxLenReached = true;
112 eofReached = false;
113 return ret;
114 }
115
116 if (eofReadLine_) {
117 std::string ret = readLineBuffer_;
118 readLineBuffer_.clear();
119 maxLenReached = false;
120 eofReached = ret.empty();
121 return ret;
122 }
123
124 readLineBuffer_.resize(maxLen);
125 const size_t nRead =
126 read(&readLineBuffer_[prevSize], maxLen - prevSize);
127 if (nRead < maxLen - prevSize)
128 eofReadLine_ = true;
129 readLineBuffer_.resize(prevSize + nRead);
130 }
131 }
132
133 // ---------------------------------------------------------------------------
134
135 #ifdef _WIN32
136
137 /* The bulk of utf8towc()/utf8fromwc() is derived from the utf.c module from
138 * FLTK. It was originally downloaded from:
139 * http://svn.easysw.com/public/fltk/fltk/trunk/src/utf.c
140 * And already used by GDAL
141 */
142 /************************************************************************/
143 /* ==================================================================== */
144 /* UTF.C code from FLTK with some modifications. */
145 /* ==================================================================== */
146 /************************************************************************/
147
148 /* Set to 1 to turn bad UTF8 bytes into ISO-8859-1. If this is to zero
149 they are instead turned into the Unicode REPLACEMENT CHARACTER, of
150 value 0xfffd.
151 If this is on utf8decode will correctly map most (perhaps all)
152 human-readable text that is in ISO-8859-1. This may allow you
153 to completely ignore character sets in your code because virtually
154 everything is either ISO-8859-1 or UTF-8.
155 */
156 #define ERRORS_TO_ISO8859_1 1
157
158 /* Set to 1 to turn bad UTF8 bytes in the 0x80-0x9f range into the
159 Unicode index for Microsoft's CP1252 character set. You should
160 also set ERRORS_TO_ISO8859_1. With this a huge amount of more
161 available text (such as all web pages) are correctly converted
162 to Unicode.
163 */
164 #define ERRORS_TO_CP1252 1
165
166 /* A number of Unicode code points are in fact illegal and should not
167 be produced by a UTF-8 converter. Turn this on will replace the
168 bytes in those encodings with errors. If you do this then converting
169 arbitrary 16-bit data to UTF-8 and then back is not an identity,
170 which will probably break a lot of software.
171 */
172 #define STRICT_RFC3629 0
173
174 #if ERRORS_TO_CP1252
175 // Codes 0x80..0x9f from the Microsoft CP1252 character set, translated
176 // to Unicode:
177 constexpr unsigned short cp1252[32] = {
178 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
179 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
180 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
181 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178};
182 #endif
183
184 /************************************************************************/
185 /* utf8decode() */
186 /************************************************************************/
187
188 /*
189 Decode a single UTF-8 encoded character starting at \e p. The
190 resulting Unicode value (in the range 0-0x10ffff) is returned,
191 and \e len is set the number of bytes in the UTF-8 encoding
192 (adding \e len to \e p will point at the next character).
193
194 If \a p points at an illegal UTF-8 encoding, including one that
195 would go past \e end, or where a code is uses more bytes than
196 necessary, then *reinterpret_cast<const unsigned char*>(p) is translated as
197 though it is
198 in the Microsoft CP1252 character set and \e len is set to 1.
199 Treating errors this way allows this to decode almost any
200 ISO-8859-1 or CP1252 text that has been mistakenly placed where
201 UTF-8 is expected, and has proven very useful.
202
203 If you want errors to be converted to error characters (as the
204 standards recommend), adding a test to see if the length is
205 unexpectedly 1 will work:
206
207 \code
208 if( *p & 0x80 )
209 { // What should be a multibyte encoding.
210 code = utf8decode(p, end, &len);
211 if( len<2 ) code = 0xFFFD; // Turn errors into REPLACEMENT CHARACTER.
212 }
213 else
214 { // Handle the 1-byte utf8 encoding:
215 code = *p;
216 len = 1;
217 }
218 \endcode
219
220 Direct testing for the 1-byte case (as shown above) will also
221 speed up the scanning of strings where the majority of characters
222 are ASCII.
223 */
utf8decode(const char * p,const char * end,int * len)224 static unsigned utf8decode(const char *p, const char *end, int *len) {
225 unsigned char c = *reinterpret_cast<const unsigned char *>(p);
226 if (c < 0x80) {
227 *len = 1;
228 return c;
229 #if ERRORS_TO_CP1252
230 } else if (c < 0xa0) {
231 *len = 1;
232 return cp1252[c - 0x80];
233 #endif
234 } else if (c < 0xc2) {
235 goto FAIL;
236 }
237 if (p + 1 >= end || (p[1] & 0xc0) != 0x80)
238 goto FAIL;
239 if (c < 0xe0) {
240 *len = 2;
241 return ((p[0] & 0x1f) << 6) + ((p[1] & 0x3f));
242 } else if (c == 0xe0) {
243 if ((reinterpret_cast<const unsigned char *>(p))[1] < 0xa0)
244 goto FAIL;
245 goto UTF8_3;
246 #if STRICT_RFC3629
247 } else if (c == 0xed) {
248 // RFC 3629 says surrogate chars are illegal.
249 if ((reinterpret_cast<const unsigned char *>(p))[1] >= 0xa0)
250 goto FAIL;
251 goto UTF8_3;
252 } else if (c == 0xef) {
253 // 0xfffe and 0xffff are also illegal characters.
254 if ((reinterpret_cast<const unsigned char *>(p))[1] == 0xbf &&
255 (reinterpret_cast<const unsigned char *>(p))[2] >= 0xbe)
256 goto FAIL;
257 goto UTF8_3;
258 #endif
259 } else if (c < 0xf0) {
260 UTF8_3:
261 if (p + 2 >= end || (p[2] & 0xc0) != 0x80)
262 goto FAIL;
263 *len = 3;
264 return ((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6) + ((p[2] & 0x3f));
265 } else if (c == 0xf0) {
266 if ((reinterpret_cast<const unsigned char *>(p))[1] < 0x90)
267 goto FAIL;
268 goto UTF8_4;
269 } else if (c < 0xf4) {
270 UTF8_4:
271 if (p + 3 >= end || (p[2] & 0xc0) != 0x80 || (p[3] & 0xc0) != 0x80)
272 goto FAIL;
273 *len = 4;
274 #if STRICT_RFC3629
275 // RFC 3629 says all codes ending in fffe or ffff are illegal:
276 if ((p[1] & 0xf) == 0xf &&
277 (reinterpret_cast<const unsigned char *>(p))[2] == 0xbf &&
278 (reinterpret_cast<const unsigned char *>(p))[3] >= 0xbe)
279 goto FAIL;
280 #endif
281 return ((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12) +
282 ((p[2] & 0x3f) << 6) + ((p[3] & 0x3f));
283 } else if (c == 0xf4) {
284 if ((reinterpret_cast<const unsigned char *>(p))[1] > 0x8f)
285 goto FAIL; // After 0x10ffff.
286 goto UTF8_4;
287 } else {
288 FAIL:
289 *len = 1;
290 #if ERRORS_TO_ISO8859_1
291 return c;
292 #else
293 return 0xfffd; // Unicode REPLACEMENT CHARACTER
294 #endif
295 }
296 }
297
298 /************************************************************************/
299 /* utf8towc() */
300 /************************************************************************/
301
302 /* Convert a UTF-8 sequence into an array of wchar_t. These
303 are used by some system calls, especially on Windows.
304
305 \a src points at the UTF-8, and \a srclen is the number of bytes to
306 convert.
307
308 \a dst points at an array to write, and \a dstlen is the number of
309 locations in this array. At most \a dstlen-1 words will be
310 written there, plus a 0 terminating word. Thus this function
311 will never overwrite the buffer and will always return a
312 zero-terminated string. If \a dstlen is zero then \a dst can be
313 null and no data is written, but the length is returned.
314
315 The return value is the number of words that \e would be written
316 to \a dst if it were long enough, not counting the terminating
317 zero. If the return value is greater or equal to \a dstlen it
318 indicates truncation, you can then allocate a new array of size
319 return+1 and call this again.
320
321 Errors in the UTF-8 are converted as though each byte in the
322 erroneous string is in the Microsoft CP1252 encoding. This allows
323 ISO-8859-1 text mistakenly identified as UTF-8 to be printed
324 correctly.
325
326 Notice that sizeof(wchar_t) is 2 on Windows and is 4 on Linux
327 and most other systems. Where wchar_t is 16 bits, Unicode
328 characters in the range 0x10000 to 0x10ffff are converted to
329 "surrogate pairs" which take two words each (this is called UTF-16
330 encoding). If wchar_t is 32 bits this rather nasty problem is
331 avoided.
332 */
utf8towc(const char * src,unsigned srclen,wchar_t * dst,unsigned dstlen)333 static unsigned utf8towc(const char *src, unsigned srclen, wchar_t *dst,
334 unsigned dstlen) {
335 const char *p = src;
336 const char *e = src + srclen;
337 unsigned count = 0;
338 if (dstlen)
339 while (true) {
340 if (p >= e) {
341 dst[count] = 0;
342 return count;
343 }
344 if (!(*p & 0x80)) {
345 // ASCII
346 dst[count] = *p++;
347 } else {
348 int len = 0;
349 unsigned ucs = utf8decode(p, e, &len);
350 p += len;
351 #ifdef _WIN32
352 if (ucs < 0x10000) {
353 dst[count] = static_cast<wchar_t>(ucs);
354 } else {
355 // Make a surrogate pair:
356 if (count + 2 >= dstlen) {
357 dst[count] = 0;
358 count += 2;
359 break;
360 }
361 dst[count] = static_cast<wchar_t>(
362 (((ucs - 0x10000u) >> 10) & 0x3ff) | 0xd800);
363 dst[++count] = static_cast<wchar_t>((ucs & 0x3ff) | 0xdc00);
364 }
365 #else
366 dst[count] = static_cast<wchar_t>(ucs);
367 #endif
368 }
369 if (++count == dstlen) {
370 dst[count - 1] = 0;
371 break;
372 }
373 }
374 // We filled dst, measure the rest:
375 while (p < e) {
376 if (!(*p & 0x80)) {
377 p++;
378 } else {
379 int len = 0;
380 #ifdef _WIN32
381 const unsigned ucs = utf8decode(p, e, &len);
382 p += len;
383 if (ucs >= 0x10000)
384 ++count;
385 #else
386 utf8decode(p, e, &len);
387 p += len;
388 #endif
389 }
390 ++count;
391 }
392
393 return count;
394 }
395
396 // ---------------------------------------------------------------------------
397
398 struct NonValidUTF8Exception : public std::exception {};
399
400 // May throw exceptions
UTF8ToWString(const std::string & str)401 static std::wstring UTF8ToWString(const std::string &str) {
402 std::wstring wstr;
403 wstr.resize(str.size());
404 wstr.resize(utf8towc(str.data(), static_cast<unsigned>(str.size()),
405 &wstr[0], static_cast<unsigned>(wstr.size()) + 1));
406 for (const auto ch : wstr) {
407 if (ch == 0xfffd) {
408 throw NonValidUTF8Exception();
409 }
410 }
411 return wstr;
412 }
413
414 // ---------------------------------------------------------------------------
415
416 /************************************************************************/
417 /* utf8fromwc() */
418 /************************************************************************/
419 /* Turn "wide characters" as returned by some system calls
420 (especially on Windows) into UTF-8.
421
422 Up to \a dstlen bytes are written to \a dst, including a null
423 terminator. The return value is the number of bytes that would be
424 written, not counting the null terminator. If greater or equal to
425 \a dstlen then if you malloc a new array of size n+1 you will have
426 the space needed for the entire string. If \a dstlen is zero then
427 nothing is written and this call just measures the storage space
428 needed.
429
430 \a srclen is the number of words in \a src to convert. On Windows
431 this is not necessarily the number of characters, due to there
432 possibly being "surrogate pairs" in the UTF-16 encoding used.
433 On Unix wchar_t is 32 bits and each location is a character.
434
435 On Unix if a src word is greater than 0x10ffff then this is an
436 illegal character according to RFC 3629. These are converted as
437 though they are 0xFFFD (REPLACEMENT CHARACTER). Characters in the
438 range 0xd800 to 0xdfff, or ending with 0xfffe or 0xffff are also
439 illegal according to RFC 3629. However I encode these as though
440 they are legal, so that utf8towc will return the original data.
441
442 On Windows "surrogate pairs" are converted to a single character
443 and UTF-8 encoded (as 4 bytes). Mismatched halves of surrogate
444 pairs are converted as though they are individual characters.
445 */
utf8fromwc(char * dst,unsigned dstlen,const wchar_t * src,unsigned srclen)446 static unsigned int utf8fromwc(char *dst, unsigned dstlen, const wchar_t *src,
447 unsigned srclen) {
448 unsigned int i = 0;
449 unsigned int count = 0;
450 if (dstlen)
451 while (true) {
452 if (i >= srclen) {
453 dst[count] = 0;
454 return count;
455 }
456 unsigned int ucs = src[i++];
457 if (ucs < 0x80U) {
458 dst[count++] = static_cast<char>(ucs);
459 if (count >= dstlen) {
460 dst[count - 1] = 0;
461 break;
462 }
463 } else if (ucs < 0x800U) {
464 // 2 bytes.
465 if (count + 2 >= dstlen) {
466 dst[count] = 0;
467 count += 2;
468 break;
469 }
470 dst[count++] = 0xc0 | static_cast<char>(ucs >> 6);
471 dst[count++] = 0x80 | static_cast<char>(ucs & 0x3F);
472 #ifdef _WIN32
473 } else if (ucs >= 0xd800 && ucs <= 0xdbff && i < srclen &&
474 src[i] >= 0xdc00 && src[i] <= 0xdfff) {
475 // Surrogate pair.
476 unsigned int ucs2 = src[i++];
477 ucs = 0x10000U + ((ucs & 0x3ff) << 10) + (ucs2 & 0x3ff);
478 // All surrogate pairs turn into 4-byte utf8.
479 #else
480 } else if (ucs >= 0x10000) {
481 if (ucs > 0x10ffff) {
482 ucs = 0xfffd;
483 goto J1;
484 }
485 #endif
486 if (count + 4 >= dstlen) {
487 dst[count] = 0;
488 count += 4;
489 break;
490 }
491 dst[count++] = 0xf0 | static_cast<char>(ucs >> 18);
492 dst[count++] = 0x80 | static_cast<char>((ucs >> 12) & 0x3F);
493 dst[count++] = 0x80 | static_cast<char>((ucs >> 6) & 0x3F);
494 dst[count++] = 0x80 | static_cast<char>(ucs & 0x3F);
495 } else {
496 #ifndef _WIN32
497 J1:
498 #endif
499 // All others are 3 bytes:
500 if (count + 3 >= dstlen) {
501 dst[count] = 0;
502 count += 3;
503 break;
504 }
505 dst[count++] = 0xe0 | static_cast<char>(ucs >> 12);
506 dst[count++] = 0x80 | static_cast<char>((ucs >> 6) & 0x3F);
507 dst[count++] = 0x80 | static_cast<char>(ucs & 0x3F);
508 }
509 }
510
511 // We filled dst, measure the rest:
512 while (i < srclen) {
513 unsigned int ucs = src[i++];
514 if (ucs < 0x80U) {
515 count++;
516 } else if (ucs < 0x800U) {
517 // 2 bytes.
518 count += 2;
519 #ifdef _WIN32
520 } else if (ucs >= 0xd800 && ucs <= 0xdbff && i < srclen - 1 &&
521 src[i + 1] >= 0xdc00 && src[i + 1] <= 0xdfff) {
522 // Surrogate pair.
523 ++i;
524 #else
525 } else if (ucs >= 0x10000 && ucs <= 0x10ffff) {
526 #endif
527 count += 4;
528 } else {
529 count += 3;
530 }
531 }
532 return count;
533 }
534
535 // ---------------------------------------------------------------------------
536
WStringToUTF8(const std::wstring & wstr)537 static std::string WStringToUTF8(const std::wstring &wstr) {
538 std::string str;
539 str.resize(wstr.size());
540 str.resize(utf8fromwc(&str[0], static_cast<unsigned>(str.size() + 1),
541 wstr.data(), static_cast<unsigned>(wstr.size())));
542 return str;
543 }
544
545 // ---------------------------------------------------------------------------
546
Win32Recode(const char * src,unsigned src_code_page,unsigned dst_code_page)547 static std::string Win32Recode(const char *src, unsigned src_code_page,
548 unsigned dst_code_page) {
549 // Convert from source code page to Unicode.
550
551 // Compute the length in wide characters.
552 int wlen = MultiByteToWideChar(src_code_page, MB_ERR_INVALID_CHARS, src, -1,
553 nullptr, 0);
554 if (wlen == 0 && GetLastError() == ERROR_NO_UNICODE_TRANSLATION) {
555 return std::string();
556 }
557
558 // Do the actual conversion.
559 std::wstring wbuf;
560 wbuf.resize(wlen);
561 MultiByteToWideChar(src_code_page, 0, src, -1, &wbuf[0], wlen);
562
563 // Convert from Unicode to destination code page.
564
565 // Compute the length in chars.
566 int len = WideCharToMultiByte(dst_code_page, 0, &wbuf[0], -1, nullptr, 0,
567 nullptr, nullptr);
568
569 // Do the actual conversion.
570 std::string out;
571 out.resize(len);
572 WideCharToMultiByte(dst_code_page, 0, &wbuf[0], -1, &out[0], len, nullptr,
573 nullptr);
574 out.resize(strlen(out.c_str()));
575
576 return out;
577 }
578
579 // ---------------------------------------------------------------------------
580
581 class FileWin32 : public File {
582 PJ_CONTEXT *m_ctx;
583 HANDLE m_handle;
584
585 FileWin32(const FileWin32 &) = delete;
586 FileWin32 &operator=(const FileWin32 &) = delete;
587
588 protected:
FileWin32(const std::string & name,PJ_CONTEXT * ctx,HANDLE handle)589 FileWin32(const std::string &name, PJ_CONTEXT *ctx, HANDLE handle)
590 : File(name), m_ctx(ctx), m_handle(handle) {}
591
592 public:
593 ~FileWin32() override;
594
595 size_t read(void *buffer, size_t sizeBytes) override;
596 size_t write(const void *buffer, size_t sizeBytes) override;
597 bool seek(unsigned long long offset, int whence = SEEK_SET) override;
598 unsigned long long tell() override;
reassign_context(PJ_CONTEXT * ctx)599 void reassign_context(PJ_CONTEXT *ctx) override { m_ctx = ctx; }
600
601 // We may lie, but the real use case is only for network files
hasChanged() const602 bool hasChanged() const override { return false; }
603
604 static std::unique_ptr<File> open(PJ_CONTEXT *ctx, const char *filename,
605 FileAccess access);
606 };
607
608 // ---------------------------------------------------------------------------
609
~FileWin32()610 FileWin32::~FileWin32() { CloseHandle(m_handle); }
611
612 // ---------------------------------------------------------------------------
613
read(void * buffer,size_t sizeBytes)614 size_t FileWin32::read(void *buffer, size_t sizeBytes) {
615 DWORD dwSizeRead = 0;
616 size_t nResult = 0;
617
618 if (!ReadFile(m_handle, buffer, static_cast<DWORD>(sizeBytes), &dwSizeRead,
619 nullptr))
620 nResult = 0;
621 else
622 nResult = dwSizeRead;
623
624 return nResult;
625 }
626
627 // ---------------------------------------------------------------------------
628
write(const void * buffer,size_t sizeBytes)629 size_t FileWin32::write(const void *buffer, size_t sizeBytes) {
630 DWORD dwSizeWritten = 0;
631 size_t nResult = 0;
632
633 if (!WriteFile(m_handle, buffer, static_cast<DWORD>(sizeBytes),
634 &dwSizeWritten, nullptr))
635 nResult = 0;
636 else
637 nResult = dwSizeWritten;
638
639 return nResult;
640 }
641
642 // ---------------------------------------------------------------------------
643
seek(unsigned long long offset,int whence)644 bool FileWin32::seek(unsigned long long offset, int whence) {
645 LONG dwMoveMethod, dwMoveHigh;
646 uint32_t nMoveLow;
647 LARGE_INTEGER li;
648
649 switch (whence) {
650 case SEEK_CUR:
651 dwMoveMethod = FILE_CURRENT;
652 break;
653 case SEEK_END:
654 dwMoveMethod = FILE_END;
655 break;
656 case SEEK_SET:
657 default:
658 dwMoveMethod = FILE_BEGIN;
659 break;
660 }
661
662 li.QuadPart = offset;
663 nMoveLow = li.LowPart;
664 dwMoveHigh = li.HighPart;
665
666 SetLastError(0);
667 SetFilePointer(m_handle, nMoveLow, &dwMoveHigh, dwMoveMethod);
668
669 return GetLastError() == NO_ERROR;
670 }
671
672 // ---------------------------------------------------------------------------
673
tell()674 unsigned long long FileWin32::tell() {
675 LARGE_INTEGER li;
676
677 li.HighPart = 0;
678 li.LowPart = SetFilePointer(m_handle, 0, &(li.HighPart), FILE_CURRENT);
679
680 return static_cast<unsigned long long>(li.QuadPart);
681 }
682 // ---------------------------------------------------------------------------
683
open(PJ_CONTEXT * ctx,const char * filename,FileAccess access)684 std::unique_ptr<File> FileWin32::open(PJ_CONTEXT *ctx, const char *filename,
685 FileAccess access) {
686 DWORD dwDesiredAccess = access == FileAccess::READ_ONLY
687 ? GENERIC_READ
688 : GENERIC_READ | GENERIC_WRITE;
689 DWORD dwCreationDisposition =
690 access == FileAccess::CREATE ? CREATE_ALWAYS : OPEN_EXISTING;
691 DWORD dwFlagsAndAttributes = (dwDesiredAccess == GENERIC_READ)
692 ? FILE_ATTRIBUTE_READONLY
693 : FILE_ATTRIBUTE_NORMAL;
694 try {
695 HANDLE hFile = CreateFileW(
696 UTF8ToWString(std::string(filename)).c_str(), dwDesiredAccess,
697 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
698 dwCreationDisposition, dwFlagsAndAttributes, nullptr);
699 return std::unique_ptr<File>(hFile != INVALID_HANDLE_VALUE
700 ? new FileWin32(filename, ctx, hFile)
701 : nullptr);
702 } catch (const std::exception &e) {
703 pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what());
704 return nullptr;
705 }
706 }
707 #else
708
709 // ---------------------------------------------------------------------------
710
711 class FileStdio : public File {
712 PJ_CONTEXT *m_ctx;
713 FILE *m_fp;
714
715 FileStdio(const FileStdio &) = delete;
716 FileStdio &operator=(const FileStdio &) = delete;
717
718 protected:
FileStdio(const std::string & filename,PJ_CONTEXT * ctx,FILE * fp)719 FileStdio(const std::string &filename, PJ_CONTEXT *ctx, FILE *fp)
720 : File(filename), m_ctx(ctx), m_fp(fp) {}
721
722 public:
723 ~FileStdio() override;
724
725 size_t read(void *buffer, size_t sizeBytes) override;
726 size_t write(const void *buffer, size_t sizeBytes) override;
727 bool seek(unsigned long long offset, int whence = SEEK_SET) override;
728 unsigned long long tell() override;
reassign_context(PJ_CONTEXT * ctx)729 void reassign_context(PJ_CONTEXT *ctx) override { m_ctx = ctx; }
730
731 // We may lie, but the real use case is only for network files
hasChanged() const732 bool hasChanged() const override { return false; }
733
734 static std::unique_ptr<File> open(PJ_CONTEXT *ctx, const char *filename,
735 FileAccess access);
736 };
737
738 // ---------------------------------------------------------------------------
739
~FileStdio()740 FileStdio::~FileStdio() { fclose(m_fp); }
741
742 // ---------------------------------------------------------------------------
743
read(void * buffer,size_t sizeBytes)744 size_t FileStdio::read(void *buffer, size_t sizeBytes) {
745 return fread(buffer, 1, sizeBytes, m_fp);
746 }
747
748 // ---------------------------------------------------------------------------
749
write(const void * buffer,size_t sizeBytes)750 size_t FileStdio::write(const void *buffer, size_t sizeBytes) {
751 return fwrite(buffer, 1, sizeBytes, m_fp);
752 }
753
754 // ---------------------------------------------------------------------------
755
seek(unsigned long long offset,int whence)756 bool FileStdio::seek(unsigned long long offset, int whence) {
757 // TODO one day: use 64-bit offset compatible API
758 if (offset != static_cast<unsigned long long>(static_cast<long>(offset))) {
759 pj_log(m_ctx, PJ_LOG_ERROR,
760 "Attempt at seeking to a 64 bit offset. Not supported yet");
761 return false;
762 }
763 return fseek(m_fp, static_cast<long>(offset), whence) == 0;
764 }
765
766 // ---------------------------------------------------------------------------
767
tell()768 unsigned long long FileStdio::tell() {
769 // TODO one day: use 64-bit offset compatible API
770 return ftell(m_fp);
771 }
772
773 // ---------------------------------------------------------------------------
774
open(PJ_CONTEXT * ctx,const char * filename,FileAccess access)775 std::unique_ptr<File> FileStdio::open(PJ_CONTEXT *ctx, const char *filename,
776 FileAccess access) {
777 auto fp = fopen(filename,
778 access == FileAccess::READ_ONLY
779 ? "rb"
780 : access == FileAccess::READ_UPDATE ? "r+b" : "w+b");
781 return std::unique_ptr<File>(fp ? new FileStdio(filename, ctx, fp)
782 : nullptr);
783 }
784
785 #endif // _WIN32
786
787 // ---------------------------------------------------------------------------
788
789 #ifndef REMOVE_LEGACY_SUPPORT
790
791 class FileLegacyAdapter : public File {
792 PJ_CONTEXT *m_ctx;
793 PAFile m_fp;
794
795 FileLegacyAdapter(const FileLegacyAdapter &) = delete;
796 FileLegacyAdapter &operator=(const FileLegacyAdapter &) = delete;
797
798 protected:
FileLegacyAdapter(const std::string & filename,PJ_CONTEXT * ctx,PAFile fp)799 FileLegacyAdapter(const std::string &filename, PJ_CONTEXT *ctx, PAFile fp)
800 : File(filename), m_ctx(ctx), m_fp(fp) {}
801
802 public:
803 ~FileLegacyAdapter() override;
804
805 size_t read(void *buffer, size_t sizeBytes) override;
write(const void *,size_t)806 size_t write(const void *, size_t) override { return 0; }
807 bool seek(unsigned long long offset, int whence = SEEK_SET) override;
808 unsigned long long tell() override;
reassign_context(PJ_CONTEXT * ctx)809 void reassign_context(PJ_CONTEXT *ctx) override { m_ctx = ctx; }
810
811 // We may lie, but the real use case is only for network files
hasChanged() const812 bool hasChanged() const override { return false; }
813
814 static std::unique_ptr<File> open(PJ_CONTEXT *ctx, const char *filename,
815 FileAccess access);
816 };
817
818 // ---------------------------------------------------------------------------
819
~FileLegacyAdapter()820 FileLegacyAdapter::~FileLegacyAdapter() { pj_ctx_fclose(m_ctx, m_fp); }
821
822 // ---------------------------------------------------------------------------
823
read(void * buffer,size_t sizeBytes)824 size_t FileLegacyAdapter::read(void *buffer, size_t sizeBytes) {
825 return pj_ctx_fread(m_ctx, buffer, 1, sizeBytes, m_fp);
826 }
827
828 // ---------------------------------------------------------------------------
829
seek(unsigned long long offset,int whence)830 bool FileLegacyAdapter::seek(unsigned long long offset, int whence) {
831 if (offset != static_cast<unsigned long long>(static_cast<long>(offset))) {
832 pj_log(m_ctx, PJ_LOG_ERROR,
833 "Attempt at seeking to a 64 bit offset. Not supported yet");
834 return false;
835 }
836 return pj_ctx_fseek(m_ctx, m_fp, static_cast<long>(offset), whence) == 0;
837 }
838
839 // ---------------------------------------------------------------------------
840
tell()841 unsigned long long FileLegacyAdapter::tell() {
842 return pj_ctx_ftell(m_ctx, m_fp);
843 }
844
845 // ---------------------------------------------------------------------------
846
847 std::unique_ptr<File>
open(PJ_CONTEXT * ctx,const char * filename,FileAccess)848 FileLegacyAdapter::open(PJ_CONTEXT *ctx, const char *filename, FileAccess) {
849 auto fid = pj_ctx_fopen(ctx, filename, "rb");
850 return std::unique_ptr<File>(fid ? new FileLegacyAdapter(filename, ctx, fid)
851 : nullptr);
852 }
853
854 #endif // REMOVE_LEGACY_SUPPORT
855
856 // ---------------------------------------------------------------------------
857
858 class FileApiAdapter : public File {
859 PJ_CONTEXT *m_ctx;
860 PROJ_FILE_HANDLE *m_fp;
861
862 FileApiAdapter(const FileApiAdapter &) = delete;
863 FileApiAdapter &operator=(const FileApiAdapter &) = delete;
864
865 protected:
FileApiAdapter(const std::string & filename,PJ_CONTEXT * ctx,PROJ_FILE_HANDLE * fp)866 FileApiAdapter(const std::string &filename, PJ_CONTEXT *ctx,
867 PROJ_FILE_HANDLE *fp)
868 : File(filename), m_ctx(ctx), m_fp(fp) {}
869
870 public:
871 ~FileApiAdapter() override;
872
873 size_t read(void *buffer, size_t sizeBytes) override;
874 size_t write(const void *, size_t) override;
875 bool seek(unsigned long long offset, int whence = SEEK_SET) override;
876 unsigned long long tell() override;
reassign_context(PJ_CONTEXT * ctx)877 void reassign_context(PJ_CONTEXT *ctx) override { m_ctx = ctx; }
878
879 // We may lie, but the real use case is only for network files
hasChanged() const880 bool hasChanged() const override { return false; }
881
882 static std::unique_ptr<File> open(PJ_CONTEXT *ctx, const char *filename,
883 FileAccess access);
884 };
885
886 // ---------------------------------------------------------------------------
887
~FileApiAdapter()888 FileApiAdapter::~FileApiAdapter() {
889 m_ctx->fileApi.close_cbk(m_ctx, m_fp, m_ctx->fileApi.user_data);
890 }
891
892 // ---------------------------------------------------------------------------
893
read(void * buffer,size_t sizeBytes)894 size_t FileApiAdapter::read(void *buffer, size_t sizeBytes) {
895 return m_ctx->fileApi.read_cbk(m_ctx, m_fp, buffer, sizeBytes,
896 m_ctx->fileApi.user_data);
897 }
898
899 // ---------------------------------------------------------------------------
900
write(const void * buffer,size_t sizeBytes)901 size_t FileApiAdapter::write(const void *buffer, size_t sizeBytes) {
902 return m_ctx->fileApi.write_cbk(m_ctx, m_fp, buffer, sizeBytes,
903 m_ctx->fileApi.user_data);
904 }
905
906 // ---------------------------------------------------------------------------
907
seek(unsigned long long offset,int whence)908 bool FileApiAdapter::seek(unsigned long long offset, int whence) {
909 return m_ctx->fileApi.seek_cbk(m_ctx, m_fp, static_cast<long long>(offset),
910 whence, m_ctx->fileApi.user_data) != 0;
911 }
912
913 // ---------------------------------------------------------------------------
914
tell()915 unsigned long long FileApiAdapter::tell() {
916 return m_ctx->fileApi.tell_cbk(m_ctx, m_fp, m_ctx->fileApi.user_data);
917 }
918
919 // ---------------------------------------------------------------------------
920
open(PJ_CONTEXT * ctx,const char * filename,FileAccess eAccess)921 std::unique_ptr<File> FileApiAdapter::open(PJ_CONTEXT *ctx,
922 const char *filename,
923 FileAccess eAccess) {
924 PROJ_OPEN_ACCESS eCAccess = PROJ_OPEN_ACCESS_READ_ONLY;
925 switch (eAccess) {
926 case FileAccess::READ_ONLY:
927 // Initialized above
928 break;
929 case FileAccess::READ_UPDATE:
930 eCAccess = PROJ_OPEN_ACCESS_READ_UPDATE;
931 break;
932 case FileAccess::CREATE:
933 eCAccess = PROJ_OPEN_ACCESS_CREATE;
934 break;
935 }
936 auto fp =
937 ctx->fileApi.open_cbk(ctx, filename, eCAccess, ctx->fileApi.user_data);
938 return std::unique_ptr<File>(fp ? new FileApiAdapter(filename, ctx, fp)
939 : nullptr);
940 }
941
942 // ---------------------------------------------------------------------------
943
open(PJ_CONTEXT * ctx,const char * filename,FileAccess access)944 std::unique_ptr<File> FileManager::open(PJ_CONTEXT *ctx, const char *filename,
945 FileAccess access) {
946 if (starts_with(filename, "http://") || starts_with(filename, "https://")) {
947 if (!proj_context_is_network_enabled(ctx)) {
948 pj_log(
949 ctx, PJ_LOG_ERROR,
950 "Attempt at accessing remote resource not authorized. Either "
951 "set PROJ_NETWORK=ON or "
952 "proj_context_set_enable_network(ctx, TRUE)");
953 return nullptr;
954 }
955 return pj_network_file_open(ctx, filename);
956 }
957 #ifndef REMOVE_LEGACY_SUPPORT
958 // If the user has specified a legacy fileapi, use it
959 if (ctx->fileapi_legacy != pj_get_default_fileapi()) {
960 return FileLegacyAdapter::open(ctx, filename, access);
961 }
962 #endif
963 if (ctx->fileApi.open_cbk != nullptr) {
964 return FileApiAdapter::open(ctx, filename, access);
965 }
966 #ifdef _WIN32
967 return FileWin32::open(ctx, filename, access);
968 #else
969 return FileStdio::open(ctx, filename, access);
970 #endif
971 }
972
973 // ---------------------------------------------------------------------------
974
exists(PJ_CONTEXT * ctx,const char * filename)975 bool FileManager::exists(PJ_CONTEXT *ctx, const char *filename) {
976 if (ctx->fileApi.exists_cbk) {
977 return ctx->fileApi.exists_cbk(ctx, filename, ctx->fileApi.user_data) !=
978 0;
979 }
980
981 #ifdef _WIN32
982 struct __stat64 buf;
983 try {
984 return _wstat64(UTF8ToWString(filename).c_str(), &buf) == 0;
985 } catch (const std::exception &e) {
986 pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what());
987 return false;
988 }
989 #else
990 (void)ctx;
991 struct stat sStat;
992 return stat(filename, &sStat) == 0;
993 #endif
994 }
995
996 // ---------------------------------------------------------------------------
997
mkdir(PJ_CONTEXT * ctx,const char * filename)998 bool FileManager::mkdir(PJ_CONTEXT *ctx, const char *filename) {
999 if (ctx->fileApi.mkdir_cbk) {
1000 return ctx->fileApi.mkdir_cbk(ctx, filename, ctx->fileApi.user_data) !=
1001 0;
1002 }
1003
1004 #ifdef _WIN32
1005 try {
1006 return _wmkdir(UTF8ToWString(filename).c_str()) == 0;
1007 } catch (const std::exception &e) {
1008 pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what());
1009 return false;
1010 }
1011 #else
1012 (void)ctx;
1013 return ::mkdir(filename, 0755) == 0;
1014 #endif
1015 }
1016
1017 // ---------------------------------------------------------------------------
1018
unlink(PJ_CONTEXT * ctx,const char * filename)1019 bool FileManager::unlink(PJ_CONTEXT *ctx, const char *filename) {
1020 if (ctx->fileApi.unlink_cbk) {
1021 return ctx->fileApi.unlink_cbk(ctx, filename, ctx->fileApi.user_data) !=
1022 0;
1023 }
1024
1025 #ifdef _WIN32
1026 try {
1027 return _wunlink(UTF8ToWString(filename).c_str()) == 0;
1028 } catch (const std::exception &e) {
1029 pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what());
1030 return false;
1031 }
1032 #else
1033 (void)ctx;
1034 return ::unlink(filename) == 0;
1035 #endif
1036 }
1037
1038 // ---------------------------------------------------------------------------
1039
rename(PJ_CONTEXT * ctx,const char * oldPath,const char * newPath)1040 bool FileManager::rename(PJ_CONTEXT *ctx, const char *oldPath,
1041 const char *newPath) {
1042 if (ctx->fileApi.rename_cbk) {
1043 return ctx->fileApi.rename_cbk(ctx, oldPath, newPath,
1044 ctx->fileApi.user_data) != 0;
1045 }
1046
1047 #ifdef _WIN32
1048 try {
1049 return _wrename(UTF8ToWString(oldPath).c_str(),
1050 UTF8ToWString(newPath).c_str()) == 0;
1051 } catch (const std::exception &e) {
1052 pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what());
1053 return false;
1054 }
1055 #else
1056 (void)ctx;
1057 return ::rename(oldPath, newPath) == 0;
1058 #endif
1059 }
1060
1061 // ---------------------------------------------------------------------------
1062
getProjLibEnvVar(PJ_CONTEXT * ctx)1063 std::string FileManager::getProjLibEnvVar(PJ_CONTEXT *ctx) {
1064 if (!ctx->env_var_proj_lib.empty()) {
1065 return ctx->env_var_proj_lib;
1066 }
1067 (void)ctx;
1068 std::string str;
1069 const char *envvar = getenv("PROJ_LIB");
1070 if (!envvar)
1071 return str;
1072 str = envvar;
1073 #ifdef _WIN32
1074 // Assume this is UTF-8. If not try to convert from ANSI page
1075 bool looksLikeUTF8 = false;
1076 try {
1077 UTF8ToWString(envvar);
1078 looksLikeUTF8 = true;
1079 } catch (const std::exception &) {
1080 }
1081 if (!looksLikeUTF8 || !exists(ctx, envvar)) {
1082 str = Win32Recode(envvar, CP_ACP, CP_UTF8);
1083 if (str.empty() || !exists(ctx, str.c_str()))
1084 str = envvar;
1085 }
1086 #endif
1087 ctx->env_var_proj_lib = str;
1088 return str;
1089 }
1090
1091 NS_PROJ_END
1092
1093 // ---------------------------------------------------------------------------
1094
CreateDirectoryRecursively(PJ_CONTEXT * ctx,const std::string & path)1095 static void CreateDirectoryRecursively(PJ_CONTEXT *ctx,
1096 const std::string &path) {
1097 if (NS_PROJ::FileManager::exists(ctx, path.c_str()))
1098 return;
1099 auto pos = path.find_last_of("/\\");
1100 if (pos == 0 || pos == std::string::npos)
1101 return;
1102 CreateDirectoryRecursively(ctx, path.substr(0, pos));
1103 NS_PROJ::FileManager::mkdir(ctx, path.c_str());
1104 }
1105
1106 //! @endcond
1107
1108 // ---------------------------------------------------------------------------
1109
1110 /** Set a file API
1111 *
1112 * All callbacks should be provided (non NULL pointers). If read-only usage
1113 * is intended, then the callbacks might have a dummy implementation.
1114 *
1115 * \note Those callbacks will not be used for SQLite3 database access. If
1116 * custom I/O is desired for that, then proj_context_set_sqlite3_vfs_name()
1117 * should be used.
1118 *
1119 * @param ctx PROJ context, or NULL
1120 * @param fileapi Pointer to file API structure (content will be copied).
1121 * @param user_data Arbitrary pointer provided by the user, and passed to the
1122 * above callbacks. May be NULL.
1123 * @return TRUE in case of success.
1124 * @since 7.0
1125 */
proj_context_set_fileapi(PJ_CONTEXT * ctx,const PROJ_FILE_API * fileapi,void * user_data)1126 int proj_context_set_fileapi(PJ_CONTEXT *ctx, const PROJ_FILE_API *fileapi,
1127 void *user_data) {
1128 if (ctx == nullptr) {
1129 ctx = pj_get_default_ctx();
1130 }
1131 if (!fileapi) {
1132 return false;
1133 }
1134 if (fileapi->version != 1) {
1135 return false;
1136 }
1137 if (!fileapi->open_cbk || !fileapi->close_cbk || !fileapi->read_cbk ||
1138 !fileapi->write_cbk || !fileapi->seek_cbk || !fileapi->tell_cbk ||
1139 !fileapi->exists_cbk || !fileapi->mkdir_cbk || !fileapi->unlink_cbk ||
1140 !fileapi->rename_cbk) {
1141 return false;
1142 }
1143 ctx->fileApi.open_cbk = fileapi->open_cbk;
1144 ctx->fileApi.close_cbk = fileapi->close_cbk;
1145 ctx->fileApi.read_cbk = fileapi->read_cbk;
1146 ctx->fileApi.write_cbk = fileapi->write_cbk;
1147 ctx->fileApi.seek_cbk = fileapi->seek_cbk;
1148 ctx->fileApi.tell_cbk = fileapi->tell_cbk;
1149 ctx->fileApi.exists_cbk = fileapi->exists_cbk;
1150 ctx->fileApi.mkdir_cbk = fileapi->mkdir_cbk;
1151 ctx->fileApi.unlink_cbk = fileapi->unlink_cbk;
1152 ctx->fileApi.rename_cbk = fileapi->rename_cbk;
1153 ctx->fileApi.user_data = user_data;
1154 return true;
1155 }
1156
1157 // ---------------------------------------------------------------------------
1158
1159 /** Set the name of a custom SQLite3 VFS.
1160 *
1161 * This should be a valid SQLite3 VFS name, such as the one passed to the
1162 * sqlite3_vfs_register(). See https://www.sqlite.org/vfs.html
1163 *
1164 * It will be used to read proj.db or create&access the cache.db file in the
1165 * PROJ user writable directory.
1166 *
1167 * @param ctx PROJ context, or NULL
1168 * @param name SQLite3 VFS name. If NULL is passed, default implementation by
1169 * SQLite will be used.
1170 * @since 7.0
1171 */
proj_context_set_sqlite3_vfs_name(PJ_CONTEXT * ctx,const char * name)1172 void proj_context_set_sqlite3_vfs_name(PJ_CONTEXT *ctx, const char *name) {
1173 if (ctx == nullptr) {
1174 ctx = pj_get_default_ctx();
1175 }
1176 ctx->custom_sqlite3_vfs_name = name ? name : std::string();
1177 }
1178
1179 // ---------------------------------------------------------------------------
1180
1181 /** Get the PROJ user writable directory for datumgrid files.
1182 *
1183 * @param ctx PROJ context, or NULL
1184 * @param create If set to TRUE, create the directory if it does not exist
1185 * already.
1186 * @return The path to the PROJ user writable directory.
1187 * @since 7.1
1188 */
1189
proj_context_get_user_writable_directory(PJ_CONTEXT * ctx,int create)1190 const char *proj_context_get_user_writable_directory(PJ_CONTEXT *ctx,
1191 int create) {
1192 if (!ctx)
1193 ctx = pj_get_default_ctx();
1194 if (ctx->user_writable_directory.empty()) {
1195 // For testing purposes only
1196 const char *env_var_PROJ_USER_WRITABLE_DIRECTORY =
1197 getenv("PROJ_USER_WRITABLE_DIRECTORY");
1198 if (env_var_PROJ_USER_WRITABLE_DIRECTORY &&
1199 env_var_PROJ_USER_WRITABLE_DIRECTORY[0] != '\0') {
1200 ctx->user_writable_directory = env_var_PROJ_USER_WRITABLE_DIRECTORY;
1201 }
1202 }
1203 if (ctx->user_writable_directory.empty()) {
1204 std::string path;
1205 #ifdef _WIN32
1206 #ifdef __MINGW32__
1207 std::wstring wPath;
1208 wPath.resize(MAX_PATH);
1209 if (SHGetFolderPathW(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0,
1210 &wPath[0]) == S_OK) {
1211 wPath.resize(wcslen(wPath.data()));
1212 path = NS_PROJ::WStringToUTF8(wPath);
1213 #else
1214 wchar_t *wPath;
1215 if (SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &wPath) ==
1216 S_OK) {
1217 std::wstring ws(wPath);
1218 std::string str = NS_PROJ::WStringToUTF8(ws);
1219 path = str;
1220 CoTaskMemFree(wPath);
1221 #endif
1222 } else {
1223 const char *local_app_data = getenv("LOCALAPPDATA");
1224 if (!local_app_data) {
1225 local_app_data = getenv("TEMP");
1226 if (!local_app_data) {
1227 local_app_data = "c:/users";
1228 }
1229 }
1230 path = local_app_data;
1231 }
1232 #else
1233 const char *xdg_data_home = getenv("XDG_DATA_HOME");
1234 if (xdg_data_home != nullptr) {
1235 path = xdg_data_home;
1236 } else {
1237 const char *home = getenv("HOME");
1238 if (home && access(home, W_OK) == 0) {
1239 #if defined(__MACH__) && defined(__APPLE__)
1240 path = std::string(home) + "/Library/Application Support";
1241 #else
1242 path = std::string(home) + "/.local/share";
1243 #endif
1244 } else {
1245 path = "/tmp";
1246 }
1247 }
1248 #endif
1249 path += "/proj";
1250 ctx->user_writable_directory = path;
1251 }
1252 if (create != FALSE) {
1253 CreateDirectoryRecursively(ctx, ctx->user_writable_directory);
1254 }
1255 return ctx->user_writable_directory.c_str();
1256 }
1257
1258 /** Get the URL endpoint to query for remote grids.
1259 *
1260 * @param ctx PROJ context, or NULL
1261 * @return Endpoint URL. The returned pointer would be invalidated
1262 * by a later call to proj_context_set_url_endpoint()
1263 * @since 7.1
1264 */
1265 const char *proj_context_get_url_endpoint(PJ_CONTEXT *ctx) {
1266 if (ctx == nullptr) {
1267 ctx = pj_get_default_ctx();
1268 }
1269 if (!ctx->endpoint.empty()) {
1270 return ctx->endpoint.c_str();
1271 }
1272 pj_load_ini(ctx);
1273 return ctx->endpoint.c_str();
1274 }
1275
1276 // ---------------------------------------------------------------------------
1277
1278 //! @cond Doxygen_Suppress
1279
1280 // ---------------------------------------------------------------------------
1281
1282 void pj_context_set_user_writable_directory(PJ_CONTEXT *ctx,
1283 const std::string &path) {
1284 if (!ctx)
1285 ctx = pj_get_default_ctx();
1286 ctx->user_writable_directory = path;
1287 }
1288
1289 // ---------------------------------------------------------------------------
1290
1291 #ifdef WIN32
1292 static const char dir_chars[] = "/\\";
1293 #else
1294 static const char dir_chars[] = "/";
1295 #endif
1296
1297 static bool is_tilde_slash(const char *name) {
1298 return *name == '~' && strchr(dir_chars, name[1]);
1299 }
1300
1301 static bool is_rel_or_absolute_filename(const char *name) {
1302 return strchr(dir_chars, *name) ||
1303 (*name == '.' && strchr(dir_chars, name[1])) ||
1304 (!strncmp(name, "..", 2) && strchr(dir_chars, name[2])) ||
1305 (name[0] != '\0' && name[1] == ':' && strchr(dir_chars, name[2]));
1306 }
1307
1308 // ---------------------------------------------------------------------------
1309
1310 static std::string pj_get_relative_share_proj_internal_no_check() {
1311 #if defined(_WIN32) || defined(HAVE_LIBDL)
1312 #ifdef _WIN32
1313 HMODULE hm = NULL;
1314 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
1315 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
1316 (LPCSTR)&pj_get_relative_share_proj, &hm) == 0) {
1317 return std::string();
1318 }
1319
1320 DWORD path_size = 1024;
1321
1322 std::wstring wout;
1323 for (;;) {
1324 wout.clear();
1325 wout.resize(path_size);
1326 DWORD result = GetModuleFileNameW(hm, &wout[0], path_size - 1);
1327 DWORD last_error = GetLastError();
1328
1329 if (result == 0) {
1330 return std::string();
1331 } else if (result == path_size - 1) {
1332 if (ERROR_INSUFFICIENT_BUFFER != last_error) {
1333 return std::string();
1334 }
1335 path_size = path_size * 2;
1336 } else {
1337 break;
1338 }
1339 }
1340 wout.resize(wcslen(wout.c_str()));
1341 std::string out = NS_PROJ::WStringToUTF8(wout);
1342 constexpr char dir_sep = '\\';
1343 #else
1344 Dl_info info;
1345 if (!dladdr((const void *)pj_get_relative_share_proj, &info)) {
1346 return std::string();
1347 }
1348 std::string out(info.dli_fname);
1349 constexpr char dir_sep = '/';
1350 // "optimization" for cmake builds where RUNPATH is set to ${prefix}/lib
1351 out = replaceAll(out, "/bin/../", "/");
1352 #ifdef __linux
1353 // If we get a filename without any path, this is most likely a static
1354 // binary. Resolve the executable name
1355 if (out.find(dir_sep) == std::string::npos) {
1356 constexpr size_t BUFFER_SIZE = 1024;
1357 std::vector<char> path(BUFFER_SIZE + 1);
1358 ssize_t nResultLen = readlink("/proc/self/exe", &path[0], BUFFER_SIZE);
1359 if (nResultLen >= 0 && static_cast<size_t>(nResultLen) < BUFFER_SIZE) {
1360 out.assign(path.data(), static_cast<size_t>(nResultLen));
1361 }
1362 }
1363 #endif
1364 if (starts_with(out, "./"))
1365 out = out.substr(2);
1366 #endif
1367 auto pos = out.find_last_of(dir_sep);
1368 if (pos == std::string::npos) {
1369 // The initial path was something like libproj.so"
1370 out = "../share/proj";
1371 return out;
1372 }
1373 out.resize(pos);
1374 pos = out.find_last_of(dir_sep);
1375 if (pos == std::string::npos) {
1376 // The initial path was something like bin/libproj.so"
1377 out = "share/proj";
1378 return out;
1379 }
1380 out.resize(pos);
1381 // The initial path was something like foo/bin/libproj.so"
1382 out += "/share/proj";
1383 return out;
1384 #else
1385 return std::string();
1386 #endif
1387 }
1388
1389 static std::string
1390 pj_get_relative_share_proj_internal_check_exists(PJ_CONTEXT *ctx) {
1391 if (ctx == nullptr) {
1392 ctx = pj_get_default_ctx();
1393 }
1394 std::string path(pj_get_relative_share_proj_internal_no_check());
1395 if (!path.empty() && NS_PROJ::FileManager::exists(ctx, path.c_str())) {
1396 return path;
1397 }
1398 return std::string();
1399 }
1400
1401 std::string pj_get_relative_share_proj(PJ_CONTEXT *ctx) {
1402 static std::string path(
1403 pj_get_relative_share_proj_internal_check_exists(ctx));
1404 return path;
1405 }
1406
1407 // ---------------------------------------------------------------------------
1408
1409 static const char *get_path_from_relative_share_proj(PJ_CONTEXT *ctx,
1410 const char *name,
1411 std::string &out) {
1412 out = pj_get_relative_share_proj(ctx);
1413 if (out.empty()) {
1414 return nullptr;
1415 }
1416 out += '/';
1417 out += name;
1418
1419 return NS_PROJ::FileManager::exists(ctx, out.c_str()) ? out.c_str()
1420 : nullptr;
1421 }
1422
1423 /************************************************************************/
1424 /* pj_open_lib_internal() */
1425 /************************************************************************/
1426
1427 #ifdef WIN32
1428 static const char dirSeparator = ';';
1429 #else
1430 static const char dirSeparator = ':';
1431 #endif
1432
1433 static const char *proj_lib_name =
1434 #ifdef PROJ_LIB
1435 PROJ_LIB;
1436 #else
1437 nullptr;
1438 #endif
1439
1440 static bool dontReadUserWritableDirectory() {
1441 // Env var mostly for testing purposes and being independent from
1442 // an existing installation
1443 const char *envVar = getenv("PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY");
1444 return envVar != nullptr && envVar[0] != '\0';
1445 }
1446
1447 static void *
1448 pj_open_lib_internal(projCtx ctx, const char *name, const char *mode,
1449 void *(*open_file)(projCtx, const char *, const char *),
1450 char *out_full_filename, size_t out_full_filename_size) {
1451 try {
1452 std::string fname;
1453 const char *sysname = nullptr;
1454 void *fid = nullptr;
1455 std::string projLib;
1456
1457 if (ctx == nullptr) {
1458 ctx = pj_get_default_ctx();
1459 }
1460
1461 if (out_full_filename != nullptr && out_full_filename_size > 0)
1462 out_full_filename[0] = '\0';
1463
1464 /* check if ~/name */
1465 if (is_tilde_slash(name))
1466 if ((sysname = getenv("HOME")) != nullptr) {
1467 fname = sysname;
1468 fname += DIR_CHAR;
1469 fname += name;
1470 sysname = fname.c_str();
1471 } else
1472 return nullptr;
1473
1474 /* or fixed path: /name, ./name or ../name */
1475 else if (is_rel_or_absolute_filename(name)) {
1476 sysname = name;
1477 #ifdef _WIN32
1478 try {
1479 NS_PROJ::UTF8ToWString(name);
1480 } catch (const std::exception &) {
1481 fname = NS_PROJ::Win32Recode(name, CP_ACP, CP_UTF8);
1482 sysname = fname.c_str();
1483 }
1484 #endif
1485 }
1486
1487 else if (starts_with(name, "http://") || starts_with(name, "https://"))
1488 sysname = name;
1489
1490 /* or try to use application provided file finder */
1491 else if (ctx->file_finder != nullptr &&
1492 (sysname = ctx->file_finder(
1493 ctx, name, ctx->file_finder_user_data)) != nullptr)
1494 ;
1495
1496 else if (ctx->file_finder_legacy != nullptr &&
1497 (sysname = ctx->file_finder_legacy(name)) != nullptr)
1498 ;
1499
1500 /* The user has search paths set */
1501 else if (!ctx->search_paths.empty()) {
1502 for (const auto &path : ctx->search_paths) {
1503 try {
1504 fname = path;
1505 fname += DIR_CHAR;
1506 fname += name;
1507 sysname = fname.c_str();
1508 fid = open_file(ctx, sysname, mode);
1509 } catch (const std::exception &) {
1510 }
1511 if (fid)
1512 break;
1513 }
1514 }
1515
1516 else if (!dontReadUserWritableDirectory() &&
1517 (fid = open_file(
1518 ctx,
1519 (std::string(proj_context_get_user_writable_directory(
1520 ctx, false)) +
1521 DIR_CHAR + name)
1522 .c_str(),
1523 mode)) != nullptr) {
1524 fname = proj_context_get_user_writable_directory(ctx, false);
1525 fname += DIR_CHAR;
1526 fname += name;
1527 sysname = fname.c_str();
1528 }
1529
1530 /* if is environment PROJ_LIB defined */
1531 else if (!(projLib = NS_PROJ::FileManager::getProjLibEnvVar(ctx))
1532 .empty()) {
1533 auto paths = NS_PROJ::internal::split(projLib, dirSeparator);
1534 for (const auto &path : paths) {
1535 fname = path;
1536 fname += DIR_CHAR;
1537 fname += name;
1538 sysname = fname.c_str();
1539 fid = open_file(ctx, sysname, mode);
1540 if (fid)
1541 break;
1542 }
1543 /* check if it lives in a ../share/proj dir of the proj dll */
1544 } else if ((sysname = get_path_from_relative_share_proj(
1545 ctx, name, fname)) != nullptr) {
1546 /* or hardcoded path */
1547 } else if ((sysname = proj_lib_name) != nullptr) {
1548 fname = sysname;
1549 fname += DIR_CHAR;
1550 fname += name;
1551 sysname = fname.c_str();
1552 /* just try it bare bones */
1553 } else {
1554 sysname = name;
1555 }
1556
1557 assert(sysname); // to make Coverity Scan happy
1558 if (fid != nullptr ||
1559 (fid = open_file(ctx, sysname, mode)) != nullptr) {
1560 if (out_full_filename != nullptr && out_full_filename_size > 0) {
1561 // cppcheck-suppress nullPointer
1562 strncpy(out_full_filename, sysname, out_full_filename_size);
1563 out_full_filename[out_full_filename_size - 1] = '\0';
1564 }
1565 errno = 0;
1566 }
1567
1568 if (ctx->last_errno == 0 && errno != 0)
1569 pj_ctx_set_errno(ctx, errno);
1570
1571 pj_log(ctx, PJ_LOG_DEBUG_MAJOR, "pj_open_lib(%s): call fopen(%s) - %s",
1572 name, sysname, fid == nullptr ? "failed" : "succeeded");
1573
1574 return (fid);
1575 } catch (const std::exception &) {
1576
1577 pj_log(ctx, PJ_LOG_DEBUG_MAJOR, "pj_open_lib(%s): out of memory", name);
1578
1579 return nullptr;
1580 }
1581 }
1582
1583 /************************************************************************/
1584 /* pj_get_default_searchpaths() */
1585 /************************************************************************/
1586
1587 std::vector<std::string> pj_get_default_searchpaths(PJ_CONTEXT *ctx) {
1588 std::vector<std::string> ret;
1589
1590 // Env var mostly for testing purposes and being independent from
1591 // an existing installation
1592 const char *ignoreUserWritableDirectory =
1593 getenv("PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY");
1594 if (ignoreUserWritableDirectory == nullptr ||
1595 ignoreUserWritableDirectory[0] == '\0') {
1596 ret.push_back(proj_context_get_user_writable_directory(ctx, false));
1597 }
1598 const std::string envPROJ_LIB = NS_PROJ::FileManager::getProjLibEnvVar(ctx);
1599 if (!envPROJ_LIB.empty()) {
1600 ret.push_back(envPROJ_LIB);
1601 }
1602 if (envPROJ_LIB.empty()) {
1603 const std::string relativeSharedProj = pj_get_relative_share_proj(ctx);
1604 if (!relativeSharedProj.empty()) {
1605 ret.push_back(relativeSharedProj);
1606 }
1607 }
1608 #ifdef PROJ_LIB
1609 if (envPROJ_LIB.empty()) {
1610 ret.push_back(PROJ_LIB);
1611 }
1612 #endif
1613 return ret;
1614 }
1615
1616 /************************************************************************/
1617 /* pj_open_file_with_manager() */
1618 /************************************************************************/
1619
1620 static void *pj_open_file_with_manager(projCtx ctx, const char *name,
1621 const char * /* mode */) {
1622 return NS_PROJ::FileManager::open(ctx, name, NS_PROJ::FileAccess::READ_ONLY)
1623 .release();
1624 }
1625
1626 // ---------------------------------------------------------------------------
1627
1628 static NS_PROJ::io::DatabaseContextPtr getDBcontext(PJ_CONTEXT *ctx) {
1629 try {
1630 return ctx->get_cpp_context()->getDatabaseContext().as_nullable();
1631 } catch (const std::exception &e) {
1632 pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what());
1633 return nullptr;
1634 }
1635 }
1636
1637 /************************************************************************/
1638 /* FileManager::open_resource_file() */
1639 /************************************************************************/
1640
1641 std::unique_ptr<NS_PROJ::File>
1642 NS_PROJ::FileManager::open_resource_file(projCtx ctx, const char *name) {
1643
1644 if (ctx == nullptr) {
1645 ctx = pj_get_default_ctx();
1646 }
1647
1648 auto file = std::unique_ptr<NS_PROJ::File>(
1649 reinterpret_cast<NS_PROJ::File *>(pj_open_lib_internal(
1650 ctx, name, "rb", pj_open_file_with_manager, nullptr, 0)));
1651
1652 // Retry with the new proj grid name if the file name doesn't end with .tif
1653 std::string tmpString; // keep it in this upper scope !
1654 if (file == nullptr && !is_tilde_slash(name) &&
1655 !is_rel_or_absolute_filename(name) && !starts_with(name, "http://") &&
1656 !starts_with(name, "https://") && strcmp(name, "proj.db") != 0 &&
1657 strstr(name, ".tif") == nullptr) {
1658
1659 auto dbContext = getDBcontext(ctx);
1660 if (dbContext) {
1661 try {
1662 auto filename = dbContext->getProjGridName(name);
1663 if (!filename.empty()) {
1664 file.reset(reinterpret_cast<NS_PROJ::File *>(
1665 pj_open_lib_internal(ctx, filename.c_str(), "rb",
1666 pj_open_file_with_manager, nullptr,
1667 0)));
1668 if (file) {
1669 pj_ctx_set_errno(ctx, 0);
1670 } else {
1671 // For final network access attempt, use the new
1672 // name.
1673 tmpString = filename;
1674 name = tmpString.c_str();
1675 }
1676 }
1677 } catch (const std::exception &e) {
1678 pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what());
1679 return nullptr;
1680 }
1681 }
1682 }
1683 // Retry with the old proj grid name if the file name ends with .tif
1684 else if (file == nullptr && !is_tilde_slash(name) &&
1685 !is_rel_or_absolute_filename(name) &&
1686 !starts_with(name, "http://") && !starts_with(name, "https://") &&
1687 strstr(name, ".tif") != nullptr) {
1688
1689 auto dbContext = getDBcontext(ctx);
1690 if (dbContext) {
1691 try {
1692 auto filename = dbContext->getOldProjGridName(name);
1693 if (!filename.empty()) {
1694 file.reset(reinterpret_cast<NS_PROJ::File *>(
1695 pj_open_lib_internal(ctx, filename.c_str(), "rb",
1696 pj_open_file_with_manager, nullptr,
1697 0)));
1698 if (file) {
1699 pj_ctx_set_errno(ctx, 0);
1700 }
1701 }
1702 } catch (const std::exception &e) {
1703 pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what());
1704 return nullptr;
1705 }
1706 }
1707 }
1708
1709 if (file == nullptr && !is_tilde_slash(name) &&
1710 !is_rel_or_absolute_filename(name) && !starts_with(name, "http://") &&
1711 !starts_with(name, "https://") &&
1712 proj_context_is_network_enabled(ctx)) {
1713 std::string remote_file(proj_context_get_url_endpoint(ctx));
1714 if (!remote_file.empty()) {
1715 if (remote_file.back() != '/') {
1716 remote_file += '/';
1717 }
1718 remote_file += name;
1719 file =
1720 open(ctx, remote_file.c_str(), NS_PROJ::FileAccess::READ_ONLY);
1721 if (file) {
1722 pj_log(ctx, PJ_LOG_DEBUG_MAJOR, "Using %s",
1723 remote_file.c_str());
1724 pj_ctx_set_errno(ctx, 0);
1725 }
1726 }
1727 }
1728 return file;
1729 }
1730
1731 /************************************************************************/
1732 /* pj_open_lib() */
1733 /************************************************************************/
1734
1735 #ifndef REMOVE_LEGACY_SUPPORT
1736
1737 // Used by following legacy function
1738 static void *pj_ctx_fopen_adapter(projCtx ctx, const char *name,
1739 const char *mode) {
1740 return pj_ctx_fopen(ctx, name, mode);
1741 }
1742
1743 // Legacy function
1744 PAFile pj_open_lib(projCtx ctx, const char *name, const char *mode) {
1745 return (PAFile)pj_open_lib_internal(ctx, name, mode, pj_ctx_fopen_adapter,
1746 nullptr, 0);
1747 }
1748
1749 #endif // REMOVE_LEGACY_SUPPORT
1750
1751 /************************************************************************/
1752 /* pj_find_file() */
1753 /************************************************************************/
1754
1755 /** Returns the full filename corresponding to a proj resource file specified
1756 * as a short filename.
1757 *
1758 * @param ctx context.
1759 * @param short_filename short filename (e.g. us_nga_egm96_15.tif).
1760 * Must not be NULL.
1761 * @param out_full_filename output buffer, of size out_full_filename_size, that
1762 * will receive the full filename on success.
1763 * Will be zero-terminated.
1764 * @param out_full_filename_size size of out_full_filename.
1765 * @return 1 if the file was found, 0 otherwise.
1766 */
1767 int pj_find_file(projCtx ctx, const char *short_filename,
1768 char *out_full_filename, size_t out_full_filename_size) {
1769 auto file = std::unique_ptr<NS_PROJ::File>(
1770 reinterpret_cast<NS_PROJ::File *>(pj_open_lib_internal(
1771 ctx, short_filename, "rb", pj_open_file_with_manager,
1772 out_full_filename, out_full_filename_size)));
1773
1774 // Retry with the old proj grid name if the file name ends with .tif
1775 if (file == nullptr && strstr(short_filename, ".tif") != nullptr) {
1776
1777 auto dbContext = getDBcontext(ctx);
1778 if (dbContext) {
1779 try {
1780 auto filename = dbContext->getOldProjGridName(short_filename);
1781 if (!filename.empty()) {
1782 file.reset(reinterpret_cast<NS_PROJ::File *>(
1783 pj_open_lib_internal(ctx, filename.c_str(), "rb",
1784 pj_open_file_with_manager,
1785 out_full_filename,
1786 out_full_filename_size)));
1787 }
1788 } catch (const std::exception &e) {
1789 pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what());
1790 return false;
1791 }
1792 }
1793 }
1794
1795 return file != nullptr;
1796 }
1797
1798 /************************************************************************/
1799 /* trim() */
1800 /************************************************************************/
1801
1802 static std::string trim(const std::string &s) {
1803 const auto first = s.find_first_not_of(' ');
1804 const auto last = s.find_last_not_of(' ');
1805 if (first == std::string::npos || last == std::string::npos) {
1806 return std::string();
1807 }
1808 return s.substr(first, last - first + 1);
1809 }
1810
1811 /************************************************************************/
1812 /* pj_load_ini() */
1813 /************************************************************************/
1814
1815 void pj_load_ini(projCtx ctx) {
1816 if (ctx->iniFileLoaded)
1817 return;
1818
1819 const char *endpoint_from_env = getenv("PROJ_NETWORK_ENDPOINT");
1820 if (endpoint_from_env && endpoint_from_env[0] != '\0') {
1821 ctx->endpoint = endpoint_from_env;
1822 }
1823
1824 ctx->iniFileLoaded = true;
1825 auto file = std::unique_ptr<NS_PROJ::File>(
1826 reinterpret_cast<NS_PROJ::File *>(pj_open_lib_internal(
1827 ctx, "proj.ini", "rb", pj_open_file_with_manager, nullptr, 0)));
1828 if (!file)
1829 return;
1830 file->seek(0, SEEK_END);
1831 const auto filesize = file->tell();
1832 if (filesize == 0 || filesize > 100 * 1024U)
1833 return;
1834 file->seek(0, SEEK_SET);
1835 std::string content;
1836 content.resize(static_cast<size_t>(filesize));
1837 const auto nread = file->read(&content[0], content.size());
1838 if (nread != content.size())
1839 return;
1840 content += '\n';
1841 size_t pos = 0;
1842 while (pos != std::string::npos) {
1843 const auto eol = content.find_first_of("\r\n", pos);
1844 if (eol == std::string::npos) {
1845 break;
1846 }
1847
1848 const auto equal = content.find('=', pos);
1849 if (equal < eol) {
1850 const auto key = trim(content.substr(pos, equal - pos));
1851 const auto value =
1852 trim(content.substr(equal + 1, eol - (equal + 1)));
1853 if (ctx->endpoint.empty() && key == "cdn_endpoint") {
1854 ctx->endpoint = value;
1855 } else if (key == "network") {
1856 const char *enabled = getenv("PROJ_NETWORK");
1857 if (enabled == nullptr || enabled[0] == '\0') {
1858 ctx->networking.enabled = ci_equal(value, "ON") ||
1859 ci_equal(value, "YES") ||
1860 ci_equal(value, "TRUE");
1861 }
1862 } else if (key == "cache_enabled") {
1863 ctx->gridChunkCache.enabled = ci_equal(value, "ON") ||
1864 ci_equal(value, "YES") ||
1865 ci_equal(value, "TRUE");
1866 } else if (key == "cache_size_MB") {
1867 const int val = atoi(value.c_str());
1868 ctx->gridChunkCache.max_size =
1869 val > 0 ? static_cast<long long>(val) * 1024 * 1024 : -1;
1870 } else if (key == "cache_ttl_sec") {
1871 ctx->gridChunkCache.ttl = atoi(value.c_str());
1872 } else if (key == "tmerc_default_algo") {
1873 if (value == "auto") {
1874 ctx->defaultTmercAlgo = TMercAlgo::AUTO;
1875 } else if (value == "evenden_snyder") {
1876 ctx->defaultTmercAlgo = TMercAlgo::EVENDEN_SNYDER;
1877 } else if (value == "poder_engsager") {
1878 ctx->defaultTmercAlgo = TMercAlgo::PODER_ENGSAGER;
1879 } else {
1880 pj_log(
1881 ctx, PJ_LOG_ERROR,
1882 "pj_load_ini(): Invalid value for tmerc_default_algo");
1883 }
1884 }
1885 }
1886
1887 pos = content.find_first_not_of("\r\n", eol);
1888 }
1889 }
1890
1891 //! @endcond
1892
1893 /************************************************************************/
1894 /* pj_set_finder() */
1895 /************************************************************************/
1896
1897 void pj_set_finder(const char *(*new_finder)(const char *))
1898
1899 {
1900 auto ctx = pj_get_default_ctx();
1901 if (ctx) {
1902 ctx->file_finder_legacy = new_finder;
1903 }
1904 }
1905
1906 /************************************************************************/
1907 /* proj_context_set_file_finder() */
1908 /************************************************************************/
1909
1910 /** \brief Assign a file finder callback to a context.
1911 *
1912 * This callback will be used whenever PROJ must open one of its resource files
1913 * (proj.db database, grids, etc...)
1914 *
1915 * The callback will be called with the context currently in use at the moment
1916 * where it is used (not necessarily the one provided during this call), and
1917 * with the provided user_data (which may be NULL).
1918 * The user_data must remain valid during the whole lifetime of the context.
1919 *
1920 * A finder set on the default context will be inherited by contexts created
1921 * later.
1922 *
1923 * @param ctx PROJ context, or NULL for the default context.
1924 * @param finder Finder callback. May be NULL
1925 * @param user_data User data provided to the finder callback. May be NULL.
1926 *
1927 * @since PROJ 6.0
1928 */
1929 void proj_context_set_file_finder(PJ_CONTEXT *ctx, proj_file_finder finder,
1930 void *user_data) {
1931 if (!ctx)
1932 ctx = pj_get_default_ctx();
1933 if (!ctx)
1934 return;
1935 ctx->file_finder = finder;
1936 ctx->file_finder_user_data = user_data;
1937 }
1938
1939 /************************************************************************/
1940 /* proj_context_set_search_paths() */
1941 /************************************************************************/
1942
1943 /** \brief Sets search paths.
1944 *
1945 * Those search paths will be used whenever PROJ must open one of its resource
1946 * files
1947 * (proj.db database, grids, etc...)
1948 *
1949 * If set on the default context, they will be inherited by contexts created
1950 * later.
1951 *
1952 * Starting with PROJ 7.0, the path(s) should be encoded in UTF-8.
1953 *
1954 * @param ctx PROJ context, or NULL for the default context.
1955 * @param count_paths Number of paths. 0 if paths == NULL.
1956 * @param paths Paths. May be NULL.
1957 *
1958 * @since PROJ 6.0
1959 */
1960 void proj_context_set_search_paths(PJ_CONTEXT *ctx, int count_paths,
1961 const char *const *paths) {
1962 if (!ctx)
1963 ctx = pj_get_default_ctx();
1964 if (!ctx)
1965 return;
1966 try {
1967 std::vector<std::string> vector_of_paths;
1968 for (int i = 0; i < count_paths; i++) {
1969 vector_of_paths.emplace_back(paths[i]);
1970 }
1971 ctx->set_search_paths(vector_of_paths);
1972 } catch (const std::exception &) {
1973 }
1974 }
1975
1976 /************************************************************************/
1977 /* pj_set_searchpath() */
1978 /* */
1979 /* Path control for callers that can't practically provide */
1980 /* pj_set_finder() style callbacks. Call with (0,NULL) as args */
1981 /* to clear the searchpath set. */
1982 /************************************************************************/
1983
1984 void pj_set_searchpath(int count, const char **path) {
1985 proj_context_set_search_paths(nullptr, count,
1986 const_cast<const char *const *>(path));
1987 }
1988
1989 /************************************************************************/
1990 /* proj_context_set_ca_bundle_path() */
1991 /************************************************************************/
1992
1993 /** \brief Sets CA Bundle path.
1994 *
1995 * Those CA Bundle path will be used by PROJ when curl and PROJ_NETWORK
1996 * are enabled.
1997 *
1998 * If set on the default context, they will be inherited by contexts created
1999 * later.
2000 *
2001 * The path should be encoded in UTF-8.
2002 *
2003 * @param ctx PROJ context, or NULL for the default context.
2004 * @param path Path. May be NULL.
2005 *
2006 * @since PROJ 7.2
2007 */
2008 void proj_context_set_ca_bundle_path(PJ_CONTEXT *ctx, const char *path) {
2009 if (!ctx)
2010 ctx = pj_get_default_ctx();
2011 if (!ctx)
2012 return;
2013 try {
2014 ctx->set_ca_bundle_path(path != nullptr ? path : "");
2015 } catch (const std::exception &) {
2016 }
2017 }
2018