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 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "cpl_conv.h"
30 : #include "cpl_string.h"
31 : #include "ogr_pds.h"
32 :
33 : using namespace OGRPDS;
34 :
35 : /************************************************************************/
36 : /* OGRPDSDataSource() */
37 : /************************************************************************/
38 :
39 2 : OGRPDSDataSource::OGRPDSDataSource()
40 2 : : pszName(nullptr), papoLayers(nullptr), nLayers(0)
41 : {
42 2 : }
43 :
44 : /************************************************************************/
45 : /* ~OGRPDSDataSource() */
46 : /************************************************************************/
47 :
48 4 : OGRPDSDataSource::~OGRPDSDataSource()
49 :
50 : {
51 4 : for (int i = 0; i < nLayers; i++)
52 2 : delete papoLayers[i];
53 2 : CPLFree(papoLayers);
54 :
55 2 : CPLFree(pszName);
56 4 : }
57 :
58 : /************************************************************************/
59 : /* TestCapability() */
60 : /************************************************************************/
61 :
62 0 : int OGRPDSDataSource::TestCapability(const char * /* pszCap */)
63 : {
64 0 : return FALSE;
65 : }
66 :
67 : /************************************************************************/
68 : /* GetLayer() */
69 : /************************************************************************/
70 :
71 2 : OGRLayer *OGRPDSDataSource::GetLayer(int iLayer)
72 :
73 : {
74 2 : if (iLayer < 0 || iLayer >= nLayers)
75 0 : return nullptr;
76 :
77 2 : return papoLayers[iLayer];
78 : }
79 :
80 : /************************************************************************/
81 : /* GetKeywordSub() */
82 : /************************************************************************/
83 :
84 2 : const char *OGRPDSDataSource::GetKeywordSub(const char *pszPath, int iSubscript,
85 : const char *pszDefault)
86 :
87 : {
88 2 : const char *pszResult = oKeywords.GetKeyword(pszPath, nullptr);
89 :
90 2 : if (pszResult == nullptr)
91 0 : return pszDefault;
92 :
93 2 : if (pszResult[0] != '(')
94 0 : return pszDefault;
95 :
96 : char **papszTokens =
97 2 : CSLTokenizeString2(pszResult, "(,)", CSLT_HONOURSTRINGS);
98 :
99 2 : if (iSubscript <= CSLCount(papszTokens))
100 : {
101 2 : osTempResult = papszTokens[iSubscript - 1];
102 2 : CSLDestroy(papszTokens);
103 2 : return osTempResult.c_str();
104 : }
105 :
106 0 : CSLDestroy(papszTokens);
107 0 : return pszDefault;
108 : }
109 :
110 : /************************************************************************/
111 : /* CleanString() */
112 : /* */
113 : /* Removes single or double quotes, and converts spaces to underscores. */
114 : /* The change is made in-place to CPLString. */
115 : /************************************************************************/
116 :
117 126 : void OGRPDSDataSource::CleanString(CPLString &osInput)
118 :
119 : {
120 252 : if ((osInput.size() < 2) ||
121 126 : ((osInput.at(0) != '"' || osInput.at(osInput.size() - 1) != '"') &&
122 65 : (osInput.at(0) != '\'' || osInput.at(osInput.size() - 1) != '\'')))
123 65 : return;
124 :
125 61 : char *pszWrk = CPLStrdup(osInput.c_str() + 1);
126 :
127 61 : pszWrk[strlen(pszWrk) - 1] = '\0';
128 :
129 956 : for (int i = 0; pszWrk[i] != '\0'; i++)
130 : {
131 895 : if (pszWrk[i] == ' ')
132 0 : pszWrk[i] = '_';
133 : }
134 :
135 61 : osInput = pszWrk;
136 61 : CPLFree(pszWrk);
137 : }
138 :
139 : /************************************************************************/
140 : /* LoadTable() */
141 : /************************************************************************/
142 :
143 8 : static CPLString MakeAttr(CPLString os1, CPLString os2)
144 : {
145 16 : return os1 + "." + os2;
146 : }
147 :
148 2 : bool OGRPDSDataSource::LoadTable(const char *pszFilename, int nRecordSize,
149 : CPLString osTableID)
150 : {
151 4 : CPLString osTableFilename;
152 2 : int nStartBytes = 0;
153 :
154 4 : CPLString osTableLink = "^";
155 2 : osTableLink += osTableID;
156 :
157 4 : CPLString osTable = oKeywords.GetKeyword(osTableLink, "");
158 2 : if (osTable[0] == '(')
159 : {
160 1 : osTableFilename = GetKeywordSub(osTableLink, 1, "");
161 1 : CPLString osStartRecord = GetKeywordSub(osTableLink, 2, "");
162 1 : nStartBytes = atoi(osStartRecord.c_str());
163 1 : if (nStartBytes <= 0 ||
164 1 : ((nRecordSize > 0 && nStartBytes > INT_MAX / nRecordSize)))
165 : {
166 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid StartBytes value");
167 0 : return false;
168 : }
169 1 : nStartBytes--;
170 1 : nStartBytes *= nRecordSize;
171 1 : if (osTableFilename.empty() || osStartRecord.empty() || nStartBytes < 0)
172 : {
173 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot parse %s line",
174 : osTableLink.c_str());
175 0 : return false;
176 : }
177 1 : CPLString osTPath = CPLGetPath(pszFilename);
178 1 : CleanString(osTableFilename);
179 1 : osTableFilename = CPLFormCIFilename(osTPath, osTableFilename, nullptr);
180 : }
181 : else
182 : {
183 1 : osTableFilename = oKeywords.GetKeyword(osTableLink, "");
184 1 : if (!osTableFilename.empty() && osTableFilename[0] >= '0' &&
185 0 : osTableFilename[0] <= '9')
186 : {
187 0 : nStartBytes = atoi(osTableFilename.c_str());
188 0 : if (nStartBytes <= 1)
189 : {
190 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot parse %s line",
191 : osTableFilename.c_str());
192 0 : return false;
193 : }
194 0 : nStartBytes = nStartBytes - 1;
195 0 : if (strstr(osTableFilename.c_str(), "<BYTES>") == nullptr)
196 : {
197 0 : if (nRecordSize > 0 && nStartBytes > INT_MAX / nRecordSize)
198 : {
199 0 : CPLError(CE_Failure, CPLE_NotSupported,
200 : "Too big StartBytes value");
201 0 : return false;
202 : }
203 0 : nStartBytes *= nRecordSize;
204 : }
205 0 : osTableFilename = pszFilename;
206 : }
207 : else
208 : {
209 1 : CPLString osTPath = CPLGetPath(pszFilename);
210 1 : CleanString(osTableFilename);
211 : osTableFilename =
212 1 : CPLFormCIFilename(osTPath, osTableFilename, nullptr);
213 1 : nStartBytes = 0;
214 : }
215 : }
216 :
217 : CPLString osTableName =
218 6 : oKeywords.GetKeyword(MakeAttr(osTableID, "NAME"), "");
219 2 : if (osTableName.empty())
220 : {
221 1 : if (GetLayerByName(osTableID.c_str()) == nullptr)
222 1 : osTableName = osTableID;
223 : else
224 0 : osTableName = CPLSPrintf("Layer_%d", nLayers + 1);
225 : }
226 2 : CleanString(osTableName);
227 : CPLString osTableInterchangeFormat =
228 6 : oKeywords.GetKeyword(MakeAttr(osTableID, "INTERCHANGE_FORMAT"), "");
229 : CPLString osTableRows =
230 6 : oKeywords.GetKeyword(MakeAttr(osTableID, "ROWS"), "");
231 2 : const int nRecords = atoi(osTableRows);
232 2 : if (osTableInterchangeFormat.empty() || osTableRows.empty() || nRecords < 0)
233 : {
234 0 : CPLError(CE_Failure, CPLE_NotSupported,
235 : "One of TABLE.INTERCHANGE_FORMAT or TABLE.ROWS is missing");
236 0 : return false;
237 : }
238 :
239 2 : CleanString(osTableInterchangeFormat);
240 3 : if (osTableInterchangeFormat.compare("ASCII") != 0 &&
241 1 : osTableInterchangeFormat.compare("BINARY") != 0)
242 : {
243 0 : CPLError(CE_Failure, CPLE_NotSupported,
244 : "Only INTERCHANGE_FORMAT=ASCII or BINARY is supported");
245 0 : return false;
246 : }
247 :
248 2 : VSILFILE *fp = VSIFOpenL(osTableFilename, "rb");
249 2 : if (fp == nullptr)
250 : {
251 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s",
252 : osTableFilename.c_str());
253 0 : return false;
254 : }
255 :
256 : CPLString osTableStructure =
257 6 : oKeywords.GetKeyword(MakeAttr(osTableID, "^STRUCTURE"), "");
258 2 : if (!osTableStructure.empty())
259 : {
260 2 : CPLString osTPath = CPLGetPath(pszFilename);
261 2 : CleanString(osTableStructure);
262 : osTableStructure =
263 2 : CPLFormCIFilename(osTPath, osTableStructure, nullptr);
264 : }
265 :
266 2 : GByte *pabyRecord = (GByte *)VSI_MALLOC_VERBOSE(nRecordSize + 1);
267 2 : if (pabyRecord == nullptr)
268 : {
269 0 : VSIFCloseL(fp);
270 0 : return false;
271 : }
272 2 : pabyRecord[nRecordSize] = 0;
273 :
274 2 : papoLayers = static_cast<OGRLayer **>(
275 2 : CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer *)));
276 4 : papoLayers[nLayers] = new OGRPDSLayer(
277 : osTableID, osTableName, fp, pszFilename, osTableStructure, nRecords,
278 : nStartBytes, nRecordSize, pabyRecord,
279 4 : osTableInterchangeFormat.compare("ASCII") == 0);
280 2 : nLayers++;
281 :
282 2 : return true;
283 : }
284 :
285 : /************************************************************************/
286 : /* Open() */
287 : /************************************************************************/
288 :
289 2 : int OGRPDSDataSource::Open(const char *pszFilename)
290 :
291 : {
292 2 : pszName = CPLStrdup(pszFilename);
293 :
294 : // --------------------------------------------------------------------
295 : // Does this appear to be a .PDS table file?
296 : // --------------------------------------------------------------------
297 :
298 2 : VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
299 2 : if (fp == nullptr)
300 0 : return FALSE;
301 :
302 : char szBuffer[512];
303 : int nbRead =
304 2 : static_cast<int>(VSIFReadL(szBuffer, 1, sizeof(szBuffer) - 1, fp));
305 2 : szBuffer[nbRead] = '\0';
306 :
307 2 : const char *pszPos = strstr(szBuffer, "PDS_VERSION_ID");
308 2 : const bool bIsPDS = pszPos != nullptr;
309 :
310 2 : if (!bIsPDS)
311 : {
312 0 : VSIFCloseL(fp);
313 0 : return FALSE;
314 : }
315 :
316 2 : if (!oKeywords.Ingest(fp, static_cast<int>(pszPos - szBuffer)))
317 : {
318 0 : VSIFCloseL(fp);
319 0 : return FALSE;
320 : }
321 :
322 2 : VSIFCloseL(fp);
323 4 : CPLString osRecordType = oKeywords.GetKeyword("RECORD_TYPE", "");
324 4 : CPLString osFileRecords = oKeywords.GetKeyword("FILE_RECORDS", "");
325 4 : CPLString osRecordBytes = oKeywords.GetKeyword("RECORD_BYTES", "");
326 2 : int nRecordSize = atoi(osRecordBytes);
327 6 : if (osRecordType.empty() || osFileRecords.empty() ||
328 6 : osRecordBytes.empty() || nRecordSize <= 0 ||
329 : nRecordSize > 10 * 1024 * 1024)
330 : {
331 0 : CPLError(CE_Failure, CPLE_NotSupported,
332 : "One of RECORD_TYPE, FILE_RECORDS or RECORD_BYTES is missing");
333 0 : return FALSE;
334 : }
335 2 : CleanString(osRecordType);
336 2 : if (osRecordType.compare("FIXED_LENGTH") != 0)
337 : {
338 0 : CPLError(CE_Failure, CPLE_NotSupported,
339 : "Only RECORD_TYPE=FIXED_LENGTH is supported");
340 0 : return FALSE;
341 : }
342 :
343 4 : CPLString osTable = oKeywords.GetKeyword("^TABLE", "");
344 2 : if (!osTable.empty())
345 : {
346 2 : LoadTable(pszFilename, nRecordSize, "TABLE");
347 : }
348 : else
349 : {
350 0 : fp = VSIFOpenL(pszFilename, "rb");
351 0 : if (fp == nullptr)
352 0 : return FALSE;
353 :
354 : // To avoid performance issues with datasets generated by oss-fuzz
355 0 : int nErrors = 0;
356 0 : while (nErrors < 10)
357 : {
358 0 : CPLPushErrorHandler(CPLQuietErrorHandler);
359 0 : const char *pszLine = CPLReadLine2L(fp, 256, nullptr);
360 0 : CPLPopErrorHandler();
361 0 : CPLErrorReset();
362 0 : if (pszLine == nullptr)
363 0 : break;
364 : char **papszTokens =
365 0 : CSLTokenizeString2(pszLine, " =", CSLT_HONOURSTRINGS);
366 0 : int nTokens = CSLCount(papszTokens);
367 0 : if (nTokens == 2 && papszTokens[0][0] == '^' &&
368 0 : strstr(papszTokens[0], "TABLE") != nullptr)
369 : {
370 0 : if (!LoadTable(pszFilename, nRecordSize, papszTokens[0] + 1))
371 : {
372 0 : nErrors++;
373 : }
374 : }
375 0 : CSLDestroy(papszTokens);
376 0 : papszTokens = nullptr;
377 : }
378 0 : VSIFCloseL(fp);
379 : }
380 :
381 2 : return nLayers != 0;
382 : }
|