diff --git a/apps/gdal_utils_priv.h b/apps/gdal_utils_priv.h index a91a44b15bf3..2a705cdbc2df 100644 --- a/apps/gdal_utils_priv.h +++ b/apps/gdal_utils_priv.h @@ -88,26 +88,6 @@ struct GDALWarpAppOptionsForBinary char **papszAllowInputDrivers; }; -/* Access modes */ -typedef enum -{ - ACCESS_CREATION, - ACCESS_UPDATE, /* open existing output datasource in update mode rather than - trying to create a new one */ - ACCESS_APPEND, /* append to existing layer instead of creating new */ - ACCESS_OVERWRITE /* delete the output layer and recreate it empty */ -} GDALVectorTranslateAccessMode; - -struct GDALVectorTranslateOptionsForBinary -{ - char *pszDataSource; - char *pszDestDataSource; - int bQuiet; - char **papszOpenOptions; - char *pszFormat; - GDALVectorTranslateAccessMode eAccessMode; -}; - struct GDALDEMProcessingOptionsForBinary { char *pszProcessing; @@ -135,6 +115,30 @@ struct GDALBuildVRTOptionsForBinary CPL_C_END +/* Access modes */ +typedef enum +{ + ACCESS_CREATION, + ACCESS_UPDATE, /* open existing output datasource in update mode rather than + trying to create a new one */ + ACCESS_APPEND, /* append to existing layer instead of creating new */ + ACCESS_OVERWRITE /* delete the output layer and recreate it empty */ +} GDALVectorTranslateAccessMode; + +struct GDALVectorTranslateOptionsForBinary +{ + std::string osDataSource{}; + bool bDestSpecified = false; + std::string osDestDataSource{}; + bool bQuiet = false; + CPLStringList aosOpenOptions{}; + std::string osFormat; + GDALVectorTranslateAccessMode eAccessMode = ACCESS_CREATION; + + /* Allowed input drivers. */ + CPLStringList aosAllowInputDrivers{}; +}; + struct GDALMultiDimInfoOptionsForBinary { /* Filename to open. */ diff --git a/apps/ogr2ogr_bin.cpp b/apps/ogr2ogr_bin.cpp index c92d2e876166..9558e220967f 100644 --- a/apps/ogr2ogr_bin.cpp +++ b/apps/ogr2ogr_bin.cpp @@ -81,7 +81,8 @@ static void Usage(bool bIsError, const char *pszAdditionalMsg = nullptr, " [-a_srs ] [-t_srs ] [-s_srs " "] " "[-ct ]\n" - " [-f ] [-overwrite] " + " [-if ] [-f ] " + "[-overwrite] " "[-dsco =]...\n" " [-lco =]... [-nln ] \n" " [-nlt " @@ -233,8 +234,7 @@ static void Usage(bool bIsError, const char *pszAdditionalMsg = nullptr, static GDALVectorTranslateOptionsForBinary * GDALVectorTranslateOptionsForBinaryNew() { - return static_cast( - CPLCalloc(1, sizeof(GDALVectorTranslateOptionsForBinary))); + return new GDALVectorTranslateOptionsForBinary; } /************************************************************************/ @@ -244,14 +244,7 @@ GDALVectorTranslateOptionsForBinaryNew() static void GDALVectorTranslateOptionsForBinaryFree( GDALVectorTranslateOptionsForBinary *psOptionsForBinary) { - if (psOptionsForBinary) - { - CPLFree(psOptionsForBinary->pszDataSource); - CPLFree(psOptionsForBinary->pszDestDataSource); - CSLDestroy(psOptionsForBinary->papszOpenOptions); - CPLFree(psOptionsForBinary->pszFormat); - CPLFree(psOptionsForBinary); - } + delete psOptionsForBinary; } /************************************************************************/ @@ -327,10 +320,10 @@ MAIN_START(nArgc, papszArgv) goto exit; } - if (psOptionsForBinary->pszDataSource == nullptr || - psOptionsForBinary->pszDestDataSource == nullptr) + if (psOptionsForBinary->osDataSource.empty() || + !psOptionsForBinary->bDestSpecified) { - if (psOptionsForBinary->pszDestDataSource == nullptr) + if (!psOptionsForBinary->bDestSpecified) Usage(true, "no target datasource provided"); else Usage(true, "no source datasource provided"); @@ -339,8 +332,8 @@ MAIN_START(nArgc, papszArgv) goto exit; } - if (strcmp(psOptionsForBinary->pszDestDataSource, "/vsistdout/") == 0) - psOptionsForBinary->bQuiet = TRUE; + if (psOptionsForBinary->osDestDataSource == "/vsistdout/") + psOptionsForBinary->bQuiet = true; /* -------------------------------------------------------------------- */ /* Open data source. */ @@ -350,12 +343,13 @@ MAIN_START(nArgc, papszArgv) // output Known to cause problems with at least FGdb, SQlite and GPKG // drivers. See #4270 if (psOptionsForBinary->eAccessMode != ACCESS_CREATION && - strcmp(psOptionsForBinary->pszDestDataSource, - psOptionsForBinary->pszDataSource) == 0) + psOptionsForBinary->osDestDataSource == + psOptionsForBinary->osDataSource) { - hODS = GDALOpenEx(psOptionsForBinary->pszDataSource, - GDAL_OF_UPDATE | GDAL_OF_VECTOR, nullptr, - psOptionsForBinary->papszOpenOptions, nullptr); + hODS = GDALOpenEx(psOptionsForBinary->osDataSource.c_str(), + GDAL_OF_UPDATE | GDAL_OF_VECTOR, + psOptionsForBinary->aosAllowInputDrivers.List(), + psOptionsForBinary->aosOpenOptions.List(), nullptr); GDALDriverH hDriver = hODS != nullptr ? GDALGetDatasetDriver(hODS) : nullptr; @@ -366,9 +360,10 @@ MAIN_START(nArgc, papszArgv) EQUAL(GDALGetDescription(hDriver), "SQLite") || EQUAL(GDALGetDescription(hDriver), "GPKG"))) { - hDS = GDALOpenEx(psOptionsForBinary->pszDataSource, GDAL_OF_VECTOR, - nullptr, psOptionsForBinary->papszOpenOptions, - nullptr); + hDS = GDALOpenEx( + psOptionsForBinary->osDataSource.c_str(), GDAL_OF_VECTOR, + psOptionsForBinary->aosAllowInputDrivers.List(), + psOptionsForBinary->aosOpenOptions.List(), nullptr); } else { @@ -379,8 +374,9 @@ MAIN_START(nArgc, papszArgv) else { hDS = - GDALOpenEx(psOptionsForBinary->pszDataSource, GDAL_OF_VECTOR, - nullptr, psOptionsForBinary->papszOpenOptions, nullptr); + GDALOpenEx(psOptionsForBinary->osDataSource.c_str(), GDAL_OF_VECTOR, + psOptionsForBinary->aosAllowInputDrivers.List(), + psOptionsForBinary->aosOpenOptions.List(), nullptr); } /* -------------------------------------------------------------------- */ @@ -393,7 +389,7 @@ MAIN_START(nArgc, papszArgv) fprintf(stderr, "FAILURE:\n" "Unable to open datasource `%s' with the following drivers.\n", - psOptionsForBinary->pszDataSource); + psOptionsForBinary->osDataSource.c_str()); for (int iDriver = 0; iDriver < poDM->GetDriverCount(); iDriver++) { @@ -411,16 +407,16 @@ MAIN_START(nArgc, papszArgv) goto exit; } - if (hODS != nullptr && psOptionsForBinary->pszFormat != nullptr) + if (hODS != nullptr && !psOptionsForBinary->osFormat.empty()) { GDALDriverManager *poDM = GetGDALDriverManager(); GDALDriver *poDriver = - poDM->GetDriverByName(psOptionsForBinary->pszFormat); + poDM->GetDriverByName(psOptionsForBinary->osFormat.c_str()); if (poDriver == nullptr) { fprintf(stderr, "Unable to find driver `%s'.\n", - psOptionsForBinary->pszFormat); + psOptionsForBinary->osFormat.c_str()); fprintf(stderr, "The following drivers are available:\n"); for (int iDriver = 0; iDriver < poDM->GetDriverCount(); iDriver++) @@ -452,8 +448,9 @@ MAIN_START(nArgc, papszArgv) { // TODO(schwehr): Remove scope after removing gotos int bUsageError = FALSE; - hDstDS = GDALVectorTranslate(psOptionsForBinary->pszDestDataSource, - hODS, 1, &hDS, psOptions, &bUsageError); + hDstDS = + GDALVectorTranslate(psOptionsForBinary->osDestDataSource.c_str(), + hODS, 1, &hDS, psOptions, &bUsageError); if (bUsageError) Usage(true); else diff --git a/apps/ogr2ogr_lib.cpp b/apps/ogr2ogr_lib.cpp index 6374220f7d90..4c45143373e0 100644 --- a/apps/ogr2ogr_lib.cpp +++ b/apps/ogr2ogr_lib.cpp @@ -6491,7 +6491,22 @@ GDALVectorTranslateOptions *GDALVectorTranslateOptionsNew( { psOptions->bQuiet = true; if (psOptionsForBinary) - psOptionsForBinary->bQuiet = TRUE; + psOptionsForBinary->bQuiet = true; + } + else if (EQUAL(papszArgv[i], "-if")) + { + CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1); + i++; + if (psOptionsForBinary) + { + if (GDALGetDriverByName(papszArgv[i]) == nullptr) + { + CPLError(CE_Warning, CPLE_AppDefined, + "%s is not a recognized driver", papszArgv[i]); + } + psOptionsForBinary->aosAllowInputDrivers.AddString( + papszArgv[i]); + } } else if (EQUAL(papszArgv[i], "-f") || EQUAL(papszArgv[i], "-of")) { @@ -6515,8 +6530,7 @@ GDALVectorTranslateOptions *GDALVectorTranslateOptionsNew( ++i; if (psOptionsForBinary) { - psOptionsForBinary->papszOpenOptions = CSLAddString( - psOptionsForBinary->papszOpenOptions, papszArgv[i]); + psOptionsForBinary->aosOpenOptions.AddString(papszArgv[i]); } } else if (EQUAL(papszArgv[i], "-doo")) @@ -7257,17 +7271,16 @@ GDALVectorTranslateOptions *GDALVectorTranslateOptionsNew( papszArgv[i]); return nullptr; } - else if (psOptionsForBinary && - psOptionsForBinary->pszDestDataSource == nullptr) + else if (psOptionsForBinary && !psOptionsForBinary->bDestSpecified) { iArgStart = -1; - psOptionsForBinary->pszDestDataSource = CPLStrdup(papszArgv[i]); + psOptionsForBinary->bDestSpecified = true; + psOptionsForBinary->osDestDataSource = papszArgv[i]; } - else if (psOptionsForBinary && - psOptionsForBinary->pszDataSource == nullptr) + else if (psOptionsForBinary && psOptionsForBinary->osDataSource.empty()) { iArgStart = -1; - psOptionsForBinary->pszDataSource = CPLStrdup(papszArgv[i]); + psOptionsForBinary->osDataSource = papszArgv[i]; } else { @@ -7287,26 +7300,23 @@ GDALVectorTranslateOptions *GDALVectorTranslateOptionsNew( if (psOptionsForBinary) { psOptionsForBinary->eAccessMode = psOptions->eAccessMode; - if (!psOptions->osFormat.empty()) - psOptionsForBinary->pszFormat = - CPLStrdup(psOptions->osFormat.c_str()); + psOptionsForBinary->osFormat = psOptions->osFormat; - if (!(CPLTestBool(CSLFetchNameValueDef( - psOptionsForBinary->papszOpenOptions, "NATIVE_DATA", - CSLFetchNameValueDef(psOptionsForBinary->papszOpenOptions, - "@NATIVE_DATA", "TRUE"))))) + if (!(CPLTestBool(psOptionsForBinary->aosOpenOptions.FetchNameValueDef( + "NATIVE_DATA", + psOptionsForBinary->aosOpenOptions.FetchNameValueDef( + "@NATIVE_DATA", "TRUE"))))) { psOptions->bNativeData = false; } if (psOptions->bNativeData && - CSLFetchNameValue(psOptionsForBinary->papszOpenOptions, - "NATIVE_DATA") == nullptr && - CSLFetchNameValue(psOptionsForBinary->papszOpenOptions, - "@NATIVE_DATA") == nullptr) + psOptionsForBinary->aosOpenOptions.FetchNameValue("NATIVE_DATA") == + nullptr && + psOptionsForBinary->aosOpenOptions.FetchNameValue("@NATIVE_DATA") == + nullptr) { - psOptionsForBinary->papszOpenOptions = CSLAddString( - psOptionsForBinary->papszOpenOptions, "@NATIVE_DATA=YES"); + psOptionsForBinary->aosOpenOptions.AddString("@NATIVE_DATA=YES"); } } diff --git a/apps/ogrinfo_bin.cpp b/apps/ogrinfo_bin.cpp index c39a18be48fd..214ab3fb0b38 100644 --- a/apps/ogrinfo_bin.cpp +++ b/apps/ogrinfo_bin.cpp @@ -44,7 +44,7 @@ static void Usage(bool bIsError, const char *pszErrorMsg = nullptr) { fprintf(bIsError ? stderr : stdout, "Usage: ogrinfo [--help] [--help-general]\n" - " [-json] [-ro] [-q] [-where " + " [-if ] [-json] [-ro] [-q] [-where " "|@f]\n" " [-spat ] [-geomfield " "] " @@ -138,8 +138,9 @@ MAIN_START(argc, argv) else if (psOptionsForBinary->osSQLStatement.empty()) { nFlags |= GDAL_OF_READONLY; - if (GDALIdentifyDriverEx(psOptionsForBinary->osFilename.c_str(), - GDAL_OF_VECTOR, nullptr, nullptr)) + if (GDALIdentifyDriverEx( + psOptionsForBinary->osFilename.c_str(), GDAL_OF_VECTOR, + psOptionsForBinary->aosAllowInputDrivers.List(), nullptr)) { bMayRetryUpdateMode = true; } @@ -152,7 +153,8 @@ MAIN_START(argc, argv) else nFlags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR; GDALDataset *poDS = GDALDataset::Open( - psOptionsForBinary->osFilename.c_str(), nFlags, nullptr, + psOptionsForBinary->osFilename.c_str(), nFlags, + psOptionsForBinary->aosAllowInputDrivers.List(), psOptionsForBinary->aosOpenOptions.List(), nullptr); if (poDS == nullptr && !psOptionsForBinary->bReadOnly && @@ -165,14 +167,16 @@ MAIN_START(argc, argv) // read-only mode fails, so retry in update mode poDS = GDALDataset::Open( psOptionsForBinary->osFilename.c_str(), - GDAL_OF_UPDATE | GDAL_OF_VECTOR, nullptr, + GDAL_OF_UPDATE | GDAL_OF_VECTOR, + psOptionsForBinary->aosAllowInputDrivers.List(), psOptionsForBinary->aosOpenOptions.List(), nullptr); } else if (!psOptionsForBinary->osSQLStatement.empty()) { poDS = GDALDataset::Open( psOptionsForBinary->osFilename.c_str(), - GDAL_OF_READONLY | GDAL_OF_VECTOR, nullptr, + GDAL_OF_READONLY | GDAL_OF_VECTOR, + psOptionsForBinary->aosAllowInputDrivers.List(), psOptionsForBinary->aosOpenOptions.List(), nullptr); if (poDS != nullptr && psOptionsForBinary->bVerbose) { diff --git a/autotest/utilities/test_ogr2ogr.py b/autotest/utilities/test_ogr2ogr.py index ccaef2140687..ba4c1f78578f 100755 --- a/autotest/utilities/test_ogr2ogr.py +++ b/autotest/utilities/test_ogr2ogr.py @@ -2241,3 +2241,31 @@ def test_ogr2ogr_check_identity_transformation(ogr2ogr_path, tmp_path, x, y, sri feat = ds.GetLayer(0).GetNextFeature() assert feat.GetGeometryRef().GetX() == x and feat.GetGeometryRef().GetY() == y + + +############################################################################### +# Test -if + + +@pytest.mark.require_driver("GPKG") +def test_ogr2ogr_if_ok(ogr2ogr_path): + + (ret, err) = gdaltest.runexternal_out_and_err( + ogr2ogr_path + " -if GPKG /vsimem/out.gpkg ../ogr/data/gpkg/2d_envelope.gpkg" + ) + assert ret == "" + assert err == "" + + +############################################################################### +# Test -if + + +@pytest.mark.require_driver("GPKG") +@pytest.mark.require_driver("GeoJSON") +def test_ogr2ogr_if_ko(ogr2ogr_path): + + (_, err) = gdaltest.runexternal_out_and_err( + ogr2ogr_path + " -if GeoJSON /vsimem/out.gpkg ../ogr/data/gpkg/2d_envelope.gpkg" + ) + assert "Unable to open datasource" in err diff --git a/autotest/utilities/test_ogrinfo.py b/autotest/utilities/test_ogrinfo.py index 7cdcd0676002..76415f679d02 100755 --- a/autotest/utilities/test_ogrinfo.py +++ b/autotest/utilities/test_ogrinfo.py @@ -722,3 +722,30 @@ def test_ogrinfo_empty_gpkg(ogrinfo_path, tmp_path): (ret, err) = gdaltest.runexternal_out_and_err(ogrinfo_path + f" {filename}") assert err is None or err == "", "got error/warning" + + +############################################################################### +# Test -if + + +@pytest.mark.require_driver("GPKG") +def test_ogrinfo_if_ok(ogrinfo_path): + + (ret, err) = gdaltest.runexternal_out_and_err( + ogrinfo_path + " -if GPKG ../ogr/data/gpkg/2d_envelope.gpkg" + ) + assert err is None or err == "", "got error/warning" + assert ret.find("GPKG") != -1 + + +############################################################################### +# Test -if + + +@pytest.mark.require_driver("GeoJSON") +def test_ogrinfo_if_ko(ogrinfo_path): + + (_, err) = gdaltest.runexternal_out_and_err( + ogrinfo_path + " -if GeoJSON ../ogr/data/gpkg/2d_envelope.gpkg" + ) + assert "not recognized as a supported file format" in err diff --git a/doc/source/programs/ogr2ogr.rst b/doc/source/programs/ogr2ogr.rst index c8cdcd7cfa92..0cc31653c721 100644 --- a/doc/source/programs/ogr2ogr.rst +++ b/doc/source/programs/ogr2ogr.rst @@ -22,7 +22,7 @@ Synopsis [-preserve_fid] [-fid ] [-limit ] [-spat ] [-spat_srs ] [-geomfield ] [-a_srs ] [-t_srs ] [-s_srs ] [-ct ] - [-f ] [-overwrite] [-dsco =]... + [-if ] [-f ] [-overwrite] [-dsco =]... [-lco =]... [-nln ] [-nlt |PROMOTE_TO_MULTI|CONVERT_TO_LINEAR|CONVERT_TO_CURVE] [-dim XY|XYZ|XYM|XYZM|] @@ -67,6 +67,8 @@ output coordinate system or even reprojecting the features during translation. .. include:: options/help_and_help_general.rst +.. include:: options/if.rst + .. option:: -f Output file format name, e.g. ``ESRI Shapefile``, ``MapInfo File``, diff --git a/doc/source/programs/ogrinfo.rst b/doc/source/programs/ogrinfo.rst index 7e0554553de9..221b3bb9a6a4 100644 --- a/doc/source/programs/ogrinfo.rst +++ b/doc/source/programs/ogrinfo.rst @@ -17,7 +17,7 @@ Synopsis .. code-block:: ogrinfo [--help] [--help-general] - [-json] [-ro] [-q] [-where |@f] + [-if ] [-json] [-ro] [-q] [-where |@f] [-spat ] [-geomfield ] [-fid ] [-sql |@] [-dialect ] [-al] [-rl] [-so|-features] [-fields={YES|NO}]] @@ -39,6 +39,8 @@ edit data. .. include:: options/help_and_help_general.rst +.. include:: options/if.rst + .. option:: -json Display the output in json format, conforming to the diff --git a/doc/source/programs/options/formats_raster.rst b/doc/source/programs/options/formats_raster.rst index f3d57fc7e05d..23e944453e6d 100644 --- a/doc/source/programs/options/formats_raster.rst +++ b/doc/source/programs/options/formats_raster.rst @@ -14,6 +14,8 @@ which determines the order in which they are successively probed when opening a dataset. Most of the time, this order does not matter, but in some situations, several drivers may recognize the same file. The ``-if`` option of some utilities - can be specified to ask to use a particular driver to open the dataset. + can be specified to restrict opening the dataset with a subset of drivers (generally one). + Note that it does not force those drivers to open the dataset. In particular, + some drivers have requirements on file extensions. Alternatively, the :config:`GDAL_SKIP` configuration option can also be used to exclude one or several drivers. diff --git a/doc/source/programs/options/formats_vector.rst b/doc/source/programs/options/formats_vector.rst index 86a2bfa722b8..26b79620fae0 100644 --- a/doc/source/programs/options/formats_vector.rst +++ b/doc/source/programs/options/formats_vector.rst @@ -13,6 +13,8 @@ which determines the order in which they are successively probed when opening a dataset. Most of the time, this order does not matter, but in some situations, several drivers may recognize the same file. The ``-if`` option of some utilities - can be specified to ask to use a particular driver to open the dataset. + can be specified to restrict opening the dataset with a subset of drivers (generally one). + Note that it does not force those drivers to open the dataset. In particular, + some drivers have requirements on file extensions. Alternatively, the :config:`GDAL_SKIP` configuration option can also be used to exclude one or several drivers. diff --git a/doc/source/programs/options/if.rst b/doc/source/programs/options/if.rst index 83b07eba435c..99bfd6c4b454 100644 --- a/doc/source/programs/options/if.rst +++ b/doc/source/programs/options/if.rst @@ -4,5 +4,7 @@ not necessary to specify it, but it can be used to skip automatic driver detection, when it fails to select the appropriate driver. This option can be repeated several times to specify several candidate drivers. + Note that it does not force those drivers to open the dataset. In particular, + some drivers have requirements on file extensions. .. versionadded:: 3.2