Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Common Portability Library
4 : * Purpose: Implementation of CPLKeywordParser - a class for parsing
5 : * the keyword format used for files like QuickBird .RPB files.
6 : * This is a slight variation on the NASAKeywordParser used for
7 : * the PDS/ISIS2/ISIS3 formats.
8 : * Author: Frank Warmerdam <warmerdam@pobox.com
9 : *
10 : ******************************************************************************
11 : * Copyright (c) 2008, Frank Warmerdam <warmerdam@pobox.com>
12 : * Copyright (c) 2009-2010, Even Rouault <even dot rouault at spatialys.com>
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 : //! @cond Doxygen_Suppress
34 :
35 : #include "cpl_port.h"
36 : #include "cplkeywordparser.h"
37 :
38 : #include <cctype>
39 : #include <cstring>
40 : #include <string>
41 :
42 : #include "cpl_string.h"
43 : #include "cpl_vsi.h"
44 :
45 : /************************************************************************/
46 : /* ==================================================================== */
47 : /* CPLKeywordParser */
48 : /* ==================================================================== */
49 : /************************************************************************/
50 :
51 : /************************************************************************/
52 : /* CPLKeywordParser() */
53 : /************************************************************************/
54 :
55 : CPLKeywordParser::CPLKeywordParser() = default;
56 :
57 : /************************************************************************/
58 : /* ~CPLKeywordParser() */
59 : /************************************************************************/
60 :
61 159 : CPLKeywordParser::~CPLKeywordParser()
62 :
63 : {
64 53 : CSLDestroy(papszKeywordList);
65 53 : papszKeywordList = nullptr;
66 53 : }
67 :
68 : /************************************************************************/
69 : /* Ingest() */
70 : /************************************************************************/
71 :
72 212 : int CPLKeywordParser::Ingest(VSILFILE *fp)
73 :
74 : {
75 : /* -------------------------------------------------------------------- */
76 : /* Read in buffer till we find END all on its own line. */
77 : /* -------------------------------------------------------------------- */
78 : for (; true;)
79 : {
80 212 : char szChunk[513] = {};
81 212 : const size_t nBytesRead = VSIFReadL(szChunk, 1, 512, fp);
82 :
83 212 : szChunk[nBytesRead] = '\0';
84 212 : osHeaderText += szChunk;
85 :
86 212 : if (nBytesRead < 512)
87 53 : break;
88 :
89 159 : const char *pszCheck = nullptr;
90 159 : if (osHeaderText.size() > 520)
91 117 : pszCheck = osHeaderText.c_str() + (osHeaderText.size() - 520);
92 : else
93 42 : pszCheck = szChunk;
94 :
95 159 : if (strstr(pszCheck, "\r\nEND;\r\n") != nullptr ||
96 159 : strstr(pszCheck, "\nEND;\n") != nullptr)
97 : break;
98 159 : }
99 :
100 53 : pszHeaderNext = osHeaderText.c_str();
101 :
102 : /* -------------------------------------------------------------------- */
103 : /* Process name/value pairs, keeping track of a "path stack". */
104 : /* -------------------------------------------------------------------- */
105 53 : return ReadGroup("", 0);
106 : }
107 :
108 : /************************************************************************/
109 : /* ReadGroup() */
110 : /************************************************************************/
111 :
112 161 : bool CPLKeywordParser::ReadGroup(const char *pszPathPrefix, int nRecLevel)
113 :
114 : {
115 322 : CPLString osName;
116 322 : CPLString osValue;
117 :
118 : // Arbitrary threshold to avoid stack overflow
119 161 : if (nRecLevel == 100)
120 0 : return false;
121 :
122 : for (; true;)
123 : {
124 2749 : if (!ReadPair(osName, osValue))
125 4 : return false;
126 :
127 2745 : if (EQUAL(osName, "BEGIN_GROUP") || EQUAL(osName, "GROUP"))
128 : {
129 108 : if (!ReadGroup((CPLString(pszPathPrefix) + osValue + ".").c_str(),
130 : nRecLevel + 1))
131 4 : return false;
132 : }
133 2637 : else if (STARTS_WITH_CI(osName, "END"))
134 : {
135 153 : return true;
136 : }
137 : else
138 : {
139 2484 : osName = pszPathPrefix + osName;
140 2484 : papszKeywordList =
141 2484 : CSLSetNameValue(papszKeywordList, osName, osValue);
142 : }
143 : }
144 : }
145 :
146 : /************************************************************************/
147 : /* ReadPair() */
148 : /* */
149 : /* Read a name/value pair from the input stream. Strip off */
150 : /* white space, ignore comments, split on '='. */
151 : /************************************************************************/
152 :
153 2749 : bool CPLKeywordParser::ReadPair(CPLString &osName, CPLString &osValue)
154 :
155 : {
156 2749 : osName = "";
157 2749 : osValue = "";
158 :
159 2749 : if (!ReadWord(osName))
160 0 : return false;
161 :
162 2749 : SkipWhite();
163 :
164 2749 : if (EQUAL(osName, "END"))
165 49 : return TRUE;
166 :
167 2700 : if (*pszHeaderNext != '=')
168 : {
169 : // ISIS3 does not have anything after the end group/object keyword.
170 5 : return EQUAL(osName, "End_Group") || EQUAL(osName, "End_Object");
171 : }
172 :
173 2695 : pszHeaderNext++;
174 :
175 2695 : SkipWhite();
176 :
177 2695 : osValue = "";
178 :
179 : // Handle value lists like: Name = (Red, Red)
180 : // or list of lists like: TLCList = ( (0, 0.000000), (8299, 4.811014) );
181 2695 : if (*pszHeaderNext == '(')
182 : {
183 184 : CPLString osWord;
184 92 : int nDepth = 0;
185 92 : const char *pszLastPos = pszHeaderNext;
186 :
187 1456 : while (ReadWord(osWord) && pszLastPos != pszHeaderNext)
188 : {
189 1456 : SkipWhite();
190 1456 : pszLastPos = pszHeaderNext;
191 :
192 1456 : osValue += osWord;
193 1456 : const char *pszIter = osWord.c_str();
194 1456 : bool bInQuote = false;
195 18799 : while (*pszIter != '\0')
196 : {
197 17435 : if (*pszIter == '"')
198 0 : bInQuote = !bInQuote;
199 17435 : else if (!bInQuote)
200 : {
201 17435 : if (*pszIter == '(')
202 92 : nDepth++;
203 17343 : else if (*pszIter == ')')
204 : {
205 92 : nDepth--;
206 92 : if (nDepth == 0)
207 92 : break;
208 : }
209 : }
210 17343 : pszIter++;
211 : }
212 1456 : if (*pszIter == ')' && nDepth == 0)
213 92 : break;
214 : }
215 : }
216 :
217 : else // Handle more normal "single word" values.
218 : {
219 : // Special case to handle non-conformant IMD files generated by
220 : // previous GDAL version where we omit to surround values that have
221 : // spaces with double quotes.
222 : // So we use a heuristics to handle things like:
223 : // key = value with spaces without single or double quotes at
224 : // beginning of value;[\r]\n
225 2603 : const char *pszNextLF = strchr(pszHeaderNext, '\n');
226 2603 : if (pszNextLF)
227 : {
228 2603 : std::string osTxt(pszHeaderNext, pszNextLF - pszHeaderNext);
229 2603 : const auto nCRPos = osTxt.find('\r');
230 2603 : const auto nSemiColonPos = osTxt.find(';');
231 2603 : const auto nQuotePos = osTxt.find('\'');
232 2603 : const auto nDoubleQuotePos = osTxt.find('"');
233 2603 : const auto nLTPos = osTxt.find('<');
234 2354 : if (nSemiColonPos != std::string::npos &&
235 2354 : (nCRPos == std::string::npos || (nCRPos + 1 == osTxt.size())) &&
236 1 : ((nCRPos != std::string::npos &&
237 2354 : (nSemiColonPos + 1 == nCRPos)) ||
238 2353 : (nCRPos == std::string::npos &&
239 4704 : (nSemiColonPos + 1 == osTxt.size()))) &&
240 2351 : (nQuotePos == std::string::npos || nQuotePos != 0) &&
241 595 : (nDoubleQuotePos == std::string::npos ||
242 4957 : nDoubleQuotePos != 0) &&
243 0 : (nLTPos == std::string::npos ||
244 0 : osTxt.find('>') == std::string::npos))
245 : {
246 1741 : pszHeaderNext = pszNextLF;
247 1741 : osTxt.resize(nSemiColonPos);
248 1741 : osValue = osTxt;
249 1742 : while (!osValue.empty() && osValue.back() == ' ')
250 1 : osValue.resize(osValue.size() - 1);
251 1741 : return true;
252 : }
253 : }
254 :
255 862 : if (!ReadWord(osValue))
256 0 : return false;
257 : }
258 :
259 954 : SkipWhite();
260 :
261 : // No units keyword?
262 954 : if (*pszHeaderNext != '<')
263 954 : return true;
264 :
265 : // Append units keyword. For lines that like like this:
266 : // MAP_RESOLUTION = 4.0 <PIXEL/DEGREE>
267 :
268 0 : CPLString osWord;
269 :
270 0 : osValue += " ";
271 :
272 0 : while (ReadWord(osWord))
273 : {
274 0 : SkipWhite();
275 :
276 0 : osValue += osWord;
277 0 : if (osWord.back() == '>')
278 0 : break;
279 : }
280 :
281 0 : return true;
282 : }
283 :
284 : /************************************************************************/
285 : /* ReadWord() */
286 : /************************************************************************/
287 :
288 5067 : bool CPLKeywordParser::ReadWord(CPLString &osWord)
289 :
290 : {
291 5067 : osWord = "";
292 :
293 5067 : SkipWhite();
294 :
295 5067 : if (*pszHeaderNext == '\0' || *pszHeaderNext == '=')
296 0 : return false;
297 :
298 98965 : while (*pszHeaderNext != '\0' && *pszHeaderNext != '=' &&
299 104032 : *pszHeaderNext != ';' &&
300 51264 : !isspace(static_cast<unsigned char>(*pszHeaderNext)))
301 : {
302 46949 : if (*pszHeaderNext == '"')
303 : {
304 608 : osWord += *(pszHeaderNext++);
305 4152 : while (*pszHeaderNext != '"')
306 : {
307 3544 : if (*pszHeaderNext == '\0')
308 0 : return false;
309 :
310 3544 : osWord += *(pszHeaderNext++);
311 : }
312 608 : osWord += *(pszHeaderNext++);
313 : }
314 46341 : else if (*pszHeaderNext == '\'')
315 : {
316 15 : osWord += *(pszHeaderNext++);
317 264 : while (*pszHeaderNext != '\'')
318 : {
319 249 : if (*pszHeaderNext == '\0')
320 0 : return false;
321 :
322 249 : osWord += *(pszHeaderNext++);
323 : }
324 15 : osWord += *(pszHeaderNext++);
325 : }
326 : else
327 : {
328 46326 : osWord += *pszHeaderNext;
329 46326 : pszHeaderNext++;
330 : }
331 : }
332 :
333 5067 : if (*pszHeaderNext == ';')
334 752 : pszHeaderNext++;
335 :
336 5067 : return true;
337 : }
338 :
339 : /************************************************************************/
340 : /* SkipWhite() */
341 : /************************************************************************/
342 :
343 29395 : void CPLKeywordParser::SkipWhite()
344 :
345 : {
346 : for (; true;)
347 : {
348 : // Skip white space (newline, space, tab, etc )
349 29395 : if (isspace(static_cast<unsigned char>(*pszHeaderNext)))
350 : {
351 16474 : pszHeaderNext++;
352 16474 : continue;
353 : }
354 :
355 : // Skip C style comments
356 12921 : if (*pszHeaderNext == '/' && pszHeaderNext[1] == '*')
357 : {
358 0 : pszHeaderNext += 2;
359 :
360 0 : while (*pszHeaderNext != '\0' &&
361 0 : (*pszHeaderNext != '*' || pszHeaderNext[1] != '/'))
362 : {
363 0 : pszHeaderNext++;
364 : }
365 0 : if (*pszHeaderNext == '\0')
366 0 : break;
367 :
368 0 : pszHeaderNext += 2;
369 0 : continue;
370 : }
371 :
372 : // Skip # style comments
373 12921 : if (*pszHeaderNext == '#')
374 : {
375 0 : pszHeaderNext += 1;
376 :
377 : // consume till end of line.
378 0 : while (*pszHeaderNext != '\0' && *pszHeaderNext != 10 &&
379 0 : *pszHeaderNext != 13)
380 : {
381 0 : pszHeaderNext++;
382 : }
383 0 : continue;
384 : }
385 :
386 : // not white space, return.
387 12921 : return;
388 : }
389 : }
390 :
391 : /************************************************************************/
392 : /* GetKeyword() */
393 : /************************************************************************/
394 :
395 160 : const char *CPLKeywordParser::GetKeyword(const char *pszPath,
396 : const char *pszDefault)
397 :
398 : {
399 160 : const char *pszResult = CSLFetchNameValue(papszKeywordList, pszPath);
400 160 : if (pszResult == nullptr)
401 0 : return pszDefault;
402 :
403 160 : return pszResult;
404 : }
405 :
406 : //! @endcond
|