LCOV - code coverage report
Current view: top level - ogr - ograpispy.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 740 843 87.8 %
Date: 2024-11-21 22:18:42 Functions: 89 92 96.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  OGR C API "Spy"
       5             :  * Author:   Even Rouault, even.rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "cpl_multiproc.h"
      15             : #include "ograpispy.h"
      16             : 
      17             : #include <cstdio>
      18             : #include <map>
      19             : #include <set>
      20             : 
      21             : #include "cpl_string.h"
      22             : #include "gdal.h"
      23             : #include "ogr_geometry.h"
      24             : #include "ogr_feature.h"
      25             : #include "ogr_spatialref.h"
      26             : #include "ogrsf_frmts.h"
      27             : 
      28             : #ifdef OGRAPISPY_ENABLED
      29             : 
      30             : int bOGRAPISpyEnabled = FALSE;
      31             : static CPLMutex *hOGRAPISpyMutex = nullptr;
      32             : static CPLString osSnapshotPath;
      33             : static CPLString osSpyFile;
      34             : static FILE *fpSpyFile = nullptr;
      35             : 
      36             : // Keep in sync with cpl_conv.cpp
      37             : void OGRAPISPYCPLSetConfigOption(const char *, const char *);
      38             : void OGRAPISPYCPLSetThreadLocalConfigOption(const char *, const char *);
      39             : 
      40             : namespace
      41             : {
      42             : 
      43             : class LayerDescription
      44             : {
      45             :   public:
      46             :     int iLayer = -1;
      47             : 
      48          13 :     LayerDescription() = default;
      49             : 
      50          13 :     explicit LayerDescription(int iLayerIn) : iLayer(iLayerIn)
      51             :     {
      52          13 :     }
      53             : };
      54             : 
      55           9 : class DatasetDescription
      56             : {
      57             :   public:
      58             :     int iDS = -1;
      59             :     std::map<OGRLayerH, LayerDescription> oMapLayer{};
      60             : 
      61           9 :     DatasetDescription() = default;
      62             : 
      63           9 :     explicit DatasetDescription(int iDSIn) : iDS(iDSIn)
      64             :     {
      65           9 :     }
      66             : 
      67             :     DatasetDescription &operator=(DatasetDescription &&) = default;
      68             :     ~DatasetDescription();
      69             : };
      70             : 
      71           7 : class FeatureDefnDescription
      72             : {
      73             :   public:
      74             :     OGRFeatureDefnH hFDefn = nullptr;
      75             :     int iUniqueNumber = -1;
      76             :     std::map<OGRFieldDefnH, int> oMapFieldDefn{};
      77             :     std::map<OGRGeomFieldDefnH, int> oMapGeomFieldDefn{};
      78             : 
      79           7 :     FeatureDefnDescription() = default;
      80             : 
      81           7 :     FeatureDefnDescription(OGRFeatureDefnH hFDefnIn, int iUniqueNumberIn)
      82           7 :         : hFDefn(hFDefnIn), iUniqueNumber(iUniqueNumberIn)
      83             :     {
      84           7 :     }
      85             : 
      86             :     FeatureDefnDescription(const FeatureDefnDescription &) = default;
      87             :     FeatureDefnDescription &operator=(const FeatureDefnDescription &) = default;
      88             : 
      89             :     void Free();
      90             : };
      91             : 
      92             : }  // namespace
      93             : 
      94             : static std::map<GDALDatasetH, DatasetDescription> oMapDS;
      95             : static std::set<int> oSetDSIndex;
      96             : static std::map<OGRLayerH, CPLString> oGlobalMapLayer;
      97             : static OGRLayerH hLayerGetNextFeature = nullptr;
      98             : static OGRLayerH hLayerGetLayerDefn = nullptr;
      99             : static bool bDeferGetFieldCount = false;
     100             : static int nGetNextFeatureCalls = 0;
     101             : static std::set<CPLString> aoSetCreatedDS;
     102             : static std::map<OGRFeatureDefnH, FeatureDefnDescription> oMapFDefn;
     103             : static std::map<OGRGeomFieldDefnH, CPLString> oGlobalMapGeomFieldDefn;
     104             : static std::map<OGRFieldDefnH, CPLString> oGlobalMapFieldDefn;
     105             : 
     106           1 : void FeatureDefnDescription::Free()
     107             : {
     108             :     {
     109             :         std::map<OGRGeomFieldDefnH, int>::iterator oIter =
     110           1 :             oMapGeomFieldDefn.begin();
     111           1 :         for (; oIter != oMapGeomFieldDefn.end(); ++oIter)
     112           0 :             oGlobalMapGeomFieldDefn.erase(oIter->first);
     113             :     }
     114             :     {
     115           1 :         std::map<OGRFieldDefnH, int>::iterator oIter = oMapFieldDefn.begin();
     116           1 :         for (; oIter != oMapFieldDefn.end(); ++oIter)
     117           0 :             oGlobalMapFieldDefn.erase(oIter->first);
     118             :     }
     119           1 : }
     120             : 
     121          18 : DatasetDescription::~DatasetDescription()
     122             : {
     123             :     {
     124             :         std::map<OGRLayerH, LayerDescription>::iterator oIter =
     125          18 :             oMapLayer.begin();
     126          27 :         for (; oIter != oMapLayer.end(); ++oIter)
     127           9 :             oGlobalMapLayer.erase(oIter->first);
     128             :     }
     129          18 : }
     130             : 
     131         933 : void OGRAPISpyDestroyMutex()
     132             : {
     133         933 :     if (hOGRAPISpyMutex)
     134             :     {
     135           0 :         CPLDestroyMutex(hOGRAPISpyMutex);
     136           0 :         hOGRAPISpyMutex = nullptr;
     137             : 
     138           0 :         aoSetCreatedDS.clear();
     139           0 :         oMapFDefn.clear();
     140           0 :         oGlobalMapGeomFieldDefn.clear();
     141           0 :         oGlobalMapFieldDefn.clear();
     142             :     }
     143         933 : }
     144             : 
     145         196 : static void OGRAPISpyFileReopen()
     146             : {
     147         196 :     if (fpSpyFile == nullptr)
     148             :     {
     149         182 :         fpSpyFile = fopen(osSpyFile, "ab");
     150         182 :         if (fpSpyFile == nullptr)
     151           0 :             fpSpyFile = stderr;
     152             :     }
     153         196 : }
     154             : 
     155         185 : static void OGRAPISpyFileClose()
     156             : {
     157         185 :     if (fpSpyFile != stdout && fpSpyFile != stderr)
     158             :     {
     159         185 :         fclose(fpSpyFile);
     160         185 :         fpSpyFile = nullptr;
     161             :     }
     162         185 : }
     163             : 
     164       87441 : static bool OGRAPISpyEnabled()
     165             : {
     166       87441 :     if (bOGRAPISpyEnabled < 0)
     167           8 :         return false;
     168             : 
     169       87433 :     const char *pszSpyFile = CPLGetConfigOption("OGR_API_SPY_FILE", nullptr);
     170       87455 :     bOGRAPISpyEnabled = pszSpyFile != nullptr;
     171       87455 :     if (!bOGRAPISpyEnabled)
     172             :     {
     173       87407 :         osSpyFile.resize(0);
     174       87408 :         aoSetCreatedDS.clear();
     175       87324 :         return false;
     176             :     }
     177          48 :     if (!osSpyFile.empty())
     178          43 :         return true;
     179             : 
     180           6 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     181           3 :     if (!osSpyFile.empty())
     182           0 :         return true;
     183             : 
     184           3 :     osSpyFile = pszSpyFile;
     185             : 
     186             :     const char *pszSnapshotPath =
     187           3 :         CPLGetConfigOption("OGR_API_SPY_SNAPSHOT_PATH", ".");
     188           3 :     if (EQUAL(pszSnapshotPath, "NO"))
     189           0 :         osSnapshotPath = "";
     190             :     else
     191           3 :         osSnapshotPath = pszSnapshotPath;
     192             : 
     193           3 :     if (EQUAL(pszSpyFile, "stdout"))
     194           0 :         fpSpyFile = stdout;
     195           3 :     else if (EQUAL(pszSpyFile, "stderr"))
     196           0 :         fpSpyFile = stderr;
     197             :     else
     198           3 :         fpSpyFile = fopen(pszSpyFile, "wb");
     199           3 :     if (fpSpyFile == nullptr)
     200           0 :         fpSpyFile = stderr;
     201             : 
     202           3 :     fprintf(fpSpyFile,
     203             :             "# This file is generated by the OGR_API_SPY mechanism.\n");
     204           3 :     fprintf(fpSpyFile, "import os\n");
     205           3 :     fprintf(fpSpyFile, "import shutil\n");
     206           3 :     fprintf(fpSpyFile, "from osgeo import gdal\n");
     207           3 :     fprintf(fpSpyFile, "from osgeo import ogr\n");
     208           3 :     fprintf(fpSpyFile, "from osgeo import osr\n");
     209             :     // To make pyflakes happy in case it is unused later.
     210           3 :     fprintf(fpSpyFile, "os.access\n");
     211           3 :     fprintf(fpSpyFile, "shutil.copy\n");  // Same here.
     212           3 :     fprintf(fpSpyFile, "\n");
     213             : 
     214           3 :     return true;
     215             : }
     216             : 
     217          16 : static CPLString OGRAPISpyGetOptions(char **papszOptions)
     218             : {
     219          16 :     if (papszOptions == nullptr)
     220             :     {
     221          10 :         return "[]";
     222             :     }
     223             : 
     224          12 :     CPLString options = "[";
     225          14 :     for (char **papszIter = papszOptions; *papszIter != nullptr; papszIter++)
     226             :     {
     227           8 :         if (papszIter != papszOptions)
     228           2 :             options += ", ";
     229           8 :         options += "'";
     230           8 :         options += *papszIter;
     231           8 :         options += "'";
     232             :     }
     233           6 :     options += "]";
     234             : 
     235           6 :     return options;
     236             : }
     237             : 
     238          73 : static CPLString OGRAPISpyGetString(const char *pszStr)
     239             : {
     240          73 :     if (pszStr == nullptr)
     241           2 :         return "None";
     242         142 :     CPLString osRet = "'";
     243         719 :     while (*pszStr)
     244             :     {
     245         648 :         if (*pszStr == '\'')
     246           4 :             osRet += "\\'";
     247         644 :         else if (*pszStr == '\\')
     248           0 :             osRet += "\\\\";
     249             :         else
     250         644 :             osRet += *pszStr;
     251         648 :         pszStr++;
     252             :     }
     253          71 :     osRet += "'";
     254          71 :     return osRet;
     255             : }
     256             : 
     257          74 : static CPLString OGRAPISpyGetDSVar(GDALDatasetH hDS)
     258             : {
     259          74 :     if (hDS && oMapDS.find(hDS) == oMapDS.end())
     260             :     {
     261           9 :         int i = 1;
     262           9 :         while (oSetDSIndex.find(i) != oSetDSIndex.end())
     263           0 :             i++;
     264           9 :         oMapDS[hDS] = DatasetDescription(i);
     265           9 :         oSetDSIndex.insert(i);
     266             :     }
     267          74 :     return CPLSPrintf("ds%d", hDS ? oMapDS[hDS].iDS : 0);
     268             : }
     269             : 
     270          98 : static CPLString OGRAPISpyGetLayerVar(OGRLayerH hLayer)
     271             : {
     272          98 :     return oGlobalMapLayer[hLayer];
     273             : }
     274             : 
     275          17 : static CPLString OGRAPISpyGetAndRegisterLayerVar(GDALDatasetH hDS,
     276             :                                                  OGRLayerH hLayer)
     277             : {
     278          17 :     DatasetDescription &dd = oMapDS[hDS];
     279          17 :     if (hLayer && dd.oMapLayer.find(hLayer) == dd.oMapLayer.end())
     280             :     {
     281          13 :         const int i = static_cast<int>(dd.oMapLayer.size()) + 1;
     282          13 :         dd.oMapLayer[hLayer] = LayerDescription(i);
     283          13 :         oGlobalMapLayer[hLayer] =
     284          26 :             OGRAPISpyGetDSVar(hDS) + "_" + CPLSPrintf("lyr%d", i);
     285             :     }
     286             : 
     287          34 :     return OGRAPISpyGetDSVar(hDS) + "_" +
     288          51 :            CPLSPrintf("lyr%d", hLayer ? dd.oMapLayer[hLayer].iLayer : 0);
     289             : }
     290             : 
     291          10 : static CPLString OGRAPISpyGetSRS(OGRSpatialReferenceH hSpatialRef)
     292             : {
     293          10 :     if (hSpatialRef == nullptr)
     294           6 :         return "None";
     295             : 
     296           4 :     char *pszWKT = nullptr;
     297           4 :     OGRSpatialReference::FromHandle(hSpatialRef)->exportToWkt(&pszWKT);
     298             :     const char *pszRet =
     299           4 :         CPLSPrintf(R"(osr.SpatialReference("""%s"""))", pszWKT);
     300           4 :     CPLFree(pszWKT);
     301           4 :     return pszRet;
     302             : }
     303             : 
     304          12 : static CPLString OGRAPISpyGetGeom(OGRGeometryH hGeom)
     305             : {
     306          12 :     if (hGeom == nullptr)
     307           6 :         return "None";
     308             : 
     309           6 :     char *pszWKT = nullptr;
     310           6 :     OGRGeometry::FromHandle(hGeom)->exportToWkt(&pszWKT);
     311           6 :     const char *pszRet = CPLSPrintf("ogr.CreateGeometryFromWkt('%s')", pszWKT);
     312           6 :     CPLFree(pszWKT);
     313           6 :     return pszRet;
     314             : }
     315             : 
     316             : #define casePrefixOgrDot(x)                                                    \
     317             :     case x:                                                                    \
     318             :         return "ogr." #x;
     319             : 
     320          10 : static CPLString OGRAPISpyGetGeomType(OGRwkbGeometryType eType)
     321             : {
     322          10 :     switch (eType)
     323             :     {
     324          10 :         casePrefixOgrDot(wkbUnknown) casePrefixOgrDot(wkbPoint) casePrefixOgrDot(wkbLineString) casePrefixOgrDot(
     325           0 :             wkbPolygon) casePrefixOgrDot(wkbMultiPoint) casePrefixOgrDot(wkbMultiLineString)
     326           0 :             casePrefixOgrDot(wkbMultiPolygon) casePrefixOgrDot(wkbGeometryCollection) casePrefixOgrDot(
     327           0 :                 wkbCircularString) casePrefixOgrDot(wkbCompoundCurve) casePrefixOgrDot(wkbCurvePolygon)
     328           0 :                 casePrefixOgrDot(wkbMultiCurve) casePrefixOgrDot(wkbMultiSurface) casePrefixOgrDot(
     329           0 :                     wkbCurve) casePrefixOgrDot(wkbSurface) casePrefixOgrDot(wkbNone) casePrefixOgrDot(wkbLinearRing)
     330           0 :                     casePrefixOgrDot(wkbCircularStringZ) casePrefixOgrDot(wkbCompoundCurveZ) casePrefixOgrDot(
     331           0 :                         wkbCurvePolygonZ) casePrefixOgrDot(wkbMultiCurveZ) casePrefixOgrDot(wkbMultiSurfaceZ)
     332           0 :                         casePrefixOgrDot(wkbCurveZ) casePrefixOgrDot(wkbSurfaceZ) casePrefixOgrDot(
     333           0 :                             wkbPoint25D) casePrefixOgrDot(wkbLineString25D) casePrefixOgrDot(wkbPolygon25D)
     334           0 :                             casePrefixOgrDot(wkbMultiPoint25D) casePrefixOgrDot(wkbMultiLineString25D) casePrefixOgrDot(
     335           0 :                                 wkbMultiPolygon25D) casePrefixOgrDot(wkbGeometryCollection25D)
     336           0 :                                 casePrefixOgrDot(wkbPolyhedralSurface) casePrefixOgrDot(
     337           0 :                                     wkbTIN) casePrefixOgrDot(wkbTriangle) casePrefixOgrDot(wkbPolyhedralSurfaceZ)
     338           0 :                                     casePrefixOgrDot(wkbTINZ) casePrefixOgrDot(wkbTriangleZ) casePrefixOgrDot(
     339           0 :                                         wkbPointM) casePrefixOgrDot(wkbLineStringM) casePrefixOgrDot(wkbPolygonM)
     340           0 :                                         casePrefixOgrDot(wkbMultiPointM) casePrefixOgrDot(
     341           0 :                                             wkbMultiLineStringM) casePrefixOgrDot(wkbMultiPolygonM)
     342           0 :                                             casePrefixOgrDot(wkbGeometryCollectionM) casePrefixOgrDot(
     343           0 :                                                 wkbCircularStringM) casePrefixOgrDot(wkbCompoundCurveM)
     344           0 :                                                 casePrefixOgrDot(wkbCurvePolygonM) casePrefixOgrDot(
     345           0 :                                                     wkbMultiCurveM) casePrefixOgrDot(wkbMultiSurfaceM)
     346           0 :                                                     casePrefixOgrDot(wkbCurveM) casePrefixOgrDot(
     347           0 :                                                         wkbSurfaceM) casePrefixOgrDot(wkbPolyhedralSurfaceM)
     348           0 :                                                         casePrefixOgrDot(wkbTINM) casePrefixOgrDot(
     349           0 :                                                             wkbTriangleM) casePrefixOgrDot(wkbPointZM)
     350           0 :                                                             casePrefixOgrDot(wkbLineStringZM) casePrefixOgrDot(
     351           0 :                                                                 wkbPolygonZM) casePrefixOgrDot(wkbMultiPointZM)
     352           0 :                                                                 casePrefixOgrDot(wkbMultiLineStringZM) casePrefixOgrDot(
     353           0 :                                                                     wkbMultiPolygonZM) casePrefixOgrDot(wkbGeometryCollectionZM)
     354           0 :                                                                     casePrefixOgrDot(wkbCircularStringZM) casePrefixOgrDot(
     355             :                                                                         wkbCompoundCurveZM)
     356           0 :                                                                         casePrefixOgrDot(wkbCurvePolygonZM) casePrefixOgrDot(
     357             :                                                                             wkbMultiCurveZM)
     358           0 :                                                                             casePrefixOgrDot(
     359             :                                                                                 wkbMultiSurfaceZM)
     360           0 :                                                                                 casePrefixOgrDot(
     361             :                                                                                     wkbCurveZM)
     362           0 :                                                                                     casePrefixOgrDot(
     363             :                                                                                         wkbSurfaceZM)
     364           0 :                                                                                         casePrefixOgrDot(
     365             :                                                                                             wkbPolyhedralSurfaceZM)
     366           0 :                                                                                             casePrefixOgrDot(
     367             :                                                                                                 wkbTriangleZM)
     368           0 :                                                                                                 casePrefixOgrDot(
     369             :                                                                                                     wkbTINZM)
     370             :     }
     371           0 :     return "error";
     372             : }
     373             : 
     374           8 : static CPLString OGRAPISpyGetFieldType(OGRFieldType eType)
     375             : {
     376           8 :     switch (eType)
     377             :     {
     378           2 :         casePrefixOgrDot(OFTInteger) casePrefixOgrDot(OFTInteger64)
     379           0 :             casePrefixOgrDot(OFTIntegerList) casePrefixOgrDot(OFTInteger64List)
     380           2 :                 casePrefixOgrDot(OFTReal) casePrefixOgrDot(OFTRealList)
     381           4 :                     casePrefixOgrDot(OFTString) casePrefixOgrDot(OFTStringList)
     382           0 :                         casePrefixOgrDot(OFTWideString)
     383           0 :                             casePrefixOgrDot(OFTWideStringList)
     384           0 :                                 casePrefixOgrDot(OFTBinary)
     385           0 :                                     casePrefixOgrDot(OFTDate)
     386           0 :                                         casePrefixOgrDot(OFTTime)
     387           0 :                                             casePrefixOgrDot(OFTDateTime)
     388             :     }
     389           0 :     return "error";
     390             : }
     391             : 
     392             : #undef casePrefixOgrDot
     393             : 
     394          44 : static CPLString OGRAPISpyGetFeatureDefnVar(OGRFeatureDefnH hFDefn)
     395             : {
     396             :     std::map<OGRFeatureDefnH, FeatureDefnDescription>::iterator oIter =
     397          44 :         oMapFDefn.find(hFDefn);
     398          44 :     int i = 0;
     399          44 :     if (oIter == oMapFDefn.end())
     400             :     {
     401           7 :         i = static_cast<int>(oMapFDefn.size()) + 1;
     402           7 :         oMapFDefn[hFDefn] = FeatureDefnDescription(hFDefn, i);
     403             : 
     404             :         // So that we can check when they are no longer used.
     405           7 :         OGRFeatureDefn::FromHandle(hFDefn)->Reference();
     406             :     }
     407             :     else
     408             :     {
     409          37 :         i = oIter->second.iUniqueNumber;
     410             :     }
     411          88 :     return CPLSPrintf("fdefn%d", i);
     412             : }
     413             : 
     414         188 : static void OGRAPISpyFlushDefered()
     415             : {
     416         188 :     OGRAPISpyFileReopen();
     417         188 :     if (hLayerGetLayerDefn != nullptr)
     418             :     {
     419          13 :         OGRFeatureDefnH hDefn = OGRFeatureDefn::ToHandle(
     420          13 :             OGRLayer::FromHandle(hLayerGetLayerDefn)->GetLayerDefn());
     421          26 :         fprintf(fpSpyFile, "%s = %s.GetLayerDefn()\n",
     422          26 :                 OGRAPISpyGetFeatureDefnVar(hDefn).c_str(),
     423          26 :                 OGRAPISpyGetLayerVar(hLayerGetLayerDefn).c_str());
     424             : 
     425          13 :         if (bDeferGetFieldCount)
     426             :         {
     427           2 :             fprintf(fpSpyFile, "%s.GetFieldCount()\n",
     428           4 :                     OGRAPISpyGetFeatureDefnVar(hDefn).c_str());
     429           2 :             bDeferGetFieldCount = false;
     430             :         }
     431             : 
     432          13 :         hLayerGetLayerDefn = nullptr;
     433             :     }
     434             : 
     435         188 :     if (nGetNextFeatureCalls == 1)
     436             :     {
     437           2 :         fprintf(fpSpyFile, "%s.GetNextFeature()\n",
     438           4 :                 OGRAPISpyGetLayerVar(hLayerGetNextFeature).c_str());
     439           2 :         hLayerGetNextFeature = nullptr;
     440           2 :         nGetNextFeatureCalls = 0;
     441             :     }
     442         186 :     else if (nGetNextFeatureCalls > 0)
     443             :     {
     444           2 :         fprintf(fpSpyFile, "for i in range(%d):\n", nGetNextFeatureCalls);
     445           2 :         fprintf(fpSpyFile, "    %s.GetNextFeature()\n",
     446           4 :                 OGRAPISpyGetLayerVar(hLayerGetNextFeature).c_str());
     447           2 :         hLayerGetNextFeature = nullptr;
     448           2 :         nGetNextFeatureCalls = 0;
     449             :     }
     450         188 : }
     451             : 
     452       11658 : int OGRAPISpyOpenTakeSnapshot(const char *pszName, int bUpdate)
     453             : {
     454       11663 :     if (!OGRAPISpyEnabled() || !bUpdate || osSnapshotPath.empty() ||
     455       11663 :         aoSetCreatedDS.find(pszName) != aoSetCreatedDS.end())
     456       11655 :         return -1;
     457           3 :     OGRAPISpyFlushDefered();
     458             : 
     459             :     VSIStatBufL sStat;
     460           3 :     if (VSIStatL(pszName, &sStat) == 0)
     461             :     {
     462           1 :         bOGRAPISpyEnabled = -1;
     463             :         GDALDatasetH hDS =
     464           1 :             GDALOpenEx(pszName, GDAL_OF_VECTOR, nullptr, nullptr, nullptr);
     465             :         char **papszFileList =
     466           1 :             hDS ? GDALDataset::FromHandle(hDS)->GetFileList() : nullptr;
     467           1 :         GDALClose(hDS);
     468           1 :         bOGRAPISpyEnabled = true;
     469           1 :         if (papszFileList)
     470             :         {
     471           1 :             int i = 1;
     472           2 :             CPLString osBaseDir;
     473           2 :             CPLString osSrcDir;
     474           1 :             CPLString osWorkingDir;
     475             :             while (true)
     476             :             {
     477           0 :                 osBaseDir = CPLFormFilename(
     478           1 :                     osSnapshotPath, CPLSPrintf("snapshot_%d", i), nullptr);
     479           1 :                 if (VSIStatL(osBaseDir, &sStat) != 0)
     480           1 :                     break;
     481           0 :                 i++;
     482             :             }
     483           1 :             VSIMkdir(osSnapshotPath, 0777);
     484           1 :             VSIMkdir(osBaseDir, 0777);
     485           1 :             osSrcDir = CPLFormFilename(osBaseDir, "source", nullptr);
     486           1 :             VSIMkdir(osSrcDir, 0777);
     487           1 :             osWorkingDir = CPLFormFilename(osBaseDir, "working", nullptr);
     488           1 :             VSIMkdir(osWorkingDir, 0777);
     489             : 
     490           1 :             OGRAPISpyFileReopen();
     491           1 :             fprintf(fpSpyFile, "# Take snapshot of %s\n", pszName);
     492           1 :             fprintf(fpSpyFile, "try:\n");
     493           1 :             fprintf(fpSpyFile, "    shutil.rmtree('%s')\n",
     494             :                     osWorkingDir.c_str());
     495           1 :             fprintf(fpSpyFile, "except:\n");
     496           1 :             fprintf(fpSpyFile, "    pass\n");
     497           1 :             fprintf(fpSpyFile, "os.mkdir('%s')\n", osWorkingDir.c_str());
     498           4 :             for (char **papszIter = papszFileList; *papszIter; papszIter++)
     499             :             {
     500             :                 CPLString osSnapshotSrcFile = CPLFormFilename(
     501           6 :                     osSrcDir, CPLGetFilename(*papszIter), nullptr);
     502             :                 CPLString osSnapshotWorkingFile = CPLFormFilename(
     503           6 :                     osWorkingDir, CPLGetFilename(*papszIter), nullptr);
     504           3 :                 CPL_IGNORE_RET_VAL(CPLCopyFile(osSnapshotSrcFile, *papszIter));
     505           3 :                 CPL_IGNORE_RET_VAL(
     506           3 :                     CPLCopyFile(osSnapshotWorkingFile, *papszIter));
     507           3 :                 fprintf(fpSpyFile, "shutil.copy('%s', '%s')\n",
     508             :                         osSnapshotSrcFile.c_str(),
     509             :                         osSnapshotWorkingFile.c_str());
     510             :             }
     511           1 :             CSLDestroy(papszFileList);
     512           1 :             return i;
     513             :         }
     514             :     }
     515           2 :     return -1;
     516             : }
     517             : 
     518       11658 : void OGRAPISpyOpen(const char *pszName, int bUpdate, int iSnapshot,
     519             :                    GDALDatasetH *phDS)
     520             : {
     521       11658 :     if (!OGRAPISpyEnabled())
     522       11651 :         return;
     523          14 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     524           7 :     OGRAPISpyFlushDefered();
     525             : 
     526          14 :     CPLString osName;
     527           7 :     if (iSnapshot > 0)
     528             :     {
     529             :         CPLString osBaseDir = CPLFormFilename(
     530           2 :             osSnapshotPath, CPLSPrintf("snapshot_%d", iSnapshot), nullptr);
     531           2 :         CPLString osWorkingDir = CPLFormFilename(osBaseDir, "working", nullptr);
     532             :         osName =
     533           1 :             CPLFormFilename(osWorkingDir, CPLGetFilename(pszName), nullptr);
     534           1 :         pszName = osName.c_str();
     535             : 
     536           1 :         if (*phDS != nullptr)
     537             :         {
     538           1 :             bOGRAPISpyEnabled = -1;
     539           1 :             GDALClose(GDALDataset::FromHandle(*phDS));
     540           1 :             *phDS = GDALOpenEx(pszName, GDAL_OF_VECTOR | GDAL_OF_UPDATE,
     541             :                                nullptr, nullptr, nullptr);
     542           1 :             bOGRAPISpyEnabled = true;
     543             :         }
     544             :     }
     545             : 
     546           7 :     OGRAPISpyFileReopen();
     547           7 :     if (*phDS != nullptr)
     548           3 :         fprintf(fpSpyFile, "%s = ", OGRAPISpyGetDSVar(*phDS).c_str());
     549           7 :     if (bUpdate)
     550           5 :         fprintf(fpSpyFile, "gdal.OpenEx(%s, gdal.OF_VECTOR | gdal.OF_UPDATE)\n",
     551          10 :                 OGRAPISpyGetString(pszName).c_str());
     552             :     else
     553           2 :         fprintf(fpSpyFile, "gdal.OpenEx(%s, gdal.OF_VECTOR)\n",
     554           4 :                 OGRAPISpyGetString(pszName).c_str());
     555           7 :     OGRAPISpyFileClose();
     556             : }
     557             : 
     558          11 : void OGRAPISpyPreClose(GDALDatasetH hDS)
     559             : {
     560          11 :     if (!OGRAPISpyEnabled())
     561           2 :         return;
     562          18 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     563           9 :     OGRAPISpyFlushDefered();
     564           9 :     fprintf(fpSpyFile, "ds%d = None\n", oMapDS[hDS].iDS);
     565           9 :     oSetDSIndex.erase(oMapDS[hDS].iDS);
     566           9 :     oMapDS.erase(hDS);
     567           9 :     OGRAPISpyFileClose();
     568             : }
     569             : 
     570          11 : void OGRAPISpyPostClose()
     571             : {
     572             :     {
     573          11 :         if (!OGRAPISpyEnabled())
     574           2 :             return;
     575          18 :         CPLMutexHolderD(&hOGRAPISpyMutex);
     576             :         std::map<OGRFeatureDefnH, FeatureDefnDescription>::iterator oIter =
     577           9 :             oMapFDefn.begin();
     578          18 :         std::vector<OGRFeatureDefnH> oArray;
     579          20 :         for (; oIter != oMapFDefn.end(); ++oIter)
     580             :         {
     581          11 :             FeatureDefnDescription &featureDefnDescription = oIter->second;
     582          11 :             if (OGRFeatureDefn::FromHandle(featureDefnDescription.hFDefn)
     583          11 :                     ->GetReferenceCount() == 1)
     584             :             {
     585           1 :                 oArray.push_back(featureDefnDescription.hFDefn);
     586             :             }
     587             :         }
     588          10 :         for (auto &hFDefn : oArray)
     589             :         {
     590           1 :             FeatureDefnDescription &featureDefnDescription = oMapFDefn[hFDefn];
     591             :             OGRFeatureDefn::FromHandle(featureDefnDescription.hFDefn)
     592           1 :                 ->Release();
     593           1 :             featureDefnDescription.Free();
     594           1 :             oMapFDefn.erase(hFDefn);
     595             :         }
     596             :     }
     597             : }
     598             : 
     599        2924 : void OGRAPISpyCreateDataSource(GDALDriverH hDriver, const char *pszName,
     600             :                                char **papszOptions, GDALDatasetH hDS)
     601             : {
     602        2924 :     if (!OGRAPISpyEnabled())
     603        2918 :         return;
     604          12 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     605           6 :     OGRAPISpyFlushDefered();
     606           6 :     if (hDS != nullptr)
     607           6 :         fprintf(fpSpyFile, "%s = ", OGRAPISpyGetDSVar(hDS).c_str());
     608          12 :     fprintf(fpSpyFile,
     609             :             "ogr.GetDriverByName('%s').CreateDataSource(%s, options=%s)\n",
     610             :             GDALGetDriverShortName(hDriver),
     611          12 :             OGRAPISpyGetString(pszName).c_str(),
     612          12 :             OGRAPISpyGetOptions(papszOptions).c_str());
     613           6 :     if (hDS != nullptr)
     614             :     {
     615           6 :         aoSetCreatedDS.insert(pszName);
     616             :     }
     617           6 :     OGRAPISpyFileClose();
     618             : }
     619             : 
     620         500 : void OGRAPISpyDeleteDataSource(GDALDriverH hDriver, const char *pszName)
     621             : {
     622         500 :     if (!OGRAPISpyEnabled())
     623         494 :         return;
     624          12 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     625           6 :     OGRAPISpyFlushDefered();
     626           6 :     fprintf(fpSpyFile, "ogr.GetDriverByName('%s').DeleteDataSource(%s)\n",
     627             :             GDALGetDriverShortName(hDriver),
     628          12 :             OGRAPISpyGetString(pszName).c_str());
     629           6 :     aoSetCreatedDS.erase(pszName);
     630           6 :     OGRAPISpyFileClose();
     631             : }
     632             : 
     633           3 : void OGRAPISpy_DS_GetLayer(GDALDatasetH hDS, int iLayer, OGRLayerH hLayer)
     634             : {
     635           6 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     636           3 :     OGRAPISpyFlushDefered();
     637           3 :     if (hLayer != nullptr)
     638           3 :         fprintf(fpSpyFile,
     639           6 :                 "%s = ", OGRAPISpyGetAndRegisterLayerVar(hDS, hLayer).c_str());
     640           3 :     fprintf(fpSpyFile, "%s.GetLayer(%d)\n", OGRAPISpyGetDSVar(hDS).c_str(),
     641             :             iLayer);
     642           3 :     OGRAPISpyFileClose();
     643           3 : }
     644             : 
     645           2 : void OGRAPISpy_DS_GetLayerCount(GDALDatasetH hDS)
     646             : {
     647           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     648           2 :     OGRAPISpyFlushDefered();
     649           2 :     fprintf(fpSpyFile, "%s.GetLayerCount()\n", OGRAPISpyGetDSVar(hDS).c_str());
     650           2 :     OGRAPISpyFileClose();
     651           2 : }
     652             : 
     653           4 : void OGRAPISpy_DS_GetLayerByName(GDALDatasetH hDS, const char *pszLayerName,
     654             :                                  OGRLayerH hLayer)
     655             : {
     656           8 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     657           4 :     OGRAPISpyFlushDefered();
     658           4 :     if (hLayer != nullptr)
     659           2 :         fprintf(fpSpyFile,
     660           4 :                 "%s = ", OGRAPISpyGetAndRegisterLayerVar(hDS, hLayer).c_str());
     661           8 :     fprintf(fpSpyFile, "%s.GetLayerByName(%s)\n",
     662           8 :             OGRAPISpyGetDSVar(hDS).c_str(),
     663           8 :             OGRAPISpyGetString(pszLayerName).c_str());
     664           4 :     OGRAPISpyFileClose();
     665           4 : }
     666             : 
     667           4 : void OGRAPISpy_DS_ExecuteSQL(GDALDatasetH hDS, const char *pszStatement,
     668             :                              OGRGeometryH hSpatialFilter,
     669             :                              const char *pszDialect, OGRLayerH hLayer)
     670             : {
     671           8 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     672           4 :     OGRAPISpyFlushDefered();
     673           4 :     if (hLayer != nullptr)
     674           4 :         fprintf(fpSpyFile,
     675           8 :                 "%s = ", OGRAPISpyGetAndRegisterLayerVar(hDS, hLayer).c_str());
     676          16 :     fprintf(fpSpyFile, "%s.ExecuteSQL(%s, %s, %s)\n",
     677           8 :             OGRAPISpyGetDSVar(hDS).c_str(),
     678           8 :             OGRAPISpyGetString(pszStatement).c_str(),
     679           8 :             OGRAPISpyGetGeom(hSpatialFilter).c_str(),
     680           8 :             OGRAPISpyGetString(pszDialect).c_str());
     681           4 :     OGRAPISpyFileClose();
     682           4 : }
     683             : 
     684           6 : void OGRAPISpy_DS_ReleaseResultSet(GDALDatasetH hDS, OGRLayerH hLayer)
     685             : {
     686          12 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     687           6 :     OGRAPISpyFlushDefered();
     688           8 :     fprintf(fpSpyFile, "%s.ReleaseResultSet(%s)\n",
     689          12 :             OGRAPISpyGetDSVar(hDS).c_str(),
     690          12 :             (hLayer) ? OGRAPISpyGetLayerVar(hLayer).c_str() : "None");
     691             : 
     692           6 :     DatasetDescription &dd = oMapDS[hDS];
     693           6 :     dd.oMapLayer.erase(hLayer);
     694           6 :     oGlobalMapLayer.erase(hLayer);
     695             : 
     696           6 :     OGRAPISpyFileClose();
     697           6 : }
     698             : 
     699           8 : void OGRAPISpy_DS_CreateLayer(GDALDatasetH hDS, const char *pszName,
     700             :                               OGRSpatialReferenceH hSpatialRef,
     701             :                               OGRwkbGeometryType eType, char **papszOptions,
     702             :                               OGRLayerH hLayer)
     703             : {
     704          16 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     705           8 :     OGRAPISpyFlushDefered();
     706           8 :     if (hLayer != nullptr)
     707           8 :         fprintf(fpSpyFile,
     708          16 :                 "%s = ", OGRAPISpyGetAndRegisterLayerVar(hDS, hLayer).c_str());
     709          40 :     fprintf(fpSpyFile, "%s.CreateLayer(%s, srs=%s, geom_type=%s, options=%s)\n",
     710          24 :             OGRAPISpyGetDSVar(hDS).c_str(), OGRAPISpyGetString(pszName).c_str(),
     711          16 :             OGRAPISpyGetSRS(hSpatialRef).c_str(),
     712          16 :             OGRAPISpyGetGeomType(eType).c_str(),
     713          16 :             OGRAPISpyGetOptions(papszOptions).c_str());
     714           8 :     OGRAPISpyFileClose();
     715           8 : }
     716             : 
     717           2 : void OGRAPISpy_DS_DeleteLayer(GDALDatasetH hDS, int iLayer)
     718             : {
     719           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     720           2 :     OGRAPISpyFlushDefered();
     721           2 :     fprintf(fpSpyFile, "%s.DeleteLayer(%d)\n", OGRAPISpyGetDSVar(hDS).c_str(),
     722             :             iLayer);
     723             :     // Should perhaps remove from the maps.
     724           2 :     OGRAPISpyFileClose();
     725           2 : }
     726             : 
     727           2 : void OGRAPISpy_Dataset_StartTransaction(GDALDatasetH hDS, int bForce)
     728             : {
     729           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     730           2 :     OGRAPISpyFlushDefered();
     731           2 :     fprintf(fpSpyFile, "%s.StartTransaction(%d)\n",
     732           4 :             OGRAPISpyGetDSVar(hDS).c_str(), bForce);
     733           2 :     OGRAPISpyFileClose();
     734           2 : }
     735             : 
     736           2 : void OGRAPISpy_Dataset_CommitTransaction(GDALDatasetH hDS)
     737             : {
     738           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     739           2 :     OGRAPISpyFlushDefered();
     740           2 :     fprintf(fpSpyFile, "%s.CommitTransaction()\n",
     741           4 :             OGRAPISpyGetDSVar(hDS).c_str());
     742           2 :     OGRAPISpyFileClose();
     743           2 : }
     744             : 
     745           2 : void OGRAPISpy_Dataset_RollbackTransaction(GDALDatasetH hDS)
     746             : {
     747           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     748           2 :     OGRAPISpyFlushDefered();
     749           2 :     fprintf(fpSpyFile, "%s.RollbackTransaction()\n",
     750           4 :             OGRAPISpyGetDSVar(hDS).c_str());
     751           2 :     OGRAPISpyFileClose();
     752           2 : }
     753             : 
     754           2 : void OGRAPISpy_L_GetFeatureCount(OGRLayerH hLayer, int bForce)
     755             : {
     756           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     757           2 :     OGRAPISpyFlushDefered();
     758           2 :     fprintf(fpSpyFile, "%s.GetFeatureCount(force=%d)\n",
     759           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), bForce);
     760           2 :     OGRAPISpyFileClose();
     761           2 : }
     762             : 
     763           0 : void OGRAPISpy_L_GetExtent(OGRLayerH hLayer, int bForce)
     764             : {
     765           0 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     766           0 :     OGRAPISpyFlushDefered();
     767           0 :     fprintf(fpSpyFile, "%s.GetExtent(force=%d)\n",
     768           0 :             OGRAPISpyGetLayerVar(hLayer).c_str(), bForce);
     769           0 :     OGRAPISpyFileClose();
     770           0 : }
     771             : 
     772           4 : void OGRAPISpy_L_GetExtentEx(OGRLayerH hLayer, int iGeomField, int bForce)
     773             : {
     774           8 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     775           4 :     OGRAPISpyFlushDefered();
     776           4 :     fprintf(fpSpyFile, "%s.GetExtent(geom_field=%d, force=%d)\n",
     777           8 :             OGRAPISpyGetLayerVar(hLayer).c_str(), iGeomField, bForce);
     778           4 :     OGRAPISpyFileClose();
     779           4 : }
     780             : 
     781           0 : void OGRAPISpy_L_GetExtent3D(OGRLayerH hLayer, int iGeomField, int bForce)
     782             : {
     783           0 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     784           0 :     OGRAPISpyFlushDefered();
     785           0 :     fprintf(fpSpyFile, "%s.GetExtent3D(geom_field=%d, force=%d)\n",
     786           0 :             OGRAPISpyGetLayerVar(hLayer).c_str(), iGeomField, bForce);
     787           0 :     OGRAPISpyFileClose();
     788           0 : }
     789             : 
     790           4 : void OGRAPISpy_L_SetAttributeFilter(OGRLayerH hLayer, const char *pszFilter)
     791             : {
     792           8 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     793           4 :     OGRAPISpyFlushDefered();
     794           8 :     fprintf(fpSpyFile, "%s.SetAttributeFilter(%s)\n",
     795           8 :             OGRAPISpyGetLayerVar(hLayer).c_str(),
     796           8 :             OGRAPISpyGetString(pszFilter).c_str());
     797           4 :     OGRAPISpyFileClose();
     798           4 : }
     799             : 
     800           2 : void OGRAPISpy_L_GetFeature(OGRLayerH hLayer, GIntBig nFeatureId)
     801             : {
     802           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     803           2 :     OGRAPISpyFlushDefered();
     804           2 :     fprintf(fpSpyFile, "%s.GetFeature(" CPL_FRMT_GIB ")\n",
     805           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), nFeatureId);
     806           2 :     OGRAPISpyFileClose();
     807           2 : }
     808             : 
     809           2 : void OGRAPISpy_L_SetNextByIndex(OGRLayerH hLayer, GIntBig nIndex)
     810             : {
     811           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     812           2 :     OGRAPISpyFlushDefered();
     813           2 :     fprintf(fpSpyFile, "%s.SetNextByIndex(" CPL_FRMT_GIB ")\n",
     814           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), nIndex);
     815           2 :     OGRAPISpyFileClose();
     816           2 : }
     817             : 
     818           8 : void OGRAPISpy_L_GetNextFeature(OGRLayerH hLayer)
     819             : {
     820           8 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     821           8 :     if (hLayerGetNextFeature != hLayer)
     822             :     {
     823           4 :         OGRAPISpyFlushDefered();
     824           4 :         OGRAPISpyFileClose();
     825             :     }
     826           8 :     hLayerGetNextFeature = hLayer;
     827           8 :     nGetNextFeatureCalls++;
     828           8 : }
     829             : 
     830           7 : static void OGRAPISpyDumpFeature(OGRFeatureH hFeat)
     831             : {
     832           7 :     OGRFeature *poFeature = OGRFeature::FromHandle(hFeat);
     833             : 
     834           7 :     fprintf(fpSpyFile, "f = ogr.Feature(%s)\n",
     835          14 :             OGRAPISpyGetFeatureDefnVar(
     836             :                 OGRFeatureDefn::ToHandle(poFeature->GetDefnRef()))
     837             :                 .c_str());
     838           7 :     if (poFeature->GetFID() != -1)
     839           2 :         fprintf(fpSpyFile, "f.SetFID(" CPL_FRMT_GIB ")\n", poFeature->GetFID());
     840          20 :     for (int i = 0; i < poFeature->GetFieldCount(); i++)
     841             :     {
     842          13 :         if (poFeature->IsFieldNull(i))
     843             :         {
     844           0 :             fprintf(fpSpyFile, "f.SetFieldNull(%d)\n", i);
     845             :         }
     846          13 :         else if (poFeature->IsFieldSet(i))
     847             :         {
     848          10 :             switch (poFeature->GetFieldDefnRef(i)->GetType())
     849             :             {
     850           4 :                 case OFTInteger:
     851           4 :                     fprintf(fpSpyFile, "f.SetField(%d, %d)\n", i,
     852             :                             poFeature->GetFieldAsInteger(i));
     853           4 :                     break;
     854           2 :                 case OFTReal:
     855           2 :                     fprintf(fpSpyFile, "%s",
     856             :                             CPLSPrintf("f.SetField(%d, %.16g)\n", i,
     857             :                                        poFeature->GetFieldAsDouble(i)));
     858           2 :                     break;
     859           4 :                 case OFTString:
     860           4 :                     fprintf(fpSpyFile, "f.SetField(%d, %s)\n", i,
     861           8 :                             OGRAPISpyGetString(poFeature->GetFieldAsString(i))
     862             :                                 .c_str());
     863           4 :                     break;
     864           0 :                 default:
     865           0 :                     fprintf(fpSpyFile, "f.SetField(%d, %s) #FIXME\n", i,
     866           0 :                             OGRAPISpyGetString(poFeature->GetFieldAsString(i))
     867             :                                 .c_str());
     868           0 :                     break;
     869             :             }
     870             :         }
     871             :     }
     872          14 :     for (int i = 0; i < poFeature->GetGeomFieldCount(); i++)
     873             :     {
     874           7 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
     875           7 :         if (poGeom != nullptr)
     876             :         {
     877           2 :             fprintf(fpSpyFile, "f.SetGeomField(%d, %s)\n", i,
     878           4 :                     OGRAPISpyGetGeom(OGRGeometry::ToHandle(poGeom)).c_str());
     879             :         }
     880             :     }
     881           7 :     const char *pszStyleString = poFeature->GetStyleString();
     882           7 :     if (pszStyleString != nullptr)
     883           2 :         fprintf(fpSpyFile, "f.SetStyleString(%s)\n",
     884           4 :                 OGRAPISpyGetString(pszStyleString).c_str());
     885           7 : }
     886             : 
     887           2 : void OGRAPISpy_L_SetFeature(OGRLayerH hLayer, OGRFeatureH hFeat)
     888             : {
     889           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     890           2 :     OGRAPISpyFlushDefered();
     891           2 :     OGRAPISpyDumpFeature(hFeat);
     892           2 :     fprintf(fpSpyFile, "%s.SetFeature(f)\n",
     893           4 :             OGRAPISpyGetLayerVar(hLayer).c_str());
     894             :     // In case layer defn is changed afterwards.
     895           2 :     fprintf(fpSpyFile, "f = None\n");
     896           2 :     OGRAPISpyFileClose();
     897           2 : }
     898             : 
     899           5 : void OGRAPISpy_L_CreateFeature(OGRLayerH hLayer, OGRFeatureH hFeat)
     900             : {
     901          10 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     902           5 :     OGRAPISpyFlushDefered();
     903           5 :     OGRAPISpyDumpFeature(hFeat);
     904           5 :     fprintf(fpSpyFile, "%s.CreateFeature(f)\n",
     905          10 :             OGRAPISpyGetLayerVar(hLayer).c_str());
     906             :     // In case layer defn is changed afterwards.
     907           5 :     fprintf(fpSpyFile, "f = None\n");
     908           5 :     OGRAPISpyFileClose();
     909           5 : }
     910             : 
     911           0 : void OGRAPISpy_L_UpsertFeature(OGRLayerH hLayer, OGRFeatureH hFeat)
     912             : {
     913           0 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     914           0 :     OGRAPISpyFlushDefered();
     915           0 :     OGRAPISpyDumpFeature(hFeat);
     916           0 :     fprintf(fpSpyFile, "%s.UpsertFeature(f)\n",
     917           0 :             OGRAPISpyGetLayerVar(hLayer).c_str());
     918             :     // In case layer defn is changed afterwards.
     919           0 :     fprintf(fpSpyFile, "f = None\n");
     920           0 :     OGRAPISpyFileClose();
     921           0 : }
     922             : 
     923           8 : static void OGRAPISpyDumpFieldDefn(OGRFieldDefn *poFieldDefn)
     924             : {
     925          16 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     926          16 :     fprintf(fpSpyFile, "fd = ogr.FieldDefn(%s, %s)\n",
     927          16 :             OGRAPISpyGetString(poFieldDefn->GetNameRef()).c_str(),
     928          16 :             OGRAPISpyGetFieldType(poFieldDefn->GetType()).c_str());
     929           8 :     if (poFieldDefn->GetWidth() > 0)
     930           2 :         fprintf(fpSpyFile, "fd.SetWidth(%d)\n", poFieldDefn->GetWidth());
     931           8 :     if (poFieldDefn->GetPrecision() > 0)
     932           2 :         fprintf(fpSpyFile, "fd.SetPrecision(%d)\n",
     933             :                 poFieldDefn->GetPrecision());
     934           8 :     if (!poFieldDefn->IsNullable())
     935           2 :         fprintf(fpSpyFile, "fd.SetNullable(0)\n");
     936           8 :     if (poFieldDefn->GetDefault() != nullptr)
     937           2 :         fprintf(fpSpyFile, "fd.SetDefault(%s)\n",
     938           4 :                 OGRAPISpyGetString(poFieldDefn->GetDefault()).c_str());
     939           8 : }
     940             : 
     941           6 : void OGRAPISpy_L_CreateField(OGRLayerH hLayer, OGRFieldDefnH hField,
     942             :                              int bApproxOK)
     943             : {
     944          12 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     945           6 :     OGRAPISpyFlushDefered();
     946           6 :     OGRFieldDefn *poFieldDefn = OGRFieldDefn::FromHandle(hField);
     947           6 :     OGRAPISpyDumpFieldDefn(poFieldDefn);
     948           6 :     fprintf(fpSpyFile, "%s.CreateField(fd, approx_ok=%d)\n",
     949          12 :             OGRAPISpyGetLayerVar(hLayer).c_str(), bApproxOK);
     950           6 :     OGRAPISpyFileClose();
     951           6 : }
     952             : 
     953           2 : void OGRAPISpy_L_DeleteField(OGRLayerH hLayer, int iField)
     954             : {
     955           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     956           2 :     OGRAPISpyFlushDefered();
     957           2 :     fprintf(fpSpyFile, "%s.DeleteField(%d)\n",
     958           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), iField);
     959           2 :     OGRAPISpyFileClose();
     960           2 : }
     961             : 
     962           2 : void OGRAPISpy_L_ReorderFields(OGRLayerH hLayer, int *panMap)
     963             : {
     964           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     965           2 :     OGRAPISpyFlushDefered();
     966           2 :     OGRLayer *poLayer = OGRLayer::FromHandle(hLayer);
     967           2 :     fprintf(fpSpyFile, "%s.ReorderFields([",
     968           4 :             OGRAPISpyGetLayerVar(hLayer).c_str());
     969           8 :     for (int i = 0; i < poLayer->GetLayerDefn()->GetFieldCount(); i++)
     970             :     {
     971           6 :         if (i > 0)
     972           4 :             fprintf(fpSpyFile, ", ");
     973           6 :         fprintf(fpSpyFile, "%d", panMap[i]);
     974             :     }
     975           2 :     fprintf(fpSpyFile, "])\n");
     976           2 :     OGRAPISpyFileClose();
     977           2 : }
     978             : 
     979           2 : void OGRAPISpy_L_ReorderField(OGRLayerH hLayer, int iOldFieldPos,
     980             :                               int iNewFieldPos)
     981             : {
     982           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     983           2 :     OGRAPISpyFlushDefered();
     984           2 :     fprintf(fpSpyFile, "%s.ReorderField(%d, %d)\n",
     985           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), iOldFieldPos, iNewFieldPos);
     986           2 :     OGRAPISpyFileClose();
     987           2 : }
     988             : 
     989           2 : void OGRAPISpy_L_AlterFieldDefn(OGRLayerH hLayer, int iField,
     990             :                                 OGRFieldDefnH hNewFieldDefn, int nFlags)
     991             : {
     992           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
     993           2 :     OGRAPISpyFlushDefered();
     994           2 :     OGRFieldDefn *poFieldDefn = OGRFieldDefn::FromHandle(hNewFieldDefn);
     995           2 :     OGRAPISpyDumpFieldDefn(poFieldDefn);
     996           2 :     fprintf(fpSpyFile, "%s.AlterFieldDefn(%d, fd, %d)\n",
     997           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), iField, nFlags);
     998           2 :     OGRAPISpyFileClose();
     999           2 : }
    1000             : 
    1001           2 : void OGRAPISpy_L_CreateGeomField(OGRLayerH hLayer, OGRGeomFieldDefnH hField,
    1002             :                                  int bApproxOK)
    1003             : {
    1004           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1005           2 :     OGRAPISpyFlushDefered();
    1006           2 :     OGRGeomFieldDefn *poGeomFieldDefn = OGRGeomFieldDefn::FromHandle(hField);
    1007             : 
    1008           4 :     fprintf(fpSpyFile, "geom_fd = ogr.GeomFieldDefn(%s, %s)\n",
    1009           4 :             OGRAPISpyGetString(poGeomFieldDefn->GetNameRef()).c_str(),
    1010           4 :             OGRAPISpyGetGeomType(poGeomFieldDefn->GetType()).c_str());
    1011           2 :     if (poGeomFieldDefn->GetSpatialRef() != nullptr)
    1012           2 :         fprintf(fpSpyFile, "geom_fd.SetSpatialRef(%s)\n",
    1013           4 :                 OGRAPISpyGetSRS(OGRSpatialReference::ToHandle(
    1014             :                                     const_cast<OGRSpatialReference *>(
    1015           2 :                                         poGeomFieldDefn->GetSpatialRef())))
    1016             :                     .c_str());
    1017           2 :     if (!poGeomFieldDefn->IsNullable())
    1018           2 :         fprintf(fpSpyFile, "geom_fd.SetNullable(0)\n");
    1019           2 :     fprintf(fpSpyFile, "%s.CreateGeomField(geom_fd, approx_ok=%d)\n",
    1020           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), bApproxOK);
    1021           2 :     OGRAPISpyFileClose();
    1022           2 : }
    1023             : 
    1024          22 : static void OGRAPISpy_L_Op(OGRLayerH hLayer, const char *pszMethod)
    1025             : {
    1026          44 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1027          22 :     OGRAPISpyFlushDefered();
    1028          22 :     fprintf(fpSpyFile, "%s.%s()\n", OGRAPISpyGetLayerVar(hLayer).c_str(),
    1029             :             pszMethod);
    1030          22 :     OGRAPISpyFileClose();
    1031          22 : }
    1032             : 
    1033           2 : void OGRAPISpy_L_StartTransaction(OGRLayerH hLayer)
    1034             : {
    1035           2 :     OGRAPISpy_L_Op(hLayer, "StartTransaction");
    1036           2 : }
    1037             : 
    1038           2 : void OGRAPISpy_L_CommitTransaction(OGRLayerH hLayer)
    1039             : {
    1040           2 :     OGRAPISpy_L_Op(hLayer, "CommitTransaction");
    1041           2 : }
    1042             : 
    1043           2 : void OGRAPISpy_L_RollbackTransaction(OGRLayerH hLayer)
    1044             : {
    1045           2 :     OGRAPISpy_L_Op(hLayer, "RollbackTransaction");
    1046           2 : }
    1047             : 
    1048          15 : void OGRAPISpy_L_GetLayerDefn(OGRLayerH hLayer)
    1049             : {
    1050          15 :     if (hLayer != hLayerGetLayerDefn)
    1051             :     {
    1052          13 :         OGRAPISpyFlushDefered();
    1053          13 :         hLayerGetLayerDefn = hLayer;
    1054          13 :         OGRAPISpyFileClose();
    1055             :     }
    1056          15 : }
    1057             : 
    1058           2 : void OGRAPISpy_L_GetSpatialRef(OGRLayerH hLayer)
    1059             : {
    1060           2 :     OGRAPISpy_L_Op(hLayer, "GetSpatialRef");
    1061           2 : }
    1062             : 
    1063           2 : void OGRAPISpy_L_GetSpatialFilter(OGRLayerH hLayer)
    1064             : {
    1065           2 :     OGRAPISpy_L_Op(hLayer, "GetSpatialFilter");
    1066           2 : }
    1067             : 
    1068           2 : void OGRAPISpy_L_ResetReading(OGRLayerH hLayer)
    1069             : {
    1070           2 :     OGRAPISpy_L_Op(hLayer, "ResetReading");
    1071           2 : }
    1072             : 
    1073           2 : void OGRAPISpy_L_SyncToDisk(OGRLayerH hLayer)
    1074             : {
    1075           2 :     OGRAPISpy_L_Op(hLayer, "SyncToDisk");
    1076           2 : }
    1077             : 
    1078           2 : void OGRAPISpy_L_GetFIDColumn(OGRLayerH hLayer)
    1079             : {
    1080           2 :     OGRAPISpy_L_Op(hLayer, "GetFIDColumn");
    1081           2 : }
    1082             : 
    1083           2 : void OGRAPISpy_L_GetGeometryColumn(OGRLayerH hLayer)
    1084             : {
    1085           2 :     OGRAPISpy_L_Op(hLayer, "GetGeometryColumn");
    1086           2 : }
    1087             : 
    1088           2 : void OGRAPISpy_L_GetName(OGRLayerH hLayer)
    1089             : {
    1090           2 :     OGRAPISpy_L_Op(hLayer, "GetName");
    1091           2 : }
    1092             : 
    1093           2 : void OGRAPISpy_L_GetGeomType(OGRLayerH hLayer)
    1094             : {
    1095           2 :     OGRAPISpy_L_Op(hLayer, "GetGeomType");
    1096           2 : }
    1097             : 
    1098           2 : void OGRAPISpy_L_FindFieldIndex(OGRLayerH hLayer, const char *pszFieldName,
    1099             :                                 int bExactMatch)
    1100             : {
    1101           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1102           2 :     OGRAPISpyFlushDefered();
    1103           4 :     fprintf(fpSpyFile, "%s.FindFieldIndex(%s, %d)\n",
    1104           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(),
    1105           4 :             OGRAPISpyGetString(pszFieldName).c_str(), bExactMatch);
    1106           2 :     OGRAPISpyFileClose();
    1107           2 : }
    1108             : 
    1109           2 : void OGRAPISpy_L_TestCapability(OGRLayerH hLayer, const char *pszCap)
    1110             : {
    1111           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1112           2 :     OGRAPISpyFlushDefered();
    1113           4 :     fprintf(fpSpyFile, "%s.TestCapability(%s)\n",
    1114           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(),
    1115           4 :             OGRAPISpyGetString(pszCap).c_str());
    1116           2 :     OGRAPISpyFileClose();
    1117           2 : }
    1118             : 
    1119           4 : void OGRAPISpy_L_SetSpatialFilter(OGRLayerH hLayer, OGRGeometryH hGeom)
    1120             : {
    1121           8 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1122           4 :     OGRAPISpyFlushDefered();
    1123           8 :     fprintf(fpSpyFile, "%s.SetSpatialFilter(%s)\n",
    1124           8 :             OGRAPISpyGetLayerVar(hLayer).c_str(),
    1125           8 :             OGRAPISpyGetGeom(hGeom).c_str());
    1126           4 :     OGRAPISpyFileClose();
    1127           4 : }
    1128             : 
    1129           2 : void OGRAPISpy_L_SetSpatialFilterEx(OGRLayerH hLayer, int iGeomField,
    1130             :                                     OGRGeometryH hGeom)
    1131             : {
    1132           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1133           2 :     OGRAPISpyFlushDefered();
    1134           4 :     fprintf(fpSpyFile, "%s.SetSpatialFilter(%d, %s)\n",
    1135           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), iGeomField,
    1136           4 :             OGRAPISpyGetGeom(hGeom).c_str());
    1137           2 :     OGRAPISpyFileClose();
    1138           2 : }
    1139             : 
    1140           2 : void OGRAPISpy_L_SetSpatialFilterRect(OGRLayerH hLayer, double dfMinX,
    1141             :                                       double dfMinY, double dfMaxX,
    1142             :                                       double dfMaxY)
    1143             : {
    1144           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1145           2 :     OGRAPISpyFlushDefered();
    1146           2 :     fprintf(fpSpyFile, "%s",
    1147             :             CPLSPrintf("%s.SetSpatialFilterRect(%.16g, %.16g, %.16g, %.16g)\n",
    1148           4 :                        OGRAPISpyGetLayerVar(hLayer).c_str(), dfMinX, dfMinY,
    1149             :                        dfMaxX, dfMaxY));
    1150           2 :     OGRAPISpyFileClose();
    1151           2 : }
    1152             : 
    1153           2 : void OGRAPISpy_L_SetSpatialFilterRectEx(OGRLayerH hLayer, int iGeomField,
    1154             :                                         double dfMinX, double dfMinY,
    1155             :                                         double dfMaxX, double dfMaxY)
    1156             : {
    1157           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1158           2 :     OGRAPISpyFlushDefered();
    1159           2 :     fprintf(fpSpyFile, "%s",
    1160             :             CPLSPrintf("%s.SetSpatialFilterRect(%d, "
    1161             :                        "%.16g, %.16g, %.16g, %.16g)\n",
    1162           4 :                        OGRAPISpyGetLayerVar(hLayer).c_str(), iGeomField, dfMinX,
    1163             :                        dfMinY, dfMaxX, dfMaxY));
    1164           2 :     OGRAPISpyFileClose();
    1165           2 : }
    1166             : 
    1167           2 : void OGRAPISpy_L_DeleteFeature(OGRLayerH hLayer, GIntBig nFID)
    1168             : {
    1169           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1170           2 :     OGRAPISpyFlushDefered();
    1171           2 :     fprintf(fpSpyFile, "%s.DeleteFeature(" CPL_FRMT_GIB ")\n",
    1172           4 :             OGRAPISpyGetLayerVar(hLayer).c_str(), nFID);
    1173           2 :     OGRAPISpyFileClose();
    1174           2 : }
    1175             : 
    1176           2 : void OGRAPISpy_L_SetIgnoredFields(OGRLayerH hLayer,
    1177             :                                   const char **papszIgnoredFields)
    1178             : {
    1179           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1180           2 :     OGRAPISpyFlushDefered();
    1181           4 :     fprintf(
    1182             :         fpSpyFile, "%s.SetIgnoredFields(%s)\n",
    1183           4 :         OGRAPISpyGetLayerVar(hLayer).c_str(),
    1184           4 :         OGRAPISpyGetOptions(const_cast<char **>(papszIgnoredFields)).c_str());
    1185           2 :     OGRAPISpyFileClose();
    1186           2 : }
    1187             : 
    1188           2 : void OGRAPISpy_FD_GetGeomType(OGRFeatureDefnH hDefn)
    1189             : {
    1190           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1191           2 :     OGRAPISpyFlushDefered();
    1192           2 :     fprintf(fpSpyFile, "%s.GetGeomType()\n",
    1193           4 :             OGRAPISpyGetFeatureDefnVar(hDefn).c_str());
    1194           2 :     OGRAPISpyFileClose();
    1195           2 : }
    1196             : 
    1197           6 : void OGRAPISpy_FD_GetFieldCount(OGRFeatureDefnH hDefn)
    1198             : {
    1199          12 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1200          10 :     if (hLayerGetLayerDefn != nullptr &&
    1201           4 :         OGRFeatureDefn::ToHandle(
    1202           4 :             OGRLayer::FromHandle(hLayerGetLayerDefn)->GetLayerDefn()) == hDefn)
    1203             :     {
    1204           4 :         bDeferGetFieldCount = true;
    1205             :     }
    1206             :     else
    1207             :     {
    1208           2 :         OGRAPISpyFlushDefered();
    1209           2 :         fprintf(fpSpyFile, "%s.GetFieldCount()\n",
    1210           4 :                 OGRAPISpyGetFeatureDefnVar(hDefn).c_str());
    1211           2 :         OGRAPISpyFileClose();
    1212             :     }
    1213           6 : }
    1214             : 
    1215           2 : void OGRAPISpy_FD_GetFieldDefn(OGRFeatureDefnH hDefn, int iField,
    1216             :                                OGRFieldDefnH hField)
    1217             : {
    1218           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1219           2 :     OGRAPISpyFlushDefered();
    1220           4 :     fprintf(fpSpyFile, "%s_fielddefn%d = %s.GetFieldDefn(%d)\n",
    1221           4 :             OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), iField,
    1222           4 :             OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), iField);
    1223             : 
    1224             :     std::map<OGRFieldDefnH, CPLString>::iterator oIter =
    1225           2 :         oGlobalMapFieldDefn.find(hField);
    1226           2 :     if (oIter == oGlobalMapFieldDefn.end())
    1227             :     {
    1228           2 :         oMapFDefn[hDefn].oMapFieldDefn[hField] = iField;
    1229           2 :         oGlobalMapFieldDefn[hField] =
    1230             :             CPLSPrintf("%s_fielddefn%d",
    1231           4 :                        OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), iField);
    1232             :     }
    1233             : 
    1234           2 :     OGRAPISpyFileClose();
    1235           2 : }
    1236             : 
    1237           2 : void OGRAPISpy_FD_GetFieldIndex(OGRFeatureDefnH hDefn, const char *pszFieldName)
    1238             : {
    1239           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1240           2 :     OGRAPISpyFlushDefered();
    1241           4 :     fprintf(fpSpyFile, "%s.GetFieldIndex(%s)\n",
    1242           4 :             OGRAPISpyGetFeatureDefnVar(hDefn).c_str(),
    1243           4 :             OGRAPISpyGetString(pszFieldName).c_str());
    1244           2 :     OGRAPISpyFileClose();
    1245           2 : }
    1246             : 
    1247           6 : void OGRAPISpy_Fld_GetXXXX(OGRFieldDefnH hField, const char *pszOp)
    1248             : {
    1249          12 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1250           6 :     OGRAPISpyFlushDefered();
    1251           6 :     fprintf(fpSpyFile, "%s.%s()\n", oGlobalMapFieldDefn[hField].c_str(), pszOp);
    1252           6 :     OGRAPISpyFileClose();
    1253           6 : }
    1254             : 
    1255           2 : void OGRAPISpy_FD_GetGeomFieldCount(OGRFeatureDefnH hDefn)
    1256             : {
    1257           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1258           2 :     OGRAPISpyFlushDefered();
    1259           2 :     fprintf(fpSpyFile, "%s.GetGeomFieldCount()\n",
    1260           4 :             OGRAPISpyGetFeatureDefnVar(hDefn).c_str());
    1261           2 :     OGRAPISpyFileClose();
    1262           2 : }
    1263             : 
    1264           2 : void OGRAPISpy_FD_GetGeomFieldDefn(OGRFeatureDefnH hDefn, int iGeomField,
    1265             :                                    OGRGeomFieldDefnH hGeomField)
    1266             : {
    1267           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1268           2 :     OGRAPISpyFlushDefered();
    1269           4 :     fprintf(fpSpyFile, "%s_geomfielddefn%d = %s.GetGeomFieldDefn(%d)\n",
    1270           4 :             OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), iGeomField,
    1271           4 :             OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), iGeomField);
    1272             : 
    1273             :     std::map<OGRGeomFieldDefnH, CPLString>::iterator oIter =
    1274           2 :         oGlobalMapGeomFieldDefn.find(hGeomField);
    1275           2 :     if (oIter == oGlobalMapGeomFieldDefn.end())
    1276             :     {
    1277           2 :         oMapFDefn[hDefn].oMapGeomFieldDefn[hGeomField] = iGeomField;
    1278           2 :         oGlobalMapGeomFieldDefn[hGeomField] =
    1279             :             CPLSPrintf("%s_geomfielddefn%d",
    1280           4 :                        OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), iGeomField);
    1281             :     }
    1282             : 
    1283           2 :     OGRAPISpyFileClose();
    1284           2 : }
    1285             : 
    1286           2 : void OGRAPISpy_FD_GetGeomFieldIndex(OGRFeatureDefnH hDefn,
    1287             :                                     const char *pszFieldName)
    1288             : {
    1289           4 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1290           2 :     OGRAPISpyFlushDefered();
    1291           4 :     fprintf(fpSpyFile, "%s.GetGeomFieldIndex(%s)\n",
    1292           4 :             OGRAPISpyGetFeatureDefnVar(hDefn).c_str(),
    1293           4 :             OGRAPISpyGetString(pszFieldName).c_str());
    1294           2 :     OGRAPISpyFileClose();
    1295           2 : }
    1296             : 
    1297           6 : void OGRAPISpy_GFld_GetXXXX(OGRGeomFieldDefnH hGeomField, const char *pszOp)
    1298             : {
    1299          12 :     CPLMutexHolderD(&hOGRAPISpyMutex);
    1300           6 :     OGRAPISpyFlushDefered();
    1301           6 :     fprintf(fpSpyFile, "%s.%s()\n", oGlobalMapGeomFieldDefn[hGeomField].c_str(),
    1302             :             pszOp);
    1303           6 :     OGRAPISpyFileClose();
    1304           6 : }
    1305             : 
    1306        4708 : void OGRAPISPYCPLSetConfigOption(const char *pszKey, const char *pszValue)
    1307             : {
    1308        4708 :     if (STARTS_WITH(pszKey, "OGR_API_SPY_") || STARTS_WITH(pszKey, "__"))
    1309           0 :         return;
    1310        4708 :     if (!OGRAPISpyEnabled())
    1311        4706 :         return;
    1312           2 :     OGRAPISpyFlushDefered();
    1313           2 :     if (pszValue)
    1314             :     {
    1315           4 :         fprintf(fpSpyFile, "gdal.SetConfigOption(%s, %s)\n",
    1316           4 :                 OGRAPISpyGetString(pszKey).c_str(),
    1317           4 :                 OGRAPISpyGetString(pszValue).c_str());
    1318             :     }
    1319             :     else
    1320             :     {
    1321           0 :         fprintf(fpSpyFile, "gdal.SetConfigOption(%s, None)\n",
    1322           0 :                 OGRAPISpyGetString(pszKey).c_str());
    1323             :     }
    1324           2 :     OGRAPISpyFileClose();
    1325             : }
    1326             : 
    1327       55978 : void OGRAPISPYCPLSetThreadLocalConfigOption(const char *pszKey,
    1328             :                                             const char *pszValue)
    1329             : {
    1330       55978 :     if (STARTS_WITH(pszKey, "OGR_API_SPY_") || STARTS_WITH(pszKey, "__"))
    1331          12 :         return;
    1332       55966 :     if (!OGRAPISpyEnabled())
    1333       55912 :         return;
    1334           0 :     OGRAPISpyFlushDefered();
    1335           0 :     if (pszValue)
    1336             :     {
    1337           0 :         fprintf(fpSpyFile,
    1338             :                 "gdal.SetConfigOption(%s, %s) # SetThreadLocalConfigOption "
    1339             :                 "actually\n",
    1340           0 :                 OGRAPISpyGetString(pszKey).c_str(),
    1341           0 :                 OGRAPISpyGetString(pszValue).c_str());
    1342             :     }
    1343             :     else
    1344             :     {
    1345           0 :         fprintf(fpSpyFile,
    1346             :                 "gdal.SetConfigOption(%s, None) # SetThreadLocalConfigOption "
    1347             :                 "actually\n",
    1348           0 :                 OGRAPISpyGetString(pszKey).c_str());
    1349             :     }
    1350           0 :     OGRAPISpyFileClose();
    1351             : }
    1352             : 
    1353             : #endif  // OGRAPISPY_ENABLED

Generated by: LCOV version 1.14