Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: PDS Translator
4 : * Purpose: Implements OGRPDSDataSource class
5 : * Author: Even Rouault, even dot rouault at spatialys.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2010-2011, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_conv.h"
14 : #include "cpl_string.h"
15 : #include "ogr_pds.h"
16 :
17 : using namespace OGRPDS;
18 :
19 : /************************************************************************/
20 : /* OGRPDSDataSource() */
21 : /************************************************************************/
22 :
23 2 : OGRPDSDataSource::OGRPDSDataSource() : papoLayers(nullptr), nLayers(0)
24 : {
25 2 : }
26 :
27 : /************************************************************************/
28 : /* ~OGRPDSDataSource() */
29 : /************************************************************************/
30 :
31 4 : OGRPDSDataSource::~OGRPDSDataSource()
32 :
33 : {
34 4 : for (int i = 0; i < nLayers; i++)
35 2 : delete papoLayers[i];
36 2 : CPLFree(papoLayers);
37 4 : }
38 :
39 : /************************************************************************/
40 : /* GetLayer() */
41 : /************************************************************************/
42 :
43 2 : OGRLayer *OGRPDSDataSource::GetLayer(int iLayer)
44 :
45 : {
46 2 : if (iLayer < 0 || iLayer >= nLayers)
47 0 : return nullptr;
48 :
49 2 : return papoLayers[iLayer];
50 : }
51 :
52 : /************************************************************************/
53 : /* GetKeywordSub() */
54 : /************************************************************************/
55 :
56 2 : const char *OGRPDSDataSource::GetKeywordSub(const char *pszPath, int iSubscript,
57 : const char *pszDefault)
58 :
59 : {
60 2 : const char *pszResult = oKeywords.GetKeyword(pszPath, nullptr);
61 :
62 2 : if (pszResult == nullptr)
63 0 : return pszDefault;
64 :
65 2 : if (pszResult[0] != '(')
66 0 : return pszDefault;
67 :
68 : char **papszTokens =
69 2 : CSLTokenizeString2(pszResult, "(,)", CSLT_HONOURSTRINGS);
70 :
71 2 : if (iSubscript <= CSLCount(papszTokens))
72 : {
73 2 : osTempResult = papszTokens[iSubscript - 1];
74 2 : CSLDestroy(papszTokens);
75 2 : return osTempResult.c_str();
76 : }
77 :
78 0 : CSLDestroy(papszTokens);
79 0 : return pszDefault;
80 : }
81 :
82 : /************************************************************************/
83 : /* CleanString() */
84 : /* */
85 : /* Removes single or double quotes, and converts spaces to underscores. */
86 : /* The change is made in-place to CPLString. */
87 : /************************************************************************/
88 :
89 126 : void OGRPDSDataSource::CleanString(CPLString &osInput)
90 :
91 : {
92 252 : if ((osInput.size() < 2) ||
93 126 : ((osInput.at(0) != '"' || osInput.at(osInput.size() - 1) != '"') &&
94 65 : (osInput.at(0) != '\'' || osInput.at(osInput.size() - 1) != '\'')))
95 65 : return;
96 :
97 61 : char *pszWrk = CPLStrdup(osInput.c_str() + 1);
98 :
99 61 : pszWrk[strlen(pszWrk) - 1] = '\0';
100 :
101 956 : for (int i = 0; pszWrk[i] != '\0'; i++)
102 : {
103 895 : if (pszWrk[i] == ' ')
104 0 : pszWrk[i] = '_';
105 : }
106 :
107 61 : osInput = pszWrk;
108 61 : CPLFree(pszWrk);
109 : }
110 :
111 : /************************************************************************/
112 : /* LoadTable() */
113 : /************************************************************************/
114 :
115 8 : static CPLString MakeAttr(CPLString os1, CPLString os2)
116 : {
117 16 : return os1 + "." + os2;
118 : }
119 :
120 2 : bool OGRPDSDataSource::LoadTable(const char *pszFilename, int nRecordSize,
121 : CPLString osTableID)
122 : {
123 4 : CPLString osTableFilename;
124 2 : int nStartBytes = 0;
125 :
126 4 : CPLString osTableLink = "^";
127 2 : osTableLink += osTableID;
128 :
129 4 : CPLString osTable = oKeywords.GetKeyword(osTableLink, "");
130 2 : if (osTable[0] == '(')
131 : {
132 1 : osTableFilename = GetKeywordSub(osTableLink, 1, "");
133 1 : CPLString osStartRecord = GetKeywordSub(osTableLink, 2, "");
134 1 : nStartBytes = atoi(osStartRecord.c_str());
135 1 : if (nStartBytes <= 0 ||
136 1 : ((nRecordSize > 0 && nStartBytes > INT_MAX / nRecordSize)))
137 : {
138 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid StartBytes value");
139 0 : return false;
140 : }
141 1 : nStartBytes--;
142 1 : nStartBytes *= nRecordSize;
143 1 : if (osTableFilename.empty() || osStartRecord.empty() || nStartBytes < 0)
144 : {
145 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot parse %s line",
146 : osTableLink.c_str());
147 0 : return false;
148 : }
149 1 : CPLString osTPath = CPLGetPathSafe(pszFilename);
150 1 : CleanString(osTableFilename);
151 : osTableFilename =
152 1 : CPLFormCIFilenameSafe(osTPath, osTableFilename, nullptr);
153 : }
154 : else
155 : {
156 1 : osTableFilename = oKeywords.GetKeyword(osTableLink, "");
157 1 : if (!osTableFilename.empty() && osTableFilename[0] >= '0' &&
158 0 : osTableFilename[0] <= '9')
159 : {
160 0 : nStartBytes = atoi(osTableFilename.c_str());
161 0 : if (nStartBytes <= 1)
162 : {
163 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot parse %s line",
164 : osTableFilename.c_str());
165 0 : return false;
166 : }
167 0 : nStartBytes = nStartBytes - 1;
168 0 : if (strstr(osTableFilename.c_str(), "<BYTES>") == nullptr)
169 : {
170 0 : if (nRecordSize > 0 && nStartBytes > INT_MAX / nRecordSize)
171 : {
172 0 : CPLError(CE_Failure, CPLE_NotSupported,
173 : "Too big StartBytes value");
174 0 : return false;
175 : }
176 0 : nStartBytes *= nRecordSize;
177 : }
178 0 : osTableFilename = pszFilename;
179 : }
180 : else
181 : {
182 1 : CPLString osTPath = CPLGetPathSafe(pszFilename);
183 1 : CleanString(osTableFilename);
184 : osTableFilename =
185 1 : CPLFormCIFilenameSafe(osTPath, osTableFilename, nullptr);
186 1 : nStartBytes = 0;
187 : }
188 : }
189 :
190 : CPLString osTableName =
191 6 : oKeywords.GetKeyword(MakeAttr(osTableID, "NAME"), "");
192 2 : if (osTableName.empty())
193 : {
194 1 : if (GetLayerByName(osTableID.c_str()) == nullptr)
195 1 : osTableName = osTableID;
196 : else
197 0 : osTableName = CPLSPrintf("Layer_%d", nLayers + 1);
198 : }
199 2 : CleanString(osTableName);
200 : CPLString osTableInterchangeFormat =
201 6 : oKeywords.GetKeyword(MakeAttr(osTableID, "INTERCHANGE_FORMAT"), "");
202 : CPLString osTableRows =
203 6 : oKeywords.GetKeyword(MakeAttr(osTableID, "ROWS"), "");
204 2 : const int nRecords = atoi(osTableRows);
205 2 : if (osTableInterchangeFormat.empty() || osTableRows.empty() || nRecords < 0)
206 : {
207 0 : CPLError(CE_Failure, CPLE_NotSupported,
208 : "One of TABLE.INTERCHANGE_FORMAT or TABLE.ROWS is missing");
209 0 : return false;
210 : }
211 :
212 2 : CleanString(osTableInterchangeFormat);
213 3 : if (osTableInterchangeFormat.compare("ASCII") != 0 &&
214 1 : osTableInterchangeFormat.compare("BINARY") != 0)
215 : {
216 0 : CPLError(CE_Failure, CPLE_NotSupported,
217 : "Only INTERCHANGE_FORMAT=ASCII or BINARY is supported");
218 0 : return false;
219 : }
220 :
221 2 : VSILFILE *fp = VSIFOpenL(osTableFilename, "rb");
222 2 : if (fp == nullptr)
223 : {
224 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s",
225 : osTableFilename.c_str());
226 0 : return false;
227 : }
228 :
229 : CPLString osTableStructure =
230 6 : oKeywords.GetKeyword(MakeAttr(osTableID, "^STRUCTURE"), "");
231 2 : if (!osTableStructure.empty())
232 : {
233 2 : CPLString osTPath = CPLGetPathSafe(pszFilename);
234 2 : CleanString(osTableStructure);
235 : osTableStructure =
236 2 : CPLFormCIFilenameSafe(osTPath, osTableStructure, nullptr);
237 : }
238 :
239 2 : GByte *pabyRecord = (GByte *)VSI_MALLOC_VERBOSE(nRecordSize + 1);
240 2 : if (pabyRecord == nullptr)
241 : {
242 0 : VSIFCloseL(fp);
243 0 : return false;
244 : }
245 2 : pabyRecord[nRecordSize] = 0;
246 :
247 2 : papoLayers = static_cast<OGRLayer **>(
248 2 : CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer *)));
249 4 : papoLayers[nLayers] = new OGRPDSLayer(
250 : osTableID, osTableName, fp, pszFilename, osTableStructure, nRecords,
251 : nStartBytes, nRecordSize, pabyRecord,
252 4 : osTableInterchangeFormat.compare("ASCII") == 0);
253 2 : nLayers++;
254 :
255 2 : return true;
256 : }
257 :
258 : /************************************************************************/
259 : /* Open() */
260 : /************************************************************************/
261 :
262 2 : int OGRPDSDataSource::Open(const char *pszFilename)
263 :
264 : {
265 : // --------------------------------------------------------------------
266 : // Does this appear to be a .PDS table file?
267 : // --------------------------------------------------------------------
268 :
269 2 : VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
270 2 : if (fp == nullptr)
271 0 : return FALSE;
272 :
273 : char szBuffer[512];
274 : int nbRead =
275 2 : static_cast<int>(VSIFReadL(szBuffer, 1, sizeof(szBuffer) - 1, fp));
276 2 : szBuffer[nbRead] = '\0';
277 :
278 2 : const char *pszPos = strstr(szBuffer, "PDS_VERSION_ID");
279 2 : const bool bIsPDS = pszPos != nullptr;
280 :
281 2 : if (!bIsPDS)
282 : {
283 0 : VSIFCloseL(fp);
284 0 : return FALSE;
285 : }
286 :
287 2 : if (!oKeywords.Ingest(fp, static_cast<int>(pszPos - szBuffer)))
288 : {
289 0 : VSIFCloseL(fp);
290 0 : return FALSE;
291 : }
292 :
293 2 : VSIFCloseL(fp);
294 4 : CPLString osRecordType = oKeywords.GetKeyword("RECORD_TYPE", "");
295 4 : CPLString osFileRecords = oKeywords.GetKeyword("FILE_RECORDS", "");
296 4 : CPLString osRecordBytes = oKeywords.GetKeyword("RECORD_BYTES", "");
297 2 : int nRecordSize = atoi(osRecordBytes);
298 6 : if (osRecordType.empty() || osFileRecords.empty() ||
299 6 : osRecordBytes.empty() || nRecordSize <= 0 ||
300 : nRecordSize > 10 * 1024 * 1024)
301 : {
302 0 : CPLError(CE_Failure, CPLE_NotSupported,
303 : "One of RECORD_TYPE, FILE_RECORDS or RECORD_BYTES is missing");
304 0 : return FALSE;
305 : }
306 2 : CleanString(osRecordType);
307 2 : if (osRecordType.compare("FIXED_LENGTH") != 0)
308 : {
309 0 : CPLError(CE_Failure, CPLE_NotSupported,
310 : "Only RECORD_TYPE=FIXED_LENGTH is supported");
311 0 : return FALSE;
312 : }
313 :
314 4 : CPLString osTable = oKeywords.GetKeyword("^TABLE", "");
315 2 : if (!osTable.empty())
316 : {
317 2 : LoadTable(pszFilename, nRecordSize, "TABLE");
318 : }
319 : else
320 : {
321 0 : fp = VSIFOpenL(pszFilename, "rb");
322 0 : if (fp == nullptr)
323 0 : return FALSE;
324 :
325 : // To avoid performance issues with datasets generated by oss-fuzz
326 0 : int nErrors = 0;
327 0 : while (nErrors < 10)
328 : {
329 0 : CPLPushErrorHandler(CPLQuietErrorHandler);
330 0 : const char *pszLine = CPLReadLine2L(fp, 256, nullptr);
331 0 : CPLPopErrorHandler();
332 0 : CPLErrorReset();
333 0 : if (pszLine == nullptr)
334 0 : break;
335 : char **papszTokens =
336 0 : CSLTokenizeString2(pszLine, " =", CSLT_HONOURSTRINGS);
337 0 : int nTokens = CSLCount(papszTokens);
338 0 : if (nTokens == 2 && papszTokens[0][0] == '^' &&
339 0 : strstr(papszTokens[0], "TABLE") != nullptr)
340 : {
341 0 : if (!LoadTable(pszFilename, nRecordSize, papszTokens[0] + 1))
342 : {
343 0 : nErrors++;
344 : }
345 : }
346 0 : CSLDestroy(papszTokens);
347 0 : papszTokens = nullptr;
348 : }
349 0 : VSIFCloseL(fp);
350 : }
351 :
352 2 : return nLayers != 0;
353 : }
|