Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: ERMapper .ers Driver
4 : * Purpose: Implementation of ERSHdrNode class for parsing/accessing .ers hdr.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.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 "ershdrnode.h"
32 :
33 : /************************************************************************/
34 : /* ERSHdrNode() */
35 : /************************************************************************/
36 :
37 284 : ERSHdrNode::ERSHdrNode()
38 : : nItemMax(0), nItemCount(0), papszItemName(nullptr),
39 284 : papszItemValue(nullptr), papoItemChild(nullptr)
40 : {
41 284 : }
42 :
43 : /************************************************************************/
44 : /* ~ERSHdrNode() */
45 : /************************************************************************/
46 :
47 284 : ERSHdrNode::~ERSHdrNode()
48 :
49 : {
50 1562 : for (int i = 0; i < nItemCount; i++)
51 : {
52 1278 : if (papoItemChild[i] != nullptr)
53 222 : delete papoItemChild[i];
54 1278 : if (papszItemValue[i] != nullptr)
55 1056 : CPLFree(papszItemValue[i]);
56 1278 : CPLFree(papszItemName[i]);
57 : }
58 :
59 284 : CPLFree(papszItemName);
60 284 : CPLFree(papszItemValue);
61 284 : CPLFree(papoItemChild);
62 284 : }
63 :
64 : /************************************************************************/
65 : /* MakeSpace() */
66 : /* */
67 : /* Ensure we have room for at least one more entry in our item */
68 : /* lists. */
69 : /************************************************************************/
70 :
71 1278 : void ERSHdrNode::MakeSpace()
72 :
73 : {
74 1278 : if (nItemCount == nItemMax)
75 : {
76 287 : nItemMax = (int)(nItemMax * 1.3) + 10;
77 287 : papszItemName =
78 287 : (char **)CPLRealloc(papszItemName, sizeof(char *) * nItemMax);
79 287 : papszItemValue =
80 287 : (char **)CPLRealloc(papszItemValue, sizeof(char *) * nItemMax);
81 287 : papoItemChild =
82 287 : (ERSHdrNode **)CPLRealloc(papoItemChild, sizeof(void *) * nItemMax);
83 : }
84 1278 : }
85 :
86 : /************************************************************************/
87 : /* ReadLine() */
88 : /* */
89 : /* Read one virtual line from the input source. Multiple lines */
90 : /* will be appended for objects enclosed in {}. */
91 : /************************************************************************/
92 :
93 1128 : int ERSHdrNode::ReadLine(VSILFILE *fp, CPLString &osLine)
94 :
95 : {
96 1128 : int nBracketLevel = 0;
97 1128 : bool bInQuote = false;
98 1128 : size_t i = 0;
99 1128 : bool bLastCharWasSlashInQuote = false;
100 :
101 1128 : osLine = "";
102 19 : do
103 : {
104 1147 : const char *pszNewLine = CPLReadLineL(fp);
105 :
106 1147 : if (pszNewLine == nullptr)
107 0 : return FALSE;
108 :
109 1147 : osLine += pszNewLine;
110 :
111 24171 : for (; i < osLine.length(); i++)
112 : {
113 23024 : const char ch = osLine[i];
114 23024 : if (bLastCharWasSlashInQuote)
115 : {
116 12 : bLastCharWasSlashInQuote = false;
117 : }
118 23012 : else if (ch == '"')
119 398 : bInQuote = !bInQuote;
120 22614 : else if (ch == '{' && !bInQuote)
121 20 : nBracketLevel++;
122 22594 : else if (ch == '}' && !bInQuote)
123 20 : nBracketLevel--;
124 : // We have to ignore escaped quotes and backslashes in strings.
125 22574 : else if (ch == '\\' && bInQuote)
126 : {
127 12 : bLastCharWasSlashInQuote = true;
128 : }
129 : // A comment is a '#' up to the end of the line.
130 22562 : else if (ch == '#' && !bInQuote)
131 : {
132 12 : osLine = osLine.substr(0, i) + "\n";
133 : }
134 : }
135 1147 : } while (nBracketLevel > 0);
136 :
137 1128 : return TRUE;
138 : }
139 :
140 : /************************************************************************/
141 : /* ParseHeader() */
142 : /* */
143 : /* We receive the FILE * positioned at the start of the file */
144 : /* and read all children. This allows reading comment lines */
145 : /* at the start of the file. */
146 : /************************************************************************/
147 :
148 65 : int ERSHdrNode::ParseHeader(VSILFILE *fp)
149 :
150 : {
151 : while (true)
152 : {
153 : /* --------------------------------------------------------------------
154 : */
155 : /* Read the next line */
156 : /* --------------------------------------------------------------------
157 : */
158 65 : CPLString osLine;
159 : size_t iOff;
160 :
161 65 : if (!ReadLine(fp, osLine))
162 0 : return FALSE;
163 :
164 : /* --------------------------------------------------------------------
165 : */
166 : /* Got a DatasetHeader Begin */
167 : /* --------------------------------------------------------------------
168 : */
169 65 : else if ((iOff = osLine.ifind(" Begin")) != std::string::npos)
170 : {
171 62 : CPLString osName = osLine.substr(0, iOff);
172 62 : osName.Trim();
173 :
174 62 : if (osName.tolower() == CPLString("DatasetHeader").tolower())
175 : {
176 62 : return ParseChildren(fp);
177 : }
178 : }
179 3 : }
180 : }
181 :
182 : /************************************************************************/
183 : /* ParseChildren() */
184 : /* */
185 : /* We receive the FILE * positioned after the "Object Begin" */
186 : /* line for this object, and are responsible for reading all */
187 : /* children. We should return after consuming the */
188 : /* corresponding End line for this object. Really the first */
189 : /* unmatched End since we don't know what object we are. */
190 : /* */
191 : /* This function is used recursively to read sub-objects. */
192 : /************************************************************************/
193 :
194 184 : int ERSHdrNode::ParseChildren(VSILFILE *fp, int nRecLevel)
195 :
196 : {
197 184 : if (nRecLevel == 100) // arbitrary limit
198 : {
199 0 : CPLError(CE_Failure, CPLE_AppDefined,
200 : "Too many recursion level while parsing .ers header");
201 0 : return FALSE;
202 : }
203 :
204 : while (true)
205 : {
206 : /* --------------------------------------------------------------------
207 : */
208 : /* Read the next line (or multi-line for bracketed value). */
209 : /* --------------------------------------------------------------------
210 : */
211 1063 : CPLString osLine;
212 :
213 1063 : if (!ReadLine(fp, osLine))
214 0 : return FALSE;
215 :
216 : /* --------------------------------------------------------------------
217 : */
218 : /* Got a Name=Value. */
219 : /* --------------------------------------------------------------------
220 : */
221 : size_t iOff;
222 :
223 1063 : if ((iOff = osLine.find_first_of('=')) != std::string::npos)
224 : {
225 : CPLString osName =
226 1496 : iOff == 0 ? std::string() : osLine.substr(0, iOff);
227 748 : osName.Trim();
228 :
229 748 : CPLString osValue = osLine.c_str() + iOff + 1;
230 748 : osValue.Trim();
231 :
232 748 : MakeSpace();
233 748 : papszItemName[nItemCount] = CPLStrdup(osName);
234 748 : papszItemValue[nItemCount] = CPLStrdup(osValue);
235 748 : papoItemChild[nItemCount] = nullptr;
236 :
237 748 : nItemCount++;
238 : }
239 :
240 : /* --------------------------------------------------------------------
241 : */
242 : /* Got a Begin for an object. */
243 : /* --------------------------------------------------------------------
244 : */
245 315 : else if ((iOff = osLine.ifind(" Begin")) != std::string::npos)
246 : {
247 122 : CPLString osName = osLine.substr(0, iOff);
248 122 : osName.Trim();
249 :
250 122 : MakeSpace();
251 122 : papszItemName[nItemCount] = CPLStrdup(osName);
252 122 : papszItemValue[nItemCount] = nullptr;
253 122 : papoItemChild[nItemCount] = new ERSHdrNode();
254 :
255 122 : nItemCount++;
256 :
257 122 : if (!papoItemChild[nItemCount - 1]->ParseChildren(fp,
258 : nRecLevel + 1))
259 0 : return FALSE;
260 : }
261 :
262 : /* --------------------------------------------------------------------
263 : */
264 : /* Got an End for our object. Well, at least we *assume* it */
265 : /* must be for our object. */
266 : /* --------------------------------------------------------------------
267 : */
268 193 : else if (osLine.ifind(" End") != std::string::npos)
269 : {
270 184 : return TRUE;
271 : }
272 :
273 : /* --------------------------------------------------------------------
274 : */
275 : /* Error? */
276 : /* --------------------------------------------------------------------
277 : */
278 9 : else if (osLine.Trim().length() > 0)
279 : {
280 0 : CPLError(CE_Failure, CPLE_AppDefined,
281 : "Unexpected line parsing .ecw:\n%s", osLine.c_str());
282 0 : return FALSE;
283 : }
284 879 : }
285 : }
286 :
287 : /************************************************************************/
288 : /* WriteSelf() */
289 : /* */
290 : /* Recursively write self and children to file. */
291 : /************************************************************************/
292 :
293 175 : int ERSHdrNode::WriteSelf(VSILFILE *fp, int nIndent)
294 :
295 : {
296 350 : CPLString oIndent;
297 :
298 175 : oIndent.assign(nIndent, '\t');
299 :
300 959 : for (int i = 0; i < nItemCount; i++)
301 : {
302 784 : if (papszItemValue[i] != nullptr)
303 : {
304 646 : if (VSIFPrintfL(fp, "%s%s\t= %s\n", oIndent.c_str(),
305 1292 : papszItemName[i], papszItemValue[i]) < 1)
306 0 : return FALSE;
307 : }
308 : else
309 : {
310 138 : VSIFPrintfL(fp, "%s%s Begin\n", oIndent.c_str(), papszItemName[i]);
311 138 : if (!papoItemChild[i]->WriteSelf(fp, nIndent + 1))
312 0 : return FALSE;
313 138 : if (VSIFPrintfL(fp, "%s%s End\n", oIndent.c_str(),
314 276 : papszItemName[i]) < 1)
315 0 : return FALSE;
316 : }
317 : }
318 :
319 175 : return TRUE;
320 : }
321 :
322 : /************************************************************************/
323 : /* Find() */
324 : /* */
325 : /* Find the desired entry value. The input is a path with */
326 : /* components separated by dots, relative to the current node. */
327 : /************************************************************************/
328 :
329 2393 : const char *ERSHdrNode::Find(const char *pszPath, const char *pszDefault)
330 :
331 : {
332 : /* -------------------------------------------------------------------- */
333 : /* If this is the final component of the path, search for a */
334 : /* matching child and return the value. */
335 : /* -------------------------------------------------------------------- */
336 2393 : if (strchr(pszPath, '.') == nullptr)
337 : {
338 4573 : for (int i = 0; i < nItemCount; i++)
339 : {
340 4201 : if (EQUAL(pszPath, papszItemName[i]))
341 : {
342 785 : if (papszItemValue[i] != nullptr)
343 : {
344 785 : if (papszItemValue[i][0] == '"')
345 : {
346 : // strip off quotes.
347 67 : osTempReturn = papszItemValue[i];
348 67 : if (osTempReturn.length() < 2)
349 0 : osTempReturn.clear();
350 : else
351 134 : osTempReturn = osTempReturn.substr(
352 134 : 1, osTempReturn.length() - 2);
353 67 : return osTempReturn;
354 : }
355 : else
356 718 : return papszItemValue[i];
357 : }
358 : else
359 0 : return pszDefault;
360 : }
361 : }
362 372 : return pszDefault;
363 : }
364 :
365 : /* -------------------------------------------------------------------- */
366 : /* This is a dot path - extract the first element, find a match */
367 : /* and recurse. */
368 : /* -------------------------------------------------------------------- */
369 2472 : CPLString osPathFirst, osPathRest, osPath = pszPath;
370 :
371 1236 : size_t iDot = osPath.find_first_of('.');
372 1236 : osPathFirst = osPath.substr(0, iDot);
373 1236 : osPathRest = osPath.substr(iDot + 1);
374 :
375 7785 : for (int i = 0; i < nItemCount; i++)
376 : {
377 7550 : if (EQUAL(osPathFirst, papszItemName[i]))
378 : {
379 1001 : if (papoItemChild[i] != nullptr)
380 1001 : return papoItemChild[i]->Find(osPathRest, pszDefault);
381 :
382 0 : return pszDefault;
383 : }
384 : }
385 :
386 235 : return pszDefault;
387 : }
388 :
389 : /************************************************************************/
390 : /* FindElem() */
391 : /* */
392 : /* Find a particular element from an array valued item. */
393 : /************************************************************************/
394 :
395 12 : const char *ERSHdrNode::FindElem(const char *pszPath, int iElem,
396 : const char *pszDefault)
397 :
398 : {
399 12 : const char *pszArray = Find(pszPath, nullptr);
400 :
401 12 : if (pszArray == nullptr)
402 0 : return pszDefault;
403 :
404 12 : bool bDefault = true;
405 : char **papszTokens =
406 12 : CSLTokenizeStringComplex(pszArray, "{ \t}", TRUE, FALSE);
407 12 : if (iElem >= 0 && iElem < CSLCount(papszTokens))
408 : {
409 12 : osTempReturn = papszTokens[iElem];
410 12 : bDefault = false;
411 : }
412 :
413 12 : CSLDestroy(papszTokens);
414 :
415 12 : if (bDefault)
416 0 : return pszDefault;
417 :
418 12 : return osTempReturn;
419 : }
420 :
421 : /************************************************************************/
422 : /* FindNode() */
423 : /* */
424 : /* Find the desired node. */
425 : /************************************************************************/
426 :
427 646 : ERSHdrNode *ERSHdrNode::FindNode(const char *pszPath)
428 :
429 : {
430 1292 : std::string osPathFirst, osPathRest;
431 1292 : const std::string osPath = pszPath;
432 646 : size_t iDot = osPath.find_first_of('.');
433 646 : if (iDot == std::string::npos)
434 : {
435 585 : osPathFirst = osPath;
436 : }
437 : else
438 : {
439 61 : osPathFirst = osPath.substr(0, iDot);
440 61 : osPathRest = osPath.substr(iDot + 1);
441 : }
442 :
443 3995 : for (int i = 0; i < nItemCount; i++)
444 : {
445 3836 : if (EQUAL(osPathFirst.c_str(), papszItemName[i]))
446 : {
447 487 : if (papoItemChild[i] != nullptr)
448 : {
449 487 : if (osPathRest.length() > 0)
450 61 : return papoItemChild[i]->FindNode(osPathRest.c_str());
451 : else
452 426 : return papoItemChild[i];
453 : }
454 : else
455 0 : return nullptr;
456 : }
457 : }
458 :
459 159 : return nullptr;
460 : }
461 :
462 : /************************************************************************/
463 : /* Set() */
464 : /* */
465 : /* Set a value item. */
466 : /************************************************************************/
467 :
468 778 : void ERSHdrNode::Set(const char *pszPath, const char *pszValue)
469 :
470 : {
471 778 : CPLString osPath = pszPath;
472 778 : size_t iDot = osPath.find_first_of('.');
473 :
474 : /* -------------------------------------------------------------------- */
475 : /* We have an intermediate node, find or create it and */
476 : /* recurse. */
477 : /* -------------------------------------------------------------------- */
478 778 : if (iDot != std::string::npos)
479 : {
480 920 : CPLString osPathFirst = osPath.substr(0, iDot);
481 460 : CPLString osPathRest = osPath.substr(iDot + 1);
482 460 : ERSHdrNode *poFirst = FindNode(osPathFirst);
483 :
484 460 : if (poFirst == nullptr)
485 : {
486 100 : poFirst = new ERSHdrNode();
487 :
488 100 : MakeSpace();
489 100 : papszItemName[nItemCount] = CPLStrdup(osPathFirst);
490 100 : papszItemValue[nItemCount] = nullptr;
491 100 : papoItemChild[nItemCount] = poFirst;
492 100 : nItemCount++;
493 : }
494 :
495 460 : poFirst->Set(osPathRest, pszValue);
496 460 : return;
497 : }
498 :
499 : /* -------------------------------------------------------------------- */
500 : /* This is the final item name. Find or create it. */
501 : /* -------------------------------------------------------------------- */
502 763 : for (int i = 0; i < nItemCount; i++)
503 : {
504 455 : if (EQUAL(osPath, papszItemName[i]) && papszItemValue[i] != nullptr)
505 : {
506 10 : CPLFree(papszItemValue[i]);
507 10 : papszItemValue[i] = CPLStrdup(pszValue);
508 10 : return;
509 : }
510 : }
511 :
512 308 : MakeSpace();
513 308 : papszItemName[nItemCount] = CPLStrdup(osPath);
514 308 : papszItemValue[nItemCount] = CPLStrdup(pszValue);
515 308 : papoItemChild[nItemCount] = nullptr;
516 308 : nItemCount++;
517 : }
|