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 = CPLGetPath(pszFilename);
150 1 : CleanString(osTableFilename);
151 1 : osTableFilename = CPLFormCIFilename(osTPath, osTableFilename, nullptr);
152 : }
153 : else
154 : {
155 1 : osTableFilename = oKeywords.GetKeyword(osTableLink, "");
156 1 : if (!osTableFilename.empty() && osTableFilename[0] >= '0' &&
157 0 : osTableFilename[0] <= '9')
158 : {
159 0 : nStartBytes = atoi(osTableFilename.c_str());
160 0 : if (nStartBytes <= 1)
161 : {
162 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot parse %s line",
163 : osTableFilename.c_str());
164 0 : return false;
165 : }
166 0 : nStartBytes = nStartBytes - 1;
167 0 : if (strstr(osTableFilename.c_str(), "<BYTES>") == nullptr)
168 : {
169 0 : if (nRecordSize > 0 && nStartBytes > INT_MAX / nRecordSize)
170 : {
171 0 : CPLError(CE_Failure, CPLE_NotSupported,
172 : "Too big StartBytes value");
173 0 : return false;
174 : }
175 0 : nStartBytes *= nRecordSize;
176 : }
177 0 : osTableFilename = pszFilename;
178 : }
179 : else
180 : {
181 1 : CPLString osTPath = CPLGetPath(pszFilename);
182 1 : CleanString(osTableFilename);
183 : osTableFilename =
184 1 : CPLFormCIFilename(osTPath, osTableFilename, nullptr);
185 1 : nStartBytes = 0;
186 : }
187 : }
188 :
189 : CPLString osTableName =
190 6 : oKeywords.GetKeyword(MakeAttr(osTableID, "NAME"), "");
191 2 : if (osTableName.empty())
192 : {
193 1 : if (GetLayerByName(osTableID.c_str()) == nullptr)
194 1 : osTableName = osTableID;
195 : else
196 0 : osTableName = CPLSPrintf("Layer_%d", nLayers + 1);
197 : }
198 2 : CleanString(osTableName);
199 : CPLString osTableInterchangeFormat =
200 6 : oKeywords.GetKeyword(MakeAttr(osTableID, "INTERCHANGE_FORMAT"), "");
201 : CPLString osTableRows =
202 6 : oKeywords.GetKeyword(MakeAttr(osTableID, "ROWS"), "");
203 2 : const int nRecords = atoi(osTableRows);
204 2 : if (osTableInterchangeFormat.empty() || osTableRows.empty() || nRecords < 0)
205 : {
206 0 : CPLError(CE_Failure, CPLE_NotSupported,
207 : "One of TABLE.INTERCHANGE_FORMAT or TABLE.ROWS is missing");
208 0 : return false;
209 : }
210 :
211 2 : CleanString(osTableInterchangeFormat);
212 3 : if (osTableInterchangeFormat.compare("ASCII") != 0 &&
213 1 : osTableInterchangeFormat.compare("BINARY") != 0)
214 : {
215 0 : CPLError(CE_Failure, CPLE_NotSupported,
216 : "Only INTERCHANGE_FORMAT=ASCII or BINARY is supported");
217 0 : return false;
218 : }
219 :
220 2 : VSILFILE *fp = VSIFOpenL(osTableFilename, "rb");
221 2 : if (fp == nullptr)
222 : {
223 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s",
224 : osTableFilename.c_str());
225 0 : return false;
226 : }
227 :
228 : CPLString osTableStructure =
229 6 : oKeywords.GetKeyword(MakeAttr(osTableID, "^STRUCTURE"), "");
230 2 : if (!osTableStructure.empty())
231 : {
232 2 : CPLString osTPath = CPLGetPath(pszFilename);
233 2 : CleanString(osTableStructure);
234 : osTableStructure =
235 2 : CPLFormCIFilename(osTPath, osTableStructure, nullptr);
236 : }
237 :
238 2 : GByte *pabyRecord = (GByte *)VSI_MALLOC_VERBOSE(nRecordSize + 1);
239 2 : if (pabyRecord == nullptr)
240 : {
241 0 : VSIFCloseL(fp);
242 0 : return false;
243 : }
244 2 : pabyRecord[nRecordSize] = 0;
245 :
246 2 : papoLayers = static_cast<OGRLayer **>(
247 2 : CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer *)));
248 4 : papoLayers[nLayers] = new OGRPDSLayer(
249 : osTableID, osTableName, fp, pszFilename, osTableStructure, nRecords,
250 : nStartBytes, nRecordSize, pabyRecord,
251 4 : osTableInterchangeFormat.compare("ASCII") == 0);
252 2 : nLayers++;
253 :
254 2 : return true;
255 : }
256 :
257 : /************************************************************************/
258 : /* Open() */
259 : /************************************************************************/
260 :
261 2 : int OGRPDSDataSource::Open(const char *pszFilename)
262 :
263 : {
264 : // --------------------------------------------------------------------
265 : // Does this appear to be a .PDS table file?
266 : // --------------------------------------------------------------------
267 :
268 2 : VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
269 2 : if (fp == nullptr)
270 0 : return FALSE;
271 :
272 : char szBuffer[512];
273 : int nbRead =
274 2 : static_cast<int>(VSIFReadL(szBuffer, 1, sizeof(szBuffer) - 1, fp));
275 2 : szBuffer[nbRead] = '\0';
276 :
277 2 : const char *pszPos = strstr(szBuffer, "PDS_VERSION_ID");
278 2 : const bool bIsPDS = pszPos != nullptr;
279 :
280 2 : if (!bIsPDS)
281 : {
282 0 : VSIFCloseL(fp);
283 0 : return FALSE;
284 : }
285 :
286 2 : if (!oKeywords.Ingest(fp, static_cast<int>(pszPos - szBuffer)))
287 : {
288 0 : VSIFCloseL(fp);
289 0 : return FALSE;
290 : }
291 :
292 2 : VSIFCloseL(fp);
293 4 : CPLString osRecordType = oKeywords.GetKeyword("RECORD_TYPE", "");
294 4 : CPLString osFileRecords = oKeywords.GetKeyword("FILE_RECORDS", "");
295 4 : CPLString osRecordBytes = oKeywords.GetKeyword("RECORD_BYTES", "");
296 2 : int nRecordSize = atoi(osRecordBytes);
297 6 : if (osRecordType.empty() || osFileRecords.empty() ||
298 6 : osRecordBytes.empty() || nRecordSize <= 0 ||
299 : nRecordSize > 10 * 1024 * 1024)
300 : {
301 0 : CPLError(CE_Failure, CPLE_NotSupported,
302 : "One of RECORD_TYPE, FILE_RECORDS or RECORD_BYTES is missing");
303 0 : return FALSE;
304 : }
305 2 : CleanString(osRecordType);
306 2 : if (osRecordType.compare("FIXED_LENGTH") != 0)
307 : {
308 0 : CPLError(CE_Failure, CPLE_NotSupported,
309 : "Only RECORD_TYPE=FIXED_LENGTH is supported");
310 0 : return FALSE;
311 : }
312 :
313 4 : CPLString osTable = oKeywords.GetKeyword("^TABLE", "");
314 2 : if (!osTable.empty())
315 : {
316 2 : LoadTable(pszFilename, nRecordSize, "TABLE");
317 : }
318 : else
319 : {
320 0 : fp = VSIFOpenL(pszFilename, "rb");
321 0 : if (fp == nullptr)
322 0 : return FALSE;
323 :
324 : // To avoid performance issues with datasets generated by oss-fuzz
325 0 : int nErrors = 0;
326 0 : while (nErrors < 10)
327 : {
328 0 : CPLPushErrorHandler(CPLQuietErrorHandler);
329 0 : const char *pszLine = CPLReadLine2L(fp, 256, nullptr);
330 0 : CPLPopErrorHandler();
331 0 : CPLErrorReset();
332 0 : if (pszLine == nullptr)
333 0 : break;
334 : char **papszTokens =
335 0 : CSLTokenizeString2(pszLine, " =", CSLT_HONOURSTRINGS);
336 0 : int nTokens = CSLCount(papszTokens);
337 0 : if (nTokens == 2 && papszTokens[0][0] == '^' &&
338 0 : strstr(papszTokens[0], "TABLE") != nullptr)
339 : {
340 0 : if (!LoadTable(pszFilename, nRecordSize, papszTokens[0] + 1))
341 : {
342 0 : nErrors++;
343 : }
344 : }
345 0 : CSLDestroy(papszTokens);
346 0 : papszTokens = nullptr;
347 : }
348 0 : VSIFCloseL(fp);
349 : }
350 :
351 2 : return nLayers != 0;
352 : }
|