1 /******************************************************************************
2  *
3  * Project:  OpenGIS Simple Features Reference Implementation
4  * Purpose:  Simple client for viewing OGR driver data.
5  * Author:   Frank Warmerdam, warmerdam@pobox.com
6  *
7  ******************************************************************************
8  * Copyright (c) 1999, Frank Warmerdam
9  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "cpl_port.h"
31 
32 #include <cstdio>
33 #include <cstdlib>
34 #include <cstring>
35 #include <set>
36 
37 #include "commonutils.h"
38 #include "cpl_conv.h"
39 #include "cpl_error.h"
40 #include "cpl_multiproc.h"
41 #include "cpl_string.h"
42 #include "cpl_vsi.h"
43 #include "gdal_version.h"
44 #include "gdal.h"
45 #include "gdal_priv.h"
46 #include "gdal_version.h"
47 #include "ogr_api.h"
48 #include "ogr_core.h"
49 #include "ogr_feature.h"
50 #include "ogr_geometry.h"
51 #include "ogr_p.h"
52 #include "ogr_spatialref.h"
53 #include "ogrsf_frmts.h"
54 
55 
56 CPL_CVSID("$Id: ogrinfo.cpp fb6aaddcfeb93b956a1a988cb142f25878ed1a35 2021-04-01 19:19:55 +0200 Even Rouault $")
57 
58 bool bVerbose = true;
59 bool bSuperQuiet = false;
60 bool bSummaryOnly = false;
61 GIntBig nFetchFID = OGRNullFID;
62 char** papszOptions = nullptr;
63 
64 /************************************************************************/
65 /*                               Usage()                                */
66 /************************************************************************/
67 
Usage(const char * pszErrorMsg=nullptr)68 static void Usage( const char* pszErrorMsg = nullptr )
69 {
70     printf("Usage: ogrinfo [--help-general] [-ro] [-q] [-where restricted_where|@filename]\n"
71            "               [-spat xmin ymin xmax ymax] [-geomfield field] [-fid fid]\n"
72            "               [-sql statement|@filename] [-dialect sql_dialect] [-al] [-rl] [-so] [-fields={YES/NO}]\n"
73            "               [-geom={YES/NO/SUMMARY}] [[-oo NAME=VALUE] ...]\n"
74            "               [-nomd] [-listmdd] [-mdd domain|`all`]*\n"
75            "               [-nocount] [-noextent] [-nogeomtype] [-wkt_format WKT1|WKT2|...]\n"
76            "               [-fielddomain name]\n"
77            "               datasource_name [layer [layer ...]]\n");
78 
79     if( pszErrorMsg != nullptr )
80         fprintf(stderr, "\nFAILURE: %s\n", pszErrorMsg);
81 
82     exit(1);
83 }
84 
85 /************************************************************************/
86 /*                       GDALInfoPrintMetadata()                        */
87 /************************************************************************/
GDALInfoPrintMetadata(GDALMajorObjectH hObject,const char * pszDomain,const char * pszDisplayedname,const char * pszIndent)88 static void GDALInfoPrintMetadata( GDALMajorObjectH hObject,
89                                    const char *pszDomain,
90                                    const char *pszDisplayedname,
91                                    const char *pszIndent )
92 {
93     bool bIsxml = false;
94 
95     if( pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "xml:") )
96         bIsxml = true;
97 
98     char **papszMetadata = GDALGetMetadata(hObject, pszDomain);
99     if( CSLCount(papszMetadata) > 0 )
100     {
101         printf("%s%s:\n", pszIndent, pszDisplayedname);
102         for( int i = 0; papszMetadata[i] != nullptr; i++ )
103         {
104             if( bIsxml )
105                 printf("%s%s\n", pszIndent, papszMetadata[i]);
106             else
107                 printf("%s  %s\n", pszIndent, papszMetadata[i]);
108         }
109     }
110 }
111 
112 /************************************************************************/
113 /*                       GDALInfoReportMetadata()                       */
114 /************************************************************************/
GDALInfoReportMetadata(GDALMajorObjectH hObject,bool bListMDD,bool bShowMetadata,char ** papszExtraMDDomains)115 static void GDALInfoReportMetadata( GDALMajorObjectH hObject,
116                                     bool bListMDD,
117                                     bool bShowMetadata,
118                                     char **papszExtraMDDomains )
119 {
120     const char* pszIndent = "";
121 
122     /* -------------------------------------------------------------------- */
123     /*      Report list of Metadata domains                                 */
124     /* -------------------------------------------------------------------- */
125     if( bListMDD )
126     {
127         char** papszMDDList = GDALGetMetadataDomainList(hObject);
128         char** papszIter = papszMDDList;
129 
130         if( papszMDDList != nullptr )
131             printf("%sMetadata domains:\n", pszIndent);
132         while( papszIter != nullptr && *papszIter != nullptr )
133         {
134             if( EQUAL(*papszIter, "") )
135                 printf("%s  (default)\n", pszIndent);
136             else
137                 printf("%s  %s\n", pszIndent, *papszIter);
138             papszIter ++;
139         }
140         CSLDestroy(papszMDDList);
141     }
142 
143     if( !bShowMetadata )
144         return;
145 
146     /* -------------------------------------------------------------------- */
147     /*      Report default Metadata domain.                                 */
148     /* -------------------------------------------------------------------- */
149     GDALInfoPrintMetadata(hObject, nullptr, "Metadata", pszIndent);
150 
151     /* -------------------------------------------------------------------- */
152     /*      Report extra Metadata domains                                   */
153     /* -------------------------------------------------------------------- */
154     if( papszExtraMDDomains != nullptr )
155     {
156         char **papszExtraMDDomainsExpanded = nullptr;
157 
158         if( EQUAL(papszExtraMDDomains[0], "all") &&
159             papszExtraMDDomains[1] == nullptr )
160         {
161             char** papszMDDList = GDALGetMetadataDomainList(hObject);
162             char** papszIter = papszMDDList;
163 
164             while( papszIter != nullptr && *papszIter != nullptr )
165             {
166                 if( !EQUAL(*papszIter, "") && !EQUAL(*papszIter, "SUBDATASETS") )
167                 {
168                     papszExtraMDDomainsExpanded = CSLAddString(papszExtraMDDomainsExpanded, *papszIter);
169                 }
170                 papszIter ++;
171             }
172             CSLDestroy(papszMDDList);
173         }
174         else
175         {
176             papszExtraMDDomainsExpanded = CSLDuplicate(papszExtraMDDomains);
177         }
178 
179         for( int iMDD = 0;
180              papszExtraMDDomainsExpanded != nullptr &&
181                papszExtraMDDomainsExpanded[iMDD] != nullptr;
182              iMDD++ )
183         {
184             char pszDisplayedname[256];
185             snprintf(pszDisplayedname, 256, "Metadata (%s)",
186                      papszExtraMDDomainsExpanded[iMDD]);
187             GDALInfoPrintMetadata(hObject, papszExtraMDDomainsExpanded[iMDD],
188                                   pszDisplayedname, pszIndent);
189         }
190 
191         CSLDestroy(papszExtraMDDomainsExpanded);
192     }
193     GDALInfoPrintMetadata(hObject, "SUBDATASETS", "Subdatasets", pszIndent);
194 }
195 
196 /************************************************************************/
197 /*                           ReportOnLayer()                            */
198 /************************************************************************/
199 
ReportOnLayer(OGRLayer * poLayer,const char * pszWHERE,const char * pszGeomField,OGRGeometry * poSpatialFilter,bool bListMDD,bool bShowMetadata,char ** papszExtraMDDomains,bool bFeatureCount,bool bExtent,bool bGeomType,const char * pszWKTFormat)200 static void ReportOnLayer( OGRLayer * poLayer, const char *pszWHERE,
201                            const char* pszGeomField,
202                            OGRGeometry *poSpatialFilter,
203                            bool bListMDD,
204                            bool bShowMetadata,
205                            char** papszExtraMDDomains,
206                            bool bFeatureCount,
207                            bool bExtent,
208                            bool bGeomType,
209                            const char* pszWKTFormat )
210 {
211     OGRFeatureDefn      *poDefn = poLayer->GetLayerDefn();
212 
213 /* -------------------------------------------------------------------- */
214 /*      Set filters if provided.                                        */
215 /* -------------------------------------------------------------------- */
216     if( pszWHERE != nullptr )
217     {
218         if( poLayer->SetAttributeFilter(pszWHERE) != OGRERR_NONE )
219         {
220             printf("FAILURE: SetAttributeFilter(%s) failed.\n", pszWHERE);
221             exit(1);
222         }
223     }
224 
225     if( poSpatialFilter != nullptr )
226     {
227         if( pszGeomField != nullptr )
228         {
229             const int iGeomField = poDefn->GetGeomFieldIndex(pszGeomField);
230             if( iGeomField >= 0 )
231                 poLayer->SetSpatialFilter(iGeomField, poSpatialFilter);
232             else
233                 printf("WARNING: Cannot find geometry field %s.\n",
234                        pszGeomField);
235         }
236         else
237         {
238             poLayer->SetSpatialFilter(poSpatialFilter);
239         }
240     }
241 
242 /* -------------------------------------------------------------------- */
243 /*      Report various overall information.                             */
244 /* -------------------------------------------------------------------- */
245     if( !bSuperQuiet )
246     {
247         printf("\n");
248         printf("Layer name: %s\n", poLayer->GetName());
249     }
250 
251     GDALInfoReportMetadata(static_cast<GDALMajorObjectH>(poLayer),
252                            bListMDD,
253                            bShowMetadata,
254                            papszExtraMDDomains);
255 
256     if( bVerbose )
257     {
258         const int nGeomFieldCount =
259             bGeomType ? poLayer->GetLayerDefn()->GetGeomFieldCount() : 0;
260         if( nGeomFieldCount > 1 )
261         {
262             for(int iGeom = 0;iGeom < nGeomFieldCount; iGeom ++ )
263             {
264                 OGRGeomFieldDefn* poGFldDefn =
265                     poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
266                 printf("Geometry (%s): %s\n", poGFldDefn->GetNameRef(),
267                        OGRGeometryTypeToName(poGFldDefn->GetType()));
268             }
269         }
270         else if( bGeomType )
271         {
272             printf("Geometry: %s\n",
273                    OGRGeometryTypeToName(poLayer->GetGeomType()));
274         }
275 
276         if( bFeatureCount )
277             printf("Feature Count: " CPL_FRMT_GIB "\n",
278                    poLayer->GetFeatureCount());
279 
280         OGREnvelope oExt;
281         if( bExtent && nGeomFieldCount > 1 )
282         {
283             for( int iGeom = 0;iGeom < nGeomFieldCount; iGeom ++ )
284             {
285                 if( poLayer->GetExtent(iGeom, &oExt, TRUE) == OGRERR_NONE )
286                 {
287                     OGRGeomFieldDefn* poGFldDefn =
288                         poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
289                     CPLprintf("Extent (%s): (%f, %f) - (%f, %f)\n",
290                               poGFldDefn->GetNameRef(),
291                               oExt.MinX, oExt.MinY, oExt.MaxX, oExt.MaxY);
292                 }
293             }
294         }
295         else if( bExtent && poLayer->GetExtent(&oExt, TRUE) == OGRERR_NONE )
296         {
297             CPLprintf("Extent: (%f, %f) - (%f, %f)\n",
298                       oExt.MinX, oExt.MinY, oExt.MaxX, oExt.MaxY);
299         }
300 
301         const auto displayDataAxisMapping = [](const OGRSpatialReference* poSRS) {
302             const auto mapping = poSRS->GetDataAxisToSRSAxisMapping();
303             printf("Data axis to CRS axis mapping: ");
304             for( size_t i = 0; i < mapping.size(); i++ )
305             {
306                 if( i > 0 )
307                 {
308                     printf(",");
309                 }
310                 printf("%d", mapping[i]);
311             }
312             printf("\n");
313         };
314 
315         if( nGeomFieldCount > 1 )
316         {
317             for(int iGeom = 0;iGeom < nGeomFieldCount; iGeom ++ )
318             {
319                 OGRGeomFieldDefn* poGFldDefn =
320                     poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
321                 OGRSpatialReference* poSRS = poGFldDefn->GetSpatialRef();
322                 char *pszWKT = nullptr;
323                 if( poSRS == nullptr )
324                 {
325                     pszWKT = CPLStrdup("(unknown)");
326                 }
327                 else
328                 {
329                     CPLString osWKTFormat("FORMAT=");
330                     osWKTFormat += pszWKTFormat;
331                     const char* const apszWKTOptions[] =
332                         { osWKTFormat.c_str(), "MULTILINE=YES", nullptr };
333                     poSRS->exportToWkt(&pszWKT, apszWKTOptions);
334                 }
335 
336                 printf("SRS WKT (%s):\n%s\n",
337                        poGFldDefn->GetNameRef(), pszWKT);
338                 CPLFree(pszWKT);
339                 if( poSRS )
340                 {
341                     displayDataAxisMapping(poSRS);
342                 }
343             }
344         }
345         else
346         {
347             char *pszWKT = nullptr;
348             auto poSRS = poLayer->GetSpatialRef();
349             if( poSRS == nullptr )
350             {
351                 pszWKT = CPLStrdup("(unknown)");
352             }
353             else
354             {
355                 CPLString osWKTFormat("FORMAT=");
356                 osWKTFormat += pszWKTFormat;
357                 const char* const apszWKTOptions[] =
358                     { osWKTFormat.c_str(), "MULTILINE=YES", nullptr };
359                 poSRS->exportToWkt(&pszWKT, apszWKTOptions);
360             }
361 
362             printf("Layer SRS WKT:\n%s\n", pszWKT);
363             CPLFree(pszWKT);
364             if( poSRS )
365             {
366                 displayDataAxisMapping(poSRS);
367             }
368         }
369 
370         if( strlen(poLayer->GetFIDColumn()) > 0 )
371             printf("FID Column = %s\n",
372                    poLayer->GetFIDColumn());
373 
374         for(int iGeom = 0;iGeom < nGeomFieldCount; iGeom ++ )
375         {
376             OGRGeomFieldDefn* poGFldDefn =
377                 poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
378             if( nGeomFieldCount == 1 &&
379                 EQUAL(poGFldDefn->GetNameRef(), "") &&
380                 poGFldDefn->IsNullable() )
381                 break;
382             printf("Geometry Column ");
383             if( nGeomFieldCount > 1 )
384                 printf("%d ", iGeom + 1);
385             if( !poGFldDefn->IsNullable() )
386                 printf("NOT NULL ");
387             printf("= %s\n", poGFldDefn->GetNameRef());
388         }
389 
390         for( int iAttr = 0; iAttr < poDefn->GetFieldCount(); iAttr++ )
391         {
392             OGRFieldDefn *poField = poDefn->GetFieldDefn(iAttr);
393             const char* pszType = (poField->GetSubType() != OFSTNone)
394                 ? CPLSPrintf(
395                       "%s(%s)",
396                       OGRFieldDefn::GetFieldTypeName(poField->GetType()),
397                       OGRFieldDefn::GetFieldSubTypeName(poField->GetSubType()))
398                 : OGRFieldDefn::GetFieldTypeName(poField->GetType());
399             printf("%s: %s (%d.%d)",
400                    poField->GetNameRef(),
401                    pszType,
402                    poField->GetWidth(),
403                    poField->GetPrecision());
404             if( poField->IsUnique() )
405                 printf(" UNIQUE");
406             if( !poField->IsNullable() )
407                 printf(" NOT NULL");
408             if( poField->GetDefault() != nullptr )
409                 printf(" DEFAULT %s", poField->GetDefault());
410             const char* pszAlias = poField->GetAlternativeNameRef();
411             if( pszAlias != nullptr && pszAlias[0])
412                 printf(", alternative name=\"%s\"", pszAlias);
413             const std::string& osDomain = poField->GetDomainName();
414             if( !osDomain.empty() )
415                 printf(", domain name=%s", osDomain.c_str());
416             printf("\n");
417         }
418     }
419 
420 /* -------------------------------------------------------------------- */
421 /*      Read, and dump features.                                        */
422 /* -------------------------------------------------------------------- */
423 
424     if( nFetchFID == OGRNullFID && !bSummaryOnly )
425     {
426         OGRFeature *poFeature = nullptr;
427         while( (poFeature = poLayer->GetNextFeature()) != nullptr )
428         {
429             if( !bSuperQuiet )
430                 poFeature->DumpReadable(nullptr, papszOptions);
431             OGRFeature::DestroyFeature(poFeature);
432         }
433     }
434     else if( nFetchFID != OGRNullFID )
435     {
436         OGRFeature *poFeature = poLayer->GetFeature(nFetchFID);
437         if( poFeature == nullptr )
438         {
439             printf("Unable to locate feature id " CPL_FRMT_GIB
440                    " on this layer.\n",
441                    nFetchFID);
442         }
443         else
444         {
445             poFeature->DumpReadable(nullptr, papszOptions);
446             OGRFeature::DestroyFeature(poFeature);
447         }
448     }
449 }
450 
451 /************************************************************************/
452 /*                        ReportFieldDomain()                           */
453 /************************************************************************/
454 
ReportFieldDomain(const OGRFieldDomain * poDomain)455 static void ReportFieldDomain(const OGRFieldDomain* poDomain)
456 {
457     printf("Domain %s:\n", poDomain->GetName().c_str());
458     const std::string& osDesc = poDomain->GetDescription();
459     if( !osDesc.empty() )
460     {
461         printf("  Description: %s\n", osDesc.c_str());
462     }
463     switch( poDomain->GetDomainType() )
464     {
465         case OFDT_CODED: printf("  Type: coded\n"); break;
466         case OFDT_RANGE: printf("  Type: range\n"); break;
467         case OFDT_GLOB:  printf("  Type: glob\n"); break;
468     }
469     const char* pszFieldType = (poDomain->GetFieldSubType() != OFSTNone)
470         ? CPLSPrintf(
471               "%s(%s)",
472               OGRFieldDefn::GetFieldTypeName(poDomain->GetFieldType()),
473               OGRFieldDefn::GetFieldSubTypeName(poDomain->GetFieldSubType()))
474         : OGRFieldDefn::GetFieldTypeName(poDomain->GetFieldType());
475     printf("  Field type: %s\n", pszFieldType);
476     switch( poDomain->GetSplitPolicy() )
477     {
478         case OFDSP_DEFAULT_VALUE:  printf("  Split policy: default value\n"); break;
479         case OFDSP_DUPLICATE:      printf("  Split policy: duplicate\n"); break;
480         case OFDSP_GEOMETRY_RATIO: printf("  Split policy: geometry ratio\n"); break;
481     }
482     switch( poDomain->GetMergePolicy() )
483     {
484         case OFDMP_DEFAULT_VALUE:     printf("  Merge policy: default value\n"); break;
485         case OFDMP_SUM:               printf("  Merge policy: sum\n"); break;
486         case OFDMP_GEOMETRY_WEIGHTED: printf("  Merge policy: geometry weighted\n"); break;
487     }
488     switch( poDomain->GetDomainType() )
489     {
490         case OFDT_CODED:
491         {
492             const auto poCodedFieldDomain =
493                 cpl::down_cast<const OGRCodedFieldDomain*>(poDomain);
494             const OGRCodedValue* enumeration = poCodedFieldDomain->GetEnumeration();
495             printf("  Coded values:\n");
496             for( int i = 0; enumeration[i].pszCode != nullptr; ++i )
497             {
498                 if( enumeration[i].pszValue )
499                 {
500                     printf("    %s: %s\n",
501                            enumeration[i].pszCode,
502                            enumeration[i].pszValue);
503                 }
504                 else
505                 {
506                     printf("    %s\n", enumeration[i].pszCode);
507                 }
508             }
509             break;
510         }
511 
512         case OFDT_RANGE:
513         {
514             const auto poRangeFieldDomain =
515                 cpl::down_cast<const OGRRangeFieldDomain*>(poDomain);
516             bool bMinIsIncluded = false;
517             const OGRField& sMin = poRangeFieldDomain->GetMin(bMinIsIncluded);
518             bool bMaxIsIncluded = false;
519             const OGRField& sMax = poRangeFieldDomain->GetMax(bMaxIsIncluded);
520             if( poDomain->GetFieldType() == OFTInteger )
521             {
522                 if( !OGR_RawField_IsUnset(&sMin) )
523                 {
524                     printf("  Minimum value: %d%s\n",
525                            sMin.Integer,
526                            bMinIsIncluded ? "" : " (excluded)");
527                 }
528                 if( !OGR_RawField_IsUnset(&sMax) )
529                 {
530                     printf("  Maximum value: %d%s\n",
531                            sMax.Integer,
532                            bMaxIsIncluded ? "" : " (excluded)");
533                 }
534             }
535             else if( poDomain->GetFieldType() == OFTInteger64 )
536             {
537                 if( !OGR_RawField_IsUnset(&sMin) )
538                 {
539                     printf("  Minimum value: " CPL_FRMT_GIB "%s\n",
540                            sMin.Integer64,
541                            bMinIsIncluded ? "" : " (excluded)");
542                 }
543                 if( !OGR_RawField_IsUnset(&sMax) )
544                 {
545                     printf("  Maximum value: " CPL_FRMT_GIB "%s\n",
546                            sMax.Integer64,
547                            bMaxIsIncluded ? "" : " (excluded)");
548                 }
549             }
550             else if( poDomain->GetFieldType() == OFTReal )
551             {
552                 if( !OGR_RawField_IsUnset(&sMin) )
553                 {
554                     printf("  Minimum value: %g%s\n",
555                            sMin.Real,
556                            bMinIsIncluded ? "" : " (excluded)");
557                 }
558                 if( !OGR_RawField_IsUnset(&sMax) )
559                 {
560                     printf("  Maximum value: %g%s\n",
561                            sMax.Real,
562                            bMaxIsIncluded ? "" : " (excluded)");
563                 }
564             }
565             break;
566         }
567 
568         case OFDT_GLOB:
569         {
570             const auto poGlobFieldDomain =
571                 cpl::down_cast<const OGRGlobFieldDomain*>(poDomain);
572             printf("  Glob: %s\n", poGlobFieldDomain->GetGlob().c_str());
573             break;
574         }
575     }
576 }
577 
578 /************************************************************************/
579 /*                             RemoveBOM()                              */
580 /************************************************************************/
581 
582 /* Remove potential UTF-8 BOM from data (must be NUL terminated) */
RemoveBOM(GByte * pabyData)583 static void RemoveBOM(GByte* pabyData)
584 {
585     if( pabyData[0] == 0xEF && pabyData[1] == 0xBB && pabyData[2] == 0xBF )
586     {
587         memmove(pabyData, pabyData + 3,
588                 strlen(reinterpret_cast<char *>(pabyData) + 3) + 1);
589     }
590 }
591 
RemoveSQLComments(char * & pszSQL)592 static void RemoveSQLComments(char*& pszSQL)
593 {
594     char** papszLines = CSLTokenizeStringComplex(pszSQL, "\r\n", FALSE, FALSE);
595     CPLString osSQL;
596     for( char** papszIter = papszLines; papszIter && *papszIter; ++papszIter )
597     {
598         const char* pszLine = *papszIter;
599         char chQuote = 0;
600         int i = 0;
601         for(; pszLine[i] != '\0'; ++i )
602         {
603             if( chQuote )
604             {
605                 if( pszLine[i] == chQuote )
606                 {
607                     if( pszLine[i+1] == chQuote )
608                     {
609                         i++;
610                     }
611                     else
612                     {
613                         chQuote = 0;
614                     }
615                 }
616             }
617             else if( pszLine[i] == '\'' || pszLine[i] == '"' )
618             {
619                 chQuote = pszLine[i];
620             }
621             else if( pszLine[i] == '-' && pszLine[i+1] == '-' )
622             {
623                 break;
624             }
625         }
626         if( i > 0 )
627         {
628             osSQL.append(pszLine, i);
629         }
630         osSQL += ' ';
631     }
632     CSLDestroy(papszLines);
633     CPLFree(pszSQL);
634     pszSQL = CPLStrdup(osSQL);
635 }
636 
637 /************************************************************************/
638 /*                                main()                                */
639 /************************************************************************/
640 
641 #define CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(nExtraArg) \
642     do { if (iArg + nExtraArg >= nArgc) \
643         Usage(CPLSPrintf("%s option requires %d argument(s)", \
644                          papszArgv[iArg], nExtraArg)); } while( false )
645 
MAIN_START(nArgc,papszArgv)646 MAIN_START(nArgc, papszArgv)
647 {
648     // Check strict compilation and runtime library version as we use C++ API.
649     if( !GDAL_CHECK_VERSION(papszArgv[0]) )
650         exit(1);
651 
652     EarlySetConfigOptions(nArgc, papszArgv);
653 
654     OGRRegisterAll();
655 
656 /* -------------------------------------------------------------------- */
657 /*      Processing command line arguments.                              */
658 /* -------------------------------------------------------------------- */
659     nArgc = OGRGeneralCmdLineProcessor(nArgc, &papszArgv, 0);
660 
661     if( nArgc < 1 )
662         exit(-nArgc);
663 
664     char *pszWHERE = nullptr;
665     const char *pszDataSource = nullptr;
666     char **papszLayers = nullptr;
667     OGRGeometry *poSpatialFilter = nullptr;
668     int nRepeatCount = 1;
669     bool bAllLayers = false;
670     char *pszSQLStatement = nullptr;
671     const char *pszDialect = nullptr;
672     int nRet = 0;
673     const char* pszGeomField = nullptr;
674     char **papszOpenOptions = nullptr;
675     char **papszExtraMDDomains = nullptr;
676     bool bListMDD = false;
677     bool bShowMetadata = true;
678     bool bFeatureCount = true;
679     bool bExtent = true;
680     bool bGeomType = true;
681     bool bDatasetGetNextFeature = false;
682     bool bReadOnly = false;
683     bool bUpdate = false;
684     const char* pszWKTFormat = "WKT2";
685     std::string osFieldDomain;
686 
687     for( int iArg = 1; iArg < nArgc; iArg++ )
688     {
689         if( EQUAL(papszArgv[iArg], "--utility_version") )
690         {
691             printf("%s was compiled against GDAL %s and "
692                    "is running against GDAL %s\n",
693                    papszArgv[0], GDAL_RELEASE_NAME,
694                    GDALVersionInfo("RELEASE_NAME"));
695             CSLDestroy(papszArgv);
696             return 0;
697         }
698         else if( EQUAL(papszArgv[iArg], "--help") )
699         {
700             Usage();
701         }
702         else if( EQUAL(papszArgv[iArg], "-ro") )
703         {
704             bReadOnly = true;
705         }
706         else if( EQUAL(papszArgv[iArg], "-update") )
707         {
708             bUpdate = true;
709         }
710         else if( EQUAL(papszArgv[iArg], "-q") ||
711                  EQUAL(papszArgv[iArg], "-quiet"))
712         {
713             bVerbose = false;
714         }
715         else if( EQUAL(papszArgv[iArg], "-qq") )
716         {
717             /* Undocumented: mainly only useful for AFL testing */
718             bVerbose = false;
719             bSuperQuiet = true;
720         }
721         else if( EQUAL(papszArgv[iArg], "-fid") )
722         {
723             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
724             nFetchFID = CPLAtoGIntBig(papszArgv[++iArg]);
725         }
726         else if( EQUAL(papszArgv[iArg], "-spat") )
727         {
728             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(4);
729 
730             OGRLinearRing oRing;
731             oRing.addPoint(CPLAtof(papszArgv[iArg+1]),
732                            CPLAtof(papszArgv[iArg+2]));
733             oRing.addPoint(CPLAtof(papszArgv[iArg+1]),
734                            CPLAtof(papszArgv[iArg+4]));
735             oRing.addPoint(CPLAtof(papszArgv[iArg+3]),
736                            CPLAtof(papszArgv[iArg+4]));
737             oRing.addPoint(CPLAtof(papszArgv[iArg+3]),
738                            CPLAtof(papszArgv[iArg+2]));
739             oRing.addPoint(CPLAtof(papszArgv[iArg+1]),
740                            CPLAtof(papszArgv[iArg+2]));
741 
742             poSpatialFilter = new OGRPolygon();
743             static_cast<OGRPolygon *>(poSpatialFilter)->addRing(&oRing);
744             iArg += 4;
745         }
746         else if( EQUAL(papszArgv[iArg], "-geomfield") )
747         {
748             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
749             pszGeomField = papszArgv[++iArg];
750         }
751         else if( EQUAL(papszArgv[iArg], "-where") )
752         {
753             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
754             iArg++;
755             CPLFree(pszWHERE);
756             GByte* pabyRet = nullptr;
757             if( papszArgv[iArg][0] == '@' &&
758                 VSIIngestFile(nullptr, papszArgv[iArg] + 1, &pabyRet,
759                               nullptr, 1024*1024) )
760             {
761                 RemoveBOM(pabyRet);
762                 pszWHERE = reinterpret_cast<char *>(pabyRet);
763             }
764             else
765             {
766                 pszWHERE = CPLStrdup(papszArgv[iArg]);
767             }
768         }
769         else if( EQUAL(papszArgv[iArg], "-sql") )
770         {
771             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
772             iArg++;
773             CPLFree(pszSQLStatement);
774             GByte* pabyRet = nullptr;
775             if( papszArgv[iArg][0] == '@' &&
776                 VSIIngestFile(nullptr, papszArgv[iArg] + 1, &pabyRet,
777                               nullptr, 1024*1024) )
778             {
779                 RemoveBOM(pabyRet);
780                 pszSQLStatement = reinterpret_cast<char *>(pabyRet);
781                 RemoveSQLComments(pszSQLStatement);
782             }
783             else
784             {
785                 pszSQLStatement = CPLStrdup(papszArgv[iArg]);
786             }
787         }
788         else if( EQUAL(papszArgv[iArg], "-dialect") )
789         {
790             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
791             pszDialect = papszArgv[++iArg];
792         }
793         else if( EQUAL(papszArgv[iArg], "-rc") )
794         {
795             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
796             nRepeatCount = atoi(papszArgv[++iArg]);
797         }
798         else if( EQUAL(papszArgv[iArg], "-al") )
799         {
800             bAllLayers = true;
801         }
802         else if( EQUAL(papszArgv[iArg], "-so") ||
803                  EQUAL(papszArgv[iArg], "-summary")  )
804         {
805             bSummaryOnly = true;
806         }
807         else if( STARTS_WITH_CI(papszArgv[iArg], "-fields=") )
808         {
809             char* pszTemp =
810                 static_cast<char *>(CPLMalloc(32 + strlen(papszArgv[iArg])));
811             snprintf(pszTemp,
812                     32 + strlen(papszArgv[iArg]),
813                     "DISPLAY_FIELDS=%s", papszArgv[iArg] + strlen("-fields="));
814             papszOptions = CSLAddString(papszOptions, pszTemp);
815             CPLFree(pszTemp);
816         }
817         else if( STARTS_WITH_CI(papszArgv[iArg], "-geom=") )
818         {
819             char* pszTemp =
820                 static_cast<char *>(CPLMalloc(32 + strlen(papszArgv[iArg])));
821             snprintf(pszTemp,
822                     32 + strlen(papszArgv[iArg]),
823                     "DISPLAY_GEOMETRY=%s", papszArgv[iArg] + strlen("-geom="));
824             papszOptions = CSLAddString(papszOptions, pszTemp);
825             CPLFree(pszTemp);
826         }
827         else if( EQUAL(papszArgv[iArg], "-oo") )
828         {
829             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
830             papszOpenOptions = CSLAddString(papszOpenOptions,
831                                             papszArgv[++iArg]);
832         }
833         else if( EQUAL(papszArgv[iArg], "-nomd") )
834         {
835             bShowMetadata = false;
836         }
837         else if( EQUAL(papszArgv[iArg], "-listmdd") )
838         {
839             bListMDD = true;
840         }
841         else if( EQUAL(papszArgv[iArg], "-mdd") )
842         {
843             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
844             papszExtraMDDomains = CSLAddString(papszExtraMDDomains,
845                                                papszArgv[++iArg]);
846         }
847         else if( EQUAL(papszArgv[iArg], "-nocount") )
848         {
849             bFeatureCount = false;
850         }
851         else if( EQUAL(papszArgv[iArg], "-noextent") )
852         {
853             bExtent = false;
854         }
855         else if( EQUAL(papszArgv[iArg], "-nogeomtype") )
856         {
857             bGeomType = false;
858         }
859         else if( EQUAL(papszArgv[iArg], "-rl"))
860         {
861             bDatasetGetNextFeature = true;
862         }
863         else if( EQUAL(papszArgv[iArg], "-wkt_format") )
864         {
865             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
866             pszWKTFormat = papszArgv[++iArg];
867         }
868         else if( EQUAL(papszArgv[iArg], "-fielddomain") )
869         {
870             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
871             osFieldDomain = papszArgv[++iArg];
872         }
873 
874         else if( papszArgv[iArg][0] == '-' )
875         {
876             Usage(CPLSPrintf("Unknown option name '%s'", papszArgv[iArg]));
877         }
878         else if( pszDataSource == nullptr )
879         {
880             pszDataSource = papszArgv[iArg];
881         }
882         else
883         {
884             papszLayers = CSLAddString(papszLayers, papszArgv[iArg]);
885             bAllLayers = false;
886         }
887     }
888 
889     if( pszDataSource == nullptr )
890         Usage("No datasource specified.");
891 
892     if( pszDialect != nullptr && pszWHERE != nullptr &&
893         pszSQLStatement == nullptr )
894         printf("Warning: -dialect is ignored with -where. Use -sql instead");
895 
896     if( bDatasetGetNextFeature && pszSQLStatement )
897     {
898         Usage("-rl is incompatible with -sql");
899     }
900 
901 #ifdef __AFL_HAVE_MANUAL_CONTROL
902     while (__AFL_LOOP(1000)) {
903 #endif
904 /* -------------------------------------------------------------------- */
905 /*      Open data source.                                               */
906 /* -------------------------------------------------------------------- */
907     GDALDataset *poDS = static_cast<GDALDataset *>(GDALOpenEx(
908         pszDataSource,
909         ((bReadOnly || pszSQLStatement == nullptr) &&
910          !bUpdate ? GDAL_OF_READONLY : GDAL_OF_UPDATE) | GDAL_OF_VECTOR,
911         nullptr, papszOpenOptions, nullptr));
912     if( poDS == nullptr && !bReadOnly && !bUpdate &&
913         pszSQLStatement == nullptr )
914     {
915         // In some cases (empty geopackage for example), opening in read-only
916         // mode fails, so retry in update mode
917         if( GDALIdentifyDriverEx(pszDataSource, GDAL_OF_VECTOR,
918                                  nullptr, nullptr) )
919         {
920             poDS = static_cast<GDALDataset *>(GDALOpenEx(
921                 pszDataSource,
922                 GDAL_OF_UPDATE | GDAL_OF_VECTOR, nullptr,
923                 papszOpenOptions, nullptr));
924         }
925     }
926     if( poDS == nullptr && !bReadOnly && !bUpdate &&
927         pszSQLStatement != nullptr )
928     {
929         poDS = static_cast<GDALDataset *>(GDALOpenEx(
930             pszDataSource,
931             GDAL_OF_READONLY | GDAL_OF_VECTOR, nullptr,
932             papszOpenOptions, nullptr));
933         if( poDS != nullptr && bVerbose )
934         {
935             printf("Had to open data source read-only.\n");
936 #ifdef __AFL_HAVE_MANUAL_CONTROL
937             bReadOnly = true;
938 #endif
939         }
940     }
941 
942     GDALDriver *poDriver = nullptr;
943     if( poDS != nullptr )
944         poDriver = poDS->GetDriver();
945 
946 /* -------------------------------------------------------------------- */
947 /*      Report failure                                                  */
948 /* -------------------------------------------------------------------- */
949     if( poDS == nullptr )
950     {
951         printf("FAILURE:\n"
952                "Unable to open datasource `%s' with the following drivers.\n",
953                pszDataSource);
954 #ifdef __AFL_HAVE_MANUAL_CONTROL
955         continue;
956 #else
957         OGRSFDriverRegistrar *poR = OGRSFDriverRegistrar::GetRegistrar();
958         for( int iDriver = 0; iDriver < poR->GetDriverCount(); iDriver++ )
959         {
960             printf("  -> %s\n", poR->GetDriver(iDriver)->GetDescription());
961         }
962 
963         nRet = 1;
964         goto end;
965 #endif
966     }
967 
968     CPLAssert(poDriver != nullptr);
969 
970 /* -------------------------------------------------------------------- */
971 /*      Some information messages.                                      */
972 /* -------------------------------------------------------------------- */
973     if( bVerbose )
974         printf("INFO: Open of `%s'\n"
975                "      using driver `%s' successful.\n",
976                pszDataSource, poDriver->GetDescription());
977 
978     if( bVerbose && !EQUAL(pszDataSource,poDS->GetDescription()) )
979     {
980         printf("INFO: Internal data source name `%s'\n"
981                "      different from user name `%s'.\n",
982                poDS->GetDescription(), pszDataSource);
983     }
984 
985     GDALInfoReportMetadata(static_cast<GDALMajorObjectH>(poDS),
986                            bListMDD,
987                            bShowMetadata,
988                            papszExtraMDDomains);
989 
990     if( !osFieldDomain.empty() )
991     {
992         auto poDomain = poDS->GetFieldDomain(osFieldDomain);
993         if( poDomain == nullptr )
994         {
995             printf("Domain %s cannot be found.\n", osFieldDomain.c_str());
996             exit(1);
997         }
998         printf("\n");
999         ReportFieldDomain(poDomain);
1000         printf("\n");
1001     }
1002 
1003     if( bDatasetGetNextFeature )
1004     {
1005         nRepeatCount = 0;  // skip layer reporting.
1006 
1007 /* -------------------------------------------------------------------- */
1008 /*      Set filters if provided.                                        */
1009 /* -------------------------------------------------------------------- */
1010         if( pszWHERE != nullptr || poSpatialFilter != nullptr )
1011         {
1012             for( int iLayer = 0; iLayer < poDS->GetLayerCount(); iLayer++ )
1013             {
1014                 OGRLayer *poLayer = poDS->GetLayer(iLayer);
1015 
1016                 if( poLayer == nullptr )
1017                 {
1018                     printf("FAILURE: Couldn't fetch advertised layer %d!\n",
1019                            iLayer);
1020                     exit(1);
1021                 }
1022 
1023                 if( pszWHERE != nullptr )
1024                 {
1025                     if( poLayer->SetAttributeFilter(pszWHERE) != OGRERR_NONE )
1026                     {
1027                         printf("WARNING: SetAttributeFilter(%s) "
1028                                "failed on layer %s.\n",
1029                                pszWHERE, poLayer->GetName());
1030                     }
1031                 }
1032 
1033                 if( poSpatialFilter != nullptr )
1034                 {
1035                     if( pszGeomField != nullptr )
1036                     {
1037                         OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
1038                         const int iGeomField =
1039                             poDefn->GetGeomFieldIndex(pszGeomField);
1040                         if( iGeomField >= 0 )
1041                             poLayer->SetSpatialFilter(iGeomField,
1042                                                       poSpatialFilter);
1043                         else
1044                             printf("WARNING: Cannot find geometry field %s.\n",
1045                                    pszGeomField);
1046                     }
1047                     else
1048                     {
1049                         poLayer->SetSpatialFilter(poSpatialFilter);
1050                     }
1051                 }
1052             }
1053         }
1054 
1055         std::set<OGRLayer*> oSetLayers;
1056         while( true )
1057         {
1058             OGRLayer* poLayer = nullptr;
1059             OGRFeature* poFeature = poDS->GetNextFeature(&poLayer, nullptr,
1060                                                          nullptr, nullptr);
1061             if( poFeature == nullptr )
1062                 break;
1063             if( papszLayers == nullptr || poLayer == nullptr ||
1064                 CSLFindString(papszLayers, poLayer->GetName()) >= 0 )
1065             {
1066                 if( bVerbose && poLayer != nullptr &&
1067                     oSetLayers.find(poLayer) == oSetLayers.end() )
1068                 {
1069                     oSetLayers.insert(poLayer);
1070                     const bool bSummaryOnlyBackup = bSummaryOnly;
1071                     bSummaryOnly = true;
1072                     ReportOnLayer(poLayer, nullptr, nullptr, nullptr,
1073                                   bListMDD, bShowMetadata,
1074                                   papszExtraMDDomains,
1075                                   bFeatureCount,
1076                                   bExtent,
1077                                   bGeomType,
1078                                   pszWKTFormat);
1079                     bSummaryOnly = bSummaryOnlyBackup;
1080                 }
1081                 if( !bSuperQuiet && !bSummaryOnly )
1082                     poFeature->DumpReadable(nullptr, papszOptions);
1083             }
1084             OGRFeature::DestroyFeature(poFeature);
1085         }
1086     }
1087 
1088 /* -------------------------------------------------------------------- */
1089 /*      Special case for -sql clause.  No source layers required.       */
1090 /* -------------------------------------------------------------------- */
1091     else if( pszSQLStatement != nullptr )
1092     {
1093         nRepeatCount = 0;  // skip layer reporting.
1094 
1095         if( CSLCount(papszLayers) > 0 )
1096             printf("layer names ignored in combination with -sql.\n");
1097 
1098         OGRLayer *poResultSet =
1099             poDS->ExecuteSQL(
1100                 pszSQLStatement,
1101                 pszGeomField == nullptr ? poSpatialFilter : nullptr,
1102                 pszDialect);
1103 
1104         if( poResultSet != nullptr )
1105         {
1106             if( pszWHERE != nullptr )
1107             {
1108                 if( poResultSet->SetAttributeFilter(pszWHERE) != OGRERR_NONE )
1109                 {
1110                     printf("FAILURE: SetAttributeFilter(%s) failed.\n",
1111                            pszWHERE);
1112                     exit(1);
1113                 }
1114             }
1115 
1116             if( pszGeomField != nullptr )
1117                 ReportOnLayer(poResultSet, nullptr,
1118                               pszGeomField, poSpatialFilter,
1119                               bListMDD, bShowMetadata, papszExtraMDDomains,
1120                               bFeatureCount, bExtent, bGeomType, pszWKTFormat);
1121             else
1122                 ReportOnLayer(poResultSet, nullptr, nullptr, nullptr,
1123                               bListMDD, bShowMetadata, papszExtraMDDomains,
1124                               bFeatureCount, bExtent, bGeomType, pszWKTFormat);
1125             poDS->ReleaseResultSet(poResultSet);
1126         }
1127     }
1128 
1129     // coverity[tainted_data]
1130     for( int iRepeat = 0; iRepeat < nRepeatCount; iRepeat++ )
1131     {
1132         if( papszLayers == nullptr || *papszLayers == nullptr )
1133         {
1134             if( iRepeat == 0 )
1135                 CPLDebug("OGR", "GetLayerCount() = %d\n",
1136                          poDS->GetLayerCount());
1137 
1138 /* -------------------------------------------------------------------- */
1139 /*      Process each data source layer.                                 */
1140 /* -------------------------------------------------------------------- */
1141             for( int iLayer = 0; iLayer < poDS->GetLayerCount(); iLayer++ )
1142             {
1143                 OGRLayer *poLayer = poDS->GetLayer(iLayer);
1144 
1145                 if( poLayer == nullptr )
1146                 {
1147                     printf("FAILURE: Couldn't fetch advertised layer %d!\n",
1148                            iLayer);
1149                     exit(1);
1150                 }
1151 
1152                 if( !bAllLayers )
1153                 {
1154                     printf("%d: %s", iLayer + 1, poLayer->GetName());
1155 
1156                     const char* pszTitle = poLayer->GetMetadataItem("TITLE");
1157                     if( pszTitle )
1158                     {
1159                         printf(" (title: %s)", pszTitle);
1160                     }
1161 
1162                     const int nGeomFieldCount =
1163                         bGeomType ? poLayer->GetLayerDefn()->GetGeomFieldCount() : 0;
1164                     if( nGeomFieldCount > 1 )
1165                     {
1166                         printf(" (");
1167                         for( int iGeom = 0; iGeom < nGeomFieldCount; iGeom++ )
1168                         {
1169                             if( iGeom > 0 )
1170                                 printf(", ");
1171                             OGRGeomFieldDefn* poGFldDefn =
1172                                 poLayer->GetLayerDefn()->
1173                                     GetGeomFieldDefn(iGeom);
1174                             printf(
1175                                 "%s",
1176                                 OGRGeometryTypeToName(
1177                                     poGFldDefn->GetType()));
1178                         }
1179                         printf(")");
1180                     }
1181                     else if( bGeomType && poLayer->GetGeomType() != wkbUnknown )
1182                         printf(" (%s)",
1183                                OGRGeometryTypeToName(
1184                                    poLayer->GetGeomType()));
1185 
1186                     printf("\n");
1187                 }
1188                 else
1189                 {
1190                     if( iRepeat != 0 )
1191                         poLayer->ResetReading();
1192 
1193                     ReportOnLayer(poLayer, pszWHERE,
1194                                   pszGeomField, poSpatialFilter,
1195                                   bListMDD, bShowMetadata, papszExtraMDDomains,
1196                                   bFeatureCount, bExtent, bGeomType, pszWKTFormat);
1197                 }
1198             }
1199         }
1200         else
1201         {
1202 /* -------------------------------------------------------------------- */
1203 /*      Process specified data source layers.                           */
1204 /* -------------------------------------------------------------------- */
1205 
1206             for( char** papszIter = papszLayers;
1207                  *papszIter != nullptr;
1208                  ++papszIter )
1209             {
1210                 OGRLayer *poLayer = poDS->GetLayerByName(*papszIter);
1211 
1212                 if( poLayer == nullptr )
1213                 {
1214                     printf("FAILURE: Couldn't fetch requested layer %s!\n",
1215                            *papszIter);
1216                     exit(1);
1217                 }
1218 
1219                 if( iRepeat != 0 )
1220                     poLayer->ResetReading();
1221 
1222                 ReportOnLayer(poLayer, pszWHERE, pszGeomField, poSpatialFilter,
1223                               bListMDD, bShowMetadata, papszExtraMDDomains,
1224                               bFeatureCount, bExtent, bGeomType, pszWKTFormat);
1225             }
1226         }
1227     }
1228 
1229 /* -------------------------------------------------------------------- */
1230 /*      Close down.                                                     */
1231 /* -------------------------------------------------------------------- */
1232     GDALClose(poDS);
1233 
1234 #ifdef __AFL_HAVE_MANUAL_CONTROL
1235     }
1236 #else
1237 end:
1238 #endif
1239 
1240     CSLDestroy(papszArgv);
1241     CSLDestroy(papszLayers);
1242     CSLDestroy(papszOptions);
1243     CSLDestroy(papszOpenOptions);
1244     CSLDestroy(papszExtraMDDomains);
1245     if( poSpatialFilter )
1246         OGRGeometryFactory::destroyGeometry(poSpatialFilter);
1247     CPLFree(pszSQLStatement);
1248     CPLFree(pszWHERE);
1249 
1250     GDALDestroy();
1251 
1252     return nRet;
1253 }
1254 MAIN_END
1255