aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2020-01-10 21:54:34 +0100
committerEven Rouault <even.rouault@spatialys.com>2020-01-11 00:27:49 +0100
commitf6c424165264eec06af2504800ebfdd5d701efae (patch)
tree5512b667a9bd7c751c96990ec32082b8564c916b /src
parent90b6685a990b8c4931aafb508853401a89163e78 (diff)
downloadPROJ-f6c424165264eec06af2504800ebfdd5d701efae.tar.gz
PROJ-f6c424165264eec06af2504800ebfdd5d701efae.zip
Use Win32 Unicode APIs and expect all strings to be UTF-8 (fixes #1765)
For backward compatibility, if PROJ_LIB content is found to be not UTF-8 or pointing to a non existing directory, then an attempt at interpretating it in the ANSI page encoding is done. proj_context_set_search_paths() now assumes strings to be in UTF-8, and functions returning paths will also return values in UTF-8.
Diffstat (limited to 'src')
-rw-r--r--src/4D_api.cpp7
-rw-r--r--src/Makefile.am2
-rw-r--r--src/filemanager.cpp1317
-rw-r--r--src/filemanager.hpp18
-rw-r--r--src/lib_proj.cmake1
-rw-r--r--src/open_lib.cpp576
-rw-r--r--src/proj_internal.h1
7 files changed, 1265 insertions, 657 deletions
diff --git a/src/4D_api.cpp b/src/4D_api.cpp
index 9107723d..6b53363a 100644
--- a/src/4D_api.cpp
+++ b/src/4D_api.cpp
@@ -1461,10 +1461,10 @@ PJ_INFO proj_info (void) {
pj_context_get_user_writable_directory(ctx, false).c_str(),
&buf_size);
}
- const char *envPROJ_LIB = getenv("PROJ_LIB");
- buf = path_append(buf, envPROJ_LIB, &buf_size);
+ const std::string envPROJ_LIB = NS_PROJ::FileManager::getProjLibEnvVar(ctx);
+ buf = path_append(buf, envPROJ_LIB.empty() ? nullptr : envPROJ_LIB.c_str(), &buf_size);
#ifdef PROJ_LIB
- if (envPROJ_LIB == nullptr) {
+ if (envPROJ_LIB.empty()) {
buf = path_append(buf, PROJ_LIB, &buf_size);
}
#endif
@@ -1770,3 +1770,4 @@ PJ_FACTORS proj_factors(PJ *P, PJ_COORD lp) {
return factors;
}
+
diff --git a/src/Makefile.am b/src/Makefile.am
index 39667509..5e97cb4a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -190,7 +190,7 @@ libproj_la_SOURCES = \
deriv.cpp ell_set.cpp ellps.cpp errno.cpp \
factors.cpp fwd.cpp init.cpp inv.cpp \
list.cpp malloc.cpp mlfn.cpp msfn.cpp proj_mdist.cpp \
- open_lib.cpp param.cpp phi2.cpp pr_list.cpp \
+ param.cpp phi2.cpp pr_list.cpp \
qsfn.cpp strerrno.cpp \
tsfn.cpp units.cpp ctx.cpp log.cpp zpoly1.cpp rtodms.cpp \
release.cpp gauss.cpp \
diff --git a/src/filemanager.cpp b/src/filemanager.cpp
index 9acea83e..277578d1 100644
--- a/src/filemanager.cpp
+++ b/src/filemanager.cpp
@@ -33,10 +33,7 @@
#include <stdlib.h>
#include <algorithm>
-#include <codecvt>
-#include <functional>
#include <limits>
-#include <locale>
#include <string>
#include "filemanager.hpp"
@@ -114,6 +111,580 @@ File::File(const std::string &name) : name_(name) {}
File::~File() = default;
+#ifdef _WIN32
+
+/* The bulk of utf8towc()/utf8fromwc() is derived from the utf.c module from
+ * FLTK. It was originally downloaded from:
+ * http://svn.easysw.com/public/fltk/fltk/trunk/src/utf.c
+ * And already used by GDAL
+ */
+/************************************************************************/
+/* ==================================================================== */
+/* UTF.C code from FLTK with some modifications. */
+/* ==================================================================== */
+/************************************************************************/
+
+/* Set to 1 to turn bad UTF8 bytes into ISO-8859-1. If this is to zero
+ they are instead turned into the Unicode REPLACEMENT CHARACTER, of
+ value 0xfffd.
+ If this is on utf8decode will correctly map most (perhaps all)
+ human-readable text that is in ISO-8859-1. This may allow you
+ to completely ignore character sets in your code because virtually
+ everything is either ISO-8859-1 or UTF-8.
+*/
+#define ERRORS_TO_ISO8859_1 1
+
+/* Set to 1 to turn bad UTF8 bytes in the 0x80-0x9f range into the
+ Unicode index for Microsoft's CP1252 character set. You should
+ also set ERRORS_TO_ISO8859_1. With this a huge amount of more
+ available text (such as all web pages) are correctly converted
+ to Unicode.
+*/
+#define ERRORS_TO_CP1252 1
+
+/* A number of Unicode code points are in fact illegal and should not
+ be produced by a UTF-8 converter. Turn this on will replace the
+ bytes in those encodings with errors. If you do this then converting
+ arbitrary 16-bit data to UTF-8 and then back is not an identity,
+ which will probably break a lot of software.
+*/
+#define STRICT_RFC3629 0
+
+#if ERRORS_TO_CP1252
+// Codes 0x80..0x9f from the Microsoft CP1252 character set, translated
+// to Unicode:
+constexpr unsigned short cp1252[32] = {
+ 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
+ 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
+ 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
+ 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178};
+#endif
+
+/************************************************************************/
+/* utf8decode() */
+/************************************************************************/
+
+/*
+ Decode a single UTF-8 encoded character starting at \e p. The
+ resulting Unicode value (in the range 0-0x10ffff) is returned,
+ and \e len is set the number of bytes in the UTF-8 encoding
+ (adding \e len to \e p will point at the next character).
+
+ If \a p points at an illegal UTF-8 encoding, including one that
+ would go past \e end, or where a code is uses more bytes than
+ necessary, then *reinterpret_cast<const unsigned char*>(p) is translated as
+though it is
+ in the Microsoft CP1252 character set and \e len is set to 1.
+ Treating errors this way allows this to decode almost any
+ ISO-8859-1 or CP1252 text that has been mistakenly placed where
+ UTF-8 is expected, and has proven very useful.
+
+ If you want errors to be converted to error characters (as the
+ standards recommend), adding a test to see if the length is
+ unexpectedly 1 will work:
+
+\code
+ if( *p & 0x80 )
+ { // What should be a multibyte encoding.
+ code = utf8decode(p, end, &len);
+ if( len<2 ) code = 0xFFFD; // Turn errors into REPLACEMENT CHARACTER.
+ }
+ else
+ { // Handle the 1-byte utf8 encoding:
+ code = *p;
+ len = 1;
+ }
+\endcode
+
+ Direct testing for the 1-byte case (as shown above) will also
+ speed up the scanning of strings where the majority of characters
+ are ASCII.
+*/
+static unsigned utf8decode(const char *p, const char *end, int *len) {
+ unsigned char c = *reinterpret_cast<const unsigned char *>(p);
+ if (c < 0x80) {
+ *len = 1;
+ return c;
+#if ERRORS_TO_CP1252
+ } else if (c < 0xa0) {
+ *len = 1;
+ return cp1252[c - 0x80];
+#endif
+ } else if (c < 0xc2) {
+ goto FAIL;
+ }
+ if (p + 1 >= end || (p[1] & 0xc0) != 0x80)
+ goto FAIL;
+ if (c < 0xe0) {
+ *len = 2;
+ return ((p[0] & 0x1f) << 6) + ((p[1] & 0x3f));
+ } else if (c == 0xe0) {
+ if ((reinterpret_cast<const unsigned char *>(p))[1] < 0xa0)
+ goto FAIL;
+ goto UTF8_3;
+#if STRICT_RFC3629
+ } else if (c == 0xed) {
+ // RFC 3629 says surrogate chars are illegal.
+ if ((reinterpret_cast<const unsigned char *>(p))[1] >= 0xa0)
+ goto FAIL;
+ goto UTF8_3;
+ } else if (c == 0xef) {
+ // 0xfffe and 0xffff are also illegal characters.
+ if ((reinterpret_cast<const unsigned char *>(p))[1] == 0xbf &&
+ (reinterpret_cast<const unsigned char *>(p))[2] >= 0xbe)
+ goto FAIL;
+ goto UTF8_3;
+#endif
+ } else if (c < 0xf0) {
+ UTF8_3:
+ if (p + 2 >= end || (p[2] & 0xc0) != 0x80)
+ goto FAIL;
+ *len = 3;
+ return ((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6) + ((p[2] & 0x3f));
+ } else if (c == 0xf0) {
+ if ((reinterpret_cast<const unsigned char *>(p))[1] < 0x90)
+ goto FAIL;
+ goto UTF8_4;
+ } else if (c < 0xf4) {
+ UTF8_4:
+ if (p + 3 >= end || (p[2] & 0xc0) != 0x80 || (p[3] & 0xc0) != 0x80)
+ goto FAIL;
+ *len = 4;
+#if STRICT_RFC3629
+ // RFC 3629 says all codes ending in fffe or ffff are illegal:
+ if ((p[1] & 0xf) == 0xf &&
+ (reinterpret_cast<const unsigned char *>(p))[2] == 0xbf &&
+ (reinterpret_cast<const unsigned char *>(p))[3] >= 0xbe)
+ goto FAIL;
+#endif
+ return ((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12) +
+ ((p[2] & 0x3f) << 6) + ((p[3] & 0x3f));
+ } else if (c == 0xf4) {
+ if ((reinterpret_cast<const unsigned char *>(p))[1] > 0x8f)
+ goto FAIL; // After 0x10ffff.
+ goto UTF8_4;
+ } else {
+ FAIL:
+ *len = 1;
+#if ERRORS_TO_ISO8859_1
+ return c;
+#else
+ return 0xfffd; // Unicode REPLACEMENT CHARACTER
+#endif
+ }
+}
+
+/************************************************************************/
+/* utf8towc() */
+/************************************************************************/
+
+/* Convert a UTF-8 sequence into an array of wchar_t. These
+ are used by some system calls, especially on Windows.
+
+ \a src points at the UTF-8, and \a srclen is the number of bytes to
+ convert.
+
+ \a dst points at an array to write, and \a dstlen is the number of
+ locations in this array. At most \a dstlen-1 words will be
+ written there, plus a 0 terminating word. Thus this function
+ will never overwrite the buffer and will always return a
+ zero-terminated string. If \a dstlen is zero then \a dst can be
+ null and no data is written, but the length is returned.
+
+ The return value is the number of words that \e would be written
+ to \a dst if it were long enough, not counting the terminating
+ zero. If the return value is greater or equal to \a dstlen it
+ indicates truncation, you can then allocate a new array of size
+ return+1 and call this again.
+
+ Errors in the UTF-8 are converted as though each byte in the
+ erroneous string is in the Microsoft CP1252 encoding. This allows
+ ISO-8859-1 text mistakenly identified as UTF-8 to be printed
+ correctly.
+
+ Notice that sizeof(wchar_t) is 2 on Windows and is 4 on Linux
+ and most other systems. Where wchar_t is 16 bits, Unicode
+ characters in the range 0x10000 to 0x10ffff are converted to
+ "surrogate pairs" which take two words each (this is called UTF-16
+ encoding). If wchar_t is 32 bits this rather nasty problem is
+ avoided.
+*/
+static unsigned utf8towc(const char *src, unsigned srclen, wchar_t *dst,
+ unsigned dstlen) {
+ const char *p = src;
+ const char *e = src + srclen;
+ unsigned count = 0;
+ if (dstlen)
+ while (true) {
+ if (p >= e) {
+ dst[count] = 0;
+ return count;
+ }
+ if (!(*p & 0x80)) {
+ // ASCII
+ dst[count] = *p++;
+ } else {
+ int len = 0;
+ unsigned ucs = utf8decode(p, e, &len);
+ p += len;
+#ifdef _WIN32
+ if (ucs < 0x10000) {
+ dst[count] = static_cast<wchar_t>(ucs);
+ } else {
+ // Make a surrogate pair:
+ if (count + 2 >= dstlen) {
+ dst[count] = 0;
+ count += 2;
+ break;
+ }
+ dst[count] = static_cast<wchar_t>(
+ (((ucs - 0x10000u) >> 10) & 0x3ff) | 0xd800);
+ dst[++count] = static_cast<wchar_t>((ucs & 0x3ff) | 0xdc00);
+ }
+#else
+ dst[count] = static_cast<wchar_t>(ucs);
+#endif
+ }
+ if (++count == dstlen) {
+ dst[count - 1] = 0;
+ break;
+ }
+ }
+ // We filled dst, measure the rest:
+ while (p < e) {
+ if (!(*p & 0x80)) {
+ p++;
+ } else {
+ int len = 0;
+#ifdef _WIN32
+ const unsigned ucs = utf8decode(p, e, &len);
+ p += len;
+ if (ucs >= 0x10000)
+ ++count;
+#else
+ utf8decode(p, e, &len);
+ p += len;
+#endif
+ }
+ ++count;
+ }
+
+ return count;
+}
+
+// ---------------------------------------------------------------------------
+
+struct NonValidUTF8Exception : public std::exception {};
+
+// May throw exceptions
+static std::wstring UTF8ToWString(const std::string &str) {
+ std::wstring wstr;
+ wstr.resize(str.size());
+ wstr.resize(utf8towc(str.data(), static_cast<unsigned>(str.size()),
+ &wstr[0], static_cast<unsigned>(wstr.size()) + 1));
+ for (const auto ch : wstr) {
+ if (ch == 0xfffd) {
+ throw NonValidUTF8Exception();
+ }
+ }
+ return wstr;
+}
+
+// ---------------------------------------------------------------------------
+
+/************************************************************************/
+/* utf8fromwc() */
+/************************************************************************/
+/* Turn "wide characters" as returned by some system calls
+ (especially on Windows) into UTF-8.
+
+ Up to \a dstlen bytes are written to \a dst, including a null
+ terminator. The return value is the number of bytes that would be
+ written, not counting the null terminator. If greater or equal to
+ \a dstlen then if you malloc a new array of size n+1 you will have
+ the space needed for the entire string. If \a dstlen is zero then
+ nothing is written and this call just measures the storage space
+ needed.
+
+ \a srclen is the number of words in \a src to convert. On Windows
+ this is not necessarily the number of characters, due to there
+ possibly being "surrogate pairs" in the UTF-16 encoding used.
+ On Unix wchar_t is 32 bits and each location is a character.
+
+ On Unix if a src word is greater than 0x10ffff then this is an
+ illegal character according to RFC 3629. These are converted as
+ though they are 0xFFFD (REPLACEMENT CHARACTER). Characters in the
+ range 0xd800 to 0xdfff, or ending with 0xfffe or 0xffff are also
+ illegal according to RFC 3629. However I encode these as though
+ they are legal, so that utf8towc will return the original data.
+
+ On Windows "surrogate pairs" are converted to a single character
+ and UTF-8 encoded (as 4 bytes). Mismatched halves of surrogate
+ pairs are converted as though they are individual characters.
+*/
+static unsigned int utf8fromwc(char *dst, unsigned dstlen, const wchar_t *src,
+ unsigned srclen) {
+ unsigned int i = 0;
+ unsigned int count = 0;
+ if (dstlen)
+ while (true) {
+ if (i >= srclen) {
+ dst[count] = 0;
+ return count;
+ }
+ unsigned int ucs = src[i++];
+ if (ucs < 0x80U) {
+ dst[count++] = static_cast<char>(ucs);
+ if (count >= dstlen) {
+ dst[count - 1] = 0;
+ break;
+ }
+ } else if (ucs < 0x800U) {
+ // 2 bytes.
+ if (count + 2 >= dstlen) {
+ dst[count] = 0;
+ count += 2;
+ break;
+ }
+ dst[count++] = 0xc0 | static_cast<char>(ucs >> 6);
+ dst[count++] = 0x80 | static_cast<char>(ucs & 0x3F);
+#ifdef _WIN32
+ } else if (ucs >= 0xd800 && ucs <= 0xdbff && i < srclen &&
+ src[i] >= 0xdc00 && src[i] <= 0xdfff) {
+ // Surrogate pair.
+ unsigned int ucs2 = src[i++];
+ ucs = 0x10000U + ((ucs & 0x3ff) << 10) + (ucs2 & 0x3ff);
+// All surrogate pairs turn into 4-byte utf8.
+#else
+ } else if (ucs >= 0x10000) {
+ if (ucs > 0x10ffff) {
+ ucs = 0xfffd;
+ goto J1;
+ }
+#endif
+ if (count + 4 >= dstlen) {
+ dst[count] = 0;
+ count += 4;
+ break;
+ }
+ dst[count++] = 0xf0 | static_cast<char>(ucs >> 18);
+ dst[count++] = 0x80 | static_cast<char>((ucs >> 12) & 0x3F);
+ dst[count++] = 0x80 | static_cast<char>((ucs >> 6) & 0x3F);
+ dst[count++] = 0x80 | static_cast<char>(ucs & 0x3F);
+ } else {
+#ifndef _WIN32
+ J1:
+#endif
+ // All others are 3 bytes:
+ if (count + 3 >= dstlen) {
+ dst[count] = 0;
+ count += 3;
+ break;
+ }
+ dst[count++] = 0xe0 | static_cast<char>(ucs >> 12);
+ dst[count++] = 0x80 | static_cast<char>((ucs >> 6) & 0x3F);
+ dst[count++] = 0x80 | static_cast<char>(ucs & 0x3F);
+ }
+ }
+
+ // We filled dst, measure the rest:
+ while (i < srclen) {
+ unsigned int ucs = src[i++];
+ if (ucs < 0x80U) {
+ count++;
+ } else if (ucs < 0x800U) {
+ // 2 bytes.
+ count += 2;
+#ifdef _WIN32
+ } else if (ucs >= 0xd800 && ucs <= 0xdbff && i < srclen - 1 &&
+ src[i + 1] >= 0xdc00 && src[i + 1] <= 0xdfff) {
+ // Surrogate pair.
+ ++i;
+#else
+ } else if (ucs >= 0x10000 && ucs <= 0x10ffff) {
+#endif
+ count += 4;
+ } else {
+ count += 3;
+ }
+ }
+ return count;
+}
+
+// ---------------------------------------------------------------------------
+
+static std::string WStringToUTF8(const std::wstring &wstr) {
+ std::string str;
+ str.resize(wstr.size());
+ str.resize(utf8fromwc(&str[0], static_cast<unsigned>(str.size() + 1),
+ wstr.data(), static_cast<unsigned>(wstr.size())));
+ return str;
+}
+
+// ---------------------------------------------------------------------------
+
+static std::string Win32Recode(const char *src, unsigned src_code_page,
+ unsigned dst_code_page) {
+ // Convert from source code page to Unicode.
+
+ // Compute the length in wide characters.
+ int wlen = MultiByteToWideChar(src_code_page, MB_ERR_INVALID_CHARS, src, -1,
+ nullptr, 0);
+ if (wlen == 0 && GetLastError() == ERROR_NO_UNICODE_TRANSLATION) {
+ return std::string();
+ }
+
+ // Do the actual conversion.
+ std::wstring wbuf;
+ wbuf.resize(wlen);
+ MultiByteToWideChar(src_code_page, 0, src, -1, &wbuf[0], wlen);
+
+ // Convert from Unicode to destination code page.
+
+ // Compute the length in chars.
+ int len = WideCharToMultiByte(dst_code_page, 0, &wbuf[0], -1, nullptr, 0,
+ nullptr, nullptr);
+
+ // Do the actual conversion.
+ std::string out;
+ out.resize(len);
+ WideCharToMultiByte(dst_code_page, 0, &wbuf[0], -1, &out[0], len, nullptr,
+ nullptr);
+ out.resize(strlen(out.c_str()));
+
+ return out;
+}
+
+// ---------------------------------------------------------------------------
+
+class FileWin32 : public File {
+ PJ_CONTEXT *m_ctx;
+ HANDLE m_handle;
+
+ FileWin32(const FileWin32 &) = delete;
+ FileWin32 &operator=(const FileWin32 &) = delete;
+
+ protected:
+ FileWin32(const std::string &name, PJ_CONTEXT *ctx, HANDLE handle)
+ : File(name), m_ctx(ctx), m_handle(handle) {}
+
+ public:
+ ~FileWin32() override;
+
+ size_t read(void *buffer, size_t sizeBytes) override;
+ size_t write(const void *buffer, size_t sizeBytes) override;
+ bool seek(unsigned long long offset, int whence = SEEK_SET) override;
+ unsigned long long tell() override;
+ void reassign_context(PJ_CONTEXT *ctx) override { m_ctx = ctx; }
+
+ // We may lie, but the real use case is only for network files
+ bool hasChanged() const override { return false; }
+
+ static std::unique_ptr<File> open(PJ_CONTEXT *ctx, const char *filename,
+ FileAccess access);
+};
+
+// ---------------------------------------------------------------------------
+
+FileWin32::~FileWin32() { CloseHandle(m_handle); }
+
+// ---------------------------------------------------------------------------
+
+size_t FileWin32::read(void *buffer, size_t sizeBytes) {
+ DWORD dwSizeRead = 0;
+ size_t nResult = 0;
+
+ if (!ReadFile(m_handle, buffer, static_cast<DWORD>(sizeBytes), &dwSizeRead,
+ nullptr))
+ nResult = 0;
+ else
+ nResult = dwSizeRead;
+
+ return nResult;
+}
+
+// ---------------------------------------------------------------------------
+
+size_t FileWin32::write(const void *buffer, size_t sizeBytes) {
+ DWORD dwSizeWritten = 0;
+ size_t nResult = 0;
+
+ if (!WriteFile(m_handle, buffer, static_cast<DWORD>(sizeBytes),
+ &dwSizeWritten, nullptr))
+ nResult = 0;
+ else
+ nResult = dwSizeWritten;
+
+ return nResult;
+}
+
+// ---------------------------------------------------------------------------
+
+bool FileWin32::seek(unsigned long long offset, int whence) {
+ LONG dwMoveMethod, dwMoveHigh;
+ uint32_t nMoveLow;
+ LARGE_INTEGER li;
+
+ switch (whence) {
+ case SEEK_CUR:
+ dwMoveMethod = FILE_CURRENT;
+ break;
+ case SEEK_END:
+ dwMoveMethod = FILE_END;
+ break;
+ case SEEK_SET:
+ default:
+ dwMoveMethod = FILE_BEGIN;
+ break;
+ }
+
+ li.QuadPart = offset;
+ nMoveLow = li.LowPart;
+ dwMoveHigh = li.HighPart;
+
+ SetLastError(0);
+ SetFilePointer(m_handle, nMoveLow, &dwMoveHigh, dwMoveMethod);
+
+ return GetLastError() == NO_ERROR;
+}
+
+// ---------------------------------------------------------------------------
+
+unsigned long long FileWin32::tell() {
+ LARGE_INTEGER li;
+
+ li.HighPart = 0;
+ li.LowPart = SetFilePointer(m_handle, 0, &(li.HighPart), FILE_CURRENT);
+
+ return static_cast<unsigned long long>(li.QuadPart);
+}
+// ---------------------------------------------------------------------------
+
+std::unique_ptr<File> FileWin32::open(PJ_CONTEXT *ctx, const char *filename,
+ FileAccess access) {
+ DWORD dwDesiredAccess = access == FileAccess::READ_ONLY
+ ? GENERIC_READ
+ : GENERIC_READ | GENERIC_WRITE;
+ DWORD dwCreationDisposition =
+ access == FileAccess::CREATE ? CREATE_ALWAYS : OPEN_EXISTING;
+ DWORD dwFlagsAndAttributes = (dwDesiredAccess == GENERIC_READ)
+ ? FILE_ATTRIBUTE_READONLY
+ : FILE_ATTRIBUTE_NORMAL;
+ try {
+ HANDLE hFile = CreateFileW(
+ UTF8ToWString(std::string(filename)).c_str(), dwDesiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
+ dwCreationDisposition, dwFlagsAndAttributes, nullptr);
+ return std::unique_ptr<File>(hFile != INVALID_HANDLE_VALUE
+ ? new FileWin32(filename, ctx, hFile)
+ : nullptr);
+ } catch (const std::exception &e) {
+ pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what());
+ return nullptr;
+ }
+}
+#else
+
// ---------------------------------------------------------------------------
class FileStdio : public File {
@@ -131,6 +702,7 @@ class FileStdio : public File {
~FileStdio() override;
size_t read(void *buffer, size_t sizeBytes) override;
+ size_t write(const void *buffer, size_t sizeBytes) override;
bool seek(unsigned long long offset, int whence = SEEK_SET) override;
unsigned long long tell() override;
void reassign_context(PJ_CONTEXT *ctx) override { m_ctx = ctx; }
@@ -138,7 +710,8 @@ class FileStdio : public File {
// We may lie, but the real use case is only for network files
bool hasChanged() const override { return false; }
- static std::unique_ptr<File> open(PJ_CONTEXT *ctx, const char *filename);
+ static std::unique_ptr<File> open(PJ_CONTEXT *ctx, const char *filename,
+ FileAccess access);
};
// ---------------------------------------------------------------------------
@@ -153,6 +726,12 @@ size_t FileStdio::read(void *buffer, size_t sizeBytes) {
// ---------------------------------------------------------------------------
+size_t FileStdio::write(const void *buffer, size_t sizeBytes) {
+ return fwrite(buffer, 1, sizeBytes, m_fp);
+}
+
+// ---------------------------------------------------------------------------
+
bool FileStdio::seek(unsigned long long offset, int whence) {
// TODO one day: use 64-bit offset compatible API
if (offset != static_cast<unsigned long long>(static_cast<long>(offset))) {
@@ -172,12 +751,18 @@ unsigned long long FileStdio::tell() {
// ---------------------------------------------------------------------------
-std::unique_ptr<File> FileStdio::open(PJ_CONTEXT *ctx, const char *filename) {
- auto fp = fopen(filename, "rb");
+std::unique_ptr<File> FileStdio::open(PJ_CONTEXT *ctx, const char *filename,
+ FileAccess access) {
+ auto fp = fopen(filename,
+ access == FileAccess::READ_ONLY
+ ? "rb"
+ : access == FileAccess::READ_UPDATE ? "r+b" : "w+b");
return std::unique_ptr<File>(fp ? new FileStdio(filename, ctx, fp)
: nullptr);
}
+#endif // _WIN32
+
// ---------------------------------------------------------------------------
#ifndef REMOVE_LEGACY_SUPPORT
@@ -197,6 +782,7 @@ class FileLegacyAdapter : public File {
~FileLegacyAdapter() override;
size_t read(void *buffer, size_t sizeBytes) override;
+ size_t write(const void *, size_t) override { return 0; }
bool seek(unsigned long long offset, int whence = SEEK_SET) override;
unsigned long long tell() override;
void reassign_context(PJ_CONTEXT *ctx) override { m_ctx = ctx; }
@@ -204,7 +790,8 @@ class FileLegacyAdapter : public File {
// We may lie, but the real use case is only for network files
bool hasChanged() const override { return false; }
- static std::unique_ptr<File> open(PJ_CONTEXT *ctx, const char *filename);
+ static std::unique_ptr<File> open(PJ_CONTEXT *ctx, const char *filename,
+ FileAccess access);
};
// ---------------------------------------------------------------------------
@@ -236,8 +823,8 @@ unsigned long long FileLegacyAdapter::tell() {
// ---------------------------------------------------------------------------
-std::unique_ptr<File> FileLegacyAdapter::open(PJ_CONTEXT *ctx,
- const char *filename) {
+std::unique_ptr<File>
+FileLegacyAdapter::open(PJ_CONTEXT *ctx, const char *filename, FileAccess) {
auto fid = pj_ctx_fopen(ctx, filename, "rb");
return std::unique_ptr<File>(fid ? new FileLegacyAdapter(filename, ctx, fid)
: nullptr);
@@ -1370,6 +1957,7 @@ class NetworkFile : public File {
~NetworkFile() override;
size_t read(void *buffer, size_t sizeBytes) override;
+ size_t write(const void *, size_t) override { return 0; }
bool seek(unsigned long long offset, int whence) override;
unsigned long long tell() override;
void reassign_context(PJ_CONTEXT *ctx) override;
@@ -1616,11 +2204,12 @@ void NetworkFile::reassign_context(PJ_CONTEXT *ctx) {
// ---------------------------------------------------------------------------
-std::unique_ptr<File> FileManager::open(PJ_CONTEXT *ctx, const char *filename) {
+std::unique_ptr<File> FileManager::open(PJ_CONTEXT *ctx, const char *filename,
+ FileAccess access) {
#ifndef REMOVE_LEGACY_SUPPORT
// If the user has specified a legacy fileapi, use it
if (ctx->fileapi_legacy != pj_get_default_fileapi()) {
- return FileLegacyAdapter::open(ctx, filename);
+ return FileLegacyAdapter::open(ctx, filename, access);
}
#endif
if (starts_with(filename, "http://") || starts_with(filename, "https://")) {
@@ -1634,7 +2223,109 @@ std::unique_ptr<File> FileManager::open(PJ_CONTEXT *ctx, const char *filename) {
}
return NetworkFile::open(ctx, filename);
}
- return FileStdio::open(ctx, filename);
+#ifdef _WIN32
+ return FileWin32::open(ctx, filename, access);
+#else
+ return FileStdio::open(ctx, filename, access);
+#endif
+}
+
+// ---------------------------------------------------------------------------
+
+bool FileManager::exists(PJ_CONTEXT *ctx, const char *filename) {
+#ifdef _WIN32
+ struct __stat64 buf;
+ try {
+ return _wstat64(UTF8ToWString(filename).c_str(), &buf) == 0;
+ } catch (const std::exception &e) {
+ pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what());
+ return false;
+ }
+#else
+ (void)ctx;
+ struct stat sStat;
+ return stat(filename, &sStat) == 0;
+#endif
+}
+
+// ---------------------------------------------------------------------------
+
+bool FileManager::mkdir(PJ_CONTEXT *ctx, const char *filename) {
+#ifdef _WIN32
+ try {
+ return _wmkdir(UTF8ToWString(filename).c_str()) == 0;
+ } catch (const std::exception &e) {
+ pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what());
+ return false;
+ }
+#else
+ (void)ctx;
+ return ::mkdir(filename, 0755) == 0;
+#endif
+}
+
+// ---------------------------------------------------------------------------
+
+bool FileManager::unlink(PJ_CONTEXT *ctx, const char *filename) {
+#ifdef _WIN32
+ try {
+ return _wunlink(UTF8ToWString(filename).c_str()) == 0;
+ } catch (const std::exception &e) {
+ pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what());
+ return false;
+ }
+#else
+ (void)ctx;
+ return ::unlink(filename) == 0;
+#endif
+}
+
+// ---------------------------------------------------------------------------
+
+bool FileManager::rename(PJ_CONTEXT *ctx, const char *oldPath,
+ const char *newPath) {
+#ifdef _WIN32
+ try {
+ return _wrename(UTF8ToWString(oldPath).c_str(),
+ UTF8ToWString(newPath).c_str()) == 0;
+ } catch (const std::exception &e) {
+ pj_log(ctx, PJ_LOG_DEBUG, "%s", e.what());
+ return false;
+ }
+#else
+ (void)ctx;
+ return ::rename(oldPath, newPath) == 0;
+#endif
+}
+
+// ---------------------------------------------------------------------------
+
+std::string FileManager::getProjLibEnvVar(PJ_CONTEXT *ctx) {
+ if (!ctx->env_var_proj_lib.empty()) {
+ return ctx->env_var_proj_lib;
+ }
+ (void)ctx;
+ std::string str;
+ const char *envvar = getenv("PROJ_LIB");
+ if (!envvar)
+ return str;
+ str = envvar;
+#ifdef _WIN32
+ // Assume this is UTF-8. If not try to convert from ANSI page
+ bool looksLikeUTF8 = false;
+ try {
+ UTF8ToWString(envvar);
+ looksLikeUTF8 = true;
+ } catch (const std::exception &) {
+ }
+ if (!looksLikeUTF8 || !exists(ctx, envvar)) {
+ str = Win32Recode(envvar, CP_ACP, CP_UTF8);
+ if (str.empty() || !exists(ctx, str.c_str()))
+ str = envvar;
+ }
+#endif
+ ctx->env_var_proj_lib = str;
+ return str;
}
// ---------------------------------------------------------------------------
@@ -2255,48 +2946,15 @@ bool pj_context_is_network_enabled(PJ_CONTEXT *ctx) {
// ---------------------------------------------------------------------------
-#ifdef _WIN32
-
-static std::wstring UTF8ToWString(const std::string &str) {
- using convert_typeX = std::codecvt_utf8<wchar_t>;
- std::wstring_convert<convert_typeX, wchar_t> converterX;
-
- return converterX.from_bytes(str);
-}
-
-// ---------------------------------------------------------------------------
-
-static std::string WStringToUTF8(const std::wstring &wstr) {
- using convert_typeX = std::codecvt_utf8<wchar_t>;
- std::wstring_convert<convert_typeX, wchar_t> converterX;
-
- return converterX.to_bytes(wstr);
-}
-#endif
-
-// ---------------------------------------------------------------------------
-
-static void CreateDirectory(const std::string &path) {
-#ifdef _WIN32
- struct __stat64 buf;
- const auto wpath = UTF8ToWString(path);
- if (_wstat64(wpath.c_str(), &buf) == 0)
+static void CreateDirectoryRecursively(PJ_CONTEXT *ctx,
+ const std::string &path) {
+ if (NS_PROJ::FileManager::exists(ctx, path.c_str()))
return;
auto pos = path.find_last_of("/\\");
if (pos == 0 || pos == std::string::npos)
return;
- CreateDirectory(path.substr(0, pos));
- _wmkdir(wpath.c_str());
-#else
- struct stat buf;
- if (stat(path.c_str(), &buf) == 0)
- return;
- auto pos = path.find_last_of("/\\");
- if (pos == 0 || pos == std::string::npos)
- return;
- CreateDirectory(path.substr(0, pos));
- mkdir(path.c_str(), 0755);
-#endif
+ CreateDirectoryRecursively(ctx, path.substr(0, pos));
+ NS_PROJ::FileManager::mkdir(ctx, path.c_str());
}
// ---------------------------------------------------------------------------
@@ -2320,7 +2978,7 @@ std::string pj_context_get_user_writable_directory(PJ_CONTEXT *ctx,
if (SHGetFolderPathW(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0,
&wPath[0]) == S_OK) {
wPath.resize(wcslen(wPath.data()));
- path = WStringToUTF8(wPath);
+ path = NS_PROJ::WStringToUTF8(wPath);
} else {
const char *local_app_data = getenv("LOCALAPPDATA");
if (!local_app_data) {
@@ -2352,7 +3010,7 @@ std::string pj_context_get_user_writable_directory(PJ_CONTEXT *ctx,
ctx->user_writable_directory = path;
}
if (create) {
- CreateDirectory(ctx->user_writable_directory);
+ CreateDirectoryRecursively(ctx, ctx->user_writable_directory);
}
return ctx->user_writable_directory;
}
@@ -2410,6 +3068,419 @@ static std::string build_url(PJ_CONTEXT *ctx, const char *name) {
return name;
}
+// ---------------------------------------------------------------------------
+
+#ifdef _WIN32
+static const char *get_path_from_win32_projlib(PJ_CONTEXT *ctx,
+ const char *name,
+ std::string &out) {
+ /* Check if proj.db lieves in a share/proj dir parallel to bin/proj.dll */
+ /* Based in
+ * https://stackoverflow.com/questions/9112893/how-to-get-path-to-executable-in-c-running-on-windows
+ */
+
+ DWORD path_size = 1024;
+
+ std::wstring wout;
+ for (;;) {
+ wout.clear();
+ wout.resize(path_size);
+ DWORD result = GetModuleFileNameW(nullptr, &wout[0], path_size - 1);
+ DWORD last_error = GetLastError();
+
+ if (result == 0) {
+ return nullptr;
+ } else if (result == path_size - 1) {
+ if (ERROR_INSUFFICIENT_BUFFER != last_error) {
+ return nullptr;
+ }
+ path_size = path_size * 2;
+ } else {
+ break;
+ }
+ }
+ // Now remove the program's name. It was (example)
+ // "C:\programs\gmt6\bin\gdal_translate.exe"
+ wout.resize(wcslen(wout.c_str()));
+ out = NS_PROJ::WStringToUTF8(wout);
+ size_t k = out.size();
+ while (k > 0 && out[--k] != '\\') {
+ }
+ out.resize(k);
+
+ out += "/../share/proj/";
+ out += name;
+
+ return NS_PROJ::FileManager::exists(ctx, out.c_str()) ? out.c_str()
+ : nullptr;
+}
+#endif
+
+/************************************************************************/
+/* pj_open_lib_internal() */
+/************************************************************************/
+
+#ifdef WIN32
+static const char dirSeparator = ';';
+#else
+static const char dirSeparator = ':';
+#endif
+
+static const char *proj_lib_name =
+#ifdef PROJ_LIB
+ PROJ_LIB;
+#else
+ nullptr;
+#endif
+
+static bool ignoreUserWritableDirectory() {
+ // Env var mostly for testing purposes and being independent from
+ // an existing installation
+ const char *envVarIgnoreUserWritableDirectory =
+ getenv("PROJ_IGNORE_USER_WRITABLE_DIRECTORY");
+ return envVarIgnoreUserWritableDirectory != nullptr &&
+ envVarIgnoreUserWritableDirectory[0] != '\0';
+}
+
+static void *
+pj_open_lib_internal(projCtx ctx, const char *name, const char *mode,
+ void *(*open_file)(projCtx, const char *, const char *),
+ char *out_full_filename, size_t out_full_filename_size) {
+ try {
+ std::string fname;
+ const char *sysname = nullptr;
+ void *fid = nullptr;
+ std::string projLib;
+
+ if (ctx == nullptr) {
+ ctx = pj_get_default_ctx();
+ }
+
+ if (out_full_filename != nullptr && out_full_filename_size > 0)
+ out_full_filename[0] = '\0';
+
+ /* check if ~/name */
+ if (is_tilde_slash(name))
+ if ((sysname = getenv("HOME")) != nullptr) {
+ fname = sysname;
+ fname += DIR_CHAR;
+ fname += name;
+ sysname = fname.c_str();
+ } else
+ return nullptr;
+
+ /* or fixed path: /name, ./name or ../name */
+ else if (is_rel_or_absolute_filename(name)) {
+ sysname = name;
+#ifdef _WIN32
+ try {
+ NS_PROJ::UTF8ToWString(name);
+ } catch (const std::exception &) {
+ fname = NS_PROJ::Win32Recode(name, CP_ACP, CP_UTF8);
+ sysname = fname.c_str();
+ }
+#endif
+ }
+
+ else if (starts_with(name, "http://") || starts_with(name, "https://"))
+ sysname = name;
+
+ /* or try to use application provided file finder */
+ else if (ctx->file_finder != nullptr &&
+ (sysname = ctx->file_finder(
+ ctx, name, ctx->file_finder_user_data)) != nullptr)
+ ;
+
+ else if (ctx->file_finder_legacy != nullptr &&
+ (sysname = ctx->file_finder_legacy(name)) != nullptr)
+ ;
+
+ /* The user has search paths set */
+ else if (!ctx->search_paths.empty()) {
+ for (const auto &path : ctx->search_paths) {
+ try {
+ fname = path;
+ fname += DIR_CHAR;
+ fname += name;
+ sysname = fname.c_str();
+ fid = open_file(ctx, sysname, mode);
+ } catch (const std::exception &) {
+ }
+ if (fid)
+ break;
+ }
+ }
+
+ else if (!ignoreUserWritableDirectory() &&
+ (fid = open_file(
+ ctx, (pj_context_get_user_writable_directory(ctx, false) +
+ DIR_CHAR + name)
+ .c_str(),
+ mode)) != nullptr) {
+ fname = pj_context_get_user_writable_directory(ctx, false);
+ fname += DIR_CHAR;
+ fname += name;
+ sysname = fname.c_str();
+ }
+
+ /* if is environment PROJ_LIB defined */
+ else if (!(projLib = NS_PROJ::FileManager::getProjLibEnvVar(ctx))
+ .empty()) {
+ auto paths = NS_PROJ::internal::split(projLib, dirSeparator);
+ for (const auto &path : paths) {
+ fname = path;
+ fname += DIR_CHAR;
+ fname += name;
+ sysname = fname.c_str();
+ fid = open_file(ctx, sysname, mode);
+ if (fid)
+ break;
+ }
+#ifdef _WIN32
+ /* check if it lives in a ../share/proj dir of the proj dll */
+ } else if ((sysname = get_path_from_win32_projlib(ctx, name, fname)) !=
+ nullptr) {
+#endif
+ /* or hardcoded path */
+ } else if ((sysname = proj_lib_name) != nullptr) {
+ fname = sysname;
+ fname += DIR_CHAR;
+ fname += name;
+ sysname = fname.c_str();
+ /* just try it bare bones */
+ } else {
+ sysname = name;
+ }
+
+ assert(sysname); // to make Coverity Scan happy
+ if (fid != nullptr ||
+ (fid = open_file(ctx, sysname, mode)) != nullptr) {
+ if (out_full_filename != nullptr && out_full_filename_size > 0) {
+ // cppcheck-suppress nullPointer
+ strncpy(out_full_filename, sysname, out_full_filename_size);
+ out_full_filename[out_full_filename_size - 1] = '\0';
+ }
+ errno = 0;
+ }
+
+ if (ctx->last_errno == 0 && errno != 0)
+ pj_ctx_set_errno(ctx, errno);
+
+ pj_log(ctx, PJ_LOG_DEBUG_MAJOR, "pj_open_lib(%s): call fopen(%s) - %s",
+ name, sysname, fid == nullptr ? "failed" : "succeeded");
+
+ return (fid);
+ } catch (const std::exception &) {
+
+ pj_log(ctx, PJ_LOG_DEBUG_MAJOR, "pj_open_lib(%s): out of memory", name);
+
+ return nullptr;
+ }
+}
+
+/************************************************************************/
+/* pj_open_file_with_manager() */
+/************************************************************************/
+
+static void *pj_open_file_with_manager(projCtx ctx, const char *name,
+ const char * /* mode */) {
+ return NS_PROJ::FileManager::open(ctx, name, NS_PROJ::FileAccess::READ_ONLY)
+ .release();
+}
+
+/************************************************************************/
+/* FileManager::open_resource_file() */
+/************************************************************************/
+
+std::unique_ptr<NS_PROJ::File>
+NS_PROJ::FileManager::open_resource_file(projCtx ctx, const char *name) {
+ auto file = std::unique_ptr<NS_PROJ::File>(
+ reinterpret_cast<NS_PROJ::File *>(pj_open_lib_internal(
+ ctx, name, "rb", pj_open_file_with_manager, nullptr, 0)));
+ if (file == nullptr && !is_tilde_slash(name) &&
+ !is_rel_or_absolute_filename(name) && !starts_with(name, "http://") &&
+ !starts_with(name, "https://") && pj_context_is_network_enabled(ctx)) {
+ std::string remote_file(pj_context_get_url_endpoint(ctx));
+ if (!remote_file.empty()) {
+ if (remote_file.back() != '/') {
+ remote_file += '/';
+ }
+ remote_file += name;
+ auto pos = remote_file.rfind('.');
+ if (pos + 4 == remote_file.size()) {
+ remote_file = remote_file.substr(0, pos) + ".tif";
+ file = open(ctx, remote_file.c_str(),
+ NS_PROJ::FileAccess::READ_ONLY);
+ if (file) {
+ pj_log(ctx, PJ_LOG_DEBUG_MAJOR, "Using %s",
+ remote_file.c_str());
+ pj_ctx_set_errno(ctx, 0);
+ }
+ } else {
+ // For example for resource files like 'alaska'
+ auto remote_file_tif = remote_file + ".tif";
+ file = open(ctx, remote_file_tif.c_str(),
+ NS_PROJ::FileAccess::READ_ONLY);
+ if (file) {
+ pj_log(ctx, PJ_LOG_DEBUG_MAJOR, "Using %s",
+ remote_file_tif.c_str());
+ pj_ctx_set_errno(ctx, 0);
+ } else {
+ // Init files
+ file = open(ctx, remote_file.c_str(),
+ NS_PROJ::FileAccess::READ_ONLY);
+ if (file) {
+ pj_log(ctx, PJ_LOG_DEBUG_MAJOR, "Using %s",
+ remote_file.c_str());
+ pj_ctx_set_errno(ctx, 0);
+ }
+ }
+ }
+ }
+ }
+ return file;
+}
+
+/************************************************************************/
+/* pj_open_lib() */
+/************************************************************************/
+
+#ifndef REMOVE_LEGACY_SUPPORT
+
+// Used by following legacy function
+static void *pj_ctx_fopen_adapter(projCtx ctx, const char *name,
+ const char *mode) {
+ return pj_ctx_fopen(ctx, name, mode);
+}
+
+// Legacy function
+PAFile pj_open_lib(projCtx ctx, const char *name, const char *mode) {
+ return (PAFile)pj_open_lib_internal(ctx, name, mode, pj_ctx_fopen_adapter,
+ nullptr, 0);
+}
+
+#endif // REMOVE_LEGACY_SUPPORT
+
+/************************************************************************/
+/* pj_find_file() */
+/************************************************************************/
+
+/** Returns the full filename corresponding to a proj resource file specified
+ * as a short filename.
+ *
+ * @param ctx context.
+ * @param short_filename short filename (e.g. egm96_15.gtx). Must not be NULL.
+ * @param out_full_filename output buffer, of size out_full_filename_size, that
+ * will receive the full filename on success.
+ * Will be zero-terminated.
+ * @param out_full_filename_size size of out_full_filename.
+ * @return 1 if the file was found, 0 otherwise.
+ */
+int pj_find_file(projCtx ctx, const char *short_filename,
+ char *out_full_filename, size_t out_full_filename_size) {
+ auto f = reinterpret_cast<NS_PROJ::File *>(pj_open_lib_internal(
+ ctx, short_filename, "rb", pj_open_file_with_manager, out_full_filename,
+ out_full_filename_size));
+ if (f != nullptr) {
+ delete f;
+ return 1;
+ }
+ return 0;
+}
+
+/************************************************************************/
+/* pj_context_get_url_endpoint() */
+/************************************************************************/
+
+std::string pj_context_get_url_endpoint(PJ_CONTEXT *ctx) {
+ if (!ctx->endpoint.empty()) {
+ return ctx->endpoint;
+ }
+ pj_load_ini(ctx);
+ return ctx->endpoint;
+}
+
+/************************************************************************/
+/* trim() */
+/************************************************************************/
+
+static std::string trim(const std::string &s) {
+ const auto first = s.find_first_not_of(' ');
+ const auto last = s.find_last_not_of(' ');
+ if (first == std::string::npos || last == std::string::npos) {
+ return std::string();
+ }
+ return s.substr(first, last - first + 1);
+}
+
+/************************************************************************/
+/* pj_load_ini() */
+/************************************************************************/
+
+void pj_load_ini(projCtx ctx) {
+ if (ctx->iniFileLoaded)
+ return;
+
+ const char *endpoint_from_env = getenv("PROJ_NETWORK_ENDPOINT");
+ if (endpoint_from_env && endpoint_from_env[0] != '\0') {
+ ctx->endpoint = endpoint_from_env;
+ }
+
+ ctx->iniFileLoaded = true;
+ auto file = std::unique_ptr<NS_PROJ::File>(
+ reinterpret_cast<NS_PROJ::File *>(pj_open_lib_internal(
+ ctx, "proj.ini", "rb", pj_open_file_with_manager, nullptr, 0)));
+ if (!file)
+ return;
+ file->seek(0, SEEK_END);
+ const auto filesize = file->tell();
+ if (filesize == 0 || filesize > 100 * 1024U)
+ return;
+ file->seek(0, SEEK_SET);
+ std::string content;
+ content.resize(static_cast<size_t>(filesize));
+ const auto nread = file->read(&content[0], content.size());
+ if (nread != content.size())
+ return;
+ content += '\n';
+ size_t pos = 0;
+ while (pos != std::string::npos) {
+ const auto eol = content.find_first_of("\r\n", pos);
+ if (eol == std::string::npos) {
+ break;
+ }
+
+ const auto equal = content.find('=', pos);
+ if (equal < eol) {
+ const auto key = trim(content.substr(pos, equal - pos));
+ const auto value =
+ trim(content.substr(equal + 1, eol - (equal + 1)));
+ if (ctx->endpoint.empty() && key == "cdn_endpoint") {
+ ctx->endpoint = value;
+ } else if (key == "network") {
+ const char *enabled = getenv("PROJ_NETWORK");
+ if (enabled == nullptr || enabled[0] == '\0') {
+ ctx->networking.enabled = ci_equal(value, "ON") ||
+ ci_equal(value, "YES") ||
+ ci_equal(value, "TRUE");
+ }
+ } else if (key == "cache_enabled") {
+ ctx->gridChunkCache.enabled = ci_equal(value, "ON") ||
+ ci_equal(value, "YES") ||
+ ci_equal(value, "TRUE");
+ } else if (key == "cache_size_MB") {
+ const int val = atoi(value.c_str());
+ ctx->gridChunkCache.max_size =
+ val > 0 ? static_cast<long long>(val) * 1024 * 1024 : -1;
+ } else if (key == "cache_ttl_sec") {
+ ctx->gridChunkCache.ttl = atoi(value.c_str());
+ }
+ }
+
+ pos = content.find_first_not_of("\r\n", eol);
+ }
+}
+
//! @endcond
// ---------------------------------------------------------------------------
@@ -2458,7 +3529,8 @@ int proj_is_download_needed(PJ_CONTEXT *ctx, const char *url_or_filename,
const auto localFilename(
pj_context_get_user_writable_directory(ctx, false) + filename);
- auto f = NS_PROJ::FileManager::open(ctx, localFilename.c_str());
+ auto f = NS_PROJ::FileManager::open(ctx, localFilename.c_str(),
+ NS_PROJ::FileAccess::READ_ONLY);
if (!f) {
return true;
}
@@ -2604,7 +3676,8 @@ int proj_download_file(PJ_CONTEXT *ctx, const char *url_or_filename,
char szUniqueSuffix[128];
snprintf(szUniqueSuffix, sizeof(szUniqueSuffix), "%d_%p", nPID, &url);
const auto localFilenameTmp(localFilename + szUniqueSuffix);
- FILE *f = fopen(localFilenameTmp.c_str(), "wb");
+ auto f = NS_PROJ::FileManager::open(ctx, localFilenameTmp.c_str(),
+ NS_PROJ::FileAccess::CREATE);
if (!f) {
pj_log(ctx, PJ_LOG_ERROR, "Cannot create %s", localFilenameTmp.c_str());
return false;
@@ -2629,8 +3702,8 @@ int proj_download_file(PJ_CONTEXT *ctx, const char *url_or_filename,
errorBuffer.resize(strlen(errorBuffer.data()));
pj_log(ctx, PJ_LOG_ERROR, "Cannot open %s: %s", url.c_str(),
errorBuffer.c_str());
- fclose(f);
- unlink(localFilenameTmp.c_str());
+ f.reset();
+ NS_PROJ::FileManager::unlink(ctx, localFilenameTmp.c_str());
return false;
}
@@ -2639,8 +3712,8 @@ int proj_download_file(PJ_CONTEXT *ctx, const char *url_or_filename,
NS_PROJ::FileProperties props;
if (!NS_PROJ::NetworkFile::get_props_from_headers(ctx, handle, props)) {
ctx->networking.close(ctx, handle, ctx->networking.user_data);
- fclose(f);
- unlink(localFilenameTmp.c_str());
+ f.reset();
+ NS_PROJ::FileManager::unlink(ctx, localFilenameTmp.c_str());
return false;
}
@@ -2648,15 +3721,15 @@ int proj_download_file(PJ_CONTEXT *ctx, const char *url_or_filename,
std::min(static_cast<unsigned long long>(buffer.size()), props.size)) {
pj_log(ctx, PJ_LOG_ERROR, "Did not get as many bytes as expected");
ctx->networking.close(ctx, handle, ctx->networking.user_data);
- fclose(f);
- unlink(localFilenameTmp.c_str());
+ f.reset();
+ NS_PROJ::FileManager::unlink(ctx, localFilenameTmp.c_str());
return false;
}
- if (fwrite(buffer.data(), size_read, 1, f) != 1) {
+ if (f->write(buffer.data(), size_read) != size_read) {
pj_log(ctx, PJ_LOG_ERROR, "Write error");
ctx->networking.close(ctx, handle, ctx->networking.user_data);
- fclose(f);
- unlink(localFilenameTmp.c_str());
+ f.reset();
+ NS_PROJ::FileManager::unlink(ctx, localFilenameTmp.c_str());
return false;
}
@@ -2673,15 +3746,15 @@ int proj_download_file(PJ_CONTEXT *ctx, const char *url_or_filename,
if (size_read < buffer.size()) {
pj_log(ctx, PJ_LOG_ERROR, "Did not get as many bytes as expected");
ctx->networking.close(ctx, handle, ctx->networking.user_data);
- fclose(f);
- unlink(localFilenameTmp.c_str());
+ f.reset();
+ NS_PROJ::FileManager::unlink(ctx, localFilenameTmp.c_str());
return false;
}
- if (fwrite(buffer.data(), size_read, 1, f) != 1) {
+ if (f->write(buffer.data(), size_read) != size_read) {
pj_log(ctx, PJ_LOG_ERROR, "Write error");
ctx->networking.close(ctx, handle, ctx->networking.user_data);
- fclose(f);
- unlink(localFilenameTmp.c_str());
+ f.reset();
+ NS_PROJ::FileManager::unlink(ctx, localFilenameTmp.c_str());
return false;
}
@@ -2690,17 +3763,17 @@ int proj_download_file(PJ_CONTEXT *ctx, const char *url_or_filename,
!progress_cbk(ctx, double(totalDownloaded) / props.size,
user_data)) {
ctx->networking.close(ctx, handle, ctx->networking.user_data);
- fclose(f);
- unlink(localFilenameTmp.c_str());
+ f.reset();
+ NS_PROJ::FileManager::unlink(ctx, localFilenameTmp.c_str());
return false;
}
}
ctx->networking.close(ctx, handle, ctx->networking.user_data);
- fclose(f);
-
- unlink(localFilename.c_str());
- if (rename(localFilenameTmp.c_str(), localFilename.c_str()) != 0) {
+ f.reset();
+ NS_PROJ::FileManager::unlink(ctx, localFilename.c_str());
+ if (!NS_PROJ::FileManager::rename(ctx, localFilenameTmp.c_str(),
+ localFilename.c_str())) {
pj_log(ctx, PJ_LOG_ERROR, "Cannot rename %s to %s",
localFilenameTmp.c_str(), localFilename.c_str());
return false;
@@ -2766,3 +3839,99 @@ int proj_download_file(PJ_CONTEXT *ctx, const char *url_or_filename,
}
return true;
}
+
+/************************************************************************/
+/* pj_set_finder() */
+/************************************************************************/
+
+void pj_set_finder(const char *(*new_finder)(const char *))
+
+{
+ auto ctx = pj_get_default_ctx();
+ if (ctx) {
+ ctx->file_finder_legacy = new_finder;
+ }
+}
+
+/************************************************************************/
+/* proj_context_set_file_finder() */
+/************************************************************************/
+
+/** \brief Assign a file finder callback to a context.
+ *
+ * This callback will be used whenever PROJ must open one of its resource files
+ * (proj.db database, grids, etc...)
+ *
+ * The callback will be called with the context currently in use at the moment
+ * where it is used (not necessarily the one provided during this call), and
+ * with the provided user_data (which may be NULL).
+ * The user_data must remain valid during the whole lifetime of the context.
+ *
+ * A finder set on the default context will be inherited by contexts created
+ * later.
+ *
+ * @param ctx PROJ context, or NULL for the default context.
+ * @param finder Finder callback. May be NULL
+ * @param user_data User data provided to the finder callback. May be NULL.
+ *
+ * @since PROJ 6.0
+ */
+void proj_context_set_file_finder(PJ_CONTEXT *ctx, proj_file_finder finder,
+ void *user_data) {
+ if (!ctx)
+ ctx = pj_get_default_ctx();
+ if (!ctx)
+ return;
+ ctx->file_finder = finder;
+ ctx->file_finder_user_data = user_data;
+}
+
+/************************************************************************/
+/* proj_context_set_search_paths() */
+/************************************************************************/
+
+/** \brief Sets search paths.
+ *
+ * Those search paths will be used whenever PROJ must open one of its resource
+ * files
+ * (proj.db database, grids, etc...)
+ *
+ * If set on the default context, they will be inherited by contexts created
+ * later.
+ *
+ * Starting with PROJ 7.0, the path(s) should be encoded in UTF-8.
+ *
+ * @param ctx PROJ context, or NULL for the default context.
+ * @param count_paths Number of paths. 0 if paths == NULL.
+ * @param paths Paths. May be NULL.
+ *
+ * @since PROJ 6.0
+ */
+void proj_context_set_search_paths(PJ_CONTEXT *ctx, int count_paths,
+ const char *const *paths) {
+ if (!ctx)
+ ctx = pj_get_default_ctx();
+ if (!ctx)
+ return;
+ try {
+ std::vector<std::string> vector_of_paths;
+ for (int i = 0; i < count_paths; i++) {
+ vector_of_paths.emplace_back(paths[i]);
+ }
+ ctx->set_search_paths(vector_of_paths);
+ } catch (const std::exception &) {
+ }
+}
+
+/************************************************************************/
+/* pj_set_searchpath() */
+/* */
+/* Path control for callers that can't practically provide */
+/* pj_set_finder() style callbacks. Call with (0,NULL) as args */
+/* to clear the searchpath set. */
+/************************************************************************/
+
+void pj_set_searchpath(int count, const char **path) {
+ proj_context_set_search_paths(nullptr, count,
+ const_cast<const char *const *>(path));
+}
diff --git a/src/filemanager.hpp b/src/filemanager.hpp
index 9793267c..bc12a303 100644
--- a/src/filemanager.hpp
+++ b/src/filemanager.hpp
@@ -39,13 +39,26 @@ NS_PROJ_START
class File;
+enum class FileAccess {
+ READ_ONLY, // "rb"
+ READ_UPDATE, // "r+b"
+ CREATE, // "w+b"
+};
+
class FileManager {
private:
FileManager() = delete;
public:
// "Low-level" interface.
- static std::unique_ptr<File> open(PJ_CONTEXT *ctx, const char *filename);
+ static std::unique_ptr<File> open(PJ_CONTEXT *ctx, const char *filename,
+ FileAccess access);
+ static bool exists(PJ_CONTEXT *ctx, const char *filename);
+ static bool mkdir(PJ_CONTEXT *ctx, const char *filename);
+ static bool unlink(PJ_CONTEXT *ctx, const char *filename);
+ static bool rename(PJ_CONTEXT *ctx, const char *oldPath,
+ const char *newPath);
+ static std::string getProjLibEnvVar(PJ_CONTEXT *ctx);
// "High-level" interface, honoring PROJ_LIB and the like.
static std::unique_ptr<File> open_resource_file(PJ_CONTEXT *ctx,
@@ -66,6 +79,7 @@ class File {
public:
virtual ~File();
virtual size_t read(void *buffer, size_t sizeBytes) = 0;
+ virtual size_t write(const void *buffer, size_t sizeBytes) = 0;
virtual bool seek(unsigned long long offset, int whence = SEEK_SET) = 0;
virtual unsigned long long tell() = 0;
virtual void reassign_context(PJ_CONTEXT *ctx) = 0;
@@ -78,4 +92,4 @@ NS_PROJ_END
//! @endcond Doxygen_Suppress
-#endif // FILEMANAGER_HPP_INCLUDED \ No newline at end of file
+#endif // FILEMANAGER_HPP_INCLUDED
diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake
index fdb59434..14ce04a2 100644
--- a/src/lib_proj.cmake
+++ b/src/lib_proj.cmake
@@ -247,7 +247,6 @@ set(SRC_LIBPROJ_CORE
mlfn.cpp
msfn.cpp
mutex.cpp
- open_lib.cpp
param.cpp
phi2.cpp
pipeline.cpp
diff --git a/src/open_lib.cpp b/src/open_lib.cpp
deleted file mode 100644
index ae387281..00000000
--- a/src/open_lib.cpp
+++ /dev/null
@@ -1,576 +0,0 @@
-/******************************************************************************
- * Project: PROJ.4
- * Purpose: Implementation of pj_open_lib(), and pj_set_finder(). These
- * provide a standard interface for opening projections support
- * data files.
- * Author: Gerald Evenden, Frank Warmerdam <warmerdam@pobox.com>
- *
- ******************************************************************************
- * Copyright (c) 1995, Gerald Evenden
- * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *****************************************************************************/
-
-#define PJ_LIB__
-
-#ifndef FROM_PROJ_CPP
-#define FROM_PROJ_CPP
-#endif
-
-#include <assert.h>
-#include <errno.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "proj/internal/internal.hpp"
-
-#include "proj_internal.h"
-#include "filemanager.hpp"
-
-static const char * proj_lib_name =
-#ifdef PROJ_LIB
-PROJ_LIB;
-#else
-nullptr;
-#endif
-
-using namespace NS_PROJ::internal;
-
-/************************************************************************/
-/* pj_set_finder() */
-/************************************************************************/
-
-void pj_set_finder( const char *(*new_finder)(const char *) )
-
-{
- auto ctx = pj_get_default_ctx();
- if( ctx ) {
- ctx->file_finder_legacy = new_finder;
- }
-}
-
-/************************************************************************/
-/* proj_context_set_file_finder() */
-/************************************************************************/
-
-/** \brief Assign a file finder callback to a context.
- *
- * This callback will be used whenever PROJ must open one of its resource files
- * (proj.db database, grids, etc...)
- *
- * The callback will be called with the context currently in use at the moment
- * where it is used (not necessarily the one provided during this call), and
- * with the provided user_data (which may be NULL).
- * The user_data must remain valid during the whole lifetime of the context.
- *
- * A finder set on the default context will be inherited by contexts created
- * later.
- *
- * @param ctx PROJ context, or NULL for the default context.
- * @param finder Finder callback. May be NULL
- * @param user_data User data provided to the finder callback. May be NULL.
- *
- * @since PROJ 6.0
- */
-void proj_context_set_file_finder(PJ_CONTEXT *ctx, proj_file_finder finder,
- void* user_data)
-{
- if( !ctx )
- ctx = pj_get_default_ctx();
- if( !ctx )
- return;
- ctx->file_finder = finder;
- ctx->file_finder_user_data = user_data;
-}
-
-/************************************************************************/
-/* proj_context_set_search_paths() */
-/************************************************************************/
-
-
-/** \brief Sets search paths.
- *
- * Those search paths will be used whenever PROJ must open one of its resource files
- * (proj.db database, grids, etc...)
- *
- * If set on the default context, they will be inherited by contexts created
- * later.
- *
- * @param ctx PROJ context, or NULL for the default context.
- * @param count_paths Number of paths. 0 if paths == NULL.
- * @param paths Paths. May be NULL.
- *
- * @since PROJ 6.0
- */
-void proj_context_set_search_paths(PJ_CONTEXT *ctx,
- int count_paths,
- const char* const* paths)
-{
- if( !ctx )
- ctx = pj_get_default_ctx();
- if( !ctx )
- return;
- try {
- std::vector<std::string> vector_of_paths;
- for (int i = 0; i < count_paths; i++)
- {
- vector_of_paths.emplace_back(paths[i]);
- }
- ctx->set_search_paths(vector_of_paths);
- } catch( const std::exception& )
- {
- }
-}
-
-/************************************************************************/
-/* pj_set_searchpath() */
-/* */
-/* Path control for callers that can't practically provide */
-/* pj_set_finder() style callbacks. Call with (0,NULL) as args */
-/* to clear the searchpath set. */
-/************************************************************************/
-
-void pj_set_searchpath ( int count, const char **path )
-{
- proj_context_set_search_paths( nullptr, count, const_cast<const char* const*>(path) );
-}
-
-#ifdef _WIN32
-#include <windows.h>
-#include <sys/stat.h>
-static const char *get_path_from_win32_projlib(const char *name, std::string& out) {
- /* Check if proj.db lieves in a share/proj dir parallel to bin/proj.dll */
- /* Based in https://stackoverflow.com/questions/9112893/how-to-get-path-to-executable-in-c-running-on-windows */
-
- DWORD path_size = 1024;
-
- for (;;) {
- out.resize(path_size);
- memset(&out[0], 0, path_size);
- DWORD result = GetModuleFileNameA(nullptr, &out[0], path_size - 1);
- DWORD last_error = GetLastError();
-
- if (result == 0) {
- return nullptr;
- }
- else if (result == path_size - 1) {
- if (ERROR_INSUFFICIENT_BUFFER != last_error) {
- return nullptr;
- }
- path_size = path_size * 2;
- }
- else {
- break;
- }
- }
- // Now remove the program's name. It was (example) "C:\programs\gmt6\bin\gdal_translate.exe"
- size_t k = strlen(out.c_str());
- while (k > 0 && out[--k] != '\\') {}
- out.resize(k);
-
- out += "/../share/proj/";
- out += name;
-
- struct stat fileInfo;
- if (stat(out.c_str(), &fileInfo) == 0) // Check if file exists (probably there are simpler ways)
- return out.c_str();
- else {
- return nullptr;
- }
-}
-#endif
-
-/************************************************************************/
-/* pj_open_lib_internal() */
-/************************************************************************/
-
-#ifdef WIN32
-static const char dir_chars[] = "/\\";
-static const char dirSeparator = ';';
-#else
-static const char dir_chars[] = "/";
-static const char dirSeparator = ':';
-#endif
-
-static bool is_tilde_slash(const char* name)
-{
- return *name == '~' && strchr(dir_chars,name[1]);
-}
-
-static bool is_rel_or_absolute_filename(const char *name)
-{
- return strchr(dir_chars,*name)
- || (*name == '.' && strchr(dir_chars,name[1]))
- || (!strncmp(name, "..", 2) && strchr(dir_chars,name[2]))
- || (name[0] != '\0' && name[1] == ':' && strchr(dir_chars,name[2]));
-}
-
-static bool ignoreUserWritableDirectory()
-{
- // Env var mostly for testing purposes and being independent from
- // an existing installation
- const char* envVarIgnoreUserWritableDirectory =
- getenv("PROJ_IGNORE_USER_WRITABLE_DIRECTORY");
- return envVarIgnoreUserWritableDirectory != nullptr &&
- envVarIgnoreUserWritableDirectory[0] != '\0';
-}
-
-static void*
-pj_open_lib_internal(projCtx ctx, const char *name, const char *mode,
- void* (*open_file)(projCtx, const char*, const char*),
- char* out_full_filename, size_t out_full_filename_size) {
- try {
- std::string fname;
- const char *sysname = nullptr;
- void* fid = nullptr;
-
- if( ctx == nullptr ) {
- ctx = pj_get_default_ctx();
- }
-
- if( out_full_filename != nullptr && out_full_filename_size > 0 )
- out_full_filename[0] = '\0';
-
- /* check if ~/name */
- if (is_tilde_slash(name))
- if ((sysname = getenv("HOME")) != nullptr) {
- fname = sysname;
- fname += DIR_CHAR;
- fname += name;
- sysname = fname.c_str();
- } else
- return nullptr;
-
- /* or fixed path: /name, ./name or ../name or http[s]:// */
- else if (is_rel_or_absolute_filename(name)
- || starts_with(name, "http://")
- || starts_with(name, "https://"))
- sysname = name;
-
- /* or try to use application provided file finder */
- else if( ctx->file_finder != nullptr && (sysname = ctx->file_finder( ctx, name, ctx->file_finder_user_data )) != nullptr )
- ;
-
- else if( ctx->file_finder_legacy != nullptr && (sysname = ctx->file_finder_legacy( name )) != nullptr )
- ;
-
- /* The user has search paths set */
- else if( !ctx->search_paths.empty() ) {
- for( const auto& path: ctx->search_paths ) {
- try {
- fname = path;
- fname += DIR_CHAR;
- fname += name;
- sysname = fname.c_str();
- fid = open_file(ctx, sysname, mode);
- } catch( const std::exception& )
- {
- }
- if( fid )
- break;
- }
- }
-
- else if( !ignoreUserWritableDirectory() &&
- (fid = open_file(ctx,
- (pj_context_get_user_writable_directory(ctx, false) +
- DIR_CHAR + name).c_str(), mode)) != nullptr ) {
- fname = pj_context_get_user_writable_directory(ctx, false);
- fname += DIR_CHAR;
- fname += name;
- sysname = fname.c_str();
- }
-
- /* if is environment PROJ_LIB defined */
- else if ((sysname = getenv("PROJ_LIB")) != nullptr) {
- auto paths = NS_PROJ::internal::split(std::string(sysname), dirSeparator);
- for( const auto& path: paths ) {
- fname = path;
- fname += DIR_CHAR;
- fname += name;
- sysname = fname.c_str();
- fid = open_file(ctx, sysname, mode);
- if( fid )
- break;
- }
-#ifdef _WIN32
- /* check if it lives in a ../share/proj dir of the proj dll */
- } else if ((sysname = get_path_from_win32_projlib(name, fname)) != nullptr) {
-#endif
- /* or hardcoded path */
- } else if ((sysname = proj_lib_name) != nullptr) {
- fname = sysname;
- fname += DIR_CHAR;
- fname += name;
- sysname = fname.c_str();
- /* just try it bare bones */
- } else {
- sysname = name;
- }
-
- assert(sysname); // to make Coverity Scan happy
- if ( fid != nullptr || (fid = open_file(ctx, sysname, mode)) != nullptr)
- {
- if( out_full_filename != nullptr && out_full_filename_size > 0 )
- {
- // cppcheck-suppress nullPointer
- strncpy(out_full_filename, sysname, out_full_filename_size);
- out_full_filename[out_full_filename_size-1] = '\0';
- }
- errno = 0;
- }
-
- if( ctx->last_errno == 0 && errno != 0 )
- pj_ctx_set_errno( ctx, errno );
-
- pj_log( ctx, PJ_LOG_DEBUG_MAJOR,
- "pj_open_lib(%s): call fopen(%s) - %s",
- name, sysname,
- fid == nullptr ? "failed" : "succeeded" );
-
- return(fid);
- }
- catch( const std::exception& ) {
-
- pj_log( ctx, PJ_LOG_DEBUG_MAJOR,
- "pj_open_lib(%s): out of memory",
- name );
-
- return nullptr;
- }
-}
-
-/************************************************************************/
-/* pj_open_file_with_manager() */
-/************************************************************************/
-
-static void* pj_open_file_with_manager(projCtx ctx, const char *name,
- const char * /* mode */)
-{
- return NS_PROJ::FileManager::open(ctx, name).release();
-}
-
-/************************************************************************/
-/* FileManager::open_resource_file() */
-/************************************************************************/
-
-std::unique_ptr<NS_PROJ::File> NS_PROJ::FileManager::open_resource_file(
- projCtx ctx, const char *name)
-{
- auto file = std::unique_ptr<NS_PROJ::File>(
- reinterpret_cast<NS_PROJ::File*>(
- pj_open_lib_internal(ctx, name, "rb",
- pj_open_file_with_manager,
- nullptr, 0)));
- if( file == nullptr &&
- !is_tilde_slash(name) &&
- !is_rel_or_absolute_filename(name) &&
- !starts_with(name, "http://") &&
- !starts_with(name, "https://") &&
- pj_context_is_network_enabled(ctx) ) {
- std::string remote_file(pj_context_get_url_endpoint(ctx));
- if( !remote_file.empty() ) {
- if( remote_file.back() != '/' ) {
- remote_file += '/';
- }
- remote_file += name;
- auto pos = remote_file.rfind('.');
- if( pos + 4 == remote_file.size() ) {
- remote_file = remote_file.substr(0, pos) + ".tif";
- file = open(ctx, remote_file.c_str());
- if( file ) {
- pj_log( ctx, PJ_LOG_DEBUG_MAJOR,
- "Using %s", remote_file.c_str() );
- pj_ctx_set_errno( ctx, 0 );
- }
- } else {
- // For example for resource files like 'alaska'
- auto remote_file_tif = remote_file + ".tif";
- file = open(ctx, remote_file_tif.c_str());
- if( file ) {
- pj_log( ctx, PJ_LOG_DEBUG_MAJOR,
- "Using %s", remote_file_tif.c_str() );
- pj_ctx_set_errno( ctx, 0 );
- } else {
- // Init files
- file = open(ctx, remote_file.c_str());
- if( file ) {
- pj_log( ctx, PJ_LOG_DEBUG_MAJOR,
- "Using %s", remote_file.c_str() );
- pj_ctx_set_errno( ctx, 0 );
- }
- }
- }
- }
- }
- return file;
-}
-
-/************************************************************************/
-/* pj_open_lib() */
-/************************************************************************/
-
-#ifndef REMOVE_LEGACY_SUPPORT
-
-// Used by following legacy function
-static void* pj_ctx_fopen_adapter(projCtx ctx, const char *name, const char *mode)
-{
- return pj_ctx_fopen(ctx, name, mode);
-}
-
-// Legacy function
-PAFile
-pj_open_lib(projCtx ctx, const char *name, const char *mode) {
- return (PAFile)pj_open_lib_internal(ctx, name, mode, pj_ctx_fopen_adapter, nullptr, 0);
-}
-
-#endif // REMOVE_LEGACY_SUPPORT
-
-/************************************************************************/
-/* pj_find_file() */
-/************************************************************************/
-
-
-/** Returns the full filename corresponding to a proj resource file specified
- * as a short filename.
- *
- * @param ctx context.
- * @param short_filename short filename (e.g. egm96_15.gtx). Must not be NULL.
- * @param out_full_filename output buffer, of size out_full_filename_size, that
- * will receive the full filename on success.
- * Will be zero-terminated.
- * @param out_full_filename_size size of out_full_filename.
- * @return 1 if the file was found, 0 otherwise.
- */
-int pj_find_file(projCtx ctx, const char *short_filename,
- char* out_full_filename, size_t out_full_filename_size)
-{
- auto f = reinterpret_cast<NS_PROJ::File*>(
- pj_open_lib_internal(ctx, short_filename, "rb",
- pj_open_file_with_manager,
- out_full_filename,
- out_full_filename_size));
- if( f != nullptr )
- {
- delete f;
- return 1;
- }
- return 0;
-}
-
-/************************************************************************/
-/* pj_context_get_url_endpoint() */
-/************************************************************************/
-
-std::string pj_context_get_url_endpoint(PJ_CONTEXT* ctx)
-{
- if( !ctx->endpoint.empty() ) {
- return ctx->endpoint;
- }
- pj_load_ini(ctx);
- return ctx->endpoint;
-}
-
-/************************************************************************/
-/* trim() */
-/************************************************************************/
-
-static std::string trim(const std::string& s) {
- const auto first = s.find_first_not_of(' ');
- const auto last = s.find_last_not_of(' ');
- if( first == std::string::npos || last == std::string::npos ) {
- return std::string();
- }
- return s.substr(first, last - first + 1);
-}
-
-/************************************************************************/
-/* pj_load_ini() */
-/************************************************************************/
-
-void pj_load_ini(projCtx ctx)
-{
- if( ctx->iniFileLoaded )
- return;
-
- const char* endpoint_from_env = getenv("PROJ_NETWORK_ENDPOINT");
- if( endpoint_from_env && endpoint_from_env[0] != '\0' ) {
- ctx->endpoint = endpoint_from_env;
- }
-
- ctx->iniFileLoaded = true;
- auto file = std::unique_ptr<NS_PROJ::File>(
- reinterpret_cast<NS_PROJ::File*>(
- pj_open_lib_internal(ctx, "proj.ini", "rb",
- pj_open_file_with_manager,
- nullptr, 0)));
- if( !file )
- return;
- file->seek(0, SEEK_END);
- const auto filesize = file->tell();
- if( filesize == 0 || filesize > 100 * 1024U )
- return;
- file->seek(0, SEEK_SET);
- std::string content;
- content.resize(static_cast<size_t>(filesize));
- const auto nread = file->read(&content[0], content.size());
- if( nread != content.size() )
- return;
- content += '\n';
- size_t pos = 0;
- while( pos != std::string::npos ) {
- const auto eol = content.find_first_of("\r\n", pos);
- if( eol == std::string::npos ) {
- break;
- }
-
- const auto equal = content.find('=', pos);
- if( equal < eol )
- {
- const auto key = trim(content.substr(pos, equal-pos));
- const auto value = trim(content.substr(equal + 1,
- eol - (equal+1)));
- if( ctx->endpoint.empty() && key == "cdn_endpoint" ) {
- ctx->endpoint = value;
- } else if ( key == "network" ) {
- const char *enabled = getenv("PROJ_NETWORK");
- if (enabled == nullptr || enabled[0] == '\0') {
- ctx->networking.enabled = ci_equal(value, "ON") ||
- ci_equal(value, "YES") ||
- ci_equal(value, "TRUE");
- }
- } else if ( key == "cache_enabled" ) {
- ctx->gridChunkCache.enabled = ci_equal(value, "ON") ||
- ci_equal(value, "YES") ||
- ci_equal(value, "TRUE");
- } else if ( key == "cache_size_MB" ) {
- const int val = atoi(value.c_str());
- ctx->gridChunkCache.max_size = val > 0 ?
- static_cast<long long>(val) * 1024 * 1024 : -1;
- } else if ( key == "cache_ttl_sec" ) {
- ctx->gridChunkCache.ttl = atoi(value.c_str());
- }
- }
-
- pos = content.find_first_not_of("\r\n", eol);
- }
-}
diff --git a/src/proj_internal.h b/src/proj_internal.h
index ce7b9d74..fb8f294c 100644
--- a/src/proj_internal.h
+++ b/src/proj_internal.h
@@ -696,6 +696,7 @@ struct projCtx_t {
int use_proj4_init_rules = -1; /* -1 = unknown, 0 = no, 1 = yes */
int epsg_file_exists = -1; /* -1 = unknown, 0 = no, 1 = yes */
+ std::string env_var_proj_lib{}; // content of PROJ_LIB environment variable. Use Filemanager::getProjLibEnvVar() to access
std::vector<std::string> search_paths{};
const char **c_compat_paths = nullptr; // same, but for projinfo usage