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 : * Permission is hereby granted, free of charge, to any person obtaining a
15 : * copy of this software and associated documentation files (the "Software"),
16 : * to deal in the Software without restriction, including without limitation
17 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 : * and/or sell copies of the Software, and to permit persons to whom the
19 : * Software is furnished to do so, subject to the following conditions:
20 : *
21 : * The above copyright notice and this permission notice shall be included
22 : * in all copies or substantial portions of the Software.
23 : *
24 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 : * DEALINGS IN THE SOFTWARE.
31 : ****************************************************************************/
32 :
33 : #include "cpl_string.h"
34 : #include "vicarkeywordhandler.h"
35 : #include "vicardataset.h"
36 :
37 : #include <algorithm>
38 : #include <limits>
39 :
40 : /************************************************************************/
41 : /* ==================================================================== */
42 : /* VICARKeywordHandler */
43 : /* ==================================================================== */
44 : /************************************************************************/
45 :
46 : /************************************************************************/
47 : /* VICARKeywordHandler() */
48 : /************************************************************************/
49 :
50 165 : VICARKeywordHandler::VICARKeywordHandler()
51 165 : : papszKeywordList(nullptr), pszHeaderNext(nullptr)
52 : {
53 165 : oJSon.Deinit();
54 165 : }
55 :
56 : /************************************************************************/
57 : /* ~VICARKeywordHandler() */
58 : /************************************************************************/
59 :
60 165 : VICARKeywordHandler::~VICARKeywordHandler()
61 :
62 : {
63 165 : CSLDestroy(papszKeywordList);
64 165 : }
65 :
66 : /************************************************************************/
67 : /* Ingest() */
68 : /************************************************************************/
69 :
70 112 : bool VICARKeywordHandler::Ingest(VSILFILE *fp, const GByte *pabyHeader)
71 :
72 : {
73 : /* -------------------------------------------------------------------- */
74 : /* Read in label at beginning of file. */
75 : /* -------------------------------------------------------------------- */
76 112 : if (VSIFSeekL(fp, 0, SEEK_SET) != 0)
77 0 : return false;
78 :
79 : // Find LBLSIZE Entry
80 : const char *pszLBLSIZE =
81 112 : strstr(reinterpret_cast<const char *>(pabyHeader), "LBLSIZE");
82 112 : if (!pszLBLSIZE)
83 0 : return false;
84 :
85 112 : const char *pch1 = strchr(pszLBLSIZE, '=');
86 112 : if (pch1 == nullptr)
87 0 : return false;
88 112 : ++pch1;
89 120 : while (isspace(static_cast<unsigned char>(*pch1)))
90 8 : ++pch1;
91 112 : const char *pch2 = strchr(pch1, ' ');
92 112 : if (pch2 == nullptr)
93 0 : return false;
94 :
95 224 : std::string keyval;
96 112 : keyval.assign(pch1, static_cast<size_t>(pch2 - pch1));
97 112 : int LabelSize = atoi(keyval.c_str());
98 112 : if (LabelSize <= 0 || LabelSize > 10 * 1024 * 124)
99 0 : return false;
100 :
101 112 : char *pszChunk = reinterpret_cast<char *>(VSIMalloc(LabelSize + 1));
102 112 : if (pszChunk == nullptr)
103 0 : return false;
104 112 : int nBytesRead = static_cast<int>(VSIFReadL(pszChunk, 1, LabelSize, fp));
105 112 : pszChunk[nBytesRead] = '\0';
106 :
107 112 : osHeaderText += pszChunk;
108 112 : VSIFree(pszChunk);
109 112 : pszHeaderNext = osHeaderText.c_str();
110 :
111 : /* -------------------------------------------------------------------- */
112 : /* Process name/value pairs */
113 : /* -------------------------------------------------------------------- */
114 112 : if (!Parse())
115 0 : return false;
116 :
117 : /* -------------------------------------------------------------------- */
118 : /* Now check for the Vicar End-of-Dataset Label... */
119 : /* -------------------------------------------------------------------- */
120 112 : const char *pszResult = CSLFetchNameValueDef(papszKeywordList, "EOL", "0");
121 112 : if (!EQUAL(pszResult, "1"))
122 62 : return true;
123 :
124 : /* -------------------------------------------------------------------- */
125 : /* There is a EOL! e.G. h4231_0000.nd4.06 */
126 : /* -------------------------------------------------------------------- */
127 :
128 : GUInt64 nPixelOffset;
129 : GUInt64 nLineOffset;
130 : GUInt64 nBandOffset;
131 : GUInt64 nImageOffsetWithoutNBB;
132 : GUInt64 nNBB;
133 : GUInt64 nImageSize;
134 50 : if (!VICARDataset::GetSpacings(*this, nPixelOffset, nLineOffset,
135 : nBandOffset, nImageOffsetWithoutNBB, nNBB,
136 : nImageSize))
137 0 : return false;
138 :
139 : // Position of EOL in case of compressed data
140 : const vsi_l_offset nEOCI1 = static_cast<vsi_l_offset>(
141 50 : CPLAtoGIntBig(CSLFetchNameValueDef(papszKeywordList, "EOCI1", "0")));
142 : const vsi_l_offset nEOCI2 = static_cast<vsi_l_offset>(
143 50 : CPLAtoGIntBig(CSLFetchNameValueDef(papszKeywordList, "EOCI2", "0")));
144 50 : const vsi_l_offset nEOCI = (nEOCI2 << 32) | nEOCI1;
145 :
146 50 : if (nImageOffsetWithoutNBB >
147 50 : std::numeric_limits<GUInt64>::max() - nImageSize)
148 : {
149 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid label values");
150 0 : return false;
151 : }
152 :
153 50 : const vsi_l_offset nStartEOL =
154 50 : nEOCI ? nEOCI : nImageOffsetWithoutNBB + nImageSize;
155 :
156 50 : if (VSIFSeekL(fp, nStartEOL, SEEK_SET) != 0)
157 : {
158 0 : CPLError(CE_Failure, CPLE_AppDefined, "Error seeking to EOL");
159 0 : return false;
160 : }
161 50 : char *pszEOLHeader = static_cast<char *>(VSIMalloc(32));
162 50 : if (pszEOLHeader == nullptr)
163 0 : return false;
164 50 : nBytesRead = static_cast<int>(VSIFReadL(pszEOLHeader, 1, 31, fp));
165 50 : pszEOLHeader[nBytesRead] = '\0';
166 50 : pszLBLSIZE = strstr(pszEOLHeader, "LBLSIZE");
167 50 : if (!pszLBLSIZE)
168 : {
169 0 : CPLError(CE_Failure, CPLE_AppDefined,
170 : "END-OF-DATASET LABEL NOT FOUND!");
171 0 : VSIFree(pszEOLHeader);
172 0 : return false;
173 : }
174 50 : pch1 = strchr(pszLBLSIZE, '=');
175 50 : if (pch1 == nullptr)
176 : {
177 0 : CPLError(CE_Failure, CPLE_AppDefined,
178 : "END-OF-DATASET LABEL NOT FOUND!");
179 0 : VSIFree(pszEOLHeader);
180 0 : return false;
181 : }
182 50 : ++pch1;
183 50 : while (isspace(static_cast<unsigned char>(*pch1)))
184 0 : ++pch1;
185 50 : pch2 = strchr(pch1, ' ');
186 50 : if (pch2 == nullptr)
187 : {
188 0 : CPLError(CE_Failure, CPLE_AppDefined,
189 : "END-OF-DATASET LABEL NOT FOUND!");
190 0 : VSIFree(pszEOLHeader);
191 0 : return false;
192 : }
193 50 : keyval.assign(pch1, static_cast<size_t>(pch2 - pch1));
194 50 : const auto nSkipEOLLBLSize = static_cast<size_t>(pch2 - pszEOLHeader);
195 50 : VSIFree(pszEOLHeader);
196 :
197 50 : int EOLabelSize = atoi(keyval.c_str());
198 50 : if (EOLabelSize <= 0 ||
199 50 : static_cast<size_t>(EOLabelSize) <= nSkipEOLLBLSize ||
200 : EOLabelSize > 100 * 1024 * 1024)
201 0 : return false;
202 50 : if (VSIFSeekL(fp, nStartEOL, SEEK_SET) != 0)
203 : {
204 0 : CPLError(CE_Failure, CPLE_AppDefined, "Error seeking to EOL");
205 0 : return false;
206 : }
207 50 : char *pszChunkEOL = (char *)VSIMalloc(EOLabelSize + 1);
208 50 : if (pszChunkEOL == nullptr)
209 0 : return false;
210 50 : nBytesRead = static_cast<int>(VSIFReadL(pszChunkEOL, 1, EOLabelSize, fp));
211 50 : pszChunkEOL[nBytesRead] = '\0';
212 50 : osHeaderText += pszChunkEOL + nSkipEOLLBLSize;
213 50 : VSIFree(pszChunkEOL);
214 50 : CSLDestroy(papszKeywordList);
215 50 : papszKeywordList = nullptr;
216 50 : pszHeaderNext = osHeaderText.c_str();
217 50 : return Parse();
218 : }
219 :
220 : /************************************************************************/
221 : /* Parse() */
222 : /************************************************************************/
223 :
224 : #define SYNTHETIC_END_MARKER "__END__"
225 :
226 162 : bool VICARKeywordHandler::Parse()
227 : {
228 324 : CPLString osName, osValue, osGroupName;
229 324 : CPLJSONObject oProperties;
230 324 : CPLJSONObject oTasks;
231 324 : CPLJSONObject oCurObj;
232 162 : bool bHasProperties = false;
233 162 : bool bHasTasks = false;
234 :
235 162 : oJSon = CPLJSONObject();
236 : for (; true;)
237 : {
238 6744 : if (!ReadPair(osName, osValue, osGroupName.empty() ? oJSon : oCurObj))
239 0 : return false;
240 :
241 6744 : if (EQUAL(osName, SYNTHETIC_END_MARKER))
242 162 : break;
243 :
244 6582 : if (EQUAL(osName, "PROPERTY"))
245 : {
246 114 : osGroupName = osValue;
247 114 : oCurObj = CPLJSONObject();
248 114 : bHasProperties = true;
249 114 : oProperties.Add(osValue, oCurObj);
250 : }
251 6468 : else if (EQUAL(osName, "TASK"))
252 : {
253 149 : osGroupName = osValue;
254 149 : oCurObj = CPLJSONObject();
255 149 : bHasTasks = true;
256 149 : oTasks.Add(osValue, oCurObj);
257 : }
258 : else
259 : {
260 6319 : if (!osGroupName.empty())
261 1879 : osName = osGroupName + "." + osName;
262 6319 : papszKeywordList =
263 6319 : CSLSetNameValue(papszKeywordList, osName, osValue);
264 : }
265 : }
266 162 : if (bHasProperties)
267 62 : oJSon.Add("PROPERTY", oProperties);
268 162 : if (bHasTasks)
269 109 : oJSon.Add("TASK", oTasks);
270 162 : return true;
271 : }
272 :
273 : /************************************************************************/
274 : /* ReadPair() */
275 : /* */
276 : /* Read a name/value pair from the input stream. Strip off */
277 : /* white space, ignore comments, split on '='. */
278 : /* Returns TRUE on success. */
279 : /************************************************************************/
280 :
281 6744 : bool VICARKeywordHandler::ReadPair(CPLString &osName, CPLString &osValue,
282 : CPLJSONObject &oCur)
283 : {
284 6744 : osName.clear();
285 6744 : osValue.clear();
286 :
287 6744 : if (!ReadName(osName))
288 : {
289 : // VICAR has no NULL string termination
290 162 : if (*pszHeaderNext == '\0')
291 : {
292 162 : osName = SYNTHETIC_END_MARKER;
293 162 : return true;
294 : }
295 0 : return false;
296 : }
297 :
298 6582 : bool bIsString = false;
299 6582 : if (*pszHeaderNext == '(')
300 : {
301 186 : CPLString osWord;
302 93 : pszHeaderNext++;
303 186 : CPLJSONArray oArray;
304 93 : oCur.Add(osName, oArray);
305 162 : while (ReadValue(osWord, true, bIsString))
306 : {
307 162 : if (!osValue.empty())
308 69 : osValue += ',';
309 162 : osValue += osWord;
310 162 : if (bIsString)
311 : {
312 24 : oArray.Add(osWord);
313 : }
314 138 : else if (CPLGetValueType(osWord) == CPL_VALUE_INTEGER)
315 : {
316 8 : oArray.Add(atoi(osWord));
317 : }
318 : else
319 : {
320 130 : oArray.Add(CPLAtof(osWord));
321 : }
322 162 : if (*pszHeaderNext == ')')
323 : {
324 93 : pszHeaderNext++;
325 93 : break;
326 : }
327 69 : pszHeaderNext++;
328 : }
329 : }
330 : else
331 : {
332 6489 : if (!ReadValue(osValue, false, bIsString))
333 0 : return false;
334 6489 : if (!EQUAL(osName, "PROPERTY") && !EQUAL(osName, "TASK"))
335 : {
336 6226 : if (bIsString)
337 : {
338 2864 : oCur.Add(osName, osValue);
339 : }
340 3362 : else if (CPLGetValueType(osValue) == CPL_VALUE_INTEGER)
341 : {
342 2758 : oCur.Add(osName, atoi(osValue));
343 : }
344 : else
345 : {
346 604 : oCur.Add(osName, CPLAtof(osValue));
347 : }
348 : }
349 : }
350 :
351 6582 : return true;
352 : }
353 :
354 : /************************************************************************/
355 : /* ReadName() */
356 : /************************************************************************/
357 :
358 6744 : bool VICARKeywordHandler::ReadName(CPLString &osWord)
359 :
360 : {
361 6744 : osWord.clear();
362 :
363 6744 : SkipWhite();
364 :
365 6744 : if (*pszHeaderNext == '\0')
366 162 : return false;
367 :
368 53678 : while (*pszHeaderNext != '=' && !isspace((unsigned char)*pszHeaderNext))
369 : {
370 47096 : if (*pszHeaderNext == '\0')
371 0 : return false;
372 47096 : osWord += *pszHeaderNext;
373 47096 : pszHeaderNext++;
374 : }
375 :
376 6582 : SkipWhite();
377 :
378 6582 : if (*pszHeaderNext != '=')
379 0 : return false;
380 6582 : pszHeaderNext++;
381 :
382 6582 : SkipWhite();
383 :
384 6582 : return true;
385 : }
386 :
387 : /************************************************************************/
388 : /* ReadWord() */
389 : /************************************************************************/
390 :
391 6651 : bool VICARKeywordHandler::ReadValue(CPLString &osWord, bool bInList,
392 : bool &bIsString)
393 :
394 : {
395 6651 : osWord.clear();
396 :
397 6651 : SkipWhite();
398 :
399 6651 : if (*pszHeaderNext == '\0')
400 0 : return false;
401 :
402 6651 : if (*pszHeaderNext == '\'')
403 : {
404 3141 : bIsString = true;
405 3141 : pszHeaderNext++;
406 : while (true)
407 : {
408 24003 : if (*pszHeaderNext == '\0')
409 0 : return false;
410 24003 : if (*(pszHeaderNext) == '\'')
411 : {
412 3153 : if (*(pszHeaderNext + 1) == '\'')
413 : {
414 : // Skip Double Quotes
415 12 : pszHeaderNext++;
416 : }
417 : else
418 3141 : break;
419 : }
420 20862 : osWord += *pszHeaderNext;
421 20862 : pszHeaderNext++;
422 : }
423 3141 : pszHeaderNext++;
424 : }
425 : else
426 : {
427 11413 : while (!isspace((unsigned char)*pszHeaderNext))
428 : {
429 8072 : if (*pszHeaderNext == '\0')
430 31 : return !bInList;
431 8041 : if (bInList && (*pszHeaderNext == ',' || *pszHeaderNext == ')'))
432 : {
433 138 : return true;
434 : }
435 7903 : osWord += *pszHeaderNext;
436 7903 : pszHeaderNext++;
437 : }
438 3341 : bIsString = CPLGetValueType(osWord) == CPL_VALUE_STRING;
439 : }
440 :
441 6482 : SkipWhite();
442 6482 : if (bInList && *pszHeaderNext != ',' && *pszHeaderNext != ')')
443 0 : return false;
444 :
445 6482 : return true;
446 : }
447 :
448 : /************************************************************************/
449 : /* SkipWhite() */
450 : /* Skip white spaces */
451 : /************************************************************************/
452 :
453 47424 : void VICARKeywordHandler::SkipWhite()
454 :
455 : {
456 : for (; true;)
457 : {
458 47424 : if (isspace((unsigned char)*pszHeaderNext))
459 : {
460 14383 : pszHeaderNext++;
461 14383 : continue;
462 : }
463 :
464 : // not white space, return.
465 33041 : return;
466 : }
467 : }
468 :
469 : /************************************************************************/
470 : /* GetKeyword() */
471 : /************************************************************************/
472 :
473 4999 : const char *VICARKeywordHandler::GetKeyword(const char *pszPath,
474 : const char *pszDefault) const
475 :
476 : {
477 4999 : const char *pszResult = CSLFetchNameValue(papszKeywordList, pszPath);
478 :
479 4999 : if (pszResult == nullptr)
480 1798 : return pszDefault;
481 :
482 3201 : return pszResult;
483 : }
|