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 : GUInt64 nPixelOffset;
113 : GUInt64 nLineOffset;
114 : GUInt64 nBandOffset;
115 : GUInt64 nImageOffsetWithoutNBB;
116 : GUInt64 nNBB;
117 : GUInt64 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<GUInt64>::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 50 : char *pszChunkEOL = (char *)VSIMalloc(EOLabelSize + 1);
192 50 : if (pszChunkEOL == nullptr)
193 0 : return false;
194 50 : nBytesRead = static_cast<int>(VSIFReadL(pszChunkEOL, 1, EOLabelSize, fp));
195 50 : pszChunkEOL[nBytesRead] = '\0';
196 50 : osHeaderText += pszChunkEOL + nSkipEOLLBLSize;
197 50 : VSIFree(pszChunkEOL);
198 50 : CSLDestroy(papszKeywordList);
199 50 : papszKeywordList = nullptr;
200 50 : pszHeaderNext = osHeaderText.c_str();
201 50 : return Parse();
202 : }
203 :
204 : /************************************************************************/
205 : /* Parse() */
206 : /************************************************************************/
207 :
208 : #define SYNTHETIC_END_MARKER "__END__"
209 :
210 163 : bool VICARKeywordHandler::Parse()
211 : {
212 326 : CPLString osName, osValue, osGroupName;
213 326 : CPLJSONObject oProperties;
214 326 : CPLJSONObject oTasks;
215 326 : CPLJSONObject oCurObj;
216 163 : bool bHasProperties = false;
217 163 : bool bHasTasks = false;
218 :
219 163 : oJSon = CPLJSONObject();
220 : for (; true;)
221 : {
222 6769 : if (!ReadPair(osName, osValue, osGroupName.empty() ? oJSon : oCurObj))
223 0 : return false;
224 :
225 6769 : if (EQUAL(osName, SYNTHETIC_END_MARKER))
226 163 : break;
227 :
228 6606 : if (EQUAL(osName, "PROPERTY"))
229 : {
230 114 : osGroupName = osValue;
231 114 : oCurObj = CPLJSONObject();
232 114 : bHasProperties = true;
233 114 : oProperties.Add(osValue, oCurObj);
234 : }
235 6492 : else if (EQUAL(osName, "TASK"))
236 : {
237 149 : osGroupName = osValue;
238 149 : oCurObj = CPLJSONObject();
239 149 : bHasTasks = true;
240 149 : oTasks.Add(osValue, oCurObj);
241 : }
242 : else
243 : {
244 6343 : if (!osGroupName.empty())
245 1879 : osName = osGroupName + "." + osName;
246 6343 : papszKeywordList =
247 6343 : CSLSetNameValue(papszKeywordList, osName, osValue);
248 : }
249 : }
250 163 : if (bHasProperties)
251 62 : oJSon.Add("PROPERTY", oProperties);
252 163 : if (bHasTasks)
253 109 : oJSon.Add("TASK", oTasks);
254 163 : return true;
255 : }
256 :
257 : /************************************************************************/
258 : /* ReadPair() */
259 : /* */
260 : /* Read a name/value pair from the input stream. Strip off */
261 : /* white space, ignore comments, split on '='. */
262 : /* Returns TRUE on success. */
263 : /************************************************************************/
264 :
265 6769 : bool VICARKeywordHandler::ReadPair(CPLString &osName, CPLString &osValue,
266 : CPLJSONObject &oCur)
267 : {
268 6769 : osName.clear();
269 6769 : osValue.clear();
270 :
271 6769 : if (!ReadName(osName))
272 : {
273 : // VICAR has no NULL string termination
274 163 : if (*pszHeaderNext == '\0')
275 : {
276 163 : osName = SYNTHETIC_END_MARKER;
277 163 : return true;
278 : }
279 0 : return false;
280 : }
281 :
282 6606 : bool bIsString = false;
283 6606 : if (*pszHeaderNext == '(')
284 : {
285 186 : CPLString osWord;
286 93 : pszHeaderNext++;
287 186 : CPLJSONArray oArray;
288 93 : oCur.Add(osName, oArray);
289 162 : while (ReadValue(osWord, true, bIsString))
290 : {
291 162 : if (!osValue.empty())
292 69 : osValue += ',';
293 162 : osValue += osWord;
294 162 : if (bIsString)
295 : {
296 24 : oArray.Add(osWord);
297 : }
298 138 : else if (CPLGetValueType(osWord) == CPL_VALUE_INTEGER)
299 : {
300 8 : oArray.Add(atoi(osWord));
301 : }
302 : else
303 : {
304 130 : oArray.Add(CPLAtof(osWord));
305 : }
306 162 : if (*pszHeaderNext == ')')
307 : {
308 93 : pszHeaderNext++;
309 93 : break;
310 : }
311 69 : pszHeaderNext++;
312 : }
313 : }
314 : else
315 : {
316 6513 : if (!ReadValue(osValue, false, bIsString))
317 0 : return false;
318 6513 : if (!EQUAL(osName, "PROPERTY") && !EQUAL(osName, "TASK"))
319 : {
320 6250 : if (bIsString)
321 : {
322 2874 : oCur.Add(osName, osValue);
323 : }
324 3376 : else if (CPLGetValueType(osValue) == CPL_VALUE_INTEGER)
325 : {
326 2772 : oCur.Add(osName, atoi(osValue));
327 : }
328 : else
329 : {
330 604 : oCur.Add(osName, CPLAtof(osValue));
331 : }
332 : }
333 : }
334 :
335 6606 : return true;
336 : }
337 :
338 : /************************************************************************/
339 : /* ReadName() */
340 : /************************************************************************/
341 :
342 6769 : bool VICARKeywordHandler::ReadName(CPLString &osWord)
343 :
344 : {
345 6769 : osWord.clear();
346 :
347 6769 : SkipWhite();
348 :
349 6769 : if (*pszHeaderNext == '\0')
350 163 : return false;
351 :
352 53804 : while (*pszHeaderNext != '=' && !isspace((unsigned char)*pszHeaderNext))
353 : {
354 47198 : if (*pszHeaderNext == '\0')
355 0 : return false;
356 47198 : osWord += *pszHeaderNext;
357 47198 : pszHeaderNext++;
358 : }
359 :
360 6606 : SkipWhite();
361 :
362 6606 : if (*pszHeaderNext != '=')
363 0 : return false;
364 6606 : pszHeaderNext++;
365 :
366 6606 : SkipWhite();
367 :
368 6606 : return true;
369 : }
370 :
371 : /************************************************************************/
372 : /* ReadWord() */
373 : /************************************************************************/
374 :
375 6675 : bool VICARKeywordHandler::ReadValue(CPLString &osWord, bool bInList,
376 : bool &bIsString)
377 :
378 : {
379 6675 : osWord.clear();
380 :
381 6675 : SkipWhite();
382 :
383 6675 : if (*pszHeaderNext == '\0')
384 0 : return false;
385 :
386 6675 : if (*pszHeaderNext == '\'')
387 : {
388 3151 : bIsString = true;
389 3151 : pszHeaderNext++;
390 : while (true)
391 : {
392 24057 : if (*pszHeaderNext == '\0')
393 0 : return false;
394 24057 : if (*(pszHeaderNext) == '\'')
395 : {
396 3163 : if (*(pszHeaderNext + 1) == '\'')
397 : {
398 : // Skip Double Quotes
399 12 : pszHeaderNext++;
400 : }
401 : else
402 3151 : break;
403 : }
404 20906 : osWord += *pszHeaderNext;
405 20906 : pszHeaderNext++;
406 : }
407 3151 : pszHeaderNext++;
408 : }
409 : else
410 : {
411 11447 : while (!isspace((unsigned char)*pszHeaderNext))
412 : {
413 8092 : if (*pszHeaderNext == '\0')
414 31 : return !bInList;
415 8061 : if (bInList && (*pszHeaderNext == ',' || *pszHeaderNext == ')'))
416 : {
417 138 : return true;
418 : }
419 7923 : osWord += *pszHeaderNext;
420 7923 : pszHeaderNext++;
421 : }
422 3355 : bIsString = CPLGetValueType(osWord) == CPL_VALUE_STRING;
423 : }
424 :
425 6506 : SkipWhite();
426 6506 : if (bInList && *pszHeaderNext != ',' && *pszHeaderNext != ')')
427 0 : return false;
428 :
429 6506 : return true;
430 : }
431 :
432 : /************************************************************************/
433 : /* SkipWhite() */
434 : /* Skip white spaces */
435 : /************************************************************************/
436 :
437 47603 : void VICARKeywordHandler::SkipWhite()
438 :
439 : {
440 : for (; true;)
441 : {
442 47603 : if (isspace((unsigned char)*pszHeaderNext))
443 : {
444 14441 : pszHeaderNext++;
445 14441 : continue;
446 : }
447 :
448 : // not white space, return.
449 33162 : return;
450 : }
451 : }
452 :
453 : /************************************************************************/
454 : /* GetKeyword() */
455 : /************************************************************************/
456 :
457 5028 : const char *VICARKeywordHandler::GetKeyword(const char *pszPath,
458 : const char *pszDefault) const
459 :
460 : {
461 5028 : const char *pszResult = CSLFetchNameValue(papszKeywordList, pszPath);
462 :
463 5028 : if (pszResult == nullptr)
464 1809 : return pszDefault;
465 :
466 3219 : return pszResult;
467 : }
|