Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: VICAR Driver; JPL/MIPL VICAR Format
4 : * Purpose: Implementation of VICARKeywordHandler - a class to read
5 : * keyword data from VICAR data products.
6 : * Author: Sebastian Walter <sebastian dot walter at fu-berlin dot de>
7 : *
8 : * NOTE: This driver code is loosely based on the ISIS and PDS drivers.
9 : * It is not intended to diminish the contribution of the authors.
10 : ******************************************************************************
11 : * Copyright (c) 2014, Sebastian Walter <sebastian dot walter at fu-berlin dot
12 : *de>
13 : *
14 : * SPDX-License-Identifier: MIT
15 : ****************************************************************************/
16 :
17 : #include "cpl_string.h"
18 : #include "vicarkeywordhandler.h"
19 : #include "vicardataset.h"
20 :
21 : #include <algorithm>
22 : #include <limits>
23 :
24 : /************************************************************************/
25 : /* ==================================================================== */
26 : /* VICARKeywordHandler */
27 : /* ==================================================================== */
28 : /************************************************************************/
29 :
30 : /************************************************************************/
31 : /* VICARKeywordHandler() */
32 : /************************************************************************/
33 :
34 166 : VICARKeywordHandler::VICARKeywordHandler()
35 166 : : papszKeywordList(nullptr), pszHeaderNext(nullptr)
36 : {
37 166 : oJSon.Deinit();
38 166 : }
39 :
40 : /************************************************************************/
41 : /* ~VICARKeywordHandler() */
42 : /************************************************************************/
43 :
44 166 : VICARKeywordHandler::~VICARKeywordHandler()
45 :
46 : {
47 166 : CSLDestroy(papszKeywordList);
48 166 : }
49 :
50 : /************************************************************************/
51 : /* Ingest() */
52 : /************************************************************************/
53 :
54 113 : bool VICARKeywordHandler::Ingest(VSILFILE *fp, const GByte *pabyHeader)
55 :
56 : {
57 : /* -------------------------------------------------------------------- */
58 : /* Read in label at beginning of file. */
59 : /* -------------------------------------------------------------------- */
60 113 : if (VSIFSeekL(fp, 0, SEEK_SET) != 0)
61 0 : return false;
62 :
63 : // Find LBLSIZE Entry
64 : const char *pszLBLSIZE =
65 113 : strstr(reinterpret_cast<const char *>(pabyHeader), "LBLSIZE");
66 113 : if (!pszLBLSIZE)
67 0 : return false;
68 :
69 113 : const char *pch1 = strchr(pszLBLSIZE, '=');
70 113 : if (pch1 == nullptr)
71 0 : return false;
72 113 : ++pch1;
73 121 : while (isspace(static_cast<unsigned char>(*pch1)))
74 8 : ++pch1;
75 113 : const char *pch2 = strchr(pch1, ' ');
76 113 : if (pch2 == nullptr)
77 0 : return false;
78 :
79 226 : std::string keyval;
80 113 : keyval.assign(pch1, static_cast<size_t>(pch2 - pch1));
81 113 : int LabelSize = atoi(keyval.c_str());
82 113 : if (LabelSize <= 0 || LabelSize > 10 * 1024 * 124)
83 0 : return false;
84 :
85 113 : char *pszChunk = reinterpret_cast<char *>(VSIMalloc(LabelSize + 1));
86 113 : if (pszChunk == nullptr)
87 0 : return false;
88 113 : int nBytesRead = static_cast<int>(VSIFReadL(pszChunk, 1, LabelSize, fp));
89 113 : pszChunk[nBytesRead] = '\0';
90 :
91 113 : osHeaderText += pszChunk;
92 113 : VSIFree(pszChunk);
93 113 : pszHeaderNext = osHeaderText.c_str();
94 :
95 : /* -------------------------------------------------------------------- */
96 : /* Process name/value pairs */
97 : /* -------------------------------------------------------------------- */
98 113 : if (!Parse())
99 0 : return false;
100 :
101 : /* -------------------------------------------------------------------- */
102 : /* Now check for the Vicar End-of-Dataset Label... */
103 : /* -------------------------------------------------------------------- */
104 113 : const char *pszResult = CSLFetchNameValueDef(papszKeywordList, "EOL", "0");
105 113 : if (!EQUAL(pszResult, "1"))
106 63 : return true;
107 :
108 : /* -------------------------------------------------------------------- */
109 : /* There is a EOL! e.G. h4231_0000.nd4.06 */
110 : /* -------------------------------------------------------------------- */
111 :
112 : uint64_t nPixelOffset;
113 : uint64_t nLineOffset;
114 : uint64_t nBandOffset;
115 : uint64_t nImageOffsetWithoutNBB;
116 : uint64_t nNBB;
117 : uint64_t nImageSize;
118 50 : if (!VICARDataset::GetSpacings(*this, nPixelOffset, nLineOffset,
119 : nBandOffset, nImageOffsetWithoutNBB, nNBB,
120 : nImageSize))
121 0 : return false;
122 :
123 : // Position of EOL in case of compressed data
124 : const vsi_l_offset nEOCI1 = static_cast<vsi_l_offset>(
125 50 : CPLAtoGIntBig(CSLFetchNameValueDef(papszKeywordList, "EOCI1", "0")));
126 : const vsi_l_offset nEOCI2 = static_cast<vsi_l_offset>(
127 50 : CPLAtoGIntBig(CSLFetchNameValueDef(papszKeywordList, "EOCI2", "0")));
128 50 : const vsi_l_offset nEOCI = (nEOCI2 << 32) | nEOCI1;
129 :
130 50 : if (nImageOffsetWithoutNBB >
131 50 : std::numeric_limits<uint64_t>::max() - nImageSize)
132 : {
133 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid label values");
134 0 : return false;
135 : }
136 :
137 50 : const vsi_l_offset nStartEOL =
138 50 : nEOCI ? nEOCI : nImageOffsetWithoutNBB + nImageSize;
139 :
140 50 : if (VSIFSeekL(fp, nStartEOL, SEEK_SET) != 0)
141 : {
142 0 : CPLError(CE_Failure, CPLE_AppDefined, "Error seeking to EOL");
143 0 : return false;
144 : }
145 50 : char *pszEOLHeader = static_cast<char *>(VSIMalloc(32));
146 50 : if (pszEOLHeader == nullptr)
147 0 : return false;
148 50 : nBytesRead = static_cast<int>(VSIFReadL(pszEOLHeader, 1, 31, fp));
149 50 : pszEOLHeader[nBytesRead] = '\0';
150 50 : pszLBLSIZE = strstr(pszEOLHeader, "LBLSIZE");
151 50 : if (!pszLBLSIZE)
152 : {
153 0 : CPLError(CE_Failure, CPLE_AppDefined,
154 : "END-OF-DATASET LABEL NOT FOUND!");
155 0 : VSIFree(pszEOLHeader);
156 0 : return false;
157 : }
158 50 : pch1 = strchr(pszLBLSIZE, '=');
159 50 : if (pch1 == nullptr)
160 : {
161 0 : CPLError(CE_Failure, CPLE_AppDefined,
162 : "END-OF-DATASET LABEL NOT FOUND!");
163 0 : VSIFree(pszEOLHeader);
164 0 : return false;
165 : }
166 50 : ++pch1;
167 50 : while (isspace(static_cast<unsigned char>(*pch1)))
168 0 : ++pch1;
169 50 : pch2 = strchr(pch1, ' ');
170 50 : if (pch2 == nullptr)
171 : {
172 0 : CPLError(CE_Failure, CPLE_AppDefined,
173 : "END-OF-DATASET LABEL NOT FOUND!");
174 0 : VSIFree(pszEOLHeader);
175 0 : return false;
176 : }
177 50 : keyval.assign(pch1, static_cast<size_t>(pch2 - pch1));
178 50 : const auto nSkipEOLLBLSize = static_cast<size_t>(pch2 - pszEOLHeader);
179 50 : VSIFree(pszEOLHeader);
180 :
181 50 : int EOLabelSize = atoi(keyval.c_str());
182 50 : if (EOLabelSize <= 0 ||
183 50 : static_cast<size_t>(EOLabelSize) <= nSkipEOLLBLSize ||
184 : EOLabelSize > 100 * 1024 * 1024)
185 0 : return false;
186 50 : if (VSIFSeekL(fp, nStartEOL, SEEK_SET) != 0)
187 : {
188 0 : CPLError(CE_Failure, CPLE_AppDefined, "Error seeking to EOL");
189 0 : return false;
190 : }
191 : char *pszChunkEOL =
192 50 : static_cast<char *>(VSI_MALLOC_VERBOSE(EOLabelSize + 1));
193 50 : if (pszChunkEOL == nullptr)
194 0 : return false;
195 50 : nBytesRead = static_cast<int>(VSIFReadL(pszChunkEOL, 1, EOLabelSize, fp));
196 50 : pszChunkEOL[nBytesRead] = '\0';
197 50 : osHeaderText += pszChunkEOL + nSkipEOLLBLSize;
198 50 : VSIFree(pszChunkEOL);
199 50 : CSLDestroy(papszKeywordList);
200 50 : papszKeywordList = nullptr;
201 50 : pszHeaderNext = osHeaderText.c_str();
202 50 : return Parse();
203 : }
204 :
205 : /************************************************************************/
206 : /* Parse() */
207 : /************************************************************************/
208 :
209 : #define SYNTHETIC_END_MARKER "__END__"
210 :
211 163 : bool VICARKeywordHandler::Parse()
212 : {
213 326 : CPLString osName, osValue, osGroupName;
214 326 : CPLJSONObject oProperties;
215 326 : CPLJSONObject oTasks;
216 326 : CPLJSONObject oCurObj;
217 163 : bool bHasProperties = false;
218 163 : bool bHasTasks = false;
219 :
220 163 : oJSon = CPLJSONObject();
221 : for (; true;)
222 : {
223 6769 : if (!ReadPair(osName, osValue, osGroupName.empty() ? oJSon : oCurObj))
224 0 : return false;
225 :
226 6769 : if (EQUAL(osName, SYNTHETIC_END_MARKER))
227 163 : break;
228 :
229 6606 : if (EQUAL(osName, "PROPERTY"))
230 : {
231 114 : osGroupName = osValue;
232 114 : oCurObj = CPLJSONObject();
233 114 : bHasProperties = true;
234 114 : oProperties.Add(osValue, oCurObj);
235 : }
236 6492 : else if (EQUAL(osName, "TASK"))
237 : {
238 149 : osGroupName = osValue;
239 149 : oCurObj = CPLJSONObject();
240 149 : bHasTasks = true;
241 149 : oTasks.Add(osValue, oCurObj);
242 : }
243 : else
244 : {
245 6343 : if (!osGroupName.empty())
246 1879 : osName = osGroupName + "." + osName;
247 6343 : papszKeywordList =
248 6343 : CSLSetNameValue(papszKeywordList, osName, osValue);
249 : }
250 : }
251 163 : if (bHasProperties)
252 62 : oJSon.Add("PROPERTY", oProperties);
253 163 : if (bHasTasks)
254 109 : oJSon.Add("TASK", oTasks);
255 163 : return true;
256 : }
257 :
258 : /************************************************************************/
259 : /* ReadPair() */
260 : /* */
261 : /* Read a name/value pair from the input stream. Strip off */
262 : /* white space, ignore comments, split on '='. */
263 : /* Returns TRUE on success. */
264 : /************************************************************************/
265 :
266 6769 : bool VICARKeywordHandler::ReadPair(CPLString &osName, CPLString &osValue,
267 : CPLJSONObject &oCur)
268 : {
269 6769 : osName.clear();
270 6769 : osValue.clear();
271 :
272 6769 : if (!ReadName(osName))
273 : {
274 : // VICAR has no NULL string termination
275 163 : if (*pszHeaderNext == '\0')
276 : {
277 163 : osName = SYNTHETIC_END_MARKER;
278 163 : return true;
279 : }
280 0 : return false;
281 : }
282 :
283 6606 : bool bIsString = false;
284 6606 : if (*pszHeaderNext == '(')
285 : {
286 186 : CPLString osWord;
287 93 : pszHeaderNext++;
288 186 : CPLJSONArray oArray;
289 93 : oCur.Add(osName, oArray);
290 162 : while (ReadValue(osWord, true, bIsString))
291 : {
292 162 : if (!osValue.empty())
293 69 : osValue += ',';
294 162 : osValue += osWord;
295 162 : if (bIsString)
296 : {
297 24 : oArray.Add(osWord);
298 : }
299 138 : else if (CPLGetValueType(osWord) == CPL_VALUE_INTEGER)
300 : {
301 8 : oArray.Add(atoi(osWord));
302 : }
303 : else
304 : {
305 130 : oArray.Add(CPLAtof(osWord));
306 : }
307 162 : if (*pszHeaderNext == ')')
308 : {
309 93 : pszHeaderNext++;
310 93 : break;
311 : }
312 69 : pszHeaderNext++;
313 : }
314 : }
315 : else
316 : {
317 6513 : if (!ReadValue(osValue, false, bIsString))
318 0 : return false;
319 6513 : if (!EQUAL(osName, "PROPERTY") && !EQUAL(osName, "TASK"))
320 : {
321 6250 : if (bIsString)
322 : {
323 2874 : oCur.Add(osName, osValue);
324 : }
325 3376 : else if (CPLGetValueType(osValue) == CPL_VALUE_INTEGER)
326 : {
327 2772 : oCur.Add(osName, atoi(osValue));
328 : }
329 : else
330 : {
331 604 : oCur.Add(osName, CPLAtof(osValue));
332 : }
333 : }
334 : }
335 :
336 6606 : return true;
337 : }
338 :
339 : /************************************************************************/
340 : /* ReadName() */
341 : /************************************************************************/
342 :
343 6769 : bool VICARKeywordHandler::ReadName(CPLString &osWord)
344 :
345 : {
346 6769 : osWord.clear();
347 :
348 6769 : SkipWhite();
349 :
350 6769 : if (*pszHeaderNext == '\0')
351 163 : return false;
352 :
353 53804 : while (*pszHeaderNext != '=' &&
354 47206 : !isspace(static_cast<unsigned char>(*pszHeaderNext)))
355 : {
356 47198 : if (*pszHeaderNext == '\0')
357 0 : return false;
358 47198 : osWord += *pszHeaderNext;
359 47198 : pszHeaderNext++;
360 : }
361 :
362 6606 : SkipWhite();
363 :
364 6606 : if (*pszHeaderNext != '=')
365 0 : return false;
366 6606 : pszHeaderNext++;
367 :
368 6606 : SkipWhite();
369 :
370 6606 : return true;
371 : }
372 :
373 : /************************************************************************/
374 : /* ReadWord() */
375 : /************************************************************************/
376 :
377 6675 : bool VICARKeywordHandler::ReadValue(CPLString &osWord, bool bInList,
378 : bool &bIsString)
379 :
380 : {
381 6675 : osWord.clear();
382 :
383 6675 : SkipWhite();
384 :
385 6675 : if (*pszHeaderNext == '\0')
386 0 : return false;
387 :
388 6675 : if (*pszHeaderNext == '\'')
389 : {
390 3151 : bIsString = true;
391 3151 : pszHeaderNext++;
392 : while (true)
393 : {
394 24057 : if (*pszHeaderNext == '\0')
395 0 : return false;
396 24057 : if (*(pszHeaderNext) == '\'')
397 : {
398 3163 : if (*(pszHeaderNext + 1) == '\'')
399 : {
400 : // Skip Double Quotes
401 12 : pszHeaderNext++;
402 : }
403 : else
404 3151 : break;
405 : }
406 20906 : osWord += *pszHeaderNext;
407 20906 : pszHeaderNext++;
408 : }
409 3151 : pszHeaderNext++;
410 : }
411 : else
412 : {
413 11447 : while (!isspace(static_cast<unsigned char>(*pszHeaderNext)))
414 : {
415 8092 : if (*pszHeaderNext == '\0')
416 31 : return !bInList;
417 8061 : if (bInList && (*pszHeaderNext == ',' || *pszHeaderNext == ')'))
418 : {
419 138 : return true;
420 : }
421 7923 : osWord += *pszHeaderNext;
422 7923 : pszHeaderNext++;
423 : }
424 3355 : bIsString = CPLGetValueType(osWord) == CPL_VALUE_STRING;
425 : }
426 :
427 6506 : SkipWhite();
428 6506 : if (bInList && *pszHeaderNext != ',' && *pszHeaderNext != ')')
429 0 : return false;
430 :
431 6506 : return true;
432 : }
433 :
434 : /************************************************************************/
435 : /* SkipWhite() */
436 : /* Skip white spaces */
437 : /************************************************************************/
438 :
439 47603 : void VICARKeywordHandler::SkipWhite()
440 :
441 : {
442 : for (; true;)
443 : {
444 47603 : if (isspace(static_cast<unsigned char>(*pszHeaderNext)))
445 : {
446 14441 : pszHeaderNext++;
447 14441 : continue;
448 : }
449 :
450 : // not white space, return.
451 33162 : return;
452 : }
453 : }
454 :
455 : /************************************************************************/
456 : /* GetKeyword() */
457 : /************************************************************************/
458 :
459 5028 : const char *VICARKeywordHandler::GetKeyword(const char *pszPath,
460 : const char *pszDefault) const
461 :
462 : {
463 5028 : const char *pszResult = CSLFetchNameValue(papszKeywordList, pszPath);
464 :
465 5028 : if (pszResult == nullptr)
466 1809 : return pszDefault;
467 :
468 3219 : return pszResult;
469 : }
|