LCOV - code coverage report
Current view: top level - frmts/wcs - wcsutils.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 330 479 68.9 %
Date: 2025-07-11 10:11:13 Functions: 29 35 82.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  WCS Client Driver
       4             :  * Purpose:  Implementation of utilities.
       5             :  * Author:   Ari Jolma <ari dot jolma at gmail dot com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2006, Frank Warmerdam
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  * Copyright (c) 2017, Ari Jolma
      11             :  * Copyright (c) 2017, Finnish Environment Institute
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : 
      16             : #include "ogr_spatialref.h"
      17             : #include "wcsutils.h"
      18             : #include <algorithm>
      19             : 
      20             : #define DIGITS "0123456789"
      21             : 
      22             : namespace WCSUtils
      23             : {
      24             : 
      25           0 : void Swap(double &a, double &b)
      26             : {
      27           0 :     double tmp = a;
      28           0 :     a = b;
      29           0 :     b = tmp;
      30           0 : }
      31             : 
      32          57 : std::string URLEncode(const std::string &str)
      33             : {
      34          57 :     char *pszEncoded = CPLEscapeString(str.c_str(), -1, CPLES_URL);
      35          57 :     std::string str2 = pszEncoded;
      36          57 :     CPLFree(pszEncoded);
      37          57 :     return str2;
      38             : }
      39             : 
      40         194 : std::string URLRemoveKey(const char *url, const std::string &key)
      41             : {
      42         388 :     CPLString retval = url;
      43         388 :     const std::string key_is = key + "=";
      44             :     while (true)
      45             :     {
      46         362 :         size_t pos = retval.ifind(key_is);
      47         362 :         if (pos != std::string::npos)
      48             :         {
      49         168 :             size_t end = retval.find("&", pos);
      50         168 :             retval.erase(pos, end - pos + 1);
      51             :         }
      52             :         else
      53             :         {
      54         194 :             break;
      55             :         }
      56         168 :     }
      57         194 :     if (!retval.empty() && retval.back() == '&')
      58             :     {
      59          96 :         retval.pop_back();
      60             :     }
      61         194 :     std::string retValAsStdString = std::move(retval);
      62         388 :     return retValAsStdString;
      63             : }
      64             : 
      65         117 : std::vector<std::string> &SwapFirstTwo(std::vector<std::string> &array)
      66             : {
      67         117 :     if (array.size() >= 2)
      68             :     {
      69         117 :         std::swap(array[0], array[1]);
      70             :     }
      71         117 :     return array;
      72             : }
      73             : 
      74        4015 : std::vector<std::string> Split(const char *value, const char *delim,
      75             :                                bool swap_the_first_two)
      76             : {
      77        8030 :     std::vector<std::string> array;
      78        4015 :     char **tokens = CSLTokenizeString2(
      79             :         value, delim,
      80             :         CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES | CSLT_HONOURSTRINGS);
      81        4015 :     int n = CSLCount(tokens);
      82       11874 :     for (int i = 0; i < n; ++i)
      83             :     {
      84        7859 :         array.push_back(tokens[i]);
      85             :     }
      86        4015 :     CSLDestroy(tokens);
      87        4015 :     if (swap_the_first_two && array.size() >= 2)
      88             :     {
      89         117 :         return SwapFirstTwo(array);
      90             :     }
      91        3898 :     return array;
      92             : }
      93             : 
      94         112 : std::string Join(const std::vector<std::string> &array, const char *delim,
      95             :                  bool swap_the_first_two)
      96             : {
      97         112 :     std::string str;
      98         112 :     const auto arraySize = array.size();
      99         345 :     for (unsigned int i = 0; i < arraySize; ++i)
     100             :     {
     101         233 :         if (i > 0)
     102             :         {
     103         121 :             str += delim;
     104             :         }
     105         233 :         if (swap_the_first_two)
     106             :         {
     107           0 :             if (i == 0 && arraySize >= 2)
     108             :             {
     109           0 :                 str += array[1];
     110             :             }
     111           0 :             else if (i == 1)
     112             :             {
     113           0 :                 str += array[0];
     114             :             }
     115             :         }
     116             :         else
     117             :         {
     118         233 :             str += array[i];
     119             :         }
     120             :     }
     121         112 :     return str;
     122             : }
     123             : 
     124          51 : std::vector<int> Ilist(const std::vector<std::string> &array, unsigned int from,
     125             :                        size_t count)
     126             : {
     127          51 :     std::vector<int> retval;
     128         156 :     for (unsigned int i = from; i < array.size() && i < from + count; ++i)
     129             :     {
     130         105 :         retval.push_back(atoi(array[i].c_str()));
     131             :     }
     132          51 :     return retval;
     133             : }
     134             : 
     135         294 : std::vector<double> Flist(const std::vector<std::string> &array,
     136             :                           unsigned int from, size_t count)
     137             : {
     138         294 :     std::vector<double> retval;
     139         762 :     for (unsigned int i = from; i < array.size() && i < from + count; ++i)
     140             :     {
     141         468 :         retval.push_back(CPLAtof(array[i].c_str()));
     142             :     }
     143         294 :     return retval;
     144             : }
     145             : 
     146          85 : int IndexOf(const std::string &str, const std::vector<std::string> &array)
     147             : {
     148          85 :     int index = -1;
     149         141 :     for (unsigned int i = 0; i < array.size(); ++i)
     150             :     {
     151         135 :         if (array[i] == str)
     152             :         {
     153          79 :             index = i;
     154          79 :             break;
     155             :         }
     156             :     }
     157          85 :     return index;
     158             : }
     159             : 
     160           0 : int IndexOf(int i, const std::vector<int> &array)
     161             : {
     162           0 :     int index = -1;
     163           0 :     for (unsigned int j = 0; j < array.size(); ++j)
     164             :     {
     165           0 :         if (array[j] == i)
     166             :         {
     167           0 :             index = j;
     168           0 :             break;
     169             :         }
     170             :     }
     171           0 :     return index;
     172             : }
     173             : 
     174          21 : std::vector<int> IndexOf(const std::vector<std::string> &strs,
     175             :                          const std::vector<std::string> &array)
     176             : {
     177          21 :     std::vector<int> retval;
     178          49 :     for (unsigned int i = 0; i < strs.size(); ++i)
     179             :     {
     180          28 :         retval.push_back(IndexOf(strs[i], array));
     181             :     }
     182          21 :     return retval;
     183             : }
     184             : 
     185         210 : int IndexOf(const std::string &key,
     186             :             const std::vector<std::vector<std::string>> &kvps)
     187             : {
     188         210 :     int index = -1;
     189         420 :     for (unsigned int i = 0; i < kvps.size(); ++i)
     190             :     {
     191         210 :         if (kvps[i].size() > 1 && key == kvps[i][0])
     192             :         {
     193           0 :             index = i;
     194           0 :             break;
     195             :         }
     196             :     }
     197         210 :     return index;
     198             : }
     199             : 
     200          21 : bool Contains(const std::vector<int> &array, int value)
     201             : {
     202          49 :     for (unsigned int i = 0; i < array.size(); ++i)
     203             :     {
     204          28 :         if (array[i] == value)
     205             :         {
     206           0 :             return true;
     207             :         }
     208             :     }
     209          21 :     return false;
     210             : }
     211             : 
     212           6 : std::string FromParenthesis(const std::string &s)
     213             : {
     214           6 :     size_t beg = s.find_first_of("(");
     215           6 :     size_t end = s.find_last_of(")");
     216           6 :     if (beg == std::string::npos || end == std::string::npos)
     217             :     {
     218           0 :         return "";
     219             :     }
     220           6 :     return s.substr(beg + 1, end - beg - 1);
     221             : }
     222             : 
     223             : std::vector<std::string>
     224           0 : ParseSubset(const std::vector<std::string> &subset_array,
     225             :             const std::string &dim)
     226             : {
     227             :     // array is SUBSET defs, a SUBSET def is dim[,crs](low[,high])
     228           0 :     std::vector<std::string> retval;
     229             :     unsigned int i;
     230           0 :     std::string params;
     231           0 :     for (i = 0; i < subset_array.size(); ++i)
     232             :     {
     233           0 :         params = subset_array[i];
     234           0 :         size_t pos = params.find(dim + "(");
     235           0 :         if (pos != std::string::npos)
     236             :         {
     237           0 :             retval.push_back("");  // crs
     238           0 :             break;
     239             :         }
     240           0 :         pos = params.find(dim + ",");
     241           0 :         if (pos != std::string::npos)
     242             :         {
     243           0 :             params.erase(0, pos + 1);
     244           0 :             pos = params.find("(");
     245           0 :             retval.push_back(params.substr(0, pos - 1));
     246           0 :             break;
     247             :         }
     248             :     }
     249           0 :     if (retval.size() > 0)
     250             :     {
     251             :         std::vector<std::string> params_array =
     252           0 :             Split(FromParenthesis(params).c_str(), ",");
     253           0 :         retval.push_back(params_array[0]);
     254           0 :         if (params_array.size() > 1)
     255             :         {
     256           0 :             retval.push_back(params_array[1]);
     257             :         }
     258             :         else
     259             :         {
     260           0 :             retval.push_back("");
     261             :         }
     262             :     }
     263           0 :     return retval;
     264             : }
     265             : 
     266             : /* -------------------------------------------------------------------- */
     267             : /*      FileIsReadable                                                  */
     268             : /* -------------------------------------------------------------------- */
     269             : 
     270         145 : bool FileIsReadable(const std::string &filename)
     271             : {
     272         145 :     VSILFILE *file = VSIFOpenL(filename.c_str(), "r");
     273         145 :     if (file)
     274             :     {
     275         120 :         VSIFCloseL(file);
     276         120 :         return true;
     277             :     }
     278          25 :     return false;
     279             : }
     280             : 
     281          96 : std::string RemoveExt(const std::string &filename)
     282             : {
     283          96 :     size_t pos = filename.find_last_of(".");
     284          96 :     if (pos != std::string::npos)
     285             :     {
     286          96 :         return filename.substr(0, pos);
     287             :     }
     288           0 :     return filename;
     289             : }
     290             : 
     291             : /* -------------------------------------------------------------------- */
     292             : /*      MakeDir                                                         */
     293             : /* -------------------------------------------------------------------- */
     294             : 
     295         122 : bool MakeDir(const std::string &dirname)
     296             : {
     297             :     VSIStatBufL stat;
     298         122 :     if (VSIStatL(dirname.c_str(), &stat) != 0)
     299             :     {
     300          50 :         std::string parent = CPLGetDirnameSafe(dirname.c_str());
     301          25 :         if (!parent.empty() && parent != ".")
     302             :         {
     303          25 :             if (!MakeDir(parent))
     304             :             {
     305           0 :                 return false;
     306             :             }
     307             :         }
     308          25 :         return VSIMkdir(dirname.c_str(), 0755) == 0;
     309             :     }
     310          97 :     return true;
     311             : }
     312             : 
     313             : /************************************************************************/
     314             : /*                       SearchChildWithValue()                         */
     315             : /************************************************************************/
     316             : 
     317          72 : CPLXMLNode *SearchChildWithValue(CPLXMLNode *node, const char *path,
     318             :                                  const char *value)
     319             : {
     320          72 :     if (node == nullptr)
     321             :     {
     322           0 :         return nullptr;
     323             :     }
     324         427 :     for (CPLXMLNode *child = node->psChild; child != nullptr;
     325         355 :          child = child->psNext)
     326             :     {
     327         427 :         if (EQUAL(CPLGetXMLValue(child, path, ""), value))
     328             :         {
     329          72 :             return child;
     330             :         }
     331             :     }
     332           0 :     return nullptr;
     333             : }
     334             : 
     335         256 : bool CPLGetXMLBoolean(CPLXMLNode *poRoot, const char *pszPath)
     336             : {
     337             :     // returns true if path exists and does not contain untrue value
     338         256 :     poRoot = CPLGetXMLNode(poRoot, pszPath);
     339         256 :     if (poRoot == nullptr)
     340             :     {
     341         189 :         return false;
     342             :     }
     343          67 :     return CPLTestBool(CPLGetXMLValue(poRoot, nullptr, ""));
     344             : }
     345             : 
     346         261 : bool CPLUpdateXML(CPLXMLNode *poRoot, const char *pszPath,
     347             :                   const char *new_value)
     348             : {
     349         522 :     std::string old_value = CPLGetXMLValue(poRoot, pszPath, "");
     350         261 :     if (new_value != old_value)
     351             :     {
     352         160 :         CPLSetXMLValue(poRoot, pszPath, new_value);
     353         160 :         return true;
     354             :     }
     355         101 :     return false;
     356             : }
     357             : 
     358             : /* -------------------------------------------------------------------- */
     359             : /*      XMLCopyMetadata                                                 */
     360             : /*      Copy child node 'key' into metadata as MDI element.             */
     361             : /* -------------------------------------------------------------------- */
     362             : 
     363          67 : void XMLCopyMetadata(CPLXMLNode *parent, CPLXMLNode *metadata,
     364             :                      const std::string &key)
     365             : {
     366          67 :     CPLXMLNode *node = CPLGetXMLNode(parent, key.c_str());
     367          67 :     if (node)
     368             :     {
     369          40 :         CPLAddXMLAttributeAndValue(
     370             :             CPLCreateXMLElementAndValue(metadata, "MDI",
     371             :                                         CPLGetXMLValue(node, nullptr, "")),
     372             :             "key", key.c_str());
     373             :     }
     374          67 : }
     375             : 
     376             : /* -------------------------------------------------------------------- */
     377             : /*      SetupCache                                                      */
     378             : /*      Cache is a directory                                            */
     379             : /*      The file db is the cache index with lines of unique_key=URL     */
     380             : /* -------------------------------------------------------------------- */
     381             : 
     382          97 : bool SetupCache(std::string &cache, bool clear)
     383             : {
     384          97 :     if (cache == "")
     385             :     {
     386             : #ifdef _WIN32
     387             :         const char *home = CPLGetConfigOption("USERPROFILE", nullptr);
     388             : #else
     389           1 :         const char *home = CPLGetConfigOption("HOME", nullptr);
     390             : #endif
     391           1 :         if (home)
     392             :         {
     393           1 :             cache = CPLFormFilenameSafe(home, ".gdal", nullptr);
     394             :         }
     395             :         else
     396             :         {
     397           0 :             const char *dir = CPLGetConfigOption("CPL_TMPDIR", nullptr);
     398           0 :             if (!dir)
     399           0 :                 dir = CPLGetConfigOption("TMPDIR", nullptr);
     400           0 :             if (!dir)
     401           0 :                 dir = CPLGetConfigOption("TEMP", nullptr);
     402           0 :             const char *username = CPLGetConfigOption("USERNAME", nullptr);
     403           0 :             if (!username)
     404           0 :                 username = CPLGetConfigOption("USER", nullptr);
     405           0 :             if (dir && username)
     406             :             {
     407           0 :                 std::string subdir = ".gdal_";
     408           0 :                 subdir += username;
     409           0 :                 cache = CPLFormFilenameSafe(dir, subdir.c_str(), nullptr);
     410             :             }
     411             :         }
     412           1 :         cache = CPLFormFilenameSafe(cache.c_str(), "wcs_cache", nullptr);
     413             :     }
     414          97 :     if (!MakeDir(cache))
     415             :     {
     416           0 :         return false;
     417             :     }
     418          97 :     if (clear)
     419             :     {
     420           0 :         char **folder = VSIReadDir(cache.c_str());
     421           0 :         int size = folder ? CSLCount(folder) : 0;
     422           0 :         for (int i = 0; i < size; i++)
     423             :         {
     424           0 :             if (folder[i][0] == '.')
     425             :             {
     426           0 :                 continue;
     427             :             }
     428             :             const std::string filepath =
     429           0 :                 CPLFormFilenameSafe(cache.c_str(), folder[i], nullptr);
     430           0 :             CPL_IGNORE_RET_VAL(VSIUnlink(filepath.c_str()));
     431             :         }
     432           0 :         CSLDestroy(folder);
     433             :     }
     434             :     // make sure the index exists and is writable
     435         194 :     const std::string db = CPLFormFilenameSafe(cache.c_str(), "db", nullptr);
     436          97 :     VSILFILE *f = VSIFOpenL(db.c_str(), "r");
     437          97 :     if (f)
     438             :     {
     439          72 :         VSIFCloseL(f);
     440             :     }
     441             :     else
     442             :     {
     443          25 :         f = VSIFOpenL(db.c_str(), "w");
     444          25 :         if (f)
     445             :         {
     446          25 :             VSIFCloseL(f);
     447             :         }
     448             :         else
     449             :         {
     450           0 :             CPLError(CE_Failure, CPLE_FileIO, "Can't open file '%s': %i\n",
     451           0 :                      db.c_str(), errno);
     452           0 :             return false;
     453             :         }
     454             :     }
     455          97 :     srand((unsigned int)time(
     456             :         nullptr));  // not to have the same names in the cache
     457          97 :     return true;
     458             : }
     459             : 
     460           0 : static bool CompareStrings(const std::string &a, const std::string &b)
     461             : {
     462           0 :     return a.compare(b) < 0;
     463             : }
     464             : 
     465           0 : std::vector<std::string> ReadCache(const std::string &cache)
     466             : {
     467           0 :     std::vector<std::string> contents;
     468           0 :     const std::string db = CPLFormFilenameSafe(cache.c_str(), "db", nullptr);
     469           0 :     char **data = CSLLoad(db.c_str());
     470           0 :     if (data)
     471             :     {
     472           0 :         for (int i = 0; data[i]; ++i)
     473             :         {
     474           0 :             char *val = strchr(data[i], '=');
     475           0 :             if (val != nullptr && *val == '=')
     476             :             {
     477           0 :                 val += 1;
     478           0 :                 if (strcmp(val, "bar") != 0)
     479             :                 {
     480           0 :                     contents.push_back(val);
     481             :                 }
     482             :             }
     483             :         }
     484           0 :         CSLDestroy(data);
     485             :     }
     486           0 :     std::sort(contents.begin(), contents.end(), CompareStrings);
     487           0 :     return contents;
     488             : }
     489             : 
     490             : /* -------------------------------------------------------------------- */
     491             : /*      DeleteEntryFromCache                                            */
     492             : /*      Examines the 'db' file in the cache, which contains             */
     493             : /*      unique key=value pairs, one per line. This function             */
     494             : /*      deletes pairs based on the given key and/or value.              */
     495             : /*      If key or value is empty it is not considered.                  */
     496             : /*      The key is taken as a basename of a file in the cache           */
     497             : /*      and all files with the basename is deleted.                     */
     498             : /* -------------------------------------------------------------------- */
     499             : 
     500           0 : bool DeleteEntryFromCache(const std::string &cache, const std::string &key,
     501             :                           const std::string &value)
     502             : {
     503             :     // Depending on which one of key and value is not "" delete the relevant
     504             :     // entry.
     505           0 :     const std::string db = CPLFormFilenameSafe(cache.c_str(), "db", nullptr);
     506             :     char **data =
     507           0 :         CSLLoad(db.c_str());  // returns NULL in error and for empty files
     508           0 :     char **data2 = CSLAddNameValue(nullptr, "foo", "bar");
     509           0 :     std::string filename = "";
     510           0 :     if (data)
     511             :     {
     512           0 :         for (int i = 0; data[i]; ++i)
     513             :         {
     514           0 :             char *val = strchr(data[i], '=');
     515           0 :             if (val != nullptr && *val == '=')
     516             :             {
     517           0 :                 *val = '\0';
     518           0 :                 val = val + 1;
     519           0 :                 if ((key != "" && key == data[i]) ||
     520           0 :                     (value != "" && value == val) ||
     521           0 :                     (strcmp(data[i], "foo") == 0))
     522             :                 {
     523           0 :                     if (key != "")
     524             :                     {
     525           0 :                         filename = data[i];
     526             :                     }
     527           0 :                     else if (value != "")
     528             :                     {
     529           0 :                         filename = data[i];
     530             :                     }
     531           0 :                     continue;
     532             :                 }
     533           0 :                 data2 = CSLAddNameValue(data2, data[i], val);
     534             :             }
     535             :         }
     536           0 :         CSLDestroy(data);
     537             :     }
     538           0 :     CSLSave(data2, db.c_str());  // returns 0 in error and for empty arrays
     539           0 :     CSLDestroy(data2);
     540           0 :     if (filename != "")
     541             :     {
     542           0 :         char **folder = VSIReadDir(cache.c_str());
     543           0 :         int size = folder ? CSLCount(folder) : 0;
     544           0 :         for (int i = 0; i < size; i++)
     545             :         {
     546           0 :             if (folder[i][0] == '.')
     547             :             {
     548           0 :                 continue;
     549             :             }
     550           0 :             std::string name = folder[i];
     551           0 :             if (name.find(filename) != std::string::npos)
     552             :             {
     553             :                 const std::string filepath =
     554           0 :                     CPLFormFilenameSafe(cache.c_str(), name.c_str(), nullptr);
     555           0 :                 if (VSIUnlink(filepath.c_str()) == -1)
     556             :                 {
     557             :                     // error but can't do much, raise a warning?
     558             :                 }
     559             :             }
     560             :         }
     561           0 :         CSLDestroy(folder);
     562             :     }
     563           0 :     return true;
     564             : }
     565             : 
     566             : /* -------------------------------------------------------------------- */
     567             : /*      SearchCache                                                     */
     568             : /*      The key,value pairs in the cache index file 'db' is searched    */
     569             : /*      for the first pair where the value is the given url. If one     */
     570             : /*      is found, the filename is formed from the cache directory name, */
     571             : /*      the key, and the ext.                                           */
     572             : /* -------------------------------------------------------------------- */
     573             : 
     574         144 : CPLErr SearchCache(const std::string &cache, const std::string &url,
     575             :                    std::string &filename, const std::string &ext, bool &found)
     576             : {
     577         144 :     found = false;
     578         288 :     const std::string db = CPLFormFilenameSafe(cache.c_str(), "db", nullptr);
     579         144 :     VSILFILE *f = VSIFOpenL(db.c_str(), "r");
     580         144 :     if (!f)
     581             :     {
     582           0 :         CPLError(CE_Failure, CPLE_FileIO, "Can't open file '%s': %i\n",
     583           0 :                  db.c_str(), errno);
     584           0 :         return CE_Failure;
     585             :     }
     586         216 :     while (const char *line = CPLReadLineL(f))
     587             :     {
     588         144 :         char *value = strchr((char *)line, '=');
     589         144 :         if (value == nullptr || *value != '=')
     590             :         {
     591           0 :             continue;
     592             :         }
     593         144 :         *value = '\0';
     594         144 :         if (url == (value + 1))
     595             :         {
     596          72 :             filename = line;
     597          72 :             found = true;
     598          72 :             break;
     599             :         }
     600          72 :     }
     601         144 :     VSIFCloseL(f);
     602         144 :     if (found)
     603             :     {
     604         144 :         filename = CPLFormFilenameSafe(cache.c_str(), (filename + ext).c_str(),
     605          72 :                                        nullptr);
     606          72 :         found = FileIsReadable(filename);
     607             :         // if not readable, we should delete the entry
     608             :     }
     609         144 :     return CE_None;
     610             : }
     611             : 
     612             : /* -------------------------------------------------------------------- */
     613             : /*      AddEntryToCache                                                 */
     614             : /*      A new unique key is created into the database based on the      */
     615             : /*      filename replacing X with a random ascii character.             */
     616             : /*      The returned filename is a path formed from the cache directory */
     617             : /*      name, the filename, and the ext.                                */
     618             : /* -------------------------------------------------------------------- */
     619             : 
     620          48 : CPLErr AddEntryToCache(const std::string &cache, const std::string &url,
     621             :                        std::string &filename, const std::string &ext)
     622             : {
     623             :     // todo: check for lock and do something if locked(?)
     624             :     // todo: lock the cache
     625             :     // assuming the url is not in the cache
     626          96 :     const std::string store = filename;
     627          96 :     const std::string db = CPLFormFilenameSafe(cache.c_str(), "db", nullptr);
     628          48 :     VSILFILE *f = VSIFOpenL(db.c_str(), "a");
     629          48 :     if (!f)
     630             :     {
     631           0 :         CPLError(CE_Failure, CPLE_FileIO, "Can't open file '%s': %i\n",
     632           0 :                  db.c_str(), errno);
     633           0 :         return CE_Failure;
     634             :     }
     635             : 
     636             :     // create a new file into the cache using filename as template
     637         144 :     std::string path = "";
     638             :     VSIStatBufL stat;
     639          23 :     do
     640             :     {
     641          71 :         filename = store;
     642             :         static const char chars[] =
     643             :             "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
     644         426 :         for (size_t i = 0; i < filename.length(); ++i)
     645             :         {
     646         355 :             if (filename.at(i) == 'X')
     647             :             {
     648             : #ifndef __COVERITY__
     649         355 :                 filename.replace(i, 1, 1, chars[rand() % (sizeof(chars) - 1)]);
     650             : #else
     651             :                 filename.replace(i, 1, 1, chars[i % (sizeof(chars) - 1)]);
     652             : #endif
     653             :             }
     654             :         }
     655             :         // replace X with random character from a-zA-Z
     656         142 :         path = CPLFormFilenameSafe(cache.c_str(), (filename + ext).c_str(),
     657          71 :                                    nullptr);
     658          71 :     } while (VSIStatExL(path.c_str(), &stat, VSI_STAT_EXISTS_FLAG) == 0);
     659          48 :     VSILFILE *f2 = VSIFOpenL(path.c_str(), "w");
     660          48 :     if (f2)
     661             :     {
     662          48 :         VSIFCloseL(f2);
     663             :     }
     664             : 
     665             :     std::string entry =
     666          96 :         filename + "=" + url + "\n";  // '=' for compatibility with CSL
     667          48 :     VSIFWriteL(entry.c_str(), sizeof(char), entry.size(), f);
     668          48 :     VSIFCloseL(f);
     669             : 
     670          48 :     filename = std::move(path);
     671          48 :     return CE_None;
     672             : }
     673             : 
     674             : // steps into element 'from' and adds values of elements 'keys' into the
     675             : // metadata 'path' is the key that is used for metadata and it is appended with
     676             : // 'from' path may be later used to get metadata from elements below 'from' so
     677             : // it is returned in the appended form
     678         134 : CPLXMLNode *AddSimpleMetaData(char ***metadata, CPLXMLNode *node,
     679             :                               std::string &path, const std::string &from,
     680             :                               const std::vector<std::string> &keys)
     681             : {
     682         134 :     CPLXMLNode *node2 = CPLGetXMLNode(node, from.c_str());
     683         134 :     if (node2)
     684             :     {
     685         134 :         path = path + from + ".";
     686         556 :         for (unsigned int i = 0; i < keys.size(); i++)
     687             :         {
     688         422 :             CPLXMLNode *node3 = CPLGetXMLNode(node2, keys[i].c_str());
     689         422 :             if (node3)
     690             :             {
     691         512 :                 const std::string name = path + keys[i];
     692         256 :                 CPLString value = CPLGetXMLValue(node3, nullptr, "");
     693         256 :                 value.Trim();
     694         256 :                 *metadata =
     695         256 :                     CSLSetNameValue(*metadata, name.c_str(), value.c_str());
     696             :             }
     697             :         }
     698             :     }
     699         134 :     return node2;
     700             : }
     701             : 
     702         211 : std::string GetKeywords(CPLXMLNode *root, const std::string &path,
     703             :                         const std::string &kw)
     704             : {
     705         211 :     std::string words = "";
     706             :     CPLXMLNode *keywords =
     707         211 :         (path != "") ? CPLGetXMLNode(root, path.c_str()) : root;
     708         211 :     if (keywords)
     709             :     {
     710         262 :         std::vector<unsigned int> epsg_codes;
     711       36077 :         for (CPLXMLNode *node = keywords->psChild; node != nullptr;
     712       35946 :              node = node->psNext)
     713             :         {
     714       35946 :             if (node->eType != CXT_Element)
     715             :             {
     716           6 :                 continue;
     717             :             }
     718       35940 :             if (kw == node->pszValue)
     719             :             {
     720       35886 :                 CPLString word = CPLGetXMLValue(node, nullptr, "");
     721       17943 :                 word.Trim();
     722             : 
     723             :                 // crs, replace "http://www.opengis.net/def/crs/EPSG/0/"
     724             :                 // or "urn:ogc:def:crs:EPSG::" with EPSG:
     725       17943 :                 const char *const epsg[] = {
     726             :                     "http://www.opengis.net/def/crs/EPSG/0/",
     727             :                     "urn:ogc:def:crs:EPSG::"};
     728       53829 :                 for (unsigned int i = 0; i < CPL_ARRAYSIZE(epsg); i++)
     729             :                 {
     730       35886 :                     size_t pos = word.find(epsg[i]);
     731       35886 :                     if (pos == 0)
     732             :                     {
     733             :                         std::string code =
     734       17564 :                             word.substr(strlen(epsg[i]), std::string::npos);
     735       17564 :                         if (code.find_first_not_of(DIGITS) == std::string::npos)
     736             :                         {
     737       17564 :                             epsg_codes.push_back(atoi(code.c_str()));
     738       17564 :                             continue;
     739             :                         }
     740             :                     }
     741             :                 }
     742             : 
     743             :                 // profiles, remove http://www.opengis.net/spec/
     744             :                 // interpolation, remove
     745             :                 // http://www.opengis.net/def/interpolation/OGC/1/
     746             : 
     747       17943 :                 const char *const spec[] = {
     748             :                     "http://www.opengis.net/spec/",
     749             :                     "http://www.opengis.net/def/interpolation/OGC/1/"};
     750       53829 :                 for (unsigned int i = 0; i < CPL_ARRAYSIZE(spec); i++)
     751             :                 {
     752       35886 :                     size_t pos = word.find(spec[i]);
     753       35886 :                     if (pos != std::string::npos)
     754             :                     {
     755         118 :                         word.erase(pos, strlen(spec[i]));
     756             :                     }
     757             :                 }
     758             : 
     759       17943 :                 if (words != "")
     760             :                 {
     761       17881 :                     words += ",";
     762             :                 }
     763       17943 :                 words += word;
     764             :             }
     765             :         }
     766         131 :         if (epsg_codes.size() > 0)
     767             :         {
     768           8 :             std::string codes;
     769           8 :             std::sort(epsg_codes.begin(), epsg_codes.end());
     770           8 :             unsigned int pajazzo = 0, i = 0, a = 0, b = 0;
     771             :             while (1)
     772             :             {
     773             :                 // cppcheck-suppress containerOutOfBounds
     774       17572 :                 unsigned int c = i < epsg_codes.size() ? epsg_codes[i] : 0;
     775       17572 :                 if (pajazzo == 1)
     776             :                 {
     777        1967 :                     if (c > a + 1)
     778             :                     {
     779        1194 :                         if (codes != "")
     780             :                         {
     781        1189 :                             codes += ",";
     782             :                         }
     783        1194 :                         codes += CPLString().Printf("%i", a);
     784        1194 :                         a = c;
     785             :                     }
     786         773 :                     else if (c >= a)
     787             :                     {
     788         765 :                         b = c;
     789         765 :                         pajazzo = 2;
     790             :                     }
     791             :                 }
     792       15605 :                 else if (pajazzo == 2)
     793             :                 {
     794       15597 :                     if (c > b + 1)
     795             :                     {
     796         765 :                         if (codes != "")
     797             :                         {
     798         762 :                             codes += ",";
     799             :                         }
     800         765 :                         codes += CPLString().Printf("%i:%i", a, b);
     801         765 :                         a = c;
     802         765 :                         pajazzo = 1;
     803             :                     }
     804       14832 :                     else if (c >= b)
     805             :                     {
     806       14832 :                         b = c;
     807             :                     }
     808             :                 }
     809             :                 else
     810             :                 {  // pajazzo == 0
     811           8 :                     a = c;
     812           8 :                     pajazzo = 1;
     813             :                 }
     814       17572 :                 if (i == epsg_codes.size())
     815             :                 {
     816             :                     // must empty the pajazzo before leaving
     817           8 :                     if (codes != "")
     818             :                     {
     819           8 :                         codes += ",";
     820             :                     }
     821           8 :                     if (pajazzo == 1)
     822             :                     {
     823           8 :                         codes += CPLString().Printf("%i", a);
     824             :                     }
     825           0 :                     else if (pajazzo == 2)
     826             :                     {
     827           0 :                         codes += CPLString().Printf("%i:%i", a, b);
     828             :                     }
     829           8 :                     break;
     830             :                 }
     831       17564 :                 ++i;
     832       17564 :             }
     833           8 :             if (words != "")
     834             :             {
     835           8 :                 words += ",";
     836             :             }
     837           8 :             words += "EPSG:" + codes;
     838             :         }
     839             :     }
     840         211 :     return words;
     841             : }
     842             : 
     843         147 : std::string ParseCRS(CPLXMLNode *node)
     844             : {
     845             :     // test for attrs crs (OWS) and srsName (GML), and text contents of subnode
     846             :     // (GridBaseCRS)
     847         147 :     std::string crs = CPLGetXMLValue(node, "crs", "");
     848         147 :     if (crs == "")
     849             :     {
     850          57 :         crs = CPLGetXMLValue(node, "srsName", "");
     851          57 :         if (crs == "")
     852             :         {
     853          36 :             crs = CPLGetXMLValue(node, "GridBaseCRS", "");
     854             :         }
     855             :     }
     856         147 :     if (crs == "")
     857             :     {
     858           0 :         return crs;
     859             :     }
     860             :     // split compound names
     861             :     // see for example
     862             :     // http://www.eurogeographics.org/sites/default/files/2016-01-18_INSPIRE-KEN-CovFaq.pdf
     863         147 :     size_t pos = crs.find("?");
     864         147 :     if (pos != std::string::npos)
     865             :     {
     866           3 :         if (crs.find("crs-compound?") != std::string::npos)
     867             :         {  // 1=uri&2=uri...
     868             :             // assuming the first is for X,Y
     869           3 :             crs = crs.substr(pos + 1);
     870           3 :             pos = crs.find("&");
     871           3 :             if (pos != std::string::npos)
     872             :             {
     873           3 :                 pos = pos - 2;
     874             :             }
     875           3 :             crs = crs.substr(2, pos);
     876             :         }
     877             :     }
     878         147 :     return crs;
     879             : }
     880             : 
     881             : // if appropriate, try to create WKT description from CRS name
     882             : // return false if failure
     883             : // appropriate means, that the name is a real CRS
     884          57 : bool CRS2Projection(const std::string &crs, OGRSpatialReference *sr,
     885             :                     char **projection)
     886             : {
     887          57 :     if (*projection != nullptr)
     888             :     {
     889           0 :         CPLFree(*projection);
     890             :     }
     891          57 :     *projection = nullptr;
     892          57 :     if (crs.empty())
     893             :     {
     894           0 :         return true;
     895             :     }
     896         114 :     if (crs.find(":imageCRS") != std::string::npos ||
     897         114 :         crs.find("/Index1D") != std::string::npos ||
     898         114 :         crs.find("/Index2D") != std::string::npos ||
     899         171 :         crs.find("/Index3D") != std::string::npos ||
     900          57 :         crs.find("/AnsiDate") != std::string::npos)
     901             :     {
     902             :         // not a map projection
     903           0 :         return true;
     904             :     }
     905         114 :     std::string crs2 = crs;
     906             :     // rasdaman uses urls, which return gml:ProjectedCRS XML, which is not
     907             :     // recognized by GDAL currently
     908          57 :     if (crs2.find("EPSG") != std::string::npos)
     909             :     {  // ...EPSG...(\d+)
     910          57 :         size_t pos1 = crs2.find_last_of(DIGITS);
     911          57 :         if (pos1 != std::string::npos)
     912             :         {
     913          57 :             size_t pos2 = pos1 - 1;
     914          57 :             char c = crs2.at(pos2);
     915         231 :             while (strchr(DIGITS, c))
     916             :             {
     917         174 :                 pos2 = pos2 - 1;
     918         174 :                 c = crs2.at(pos2);
     919             :             }
     920          57 :             crs2 = "EPSGA:" + crs2.substr(pos2 + 1, pos1 - pos2);
     921             :         }
     922             :     }
     923         114 :     OGRSpatialReference local_sr;
     924          57 :     OGRSpatialReference *sr_pointer = sr != nullptr ? sr : &local_sr;
     925          57 :     if (sr_pointer->SetFromUserInput(
     926             :             crs2.c_str(),
     927          57 :             OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
     928             :         OGRERR_NONE)
     929             :     {
     930          57 :         sr_pointer->exportToWkt(projection);
     931          57 :         return true;
     932             :     }
     933           0 :     return false;
     934             : }
     935             : 
     936          57 : bool CRSImpliesAxisOrderSwap(const std::string &crs, bool &swap,
     937             :                              char **projection)
     938             : {
     939         114 :     OGRSpatialReference oSRS;
     940          57 :     char *tmp = nullptr;
     941          57 :     swap = false;
     942          57 :     if (!CRS2Projection(crs, &oSRS, &tmp))
     943             :     {
     944           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     945             :                  "Unable to interpret coverage CRS '%s'.", crs.c_str());
     946           0 :         CPLFree(tmp);
     947           0 :         return false;
     948             :     }
     949          57 :     if (tmp)
     950             :     {
     951          57 :         if (projection != nullptr)
     952             :         {
     953          57 :             *projection = tmp;
     954             :         }
     955             :         else
     956             :         {
     957           0 :             CPLFree(tmp);
     958             :         }
     959          57 :         swap = oSRS.EPSGTreatsAsLatLong() || oSRS.EPSGTreatsAsNorthingEasting();
     960             :     }
     961          57 :     return true;
     962             : }
     963             : 
     964          21 : std::vector<std::vector<int>> ParseGridEnvelope(CPLXMLNode *node,
     965             :                                                 bool swap_the_first_two)
     966             : {
     967          21 :     std::vector<std::vector<int>> envelope;
     968             :     std::vector<std::string> array =
     969          42 :         Split(CPLGetXMLValue(node, "low", ""), " ", swap_the_first_two);
     970          42 :     std::vector<int> lows;
     971          66 :     for (unsigned int i = 0; i < array.size(); ++i)
     972             :     {
     973          45 :         lows.push_back(atoi(array[i].c_str()));
     974             :     }
     975          21 :     envelope.push_back(std::move(lows));
     976          21 :     array = Split(CPLGetXMLValue(node, "high", ""), " ", swap_the_first_two);
     977          42 :     std::vector<int> highs;
     978          66 :     for (unsigned int i = 0; i < array.size(); ++i)
     979             :     {
     980          45 :         highs.push_back(atoi(array[i].c_str()));
     981             :     }
     982          21 :     envelope.push_back(std::move(highs));
     983          42 :     return envelope;
     984             : }
     985             : 
     986          57 : std::vector<std::string> ParseBoundingBox(CPLXMLNode *node)
     987             : {
     988          57 :     std::vector<std::string> bbox;
     989         171 :     std::string lc = CPLGetXMLValue(node, "lowerCorner", ""), uc;
     990          57 :     if (lc == "")
     991             :     {
     992           0 :         lc = CPLGetXMLValue(node, "LowerCorner", "");
     993             :     }
     994          57 :     if (lc == "")
     995             :     {
     996           0 :         for (CPLXMLNode *n = node->psChild; n != nullptr; n = n->psNext)
     997             :         {
     998           0 :             if (n->eType != CXT_Element || !EQUAL(n->pszValue, "pos"))
     999             :             {
    1000           0 :                 continue;
    1001             :             }
    1002           0 :             if (lc == "")
    1003             :             {
    1004           0 :                 lc = CPLGetXMLValue(node, nullptr, "");
    1005             :             }
    1006             :             else
    1007             :             {
    1008           0 :                 uc = CPLGetXMLValue(node, nullptr, "");
    1009             :             }
    1010             :         }
    1011             :     }
    1012             :     else
    1013             :     {
    1014          57 :         uc = CPLGetXMLValue(node, "upperCorner", "");
    1015          57 :         if (uc == "")
    1016             :         {
    1017           0 :             uc = CPLGetXMLValue(node, "UpperCorner", "");
    1018             :         }
    1019             :     }
    1020          57 :     if (lc != "" && uc != "")
    1021             :     {
    1022          57 :         bbox.push_back(lc);
    1023          57 :         bbox.push_back(uc);
    1024             :     }
    1025             :     // time extent if node is an EnvelopeWithTimePeriod
    1026          57 :     lc = CPLGetXMLValue(node, "beginPosition", "");
    1027          57 :     if (lc != "")
    1028             :     {
    1029           0 :         uc = CPLGetXMLValue(node, "endPosition", "");
    1030           0 :         bbox.push_back(lc + "," + uc);
    1031             :     }
    1032         114 :     return bbox;
    1033             : }
    1034             : 
    1035             : }  // namespace WCSUtils

Generated by: LCOV version 1.14