Coverage Report

Created: 2025-08-11 09:23

/src/gdal/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdbdriver.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  Implements Open FileGDB OGR driver.
5
 * Author:   Even Rouault, <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_port.h"
14
#include "ogr_openfilegdb.h"
15
#include "ogropenfilegdbdrivercore.h"
16
17
#include <cstddef>
18
#include <cstring>
19
20
#include "cpl_conv.h"
21
#include "cpl_vsi.h"
22
#include "gdal.h"
23
#include "gdal_priv.h"
24
#include "gdalalgorithm.h"
25
#include "ogr_core.h"
26
27
// g++ -O2 -Wall -Wextra -g -shared -fPIC ogr/ogrsf_frmts/openfilegdb/*.cpp
28
// -o ogr_OpenFileGDB.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts
29
// -Iogr/ogrsf_frmts/mem -Iogr/ogrsf_frmts/openfilegdb -L. -lgdal
30
31
extern "C" void RegisterOGROpenFileGDB();
32
33
/************************************************************************/
34
/*                                Open()                                */
35
/************************************************************************/
36
37
static GDALDataset *OGROpenFileGDBDriverOpen(GDALOpenInfo *poOpenInfo)
38
39
12.6k
{
40
12.6k
    const char *pszFilename = poOpenInfo->pszFilename;
41
12.6k
    if (OGROpenFileGDBDriverIdentify(poOpenInfo, pszFilename) ==
42
12.6k
        GDAL_IDENTIFY_FALSE)
43
0
        return nullptr;
44
45
#ifdef DEBUG
46
    /* For AFL, so that .cur_input is detected as the archive filename */
47
    if (poOpenInfo->fpL != nullptr &&
48
        !STARTS_WITH(poOpenInfo->pszFilename, "/vsitar/") &&
49
        EQUAL(CPLGetFilename(poOpenInfo->pszFilename), ".cur_input"))
50
    {
51
        GDALOpenInfo oOpenInfo(
52
            (CPLString("/vsitar/") + poOpenInfo->pszFilename).c_str(),
53
            poOpenInfo->nOpenFlags);
54
        oOpenInfo.papszOpenOptions = poOpenInfo->papszOpenOptions;
55
        return OGROpenFileGDBDriverOpen(&oOpenInfo);
56
    }
57
#endif
58
59
12.6k
    auto poDS = std::make_unique<OGROpenFileGDBDataSource>();
60
12.6k
    bool bRetryFileGDB = false;
61
12.6k
    if (poDS->Open(poOpenInfo, bRetryFileGDB))
62
5.93k
    {
63
5.93k
        if (poDS->GetSubdatasets().size() == 2)
64
0
        {
65
            // If there is a single raster dataset, open it right away.
66
0
            GDALOpenInfo oOpenInfo(
67
0
                poDS->GetSubdatasets().FetchNameValue("SUBDATASET_1_NAME"),
68
0
                poOpenInfo->nOpenFlags);
69
0
            poDS = std::make_unique<OGROpenFileGDBDataSource>();
70
0
            if (poDS->Open(&oOpenInfo, bRetryFileGDB))
71
0
            {
72
0
                poDS->SetDescription(poOpenInfo->pszFilename);
73
0
            }
74
0
            else
75
0
            {
76
0
                poDS.reset();
77
0
            }
78
0
        }
79
5.93k
        return poDS.release();
80
5.93k
    }
81
6.74k
    else if (bRetryFileGDB)
82
0
    {
83
0
        auto poDriver = GetGDALDriverManager()->GetDriverByName("FileGDB");
84
0
        if (poDriver)
85
0
        {
86
0
            GDALOpenInfo oOpenInfo(pszFilename, poOpenInfo->nOpenFlags);
87
0
            CPLStringList aosOpenOptions;
88
0
            aosOpenOptions.SetNameValue("@MAY_USE_OPENFILEGDB", "NO");
89
0
            oOpenInfo.papszOpenOptions = aosOpenOptions.List();
90
0
            return poDriver->Open(&oOpenInfo, false);
91
0
        }
92
0
    }
93
94
6.74k
    return nullptr;
95
12.6k
}
96
97
/************************************************************************/
98
/*                              Create()                                */
99
/************************************************************************/
100
101
static GDALDataset *OGROpenFileGDBDriverCreate(const char *pszName, int nXSize,
102
                                               int nYSize, int nBands,
103
                                               GDALDataType eType,
104
                                               char ** /* papszOptions*/)
105
106
42
{
107
42
    if (!(nXSize == 0 && nYSize == 0 && nBands == 0 && eType == GDT_Unknown))
108
0
    {
109
0
        CPLError(CE_Failure, CPLE_NotSupported,
110
0
                 "OpenFileGDB::Create(): only vector datasets supported");
111
0
        return nullptr;
112
0
    }
113
114
42
    auto poDS = std::make_unique<OGROpenFileGDBDataSource>();
115
42
    if (!poDS->Create(pszName))
116
0
        return nullptr;
117
42
    return poDS.release();
118
42
}
119
120
/************************************************************************/
121
/*                     OGROpenFileGDBDriverDelete()                     */
122
/************************************************************************/
123
124
static CPLErr OGROpenFileGDBDriverDelete(const char *pszFilename)
125
32
{
126
32
    CPLStringList aosFiles(VSIReadDir(pszFilename));
127
32
    if (aosFiles.empty())
128
0
        return CE_Failure;
129
130
4.26k
    for (int i = 0; i < aosFiles.size(); ++i)
131
4.23k
    {
132
4.23k
        if (strcmp(aosFiles[i], ".") != 0 && strcmp(aosFiles[i], "..") != 0)
133
4.23k
        {
134
4.23k
            const std::string osFilename(
135
4.23k
                CPLFormFilenameSafe(pszFilename, aosFiles[i], nullptr));
136
4.23k
            if (VSIUnlink(osFilename.c_str()) != 0)
137
0
            {
138
0
                CPLError(CE_Failure, CPLE_FileIO, "Cannot delete %s",
139
0
                         osFilename.c_str());
140
0
                return CE_Failure;
141
0
            }
142
4.23k
        }
143
4.23k
    }
144
32
    if (VSIRmdir(pszFilename) != 0)
145
0
    {
146
0
        CPLError(CE_Failure, CPLE_FileIO, "Cannot delete %s", pszFilename);
147
0
        return CE_Failure;
148
0
    }
149
150
32
    return CE_None;
151
32
}
152
153
/************************************************************************/
154
/*                    OpenFileGDBRepackAlgorithm                        */
155
/************************************************************************/
156
157
#ifndef _
158
0
#define _(x) x
159
#endif
160
161
class OpenFileGDBRepackAlgorithm final : public GDALAlgorithm
162
{
163
  public:
164
    OpenFileGDBRepackAlgorithm()
165
0
        : GDALAlgorithm("repack", "Repack a FileGeoDatabase dataset",
166
0
                        "/drivers/vector/openfilegdb.html")
167
0
    {
168
0
        AddProgressArg();
169
170
0
        constexpr int type = GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_UPDATE;
171
0
        auto &arg =
172
0
            AddArg("dataset", 0, _("FileGeoDatabase dataset"), &m_dataset, type)
173
0
                .SetPositional()
174
0
                .SetRequired();
175
0
        SetAutoCompleteFunctionForFilename(arg, type);
176
0
    }
177
178
  protected:
179
    bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override;
180
181
  private:
182
    GDALArgDatasetValue m_dataset{};
183
};
184
185
bool OpenFileGDBRepackAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
186
                                         void *pProgressData)
187
0
{
188
0
    auto poDS =
189
0
        dynamic_cast<OGROpenFileGDBDataSource *>(m_dataset.GetDatasetRef());
190
0
    if (!poDS)
191
0
    {
192
0
        ReportError(CE_Failure, CPLE_AppDefined, "%s is not a FileGeoDatabase",
193
0
                    m_dataset.GetName().c_str());
194
0
        return false;
195
0
    }
196
0
    bool bSuccess = true;
197
0
    int iLayer = 0;
198
0
    for (auto &poLayer : poDS->GetLayers())
199
0
    {
200
0
        void *pScaledData = GDALCreateScaledProgress(
201
0
            static_cast<double>(iLayer) / poDS->GetLayerCount(),
202
0
            static_cast<double>(iLayer + 1) / poDS->GetLayerCount(),
203
0
            pfnProgress, pProgressData);
204
0
        const bool bRet = poLayer->Repack(
205
0
            pScaledData ? GDALScaledProgress : nullptr, pScaledData);
206
0
        GDALDestroyScaledProgress(pScaledData);
207
0
        if (!bRet)
208
0
        {
209
0
            ReportError(CE_Failure, CPLE_AppDefined,
210
0
                        "Repack of layer %s failed", poLayer->GetName());
211
0
            bSuccess = false;
212
0
        }
213
0
        ++iLayer;
214
0
    }
215
0
    return bSuccess;
216
0
}
217
218
/************************************************************************/
219
/*                 OGROpenFileGDBInstantiateAlgorithm()                 */
220
/************************************************************************/
221
222
static GDALAlgorithm *
223
OGROpenFileGDBInstantiateAlgorithm(const std::vector<std::string> &aosPath)
224
0
{
225
0
    if (aosPath.size() == 1 && aosPath[0] == "repack")
226
0
    {
227
0
        return std::make_unique<OpenFileGDBRepackAlgorithm>().release();
228
0
    }
229
0
    else
230
0
    {
231
0
        return nullptr;
232
0
    }
233
0
}
234
235
/***********************************************************************/
236
/*                       RegisterOGROpenFileGDB()                      */
237
/***********************************************************************/
238
239
void RegisterOGROpenFileGDB()
240
241
26
{
242
26
    if (!GDAL_CHECK_VERSION("OGR OpenFileGDB"))
243
0
        return;
244
245
26
    if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
246
0
        return;
247
248
26
    GDALDriver *poDriver = new GDALDriver();
249
26
    OGROpenFileGDBDriverSetCommonMetadata(poDriver);
250
251
26
    poDriver->pfnOpen = OGROpenFileGDBDriverOpen;
252
26
    poDriver->pfnCreate = OGROpenFileGDBDriverCreate;
253
26
    poDriver->pfnDelete = OGROpenFileGDBDriverDelete;
254
26
    poDriver->pfnInstantiateAlgorithm = OGROpenFileGDBInstantiateAlgorithm;
255
256
26
    GetGDALDriverManager()->RegisterDriver(poDriver);
257
26
}