Line data Source code
1 : /**********************************************************************
2 : *
3 : * Name: avc_e00read.c
4 : * Project: Arc/Info vector coverage (AVC) BIN->E00 conversion library
5 : * Language: ANSI C
6 : * Purpose: Functions to open a binary coverage and read it as if it
7 : * was an ASCII E00 file. This file is the main entry point
8 : * for the library.
9 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
10 : *
11 : **********************************************************************
12 : * Copyright (c) 1999-2005, Daniel Morissette
13 : *
14 : * SPDX-License-Identifier: MIT
15 : **********************************************************************
16 : *
17 : * $Log: avc_e00read.c,v $
18 : * Revision 1.28 2008/07/30 19:22:18 dmorissette
19 : * Move detection of EXP header directly in AVCE00ReadOpenE00() and use
20 : * VSIFGets() instead of CPLReadLine() to avoid problem with huge one line
21 : * files (GDAL/OGR ticket #1989)
22 : *
23 : * Revision 1.27 2008/07/30 18:35:53 dmorissette
24 : * Avoid scanning the whole E00 input file in AVCE00ReadOpenE00() if the
25 : * file does not start with an EXP line (GDAL/OGR ticket 1989)
26 : *
27 : * Revision 1.26 2008/07/30 16:17:46 dmorissette
28 : * Detect compressed E00 input files and refuse to open them instead of
29 : * crashing (bug 1928, GDAL/OGR ticket 2513)
30 : *
31 : * Revision 1.25 2008/07/24 20:34:12 dmorissette
32 : * Fixed VC++ WIN32 build problems in GDAL/OGR environment
33 : * (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2500)
34 : *
35 : * Revision 1.24 2008/07/24 13:49:20 dmorissette
36 : * Fixed GCC compiler warning (GDAL ticket #2495)
37 : *
38 : * Revision 1.23 2006/08/17 19:51:01 dmorissette
39 : * #include <unistd.h> to solve warning on 64 bit platforms (bug 1461)
40 : *
41 : * Revision 1.22 2006/08/17 18:56:42 dmorissette
42 : * Support for reading standalone info tables (just tables, no coverage
43 : * data) by pointing AVCE00ReadOpen() to the info directory (bug 1549).
44 : *
45 : * Revision 1.21 2006/06/27 18:38:43 dmorissette
46 : * Cleaned up E00 reading (bug 1497, patch from James F.)
47 : *
48 : * Revision 1.20 2006/06/27 18:06:34 dmorissette
49 : * Applied patch for EOP processing from James F. (bug 1497)
50 : *
51 : * Revision 1.19 2006/06/16 11:48:11 daniel
52 : * New functions to read E00 files directly as opposed to translating to
53 : * binary coverage. Used in the implementation of E00 read support in OGR.
54 : * Contributed by James E. Flemer. (bug 1497)
55 : *
56 : * Revision 1.18 2006/06/14 16:31:28 daniel
57 : * Added support for AVCCoverPC2 type (bug 1491)
58 : *
59 : * Revision 1.17 2005/06/03 03:49:58 daniel
60 : * Update email address, website url, and copyright dates
61 : *
62 : * Revision 1.16 2004/07/14 18:49:50 daniel
63 : * Fixed leak when trying to open something that's not a coverage (bug513)
64 : *
65 : * Revision 1.15 2002/08/27 15:46:15 daniel
66 : * Applied fix made in GDAL/OGR by 'aubin' (moved include ctype.h after avc.h)
67 : *
68 : * Revision 1.14 2000/09/22 19:45:21 daniel
69 : * Switch to MIT-style license
70 : *
71 : * Revision 1.13 2000/05/29 15:31:31 daniel
72 : * Added Japanese DBCS support
73 : *
74 : * Revision 1.12 2000/02/14 17:21:01 daniel
75 : * Made more robust for corrupted or invalid files in cover directory
76 : *
77 : * Revision 1.11 2000/02/02 04:26:04 daniel
78 : * Support reading TX6/TX7/RXP/RPL files in weird coverages
79 : *
80 : * Revision 1.10 2000/01/10 02:56:30 daniel
81 : * Added read support for "weird" coverages
82 : *
83 : * Revision 1.9 2000/01/07 07:12:49 daniel
84 : * Added support for reading PC Coverage TXT files
85 : *
86 : * Revision 1.8 1999/12/24 07:41:08 daniel
87 : * Check fname length before testing for extension in AVCE00ReadFindCoverType()
88 : *
89 : * Revision 1.7 1999/12/24 07:18:34 daniel
90 : * Added PC Arc/Info coverages support
91 : *
92 : * Revision 1.6 1999/08/26 17:22:18 daniel
93 : * Use VSIFopen() instead of fopen() directly
94 : *
95 : * Revision 1.5 1999/08/23 18:21:41 daniel
96 : * New syntax for AVCBinReadListTables()
97 : *
98 : * Revision 1.4 1999/05/11 02:10:01 daniel
99 : * Free psInfo struct inside AVCE00ReadClose()
100 : *
101 : * Revision 1.3 1999/04/06 19:43:26 daniel
102 : * Added E00 coverage path in EXP 0 header line
103 : *
104 : * Revision 1.2 1999/02/25 04:19:01 daniel
105 : * Added TXT, TX6/TX7, RXP and RPL support + other minor changes
106 : *
107 : * Revision 1.1 1999/01/29 16:28:52 daniel
108 : * Initial revision
109 : *
110 : **********************************************************************/
111 :
112 : #include "avc.h"
113 :
114 : #ifdef _WIN32
115 : #include <direct.h> /* getcwd() */
116 : #else
117 : #include <unistd.h> /* getcwd() */
118 : #endif
119 :
120 : #include <ctype.h> /* toupper() */
121 :
122 : // Should be 80 but let's be laxer
123 : constexpr int knMAX_CHARS_PER_LINE = 1024;
124 :
125 : static void _AVCE00ReadScanE00(AVCE00ReadE00Ptr psRead);
126 : static int _AVCE00ReadBuildSqueleton(AVCE00ReadPtr psInfo,
127 : char **papszCoverDir);
128 : static AVCCoverType _AVCE00ReadFindCoverType(char **papszCoverDir);
129 :
130 : /**********************************************************************
131 : * AVCE00ReadOpen()
132 : *
133 : * Open a Arc/Info coverage to read it as if it was an E00 file.
134 : *
135 : * You can either pass the name of the coverage directory, or the path
136 : * to one of the files in the coverage directory. The name of the
137 : * coverage MUST be included in pszCoverPath... this means that
138 : * passing "." is invalid.
139 : * The following are all valid values for pszCoverPath:
140 : * /home/data/country
141 : * /home/data/country/
142 : * /home/data/country/arc.adf
143 : * (Of course you should replace the '/' with '\\' on DOS systems!)
144 : *
145 : * Returns a new AVCE00ReadPtr handle or nullptr if the coverage could
146 : * not be opened or if it does not appear to be a valid Arc/Info coverage.
147 : *
148 : * The handle will eventually have to be released with AVCE00ReadClose().
149 : **********************************************************************/
150 454 : AVCE00ReadPtr AVCE00ReadOpen(const char *pszCoverPath)
151 : {
152 : AVCE00ReadPtr psInfo;
153 : int i, nLen, nCoverPrecision;
154 : VSIStatBufL sStatBuf;
155 454 : char **papszCoverDir = nullptr;
156 :
157 454 : CPLErrorReset();
158 :
159 : /*-----------------------------------------------------------------
160 : * pszCoverPath must be either a valid directory name or a valid
161 : * file name.
162 : *----------------------------------------------------------------*/
163 908 : if (pszCoverPath == nullptr || strlen(pszCoverPath) == 0 ||
164 454 : VSIStatL(pszCoverPath, &sStatBuf) == -1)
165 : {
166 0 : CPLError(CE_Failure, CPLE_OpenFailed, "Invalid coverage path: %s.",
167 : pszCoverPath ? pszCoverPath : "(nullptr)");
168 0 : return nullptr;
169 : }
170 :
171 : /*-----------------------------------------------------------------
172 : * Alloc the AVCE00ReadPtr handle
173 : *----------------------------------------------------------------*/
174 454 : psInfo = (AVCE00ReadPtr)CPLCalloc(1, sizeof(struct AVCE00ReadInfo_t));
175 :
176 : /*-----------------------------------------------------------------
177 : * 2 possibilities about the value passed in pszCoverPath:
178 : * - It can be the directory name of the coverage
179 : * - or it can be the path to one of the files in the coverage
180 : *
181 : * If the name passed in pszCoverPath is not a directory, then we
182 : * need to strip the last part of the filename to keep only the
183 : * path, terminated by a '/' (or a '\\').
184 : *----------------------------------------------------------------*/
185 454 : if (VSI_ISDIR(sStatBuf.st_mode))
186 : {
187 : /*-------------------------------------------------------------
188 : * OK, we have a valid directory name... make sure it is
189 : * terminated with a '/' (or '\\')
190 : *------------------------------------------------------------*/
191 424 : nLen = (int)strlen(pszCoverPath);
192 :
193 424 : if (pszCoverPath[nLen - 1] == '/' || pszCoverPath[nLen - 1] == '\\')
194 0 : psInfo->pszCoverPath = CPLStrdup(pszCoverPath);
195 : else
196 : {
197 : #ifdef _WIN32
198 : psInfo->pszCoverPath = CPLStrdup(CPLSPrintf("%s\\", pszCoverPath));
199 : #else
200 424 : psInfo->pszCoverPath = CPLStrdup(CPLSPrintf("%s/", pszCoverPath));
201 : #endif
202 : }
203 : }
204 : else
205 : {
206 : /*-------------------------------------------------------------
207 : * We are dealing with a filename.
208 : * Extract the coverage path component and store it.
209 : * The coverage path will remain terminated by a '/' or '\\' char.
210 : *------------------------------------------------------------*/
211 30 : psInfo->pszCoverPath = CPLStrdup(pszCoverPath);
212 :
213 462 : for (i = (int)strlen(psInfo->pszCoverPath) - 1;
214 462 : i > 0 && psInfo->pszCoverPath[i] != '/' &&
215 432 : psInfo->pszCoverPath[i] != '\\';
216 : i--)
217 : {
218 : }
219 :
220 30 : psInfo->pszCoverPath[i + 1] = '\0';
221 : }
222 :
223 : /*-----------------------------------------------------------------
224 : * Extract the coverage name from the coverage path. Note that
225 : * for this the coverage path must be in the form:
226 : * "dir1/dir2/dir3/covername/" ... if it is not the case, then
227 : * we would have to use getcwd() to find the current directory name...
228 : * but for now we'll just produce an error if this happens.
229 : *----------------------------------------------------------------*/
230 454 : nLen = 0;
231 454 : for (i = (int)strlen(psInfo->pszCoverPath) - 1;
232 7847 : i > 0 && psInfo->pszCoverPath[i - 1] != '/' &&
233 15248 : psInfo->pszCoverPath[i - 1] != '\\' &&
234 7397 : psInfo->pszCoverPath[i - 1] != ':';
235 : i--)
236 : {
237 7397 : nLen++;
238 : }
239 :
240 454 : if (nLen > 0)
241 : {
242 454 : psInfo->pszCoverName = CPLStrdup(psInfo->pszCoverPath + i);
243 454 : psInfo->pszCoverName[nLen] = '\0';
244 : }
245 : else
246 : {
247 0 : CPLError(CE_Failure, CPLE_OpenFailed,
248 : "Invalid coverage path (%s): "
249 : "coverage name must be included in path.",
250 : pszCoverPath);
251 :
252 0 : CPLFree(psInfo->pszCoverPath);
253 0 : CPLFree(psInfo);
254 0 : return nullptr;
255 : }
256 :
257 : /*-----------------------------------------------------------------
258 : * Read the coverage directory listing and try to establish the cover type
259 : *----------------------------------------------------------------*/
260 454 : papszCoverDir = VSIReadDir(psInfo->pszCoverPath);
261 :
262 454 : psInfo->eCoverType = _AVCE00ReadFindCoverType(papszCoverDir);
263 :
264 454 : if (psInfo->eCoverType == AVCCoverTypeUnknown)
265 : {
266 451 : CPLError(CE_Failure, CPLE_OpenFailed,
267 : "Invalid coverage (%s): directory does not appear to "
268 : "contain any supported vector coverage file.",
269 : pszCoverPath);
270 451 : CPLFree(psInfo->pszCoverName);
271 451 : CPLFree(psInfo->pszCoverPath);
272 451 : CPLFree(psInfo->pszInfoPath);
273 451 : CPLFree(psInfo);
274 451 : CSLDestroy(papszCoverDir);
275 451 : return nullptr;
276 : }
277 :
278 : /*-----------------------------------------------------------------
279 : * INFO path: PC Coverages have all files in the same dir, and unix
280 : * covers have the INFO files in ../info
281 : *----------------------------------------------------------------*/
282 3 : if (psInfo->eCoverType == AVCCoverPC || psInfo->eCoverType == AVCCoverPC2)
283 : {
284 0 : psInfo->pszInfoPath = CPLStrdup(psInfo->pszCoverPath);
285 : }
286 : else
287 : {
288 : /*-------------------------------------------------------------
289 : * Lazy way to build the INFO path: simply add "../info/"...
290 : * this could probably be improved!
291 : *------------------------------------------------------------*/
292 3 : size_t nInfoPathLen = strlen(psInfo->pszCoverPath) + 9;
293 3 : psInfo->pszInfoPath = (char *)CPLMalloc(nInfoPathLen);
294 : #ifdef _WIN32
295 : #define AVC_INFOPATH "..\\info\\"
296 : #else
297 : #define AVC_INFOPATH "../info/"
298 : #endif
299 3 : snprintf(psInfo->pszInfoPath, nInfoPathLen, "%s%s",
300 : psInfo->pszCoverPath, AVC_INFOPATH);
301 :
302 3 : AVCAdjustCaseSensitiveFilename(psInfo->pszInfoPath);
303 : }
304 :
305 : /*-----------------------------------------------------------------
306 : * For Unix coverages, check that the info directory exists and
307 : * contains the "arc.dir". In AVCCoverWeird, the arc.dir is
308 : * called "../INFO/ARCDR9".
309 : * PC Coverages have their info tables in the same directory as
310 : * the coverage files.
311 : *----------------------------------------------------------------*/
312 6 : if (((psInfo->eCoverType == AVCCoverV7 ||
313 0 : psInfo->eCoverType == AVCCoverV7Tables) &&
314 6 : !AVCFileExists(psInfo->pszInfoPath, "arc.dir")) ||
315 3 : (psInfo->eCoverType == AVCCoverWeird &&
316 0 : !AVCFileExists(psInfo->pszInfoPath, "arcdr9")))
317 : {
318 0 : CPLError(
319 : CE_Failure, CPLE_OpenFailed,
320 : "Invalid coverage (%s): 'info' directory not found or invalid.",
321 : pszCoverPath);
322 0 : CPLFree(psInfo->pszCoverName);
323 0 : CPLFree(psInfo->pszCoverPath);
324 0 : CPLFree(psInfo->pszInfoPath);
325 0 : CPLFree(psInfo);
326 0 : CSLDestroy(papszCoverDir);
327 0 : return nullptr;
328 : }
329 :
330 : /*-----------------------------------------------------------------
331 : * Make sure there was no error until now before we build skeleton.
332 : *----------------------------------------------------------------*/
333 3 : if (CPLGetLastErrorNo() != 0)
334 : {
335 0 : CPLFree(psInfo->pszCoverName);
336 0 : CPLFree(psInfo->pszCoverPath);
337 0 : CPLFree(psInfo->pszInfoPath);
338 0 : CPLFree(psInfo);
339 0 : CSLDestroy(papszCoverDir);
340 0 : return nullptr;
341 : }
342 :
343 : /*-----------------------------------------------------------------
344 : * Build the E00 file skeleton and be ready to return a E00 header...
345 : * We'll also read the coverage precision by the same way.
346 : *----------------------------------------------------------------*/
347 3 : nCoverPrecision = _AVCE00ReadBuildSqueleton(psInfo, papszCoverDir);
348 :
349 : /* Ignore warnings produced while building skeleton */
350 3 : CPLErrorReset();
351 :
352 3 : CSLDestroy(papszCoverDir);
353 3 : papszCoverDir = nullptr;
354 :
355 3 : psInfo->iCurSection = 0;
356 3 : psInfo->iCurStep = AVC_GEN_NOTSTARTED;
357 3 : psInfo->bReadAllSections = TRUE;
358 :
359 : /*-----------------------------------------------------------------
360 : * Init the E00 generator.
361 : *----------------------------------------------------------------*/
362 3 : psInfo->hGenInfo = AVCE00GenInfoAlloc(nCoverPrecision);
363 :
364 : /*-----------------------------------------------------------------
365 : * Init multibyte encoding info
366 : *----------------------------------------------------------------*/
367 3 : psInfo->psDBCSInfo = AVCAllocDBCSInfo();
368 :
369 : /*-----------------------------------------------------------------
370 : * If an error happened during the open call, cleanup and return nullptr.
371 : *----------------------------------------------------------------*/
372 3 : if (CPLGetLastErrorNo() != 0)
373 : {
374 0 : AVCE00ReadClose(psInfo);
375 0 : psInfo = nullptr;
376 : }
377 :
378 3 : return psInfo;
379 : }
380 :
381 : /**********************************************************************
382 : * AVCE00ReadOpenE00()
383 : *
384 : * Open a E00 file for reading.
385 : *
386 : * Returns a new AVCE00ReadE00Ptr handle or nullptr if the file could
387 : * not be opened or if it does not appear to be a valid E00 file.
388 : *
389 : * The handle will eventually have to be released with
390 : * AVCE00ReadCloseE00().
391 : **********************************************************************/
392 11 : AVCE00ReadE00Ptr AVCE00ReadOpenE00(const char *pszE00FileName)
393 : {
394 : AVCE00ReadE00Ptr psRead;
395 : VSIStatBufL sStatBuf;
396 : VSILFILE *fp;
397 : char *p;
398 : char szHeader[10];
399 :
400 11 : CPLErrorReset();
401 :
402 : /*-----------------------------------------------------------------
403 : * pszE00FileName must be a valid file that can be opened for
404 : * reading
405 : *----------------------------------------------------------------*/
406 11 : if (pszE00FileName == nullptr || strlen(pszE00FileName) == 0 ||
407 33 : VSIStatL(pszE00FileName, &sStatBuf) == -1 ||
408 11 : VSI_ISDIR(sStatBuf.st_mode))
409 : {
410 0 : CPLError(CE_Failure, CPLE_OpenFailed, "Invalid E00 file path: %s.",
411 : pszE00FileName ? pszE00FileName : "(nullptr)");
412 0 : return nullptr;
413 : }
414 :
415 11 : if (nullptr == (fp = VSIFOpenL(pszE00FileName, "r")))
416 0 : return nullptr;
417 :
418 : /*-----------------------------------------------------------------
419 : * Make sure the file starts with a "EXP 0" or "EXP 1" header
420 : *----------------------------------------------------------------*/
421 11 : memset(szHeader, 0, sizeof(szHeader));
422 11 : if (VSIFReadL(szHeader, 5, 1, fp) == 0 || !STARTS_WITH_CI(szHeader, "EXP "))
423 : {
424 0 : CPLError(CE_Failure, CPLE_OpenFailed,
425 : "This does not look like a E00 file: does not start with "
426 : "a EXP header.");
427 0 : VSIFCloseL(fp);
428 0 : return nullptr;
429 : }
430 11 : VSIRewindL(fp);
431 :
432 : /*-----------------------------------------------------------------
433 : * Alloc the AVCE00ReadE00Ptr handle
434 : *----------------------------------------------------------------*/
435 11 : psRead = (AVCE00ReadE00Ptr)CPLCalloc(1, sizeof(struct AVCE00ReadInfoE00_t));
436 :
437 11 : psRead->hFile = fp;
438 11 : psRead->pszCoverPath = CPLStrdup(pszE00FileName);
439 11 : psRead->eCurFileType = AVCFileUnknown;
440 :
441 : /*-----------------------------------------------------------------
442 : * Extract the coverage name from the coverage path.
443 : *----------------------------------------------------------------*/
444 22 : if (nullptr != (p = strrchr(psRead->pszCoverPath, '/')) ||
445 11 : nullptr != (p = strrchr(psRead->pszCoverPath, '\\')) ||
446 0 : nullptr != (p = strrchr(psRead->pszCoverPath, ':')))
447 : {
448 11 : psRead->pszCoverName = CPLStrdup(p + 1);
449 : }
450 : else
451 : {
452 0 : psRead->pszCoverName = CPLStrdup(psRead->pszCoverPath);
453 : }
454 11 : if (nullptr != (p = strrchr(psRead->pszCoverName, '.')))
455 : {
456 11 : *p = '\0';
457 : }
458 :
459 : /*-----------------------------------------------------------------
460 : * Make sure there was no error until now before we scan file.
461 : *----------------------------------------------------------------*/
462 11 : if (CPLGetLastErrorNo() != 0)
463 : {
464 0 : AVCE00ReadCloseE00(psRead);
465 0 : return nullptr;
466 : }
467 :
468 11 : psRead->hParseInfo = AVCE00ParseInfoAlloc();
469 :
470 : /*-----------------------------------------------------------------
471 : * Scan the E00 file for sections
472 : *----------------------------------------------------------------*/
473 11 : _AVCE00ReadScanE00(psRead);
474 11 : if (CPLGetLastErrorNo() != 0)
475 : {
476 1 : AVCE00ReadCloseE00(psRead);
477 1 : return nullptr;
478 : }
479 :
480 10 : AVCE00ReadRewindE00(psRead);
481 10 : CPLErrorReset();
482 :
483 10 : if (psRead->numSections < 1)
484 : {
485 0 : AVCE00ReadCloseE00(psRead);
486 0 : return nullptr;
487 : }
488 :
489 10 : psRead->bReadAllSections = TRUE;
490 :
491 : /*-----------------------------------------------------------------
492 : * If an error happened during the open call, cleanup and return nullptr.
493 : *----------------------------------------------------------------*/
494 10 : if (CPLGetLastErrorNo() != 0)
495 : {
496 0 : AVCE00ReadCloseE00(psRead);
497 0 : psRead = nullptr;
498 : }
499 :
500 10 : return psRead;
501 : }
502 :
503 : /**********************************************************************
504 : * AVCE00ReadClose()
505 : *
506 : * Close a coverage and release all memory used by the AVCE00ReadPtr
507 : * handle.
508 : **********************************************************************/
509 3 : void AVCE00ReadClose(AVCE00ReadPtr psInfo)
510 : {
511 3 : CPLErrorReset();
512 :
513 3 : if (psInfo == nullptr)
514 0 : return;
515 :
516 3 : CPLFree(psInfo->pszCoverPath);
517 3 : CPLFree(psInfo->pszInfoPath);
518 3 : CPLFree(psInfo->pszCoverName);
519 :
520 3 : if (psInfo->hFile)
521 0 : AVCBinReadClose(psInfo->hFile);
522 :
523 3 : if (psInfo->hGenInfo)
524 3 : AVCE00GenInfoFree(psInfo->hGenInfo);
525 :
526 3 : if (psInfo->pasSections)
527 : {
528 : int i;
529 41 : for (i = 0; i < psInfo->numSections; i++)
530 : {
531 38 : CPLFree(psInfo->pasSections[i].pszName);
532 38 : CPLFree(psInfo->pasSections[i].pszFilename);
533 : }
534 3 : CPLFree(psInfo->pasSections);
535 : }
536 :
537 3 : AVCFreeDBCSInfo(psInfo->psDBCSInfo);
538 :
539 3 : CPLFree(psInfo);
540 : }
541 :
542 : /**********************************************************************
543 : * AVCE00ReadCloseE00()
544 : *
545 : * Close a coverage and release all memory used by the AVCE00ReadE00Ptr
546 : * handle.
547 : **********************************************************************/
548 11 : void AVCE00ReadCloseE00(AVCE00ReadE00Ptr psRead)
549 : {
550 11 : if (psRead == nullptr)
551 0 : return;
552 :
553 11 : CPLFree(psRead->pszCoverPath);
554 11 : CPLFree(psRead->pszCoverName);
555 :
556 11 : if (psRead->hFile)
557 : {
558 11 : VSIFCloseL(psRead->hFile);
559 11 : psRead->hFile = nullptr;
560 : }
561 :
562 11 : if (psRead->pasSections)
563 : {
564 : int i;
565 86 : for (i = 0; i < psRead->numSections; i++)
566 : {
567 76 : CPLFree(psRead->pasSections[i].pszName);
568 76 : CPLFree(psRead->pasSections[i].pszFilename);
569 : }
570 10 : CPLFree(psRead->pasSections);
571 : }
572 :
573 : /* These Free calls handle nullptr's */
574 11 : AVCE00ParseInfoFree(psRead->hParseInfo);
575 11 : psRead->hParseInfo = nullptr;
576 :
577 11 : CPLFree(psRead);
578 : }
579 :
580 : /**********************************************************************
581 : * _AVCIncreaseSectionsArray()
582 : *
583 : * Add a number of structures to the Sections array and return the
584 : * index of the first one that was added. Note that the address of the
585 : * original array (*pasArray) is quite likely to change!
586 : *
587 : * The value of *pnumItems will be updated to reflect the new array size.
588 : **********************************************************************/
589 100 : static int _AVCIncreaseSectionsArray(AVCE00Section **pasArray, int *pnumItems,
590 : int numToAdd)
591 : {
592 : int i;
593 :
594 200 : *pasArray = (AVCE00Section *)CPLRealloc(
595 100 : *pasArray, (*pnumItems + numToAdd) * sizeof(AVCE00Section));
596 :
597 214 : for (i = 0; i < numToAdd; i++)
598 : {
599 114 : (*pasArray)[*pnumItems + i].eType = AVCFileUnknown;
600 114 : (*pasArray)[*pnumItems + i].pszName = nullptr;
601 114 : (*pasArray)[*pnumItems + i].pszFilename = nullptr;
602 114 : (*pasArray)[*pnumItems + i].nLineNum = 0;
603 114 : (*pasArray)[*pnumItems + i].nFeatureCount = -1;
604 : }
605 :
606 100 : i = *pnumItems;
607 100 : (*pnumItems) += numToAdd;
608 :
609 100 : return i;
610 : }
611 :
612 : /**********************************************************************
613 : * _AVCE00ReadFindCoverType()
614 : *
615 : * This functions tries to establish the coverage type by looking
616 : * at the coverage directory listing passed as argument.
617 : *
618 : * Returns one of AVCCoverV7 for Arc/Info V7 (Unix) coverages, or
619 : * AVCCoverPC for PC Arc/Info coverages.
620 : * AVCCoverWeird for an hybrid between V7 and PC
621 : *
622 : * If coverage type cannot be established then AVCCoverTypeUnknown is
623 : * returned.
624 : **********************************************************************/
625 454 : static AVCCoverType _AVCE00ReadFindCoverType(char **papszCoverDir)
626 : {
627 : int i, nLen;
628 454 : GBool bFoundAdfFile = FALSE, bFoundArcFile = FALSE, bFoundTableFile = FALSE,
629 454 : bFoundDbfFile = FALSE, bFoundArcDirFile = FALSE;
630 :
631 : /*-----------------------------------------------------------------
632 : * Scan the list of files, looking for well known filenames.
633 : * Start with the funky types first...
634 : *----------------------------------------------------------------*/
635 22747 : for (i = 0; papszCoverDir && papszCoverDir[i]; i++)
636 : {
637 22293 : nLen = (int)strlen(papszCoverDir[i]);
638 22293 : if (nLen > 4 && EQUAL(papszCoverDir[i] + nLen - 4, ".adf"))
639 : {
640 24 : bFoundAdfFile = TRUE;
641 : }
642 22269 : else if (nLen > 4 && EQUAL(papszCoverDir[i] + nLen - 4, ".dbf"))
643 : {
644 0 : bFoundDbfFile = TRUE;
645 : }
646 22269 : else if (EQUAL(papszCoverDir[i], "arc") ||
647 22269 : EQUAL(papszCoverDir[i], "cnt") ||
648 22269 : EQUAL(papszCoverDir[i], "pal") ||
649 22269 : EQUAL(papszCoverDir[i], "lab") ||
650 22269 : EQUAL(papszCoverDir[i], "prj") ||
651 22269 : EQUAL(papszCoverDir[i], "tol"))
652 : {
653 0 : bFoundArcFile = TRUE;
654 : }
655 22269 : else if (EQUAL(papszCoverDir[i], "aat") ||
656 22269 : EQUAL(papszCoverDir[i], "pat") ||
657 22269 : EQUAL(papszCoverDir[i], "bnd") ||
658 22269 : EQUAL(papszCoverDir[i], "tic"))
659 : {
660 0 : bFoundTableFile = TRUE;
661 : }
662 22269 : else if (EQUAL(papszCoverDir[i], "arc.dir"))
663 : {
664 0 : bFoundArcDirFile = TRUE;
665 : }
666 : }
667 :
668 : /*-----------------------------------------------------------------
669 : * Check for PC Arc/Info coverage - variant 1.
670 : * These PC coverages have files with no extension (e.g. "ARC","PAL",...)
671 : * and their tables filenames are in the form "???.dbf"
672 : *----------------------------------------------------------------*/
673 454 : if (bFoundArcFile && bFoundDbfFile)
674 0 : return AVCCoverPC;
675 :
676 : /*-----------------------------------------------------------------
677 : * Check for PC Arc/Info coverage - variant 2.
678 : * looks like a hybrid between AVCCoverPC and AVCCoverV7
679 : * These PC coverages have files with .adf extension (e.g."ARC.ADF"),
680 : * and their tables filenames are in the form "???.dbf"
681 : *----------------------------------------------------------------*/
682 454 : if (bFoundAdfFile && bFoundDbfFile)
683 0 : return AVCCoverPC2;
684 :
685 : /*-----------------------------------------------------------------
686 : * Check for the weird coverages.
687 : * Their coverage files have no extension just like PC Coverages,
688 : * and their tables have 3 letters filenames with no extension
689 : * either (e.g. "AAT", "PAT", etc.)
690 : * They also have a ../info directory, but we don't really need
691 : * to check that (not yet!).
692 : *----------------------------------------------------------------*/
693 454 : if (bFoundArcFile && bFoundTableFile)
694 0 : return AVCCoverWeird;
695 :
696 : /*-----------------------------------------------------------------
697 : * V7 Coverages... they are the easiest to recognize
698 : * because of the ".adf" file extension
699 : *----------------------------------------------------------------*/
700 454 : if (bFoundAdfFile)
701 3 : return AVCCoverV7;
702 :
703 : /*-----------------------------------------------------------------
704 : * Standalone info tables.
705 : * We were pointed at the "info" directory. We'll treat this as
706 : * a coverage with just info tables.
707 : *----------------------------------------------------------------*/
708 451 : if (bFoundArcDirFile)
709 0 : return AVCCoverV7Tables;
710 :
711 451 : return AVCCoverTypeUnknown;
712 : }
713 :
714 : /**********************************************************************
715 : * _AVCE00ReadAddJabberwockySection()
716 : *
717 : * Add to the skeleton a section that contains subsections
718 : * for all the files with a given extension.
719 : *
720 : * Returns Updated Coverage precision
721 : **********************************************************************/
722 9 : static int _AVCE00ReadAddJabberwockySection(
723 : AVCE00ReadPtr psInfo, AVCFileType eFileType, const char *pszSectionName,
724 : int nCoverPrecision, const char *pszFileExtension, char **papszCoverDir)
725 : {
726 : int iSect, iDirEntry, nLen, nExtLen;
727 9 : GBool bFoundFiles = FALSE;
728 9 : AVCBinFile *psFile = nullptr;
729 :
730 9 : nExtLen = (int)strlen(pszFileExtension);
731 :
732 : /*-----------------------------------------------------------------
733 : * Scan the directory for files with a ".txt" extension.
734 : *----------------------------------------------------------------*/
735 :
736 99 : for (iDirEntry = 0; papszCoverDir && papszCoverDir[iDirEntry]; iDirEntry++)
737 : {
738 90 : nLen = (int)strlen(papszCoverDir[iDirEntry]);
739 :
740 162 : if (nLen > nExtLen &&
741 72 : EQUAL(papszCoverDir[iDirEntry] + nLen - nExtLen,
742 162 : pszFileExtension) &&
743 0 : (psFile = AVCBinReadOpen(
744 0 : psInfo->pszCoverPath, papszCoverDir[iDirEntry],
745 : psInfo->eCoverType, eFileType, psInfo->psDBCSInfo)) != nullptr)
746 : {
747 0 : if (nCoverPrecision == AVC_DEFAULT_PREC)
748 0 : nCoverPrecision = psFile->nPrecision;
749 0 : AVCBinReadClose(psFile);
750 :
751 0 : if (bFoundFiles == FALSE)
752 : {
753 : /* Insert a "TX6 #" header before the first TX6 file
754 : */
755 0 : iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections),
756 : &(psInfo->numSections), 1);
757 0 : psInfo->pasSections[iSect].eType = AVCFileUnknown;
758 :
759 0 : psInfo->pasSections[iSect].pszName = CPLStrdup(CPLSPrintf(
760 : "%s %c", pszSectionName,
761 : (nCoverPrecision == AVC_DOUBLE_PREC) ? '3' : '2'));
762 :
763 0 : bFoundFiles = TRUE;
764 : }
765 :
766 : /* Add this file to the skeleton
767 : */
768 0 : iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections),
769 : &(psInfo->numSections), 1);
770 :
771 0 : psInfo->pasSections[iSect].eType = eFileType;
772 0 : psInfo->pasSections[iSect].pszFilename =
773 0 : CPLStrdup(papszCoverDir[iDirEntry]);
774 :
775 : /* pszName will contain only the classname without the file
776 : * extension */
777 0 : psInfo->pasSections[iSect].pszName =
778 0 : CPLStrdup(papszCoverDir[iDirEntry]);
779 0 : psInfo->pasSections[iSect].pszName[nLen - nExtLen] = '\0';
780 : }
781 : }
782 :
783 9 : if (bFoundFiles)
784 : {
785 : /* Add a line to close the TX6 section.
786 : */
787 0 : iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections),
788 : &(psInfo->numSections), 1);
789 0 : psInfo->pasSections[iSect].eType = AVCFileUnknown;
790 0 : psInfo->pasSections[iSect].pszName = CPLStrdup("JABBERWOCKY");
791 : }
792 :
793 9 : return nCoverPrecision;
794 : }
795 :
796 : /**********************************************************************
797 : * _AVCE00ReadNextLineE00()
798 : *
799 : * Processes the next line of input from the E00 file.
800 : * (See AVCE00WriteNextLine() for similar processing.)
801 : *
802 : * Returns the next object from the E00 file, or nullptr.
803 : **********************************************************************/
804 3121 : static void *_AVCE00ReadNextLineE00(AVCE00ReadE00Ptr psRead,
805 : const char *pszLine)
806 : {
807 : /* int nStatus = 0; */
808 3121 : void *psObj = nullptr;
809 :
810 3121 : AVCE00ParseInfo *psInfo = psRead->hParseInfo;
811 :
812 3121 : CPLErrorReset();
813 :
814 3121 : ++psInfo->nCurLineNum;
815 :
816 3121 : if (psInfo->bForceEndOfSection)
817 : {
818 : /*-------------------------------------------------------------
819 : * The last call encountered an implicit end of section, so
820 : * we close the section now without waiting for an end-of-section
821 : * line (there won't be any!)... and get ready to proceed with
822 : * the next section.
823 : * This is used for TABLEs.
824 : *------------------------------------------------------------*/
825 60 : AVCE00ParseSectionEnd(psInfo, pszLine, TRUE);
826 60 : psRead->eCurFileType = AVCFileUnknown;
827 : }
828 :
829 : /*-----------------------------------------------------------------
830 : * If we're at the top level inside a supersection... check if this
831 : * supersection ends here.
832 : *----------------------------------------------------------------*/
833 3121 : if (AVCE00ParseSuperSectionEnd(psInfo, pszLine) == TRUE)
834 : {
835 : /* Nothing to do... it is all been done by the call to
836 : * AVCE00ParseSuperSectionEnd()
837 : */
838 : }
839 3111 : else if (psRead->eCurFileType == AVCFileUnknown)
840 : {
841 : /*-------------------------------------------------------------
842 : * We're at the top level or inside a supersection... waiting
843 : * to encounter a valid section or supersection header
844 : * (i.e. "ARC 2", etc...)
845 : *------------------------------------------------------------*/
846 :
847 : /*-------------------------------------------------------------
848 : * First check for a supersection header (TX6, RXP, IFO, ...)
849 : *------------------------------------------------------------*/
850 276 : if (AVCE00ParseSuperSectionHeader(psInfo, pszLine) == AVCFileUnknown)
851 : {
852 : /*---------------------------------------------------------
853 : * This was not a supersection header... check if it is a simple
854 : * section header
855 : *--------------------------------------------------------*/
856 261 : psRead->eCurFileType = AVCE00ParseSectionHeader(psInfo, pszLine);
857 : }
858 : else
859 : {
860 : /* got supersection */
861 : }
862 :
863 276 : if (psRead->eCurFileType == AVCFileTABLE)
864 : {
865 : /*---------------------------------------------------------
866 : * send the first header line to the parser and wait until
867 : * the whole header has been read.
868 : *--------------------------------------------------------*/
869 55 : AVCE00ParseNextLine(psInfo, pszLine);
870 : }
871 221 : else if (psRead->eCurFileType != AVCFileUnknown)
872 : {
873 : /*---------------------------------------------------------
874 : * found a valid section header
875 : *--------------------------------------------------------*/
876 : }
877 : }
878 2835 : else if (psRead->eCurFileType == AVCFileTABLE && !psInfo->bTableHdrComplete)
879 : {
880 : /*-------------------------------------------------------------
881 : * We're reading a TABLE header... continue reading lines
882 : * from the header
883 : *
884 : * Note: When parsing a TABLE, the first object returned will
885 : * be the AVCTableDef, then data records will follow.
886 : *------------------------------------------------------------*/
887 283 : psObj = AVCE00ParseNextLine(psInfo, pszLine);
888 283 : if (psObj)
889 : {
890 : /* got table header */
891 : /* TODO: Enable return of table definition? */
892 55 : psObj = nullptr;
893 : }
894 : }
895 : else
896 : {
897 : /*-------------------------------------------------------------
898 : * We're are in the middle of a section... first check if we
899 : * have reached the end.
900 : *
901 : * note: The first call to AVCE00ParseSectionEnd() with FALSE will
902 : * not reset the parser until we close the file... and then
903 : * we call the function again to reset the parser.
904 : *------------------------------------------------------------*/
905 2552 : if (AVCE00ParseSectionEnd(psInfo, pszLine, FALSE))
906 : {
907 70 : psRead->eCurFileType = AVCFileUnknown;
908 70 : AVCE00ParseSectionEnd(psInfo, pszLine, TRUE);
909 : }
910 : else
911 : /*-------------------------------------------------------------
912 : * ... not at the end yet, so continue reading objects.
913 : *------------------------------------------------------------*/
914 : {
915 2482 : psObj = AVCE00ParseNextLine(psInfo, pszLine);
916 :
917 : if (psObj)
918 : {
919 : /* got object */
920 : }
921 : }
922 : }
923 :
924 : #if 0
925 : if (CPLGetLastErrorNo() != 0)
926 : nStatus = -1;
927 : #endif
928 :
929 3121 : return psObj;
930 : }
931 :
932 : /**********************************************************************
933 : * _AVCE00ReadBuildSqueleton()
934 : *
935 : * Build the skeleton of the E00 file corresponding to the specified
936 : * coverage and set the appropriate fields in the AVCE00ReadPtr struct.
937 : *
938 : * Note that the order of the sections in the skeleton is important
939 : * since some software may rely on this ordering when they read E00 files.
940 : *
941 : * The function returns the coverage precision that it will read from one
942 : * of the file headers.
943 : **********************************************************************/
944 3 : static int _AVCE00ReadBuildSqueleton(AVCE00ReadPtr psInfo, char **papszCoverDir)
945 : {
946 : int iTable, numTables, iFile, nLen;
947 3 : char **papszTables, **papszFiles, szCWD[75] = "", *pcTmp;
948 3 : char *pszEXPPath = nullptr;
949 3 : int nCoverPrecision = AVC_DEFAULT_PREC;
950 3 : char cPrecisionCode = '2';
951 3 : const char *szFname = nullptr;
952 3 : AVCBinFile *psFile = nullptr;
953 :
954 3 : psInfo->numSections = 0;
955 3 : psInfo->pasSections = nullptr;
956 :
957 : /*-----------------------------------------------------------------
958 : * Build the absolute coverage path to include in the EXP 0 line
959 : * This line usually contains the full path of the E00 file that
960 : * is being created, but since the lib does not write the output
961 : * file directly, there is no simple way to get that value. Instead,
962 : * we will use the absolute coverage path to which we add a .E00
963 : * extension.
964 : * We need also make sure cover path is all in uppercase.
965 : *----------------------------------------------------------------*/
966 : #ifdef _WIN32
967 : if (psInfo->pszCoverPath[0] != '\\' &&
968 : !(isalpha((unsigned char)psInfo->pszCoverPath[0]) &&
969 : psInfo->pszCoverPath[1] == ':'))
970 : #else
971 3 : if (psInfo->pszCoverPath[0] != '/')
972 : #endif
973 : {
974 3 : if (getcwd(szCWD, 74) == nullptr)
975 0 : szCWD[0] = '\0'; /* Failed: buffer may be too small */
976 :
977 3 : nLen = (int)strlen(szCWD);
978 :
979 : #ifdef _WIN32
980 : if (nLen > 0 && szCWD[nLen - 1] != '\\')
981 : strcat(szCWD, "\\");
982 : #else
983 3 : if (nLen > 0 && szCWD[nLen - 1] != '/')
984 3 : strcat(szCWD, "/");
985 : #endif
986 : }
987 :
988 3 : CPLString osCoverPathTruncated(psInfo->pszCoverPath);
989 3 : osCoverPathTruncated.pop_back();
990 3 : pszEXPPath = CPLStrdup(
991 : CPLSPrintf("EXP 0 %s%s.E00", szCWD, osCoverPathTruncated.c_str()));
992 3 : pcTmp = pszEXPPath;
993 249 : for (; *pcTmp != '\0'; pcTmp++)
994 246 : *pcTmp = (char)CPLToupper(static_cast<unsigned char>(*pcTmp));
995 :
996 : /*-----------------------------------------------------------------
997 : * EXP Header
998 : *----------------------------------------------------------------*/
999 : {
1000 3 : const int iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections),
1001 : &(psInfo->numSections), 1);
1002 3 : psInfo->pasSections[iSect].eType = AVCFileUnknown;
1003 3 : psInfo->pasSections[iSect].pszName = pszEXPPath;
1004 : }
1005 :
1006 : /*-----------------------------------------------------------------
1007 : * We have to try to open each file as we go for 2 reasons:
1008 : * - To validate the file's signature in order to detect cases like a user
1009 : * that places files such as "mystuff.txt" in the cover directory...
1010 : * this has already happened and obviously lead to problems!)
1011 : * - We also need to find the coverage's precision from the headers
1012 : *----------------------------------------------------------------*/
1013 :
1014 : /*-----------------------------------------------------------------
1015 : * ARC section (arc.adf)
1016 : *----------------------------------------------------------------*/
1017 3 : szFname =
1018 0 : (psInfo->eCoverType == AVCCoverV7 || psInfo->eCoverType == AVCCoverPC2)
1019 3 : ? "arc.adf"
1020 : : "arc";
1021 5 : if ((iFile = CSLFindString(papszCoverDir, szFname)) != -1 &&
1022 : (psFile =
1023 2 : AVCBinReadOpen(psInfo->pszCoverPath, szFname, psInfo->eCoverType,
1024 : AVCFileARC, psInfo->psDBCSInfo)) != nullptr)
1025 : {
1026 2 : if (nCoverPrecision == AVC_DEFAULT_PREC)
1027 2 : nCoverPrecision = psFile->nPrecision;
1028 2 : AVCBinReadClose(psFile);
1029 2 : const int iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections),
1030 : &(psInfo->numSections), 1);
1031 :
1032 2 : psInfo->pasSections[iSect].eType = AVCFileARC;
1033 2 : psInfo->pasSections[iSect].pszName = CPLStrdup("ARC");
1034 4 : psInfo->pasSections[iSect].pszFilename =
1035 2 : CPLStrdup(papszCoverDir[iFile]);
1036 : }
1037 :
1038 : /*-----------------------------------------------------------------
1039 : * CNT section (cnt.adf)
1040 : *----------------------------------------------------------------*/
1041 3 : szFname =
1042 0 : (psInfo->eCoverType == AVCCoverV7 || psInfo->eCoverType == AVCCoverPC2)
1043 3 : ? "cnt.adf"
1044 : : "cnt";
1045 4 : if ((iFile = CSLFindString(papszCoverDir, szFname)) != -1 &&
1046 : (psFile =
1047 1 : AVCBinReadOpen(psInfo->pszCoverPath, szFname, psInfo->eCoverType,
1048 : AVCFileCNT, psInfo->psDBCSInfo)) != nullptr)
1049 : {
1050 1 : if (nCoverPrecision == AVC_DEFAULT_PREC)
1051 0 : nCoverPrecision = psFile->nPrecision;
1052 1 : AVCBinReadClose(psFile);
1053 1 : const int iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections),
1054 : &(psInfo->numSections), 1);
1055 :
1056 1 : psInfo->pasSections[iSect].eType = AVCFileCNT;
1057 1 : psInfo->pasSections[iSect].pszName = CPLStrdup("CNT");
1058 2 : psInfo->pasSections[iSect].pszFilename =
1059 1 : CPLStrdup(papszCoverDir[iFile]);
1060 : }
1061 :
1062 : /*-----------------------------------------------------------------
1063 : * LAB section (lab.adf)
1064 : *----------------------------------------------------------------*/
1065 3 : szFname =
1066 0 : (psInfo->eCoverType == AVCCoverV7 || psInfo->eCoverType == AVCCoverPC2)
1067 3 : ? "lab.adf"
1068 : : "lab";
1069 6 : if ((iFile = CSLFindString(papszCoverDir, szFname)) != -1 &&
1070 : (psFile =
1071 3 : AVCBinReadOpen(psInfo->pszCoverPath, szFname, psInfo->eCoverType,
1072 : AVCFileLAB, psInfo->psDBCSInfo)) != nullptr)
1073 : {
1074 3 : if (nCoverPrecision == AVC_DEFAULT_PREC)
1075 1 : nCoverPrecision = psFile->nPrecision;
1076 3 : AVCBinReadClose(psFile);
1077 3 : const int iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections),
1078 : &(psInfo->numSections), 1);
1079 :
1080 3 : psInfo->pasSections[iSect].eType = AVCFileLAB;
1081 3 : psInfo->pasSections[iSect].pszName = CPLStrdup("LAB");
1082 6 : psInfo->pasSections[iSect].pszFilename =
1083 3 : CPLStrdup(papszCoverDir[iFile]);
1084 : }
1085 :
1086 : /*-----------------------------------------------------------------
1087 : * PAL section (pal.adf)
1088 : *----------------------------------------------------------------*/
1089 3 : szFname =
1090 0 : (psInfo->eCoverType == AVCCoverV7 || psInfo->eCoverType == AVCCoverPC2)
1091 3 : ? "pal.adf"
1092 : : "pal";
1093 4 : if ((iFile = CSLFindString(papszCoverDir, szFname)) != -1 &&
1094 : (psFile =
1095 1 : AVCBinReadOpen(psInfo->pszCoverPath, szFname, psInfo->eCoverType,
1096 : AVCFilePAL, psInfo->psDBCSInfo)) != nullptr)
1097 : {
1098 1 : if (nCoverPrecision == AVC_DEFAULT_PREC)
1099 0 : nCoverPrecision = psFile->nPrecision;
1100 1 : AVCBinReadClose(psFile);
1101 1 : const int iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections),
1102 : &(psInfo->numSections), 1);
1103 :
1104 1 : psInfo->pasSections[iSect].eType = AVCFilePAL;
1105 1 : psInfo->pasSections[iSect].pszName = CPLStrdup("PAL");
1106 2 : psInfo->pasSections[iSect].pszFilename =
1107 1 : CPLStrdup(papszCoverDir[iFile]);
1108 : }
1109 :
1110 : /*-----------------------------------------------------------------
1111 : * TOL section (tol.adf for single precision, par.adf for double)
1112 : *----------------------------------------------------------------*/
1113 3 : szFname =
1114 0 : (psInfo->eCoverType == AVCCoverV7 || psInfo->eCoverType == AVCCoverPC2)
1115 3 : ? "tol.adf"
1116 : : "tol";
1117 6 : if ((iFile = CSLFindString(papszCoverDir, szFname)) != -1 &&
1118 : (psFile =
1119 3 : AVCBinReadOpen(psInfo->pszCoverPath, szFname, psInfo->eCoverType,
1120 : AVCFileTOL, psInfo->psDBCSInfo)) != nullptr)
1121 : {
1122 3 : if (nCoverPrecision == AVC_DEFAULT_PREC)
1123 0 : nCoverPrecision = psFile->nPrecision;
1124 3 : AVCBinReadClose(psFile);
1125 3 : const int iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections),
1126 : &(psInfo->numSections), 1);
1127 :
1128 3 : psInfo->pasSections[iSect].eType = AVCFileTOL;
1129 3 : psInfo->pasSections[iSect].pszName = CPLStrdup("TOL");
1130 6 : psInfo->pasSections[iSect].pszFilename =
1131 3 : CPLStrdup(papszCoverDir[iFile]);
1132 : }
1133 :
1134 3 : szFname =
1135 0 : (psInfo->eCoverType == AVCCoverV7 || psInfo->eCoverType == AVCCoverPC2)
1136 3 : ? "par.adf"
1137 : : "par";
1138 3 : if ((iFile = CSLFindString(papszCoverDir, szFname)) != -1 &&
1139 : (psFile =
1140 0 : AVCBinReadOpen(psInfo->pszCoverPath, szFname, psInfo->eCoverType,
1141 : AVCFileTOL, psInfo->psDBCSInfo)) != nullptr)
1142 : {
1143 0 : if (nCoverPrecision == AVC_DEFAULT_PREC)
1144 0 : nCoverPrecision = psFile->nPrecision;
1145 0 : AVCBinReadClose(psFile);
1146 0 : const int iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections),
1147 : &(psInfo->numSections), 1);
1148 :
1149 0 : psInfo->pasSections[iSect].eType = AVCFileTOL;
1150 0 : psInfo->pasSections[iSect].pszName = CPLStrdup("TOL");
1151 0 : psInfo->pasSections[iSect].pszFilename =
1152 0 : CPLStrdup(papszCoverDir[iFile]);
1153 : }
1154 :
1155 : /*-----------------------------------------------------------------
1156 : * TXT section (txt.adf)
1157 : *----------------------------------------------------------------*/
1158 3 : szFname =
1159 0 : (psInfo->eCoverType == AVCCoverV7 || psInfo->eCoverType == AVCCoverPC2)
1160 3 : ? "txt.adf"
1161 : : "txt";
1162 3 : if ((iFile = CSLFindString(papszCoverDir, szFname)) != -1 &&
1163 : (psFile =
1164 0 : AVCBinReadOpen(psInfo->pszCoverPath, szFname, psInfo->eCoverType,
1165 : AVCFileTXT, psInfo->psDBCSInfo)) != nullptr)
1166 : {
1167 0 : if (nCoverPrecision == AVC_DEFAULT_PREC)
1168 0 : nCoverPrecision = psFile->nPrecision;
1169 0 : AVCBinReadClose(psFile);
1170 0 : const int iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections),
1171 : &(psInfo->numSections), 1);
1172 :
1173 0 : psInfo->pasSections[iSect].eType = AVCFileTXT;
1174 0 : psInfo->pasSections[iSect].pszName = CPLStrdup("TXT");
1175 0 : psInfo->pasSections[iSect].pszFilename =
1176 0 : CPLStrdup(papszCoverDir[iFile]);
1177 : }
1178 :
1179 : /*-----------------------------------------------------------------
1180 : * TX6 section (*.txt)
1181 : * Scan the directory for files with a ".txt" extension.
1182 : * Note: Never seen those in a PC Arc/Info coverage!
1183 : * In weird coverages, the filename ends with "txt" but there is no "."
1184 : *----------------------------------------------------------------*/
1185 3 : if (psInfo->eCoverType == AVCCoverV7)
1186 3 : nCoverPrecision = _AVCE00ReadAddJabberwockySection(
1187 : psInfo, AVCFileTX6, "TX6", nCoverPrecision, ".txt", papszCoverDir);
1188 0 : else if (psInfo->eCoverType == AVCCoverWeird)
1189 0 : nCoverPrecision = _AVCE00ReadAddJabberwockySection(
1190 : psInfo, AVCFileTX6, "TX6", nCoverPrecision, "txt", papszCoverDir);
1191 :
1192 : /*-----------------------------------------------------------------
1193 : * At this point, we should have read the coverage precision... and if
1194 : * we haven't yet then we'll just use single by default.
1195 : * We'll need cPrecisionCode for some of the sections that follow.
1196 : *----------------------------------------------------------------*/
1197 3 : if (nCoverPrecision == AVC_DOUBLE_PREC)
1198 0 : cPrecisionCode = '3';
1199 : else
1200 3 : cPrecisionCode = '2';
1201 :
1202 : /*-----------------------------------------------------------------
1203 : * SIN 2/3 and EOX lines ... ???
1204 : *----------------------------------------------------------------*/
1205 : {
1206 3 : int iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections),
1207 : &(psInfo->numSections), 2);
1208 3 : psInfo->pasSections[iSect].eType = AVCFileUnknown;
1209 3 : psInfo->pasSections[iSect].pszName = CPLStrdup("SIN X");
1210 3 : psInfo->pasSections[iSect].pszName[5] = cPrecisionCode;
1211 3 : iSect++;
1212 3 : psInfo->pasSections[iSect].eType = AVCFileUnknown;
1213 3 : psInfo->pasSections[iSect].pszName = CPLStrdup("EOX");
1214 : }
1215 :
1216 : /*-----------------------------------------------------------------
1217 : * LOG section (log.adf) (ends with EOL)
1218 : *----------------------------------------------------------------*/
1219 :
1220 : /*-----------------------------------------------------------------
1221 : * PRJ section (prj.adf) (ends with EOP)
1222 : *----------------------------------------------------------------*/
1223 3 : szFname =
1224 0 : (psInfo->eCoverType == AVCCoverV7 || psInfo->eCoverType == AVCCoverPC2)
1225 3 : ? "prj.adf"
1226 : : "prj";
1227 3 : if ((iFile = CSLFindString(papszCoverDir, szFname)) != -1)
1228 : {
1229 2 : const int iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections),
1230 : &(psInfo->numSections), 1);
1231 :
1232 2 : psInfo->pasSections[iSect].eType = AVCFilePRJ;
1233 2 : psInfo->pasSections[iSect].pszName = CPLStrdup("PRJ");
1234 4 : psInfo->pasSections[iSect].pszFilename =
1235 2 : CPLStrdup(papszCoverDir[iFile]);
1236 : }
1237 :
1238 : /*-----------------------------------------------------------------
1239 : * RXP section (*.rxp)
1240 : * Scan the directory for files with a ".rxp" extension.
1241 : *----------------------------------------------------------------*/
1242 3 : if (psInfo->eCoverType == AVCCoverV7)
1243 3 : _AVCE00ReadAddJabberwockySection(
1244 : psInfo, AVCFileRXP, "RXP", nCoverPrecision, ".rxp", papszCoverDir);
1245 0 : else if (psInfo->eCoverType == AVCCoverWeird)
1246 0 : _AVCE00ReadAddJabberwockySection(psInfo, AVCFileRXP, "RXP",
1247 : nCoverPrecision, "rxp", papszCoverDir);
1248 :
1249 : /*-----------------------------------------------------------------
1250 : * RPL section (*.pal)
1251 : * Scan the directory for files with a ".rpl" extension.
1252 : *----------------------------------------------------------------*/
1253 3 : if (psInfo->eCoverType == AVCCoverV7)
1254 3 : _AVCE00ReadAddJabberwockySection(
1255 : psInfo, AVCFileRPL, "RPL", nCoverPrecision, ".pal", papszCoverDir);
1256 0 : else if (psInfo->eCoverType == AVCCoverWeird)
1257 0 : _AVCE00ReadAddJabberwockySection(psInfo, AVCFileRPL, "RPL",
1258 : nCoverPrecision, "rpl", papszCoverDir);
1259 :
1260 : /*-----------------------------------------------------------------
1261 : * IFO section (tables)
1262 : *----------------------------------------------------------------*/
1263 3 : papszTables = papszFiles = nullptr;
1264 3 : if (psInfo->eCoverType == AVCCoverV7 ||
1265 0 : psInfo->eCoverType == AVCCoverV7Tables ||
1266 0 : psInfo->eCoverType == AVCCoverWeird)
1267 : {
1268 : /*-------------------------------------------------------------
1269 : * Unix coverages: get tables from the ../info/arc.dir
1270 : * Weird coverages: the arc.dir is similar but called "arcdr9"
1271 : *------------------------------------------------------------*/
1272 3 : papszTables = AVCBinReadListTables(
1273 3 : psInfo->pszInfoPath, psInfo->pszCoverName, &papszFiles,
1274 : psInfo->eCoverType, psInfo->psDBCSInfo);
1275 : }
1276 0 : else if (psInfo->eCoverType == AVCCoverPC ||
1277 0 : psInfo->eCoverType == AVCCoverPC2)
1278 : {
1279 : /*-------------------------------------------------------------
1280 : * PC coverages: look for "???.dbf" in the coverage directory
1281 : * and build the table name using the coverage name
1282 : * as the table basename, and the dbf file basename
1283 : * as the table extension.
1284 : *------------------------------------------------------------*/
1285 0 : for (iFile = 0; papszCoverDir && papszCoverDir[iFile]; iFile++)
1286 : {
1287 0 : if ((nLen = (int)strlen(papszCoverDir[iFile])) == 7 &&
1288 0 : EQUAL(papszCoverDir[iFile] + nLen - 4, ".dbf"))
1289 : {
1290 0 : papszCoverDir[iFile][nLen - 4] = '\0';
1291 0 : szFname = CPLSPrintf("%s.%s", psInfo->pszCoverName,
1292 0 : papszCoverDir[iFile]);
1293 0 : pcTmp = (char *)szFname;
1294 0 : for (; *pcTmp != '\0'; pcTmp++)
1295 0 : *pcTmp =
1296 0 : (char)CPLToupper(static_cast<unsigned char>(*pcTmp));
1297 0 : papszCoverDir[iFile][nLen - 4] = '.';
1298 :
1299 0 : papszTables = CSLAddString(papszTables, szFname);
1300 0 : papszFiles = CSLAddString(papszFiles, papszCoverDir[iFile]);
1301 : }
1302 : }
1303 : }
1304 :
1305 3 : if (papszTables != nullptr && (numTables = CSLCount(papszTables)) > 0)
1306 : {
1307 3 : int iSect = _AVCIncreaseSectionsArray(
1308 : &(psInfo->pasSections), &(psInfo->numSections), numTables + 2);
1309 :
1310 3 : psInfo->pasSections[iSect].eType = AVCFileUnknown;
1311 3 : psInfo->pasSections[iSect].pszName = CPLStrdup("IFO X");
1312 3 : psInfo->pasSections[iSect].pszName[5] = cPrecisionCode;
1313 3 : iSect++;
1314 :
1315 11 : for (iTable = 0; iTable < numTables; iTable++)
1316 : {
1317 8 : psInfo->pasSections[iSect].eType = AVCFileTABLE;
1318 8 : psInfo->pasSections[iSect].pszName = CPLStrdup(papszTables[iTable]);
1319 8 : if (papszFiles)
1320 : {
1321 16 : psInfo->pasSections[iSect].pszFilename =
1322 8 : CPLStrdup(papszFiles[iTable]);
1323 : }
1324 8 : iSect++;
1325 : }
1326 :
1327 3 : psInfo->pasSections[iSect].eType = AVCFileUnknown;
1328 3 : psInfo->pasSections[iSect].pszName = CPLStrdup("EOI");
1329 : }
1330 3 : CSLDestroy(papszTables);
1331 3 : CSLDestroy(papszFiles);
1332 :
1333 : /*-----------------------------------------------------------------
1334 : * File ends with EOS
1335 : *----------------------------------------------------------------*/
1336 3 : const int iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections),
1337 : &(psInfo->numSections), 1);
1338 3 : psInfo->pasSections[iSect].eType = AVCFileUnknown;
1339 3 : psInfo->pasSections[iSect].pszName = CPLStrdup("EOS");
1340 :
1341 6 : return nCoverPrecision;
1342 : }
1343 :
1344 : /**********************************************************************
1345 : * _AVCE00ReadScanE00()
1346 : *
1347 : * Processes an entire E00 file to find all the interesting sections.
1348 : **********************************************************************/
1349 11 : static void _AVCE00ReadScanE00(AVCE00ReadE00Ptr psRead)
1350 : {
1351 11 : AVCE00ParseInfo *psInfo = psRead->hParseInfo;
1352 :
1353 : const char *pszLine;
1354 11 : const char *pszName = nullptr;
1355 : void *obj;
1356 11 : int iSect = 0;
1357 11 : GBool bFirstLine = TRUE;
1358 :
1359 3656 : while (CPLGetLastErrorNo() == 0 &&
1360 1828 : (pszLine = CPLReadLine2L(psRead->hFile, knMAX_CHARS_PER_LINE,
1361 : nullptr)) != nullptr)
1362 : {
1363 1818 : if (bFirstLine)
1364 : {
1365 : /* Look for the first non-empty line, after the EXP header,
1366 : * trying to detect compressed E00 files. If the file is
1367 : * compressed, the first line of data should be 79 or 80 chars
1368 : * long and contain several '~' characters.
1369 : */
1370 22 : int nLen = (int)strlen(pszLine);
1371 22 : if (nLen == 0 || STARTS_WITH_CI(pszLine, "EXP "))
1372 11 : continue; /* Skip empty and EXP header lines */
1373 11 : else if ((nLen == 79 || nLen == 80) &&
1374 1 : strchr(pszLine, '~') != nullptr)
1375 : {
1376 : /* Looks like a compressed file. Just log an error and return.
1377 : * The caller should reject the file because it contains 0
1378 : * sections
1379 : */
1380 1 : CPLError(CE_Failure, CPLE_OpenFailed,
1381 : "This looks like a compressed E00 file and cannot be "
1382 : "processed directly. You may need to uncompress it "
1383 : "first using the E00compr library or the e00conv "
1384 : "program.");
1385 1 : return;
1386 : }
1387 :
1388 : /* All seems fine. Continue with normal processing */
1389 10 : bFirstLine = FALSE;
1390 : }
1391 :
1392 : /* coverity[tainted_data] */
1393 1806 : obj = _AVCE00ReadNextLineE00(psRead, pszLine);
1394 :
1395 1806 : if (obj)
1396 : {
1397 823 : pszName = nullptr;
1398 823 : switch (psInfo->eFileType)
1399 : {
1400 49 : case AVCFileARC:
1401 49 : pszName = "ARC";
1402 49 : break;
1403 :
1404 20 : case AVCFilePAL:
1405 20 : pszName = "PAL";
1406 20 : break;
1407 :
1408 20 : case AVCFileCNT:
1409 20 : pszName = "CNT";
1410 20 : break;
1411 :
1412 254 : case AVCFileLAB:
1413 254 : pszName = "LAB";
1414 254 : break;
1415 :
1416 0 : case AVCFileRPL:
1417 0 : pszName = "RPL";
1418 0 : break;
1419 :
1420 0 : case AVCFileTXT:
1421 0 : pszName = "TXT";
1422 0 : break;
1423 :
1424 0 : case AVCFileTX6:
1425 0 : pszName = "TX6";
1426 0 : break;
1427 :
1428 7 : case AVCFilePRJ:
1429 7 : pszName = "PRJ";
1430 7 : break;
1431 :
1432 373 : case AVCFileTABLE:
1433 373 : pszName = psInfo->hdr.psTableDef->szTableName;
1434 373 : break;
1435 :
1436 100 : default:
1437 100 : break;
1438 : }
1439 :
1440 823 : if (pszName &&
1441 723 : (psRead->numSections == 0 ||
1442 713 : psRead->pasSections[iSect].eType != psInfo->eFileType ||
1443 679 : !EQUAL(pszName, psRead->pasSections[iSect].pszName)))
1444 : {
1445 76 : iSect = _AVCIncreaseSectionsArray(&(psRead->pasSections),
1446 : &(psRead->numSections), 1);
1447 :
1448 76 : psRead->pasSections[iSect].eType = psInfo->eFileType;
1449 : /* psRead->pasSections[iSect].pszName =
1450 : * CPLStrdup(psRead->pszCoverName); */
1451 76 : psRead->pasSections[iSect].pszName = CPLStrdup(pszName);
1452 152 : psRead->pasSections[iSect].pszFilename =
1453 76 : CPLStrdup(psRead->pszCoverPath);
1454 76 : psRead->pasSections[iSect].nLineNum = psInfo->nStartLineNum;
1455 76 : psRead->pasSections[iSect].nFeatureCount = 0;
1456 : }
1457 :
1458 823 : if (pszName && psRead->numSections)
1459 : {
1460 : /* increase feature count for current layer */
1461 723 : ++psRead->pasSections[iSect].nFeatureCount;
1462 : }
1463 : }
1464 : }
1465 : }
1466 :
1467 : /**********************************************************************
1468 : * _AVCE00ReadNextTableLine()
1469 : *
1470 : * Return the next line of the E00 representation of a info table.
1471 : *
1472 : * This function is used by AVCE00ReadNextLine() to generate table
1473 : * output... it should never be called directly.
1474 : **********************************************************************/
1475 0 : static const char *_AVCE00ReadNextTableLine(AVCE00ReadPtr psInfo)
1476 : {
1477 0 : const char *pszLine = nullptr;
1478 : AVCE00Section *psSect;
1479 :
1480 0 : psSect = &(psInfo->pasSections[psInfo->iCurSection]);
1481 :
1482 0 : CPLAssert(psSect->eType == AVCFileTABLE);
1483 :
1484 0 : if (psInfo->iCurStep == AVC_GEN_NOTSTARTED)
1485 : {
1486 : /*---------------------------------------------------------
1487 : * Open table and start returning header
1488 : *--------------------------------------------------------*/
1489 0 : if (psInfo->eCoverType == AVCCoverPC ||
1490 0 : psInfo->eCoverType == AVCCoverPC2)
1491 : {
1492 : /*---------------------------------------------------------
1493 : * PC Arc/Info: We pass the DBF table's full filename + the
1494 : * Arc/Info table name (for E00 header)
1495 : *--------------------------------------------------------*/
1496 : char *pszFname;
1497 0 : pszFname = CPLStrdup(
1498 : CPLSPrintf("%s%s", psInfo->pszInfoPath, psSect->pszFilename));
1499 0 : psInfo->hFile =
1500 0 : AVCBinReadOpen(pszFname, psSect->pszName, psInfo->eCoverType,
1501 : psSect->eType, psInfo->psDBCSInfo);
1502 0 : CPLFree(pszFname);
1503 : }
1504 : else
1505 : {
1506 : /*---------------------------------------------------------
1507 : * AVCCoverV7 and AVCCoverWeird:
1508 : * We pass the INFO dir's path, and the Arc/Info table name
1509 : * will be searched in the arc.dir
1510 : *--------------------------------------------------------*/
1511 0 : psInfo->hFile = AVCBinReadOpen(psInfo->pszInfoPath, psSect->pszName,
1512 : psInfo->eCoverType, psSect->eType,
1513 : psInfo->psDBCSInfo);
1514 : }
1515 :
1516 : /* For some reason the file could not be opened... abort now.
1517 : * An error message should have already been produced by
1518 : * AVCBinReadOpen()
1519 : */
1520 0 : if (psInfo->hFile == nullptr)
1521 0 : return nullptr;
1522 :
1523 0 : psInfo->iCurStep = AVC_GEN_TABLEHEADER;
1524 :
1525 0 : pszLine = AVCE00GenTableHdr(psInfo->hGenInfo,
1526 0 : psInfo->hFile->hdr.psTableDef, FALSE);
1527 : }
1528 :
1529 0 : if (pszLine == nullptr && psInfo->iCurStep == AVC_GEN_TABLEHEADER)
1530 : {
1531 : /*---------------------------------------------------------
1532 : * Continue table header
1533 : *--------------------------------------------------------*/
1534 0 : pszLine = AVCE00GenTableHdr(psInfo->hGenInfo,
1535 0 : psInfo->hFile->hdr.psTableDef, TRUE);
1536 :
1537 0 : if (pszLine == nullptr)
1538 : {
1539 : /* Finished with table header... time to proceed with the
1540 : * table data.
1541 : * Reset the AVCE00GenInfo struct. so that it returns nullptr,
1542 : * which will force reading of the first record from the
1543 : * file on the next call to AVCE00ReadNextLine()
1544 : */
1545 0 : AVCE00GenReset(psInfo->hGenInfo);
1546 0 : psInfo->iCurStep = AVC_GEN_TABLEDATA;
1547 : }
1548 : }
1549 :
1550 0 : if (pszLine == nullptr && psInfo->iCurStep == AVC_GEN_TABLEDATA)
1551 : {
1552 : /*---------------------------------------------------------
1553 : * Continue with records of data
1554 : *--------------------------------------------------------*/
1555 :
1556 0 : pszLine = AVCE00GenTableRec(psInfo->hGenInfo,
1557 0 : psInfo->hFile->hdr.psTableDef->numFields,
1558 0 : psInfo->hFile->hdr.psTableDef->pasFieldDef,
1559 0 : psInfo->hFile->cur.pasFields, TRUE);
1560 :
1561 0 : if (pszLine == nullptr)
1562 : {
1563 : /* Current record is finished generating... we need to read
1564 : * a new one from the file.
1565 : */
1566 0 : if (AVCBinReadNextObject(psInfo->hFile) != nullptr)
1567 : {
1568 0 : pszLine = AVCE00GenTableRec(
1569 0 : psInfo->hGenInfo, psInfo->hFile->hdr.psTableDef->numFields,
1570 0 : psInfo->hFile->hdr.psTableDef->pasFieldDef,
1571 0 : psInfo->hFile->cur.pasFields, FALSE);
1572 : }
1573 : }
1574 : }
1575 :
1576 0 : if (pszLine == nullptr)
1577 : {
1578 : /*---------------------------------------------------------
1579 : * No more lines to output for this table ... Close it.
1580 : *--------------------------------------------------------*/
1581 0 : AVCBinReadClose(psInfo->hFile);
1582 0 : psInfo->hFile = nullptr;
1583 :
1584 : /*---------------------------------------------------------
1585 : * And now proceed to the next section...
1586 : * OK, I don't really like recursion, but it was
1587 : * the simplest way to do this, and anyways we should never
1588 : * have more than one level of recursion.
1589 : *--------------------------------------------------------*/
1590 0 : if (psInfo->bReadAllSections)
1591 0 : psInfo->iCurSection++;
1592 : else
1593 0 : psInfo->iCurSection = psInfo->numSections;
1594 0 : psInfo->iCurStep = AVC_GEN_NOTSTARTED;
1595 :
1596 0 : pszLine = AVCE00ReadNextLine(psInfo);
1597 : }
1598 :
1599 : /*-----------------------------------------------------------------
1600 : * Check for errors... if any error happened, then return nullptr.
1601 : *----------------------------------------------------------------*/
1602 0 : if (CPLGetLastErrorNo() != 0)
1603 : {
1604 0 : pszLine = nullptr;
1605 : }
1606 :
1607 0 : return pszLine;
1608 : }
1609 :
1610 : /**********************************************************************
1611 : * AVCE00ReadNextLine()
1612 : *
1613 : * Returns the next line of the E00 representation of the coverage
1614 : * or nullptr when there are no more lines to generate, or if an error happened.
1615 : * The returned line is a null-terminated string, and it does not
1616 : * include a newline character.
1617 : *
1618 : * Call CPLGetLastErrorNo() after calling AVCE00ReadNextLine() to
1619 : * make sure that the line was generated successfully.
1620 : *
1621 : * Note that AVCE00ReadNextLine() returns a reference to an
1622 : * internal buffer whose contents will
1623 : * be valid only until the next call to this function. The caller should
1624 : * not attempt to free() the returned pointer.
1625 : **********************************************************************/
1626 0 : const char *AVCE00ReadNextLine(AVCE00ReadPtr psInfo)
1627 : {
1628 0 : const char *pszLine = nullptr;
1629 : AVCE00Section *psSect;
1630 :
1631 0 : CPLErrorReset();
1632 :
1633 : /*-----------------------------------------------------------------
1634 : * Check if we have finished generating E00 output
1635 : *----------------------------------------------------------------*/
1636 0 : if (psInfo->iCurSection >= psInfo->numSections)
1637 0 : return nullptr;
1638 :
1639 0 : psSect = &(psInfo->pasSections[psInfo->iCurSection]);
1640 :
1641 : /*-----------------------------------------------------------------
1642 : * For simplicity, the generation of table output is in a separate
1643 : * function.
1644 : *----------------------------------------------------------------*/
1645 0 : if (psSect->eType == AVCFileTABLE)
1646 : {
1647 0 : return _AVCE00ReadNextTableLine(psInfo);
1648 : }
1649 :
1650 0 : if (psSect->eType == AVCFileUnknown)
1651 : {
1652 : /*-----------------------------------------------------------------
1653 : * Section not attached to any file, used to hold header lines
1654 : * or section separators, etc... just return the line directly and
1655 : * move pointer to the next section.
1656 : *----------------------------------------------------------------*/
1657 0 : pszLine = psSect->pszName;
1658 0 : if (psInfo->bReadAllSections)
1659 0 : psInfo->iCurSection++;
1660 : else
1661 0 : psInfo->iCurSection = psInfo->numSections;
1662 0 : psInfo->iCurStep = AVC_GEN_NOTSTARTED;
1663 : }
1664 : /*=================================================================
1665 : * ARC, PAL, CNT, LAB, TOL and TXT
1666 : *================================================================*/
1667 0 : else if (psInfo->iCurStep == AVC_GEN_NOTSTARTED &&
1668 0 : (psSect->eType == AVCFileARC || psSect->eType == AVCFilePAL ||
1669 0 : psSect->eType == AVCFileRPL || psSect->eType == AVCFileCNT ||
1670 0 : psSect->eType == AVCFileLAB || psSect->eType == AVCFileTOL ||
1671 0 : psSect->eType == AVCFileTXT || psSect->eType == AVCFileTX6 ||
1672 0 : psSect->eType == AVCFileRXP))
1673 : {
1674 : /*-----------------------------------------------------------------
1675 : * Start processing of an ARC, PAL, CNT, LAB or TOL section:
1676 : * Open the file, get ready to read the first object from the
1677 : * file, and return the header line.
1678 : * If the file fails to open then we will return nullptr.
1679 : *----------------------------------------------------------------*/
1680 0 : psInfo->hFile = AVCBinReadOpen(psInfo->pszCoverPath,
1681 0 : psSect->pszFilename, psInfo->eCoverType,
1682 : psSect->eType, psInfo->psDBCSInfo);
1683 :
1684 : /*-------------------------------------------------------------
1685 : * For some reason the file could not be opened... abort now.
1686 : * An error message should have already been produced by
1687 : * AVCBinReadOpen()
1688 : *------------------------------------------------------------*/
1689 0 : if (psInfo->hFile == nullptr)
1690 0 : return nullptr;
1691 :
1692 0 : pszLine = AVCE00GenStartSection(psInfo->hGenInfo, psSect->eType,
1693 0 : psSect->pszName);
1694 :
1695 : /*-------------------------------------------------------------
1696 : * Reset the AVCE00GenInfo struct. so that it returns nullptr,
1697 : * which will force reading of the first object from the
1698 : * file on the next call to AVCE00ReadNextLine()
1699 : *------------------------------------------------------------*/
1700 0 : AVCE00GenReset(psInfo->hGenInfo);
1701 0 : psInfo->iCurStep = AVC_GEN_DATA;
1702 : }
1703 0 : else if (psInfo->iCurStep == AVC_GEN_DATA &&
1704 0 : (psSect->eType == AVCFileARC || psSect->eType == AVCFilePAL ||
1705 0 : psSect->eType == AVCFileRPL || psSect->eType == AVCFileCNT ||
1706 0 : psSect->eType == AVCFileLAB || psSect->eType == AVCFileTOL ||
1707 0 : psSect->eType == AVCFileTXT || psSect->eType == AVCFileTX6 ||
1708 0 : psSect->eType == AVCFileRXP))
1709 : {
1710 : /*-----------------------------------------------------------------
1711 : * Return the next line of an ARC/PAL/CNT/TOL/TXT object...
1712 : * if necessary, read the next object from the binary file.
1713 : *----------------------------------------------------------------*/
1714 0 : pszLine = AVCE00GenObject(
1715 : psInfo->hGenInfo, psSect->eType,
1716 0 : (psSect->eType == AVCFileARC ? (void *)(psInfo->hFile->cur.psArc)
1717 0 : : psSect->eType == AVCFilePAL ? (void *)(psInfo->hFile->cur.psPal)
1718 0 : : psSect->eType == AVCFileRPL ? (void *)(psInfo->hFile->cur.psPal)
1719 0 : : psSect->eType == AVCFileCNT ? (void *)(psInfo->hFile->cur.psCnt)
1720 0 : : psSect->eType == AVCFileLAB ? (void *)(psInfo->hFile->cur.psLab)
1721 0 : : psSect->eType == AVCFileTOL ? (void *)(psInfo->hFile->cur.psTol)
1722 0 : : psSect->eType == AVCFileTXT ? (void *)(psInfo->hFile->cur.psTxt)
1723 0 : : psSect->eType == AVCFileTX6 ? (void *)(psInfo->hFile->cur.psTxt)
1724 0 : : psSect->eType == AVCFileRXP ? (void *)(psInfo->hFile->cur.psRxp)
1725 : : nullptr),
1726 : TRUE);
1727 0 : if (pszLine == nullptr)
1728 : {
1729 : /*---------------------------------------------------------
1730 : * Current object is finished generating... we need to read
1731 : * a new one from the file.
1732 : *--------------------------------------------------------*/
1733 0 : if (AVCBinReadNextObject(psInfo->hFile) != nullptr)
1734 : {
1735 : pszLine =
1736 0 : AVCE00GenObject(psInfo->hGenInfo, psSect->eType,
1737 0 : (psSect->eType == AVCFileARC
1738 0 : ? (void *)(psInfo->hFile->cur.psArc)
1739 0 : : psSect->eType == AVCFilePAL
1740 0 : ? (void *)(psInfo->hFile->cur.psPal)
1741 0 : : psSect->eType == AVCFileRPL
1742 0 : ? (void *)(psInfo->hFile->cur.psPal)
1743 0 : : psSect->eType == AVCFileCNT
1744 0 : ? (void *)(psInfo->hFile->cur.psCnt)
1745 0 : : psSect->eType == AVCFileLAB
1746 0 : ? (void *)(psInfo->hFile->cur.psLab)
1747 0 : : psSect->eType == AVCFileTOL
1748 0 : ? (void *)(psInfo->hFile->cur.psTol)
1749 0 : : psSect->eType == AVCFileTXT
1750 0 : ? (void *)(psInfo->hFile->cur.psTxt)
1751 0 : : psSect->eType == AVCFileTX6
1752 0 : ? (void *)(psInfo->hFile->cur.psTxt)
1753 0 : : psSect->eType == AVCFileRXP
1754 0 : ? (void *)(psInfo->hFile->cur.psRxp)
1755 : : nullptr),
1756 : FALSE);
1757 : }
1758 : }
1759 0 : if (pszLine == nullptr)
1760 : {
1761 : /*---------------------------------------------------------
1762 : * Still nullptr ??? This means we finished reading this file...
1763 : * Start returning the "end of section" line(s)...
1764 : *--------------------------------------------------------*/
1765 0 : AVCBinReadClose(psInfo->hFile);
1766 0 : psInfo->hFile = nullptr;
1767 0 : psInfo->iCurStep = AVC_GEN_ENDSECTION;
1768 : pszLine =
1769 0 : AVCE00GenEndSection(psInfo->hGenInfo, psSect->eType, FALSE);
1770 : }
1771 : }
1772 : /*=================================================================
1773 : * PRJ
1774 : *================================================================*/
1775 0 : else if (psInfo->iCurStep == AVC_GEN_NOTSTARTED &&
1776 0 : psSect->eType == AVCFilePRJ)
1777 : {
1778 : /*-------------------------------------------------------------
1779 : * Start processing of PRJ section... return first header line.
1780 : *------------------------------------------------------------*/
1781 : pszLine =
1782 0 : AVCE00GenStartSection(psInfo->hGenInfo, psSect->eType, nullptr);
1783 :
1784 0 : psInfo->hFile = nullptr;
1785 0 : psInfo->iCurStep = AVC_GEN_DATA;
1786 : }
1787 0 : else if (psInfo->iCurStep == AVC_GEN_DATA && psSect->eType == AVCFilePRJ)
1788 : {
1789 : /*-------------------------------------------------------------
1790 : * Return the next line of a PRJ section
1791 : *------------------------------------------------------------*/
1792 0 : if (psInfo->hFile == nullptr)
1793 : {
1794 : /*---------------------------------------------------------
1795 : * File has not been read yet...
1796 : * Read the PRJ file, and return the first PRJ line.
1797 : *--------------------------------------------------------*/
1798 0 : psInfo->hFile = AVCBinReadOpen(
1799 0 : psInfo->pszCoverPath, psSect->pszFilename, psInfo->eCoverType,
1800 : psSect->eType, psInfo->psDBCSInfo);
1801 :
1802 : /* For some reason the file could not be opened... abort now.
1803 : * An error message should have already been produced by
1804 : * AVCBinReadOpen()
1805 : */
1806 0 : if (psInfo->hFile == nullptr)
1807 0 : return nullptr;
1808 :
1809 0 : pszLine = AVCE00GenPrj(psInfo->hGenInfo,
1810 0 : psInfo->hFile->cur.papszPrj, FALSE);
1811 : }
1812 : else
1813 : {
1814 : /*---------------------------------------------------------
1815 : * Generate the next line of output.
1816 : *--------------------------------------------------------*/
1817 0 : pszLine = AVCE00GenPrj(psInfo->hGenInfo,
1818 0 : psInfo->hFile->cur.papszPrj, TRUE);
1819 : }
1820 :
1821 0 : if (pszLine == nullptr)
1822 : {
1823 : /*---------------------------------------------------------
1824 : * Still nullptr ??? This means we finished generating this PRJ
1825 : * section...
1826 : * Start returning the "end of section" line(s)...
1827 : *--------------------------------------------------------*/
1828 0 : AVCBinReadClose(psInfo->hFile);
1829 0 : psInfo->hFile = nullptr;
1830 0 : psInfo->iCurStep = AVC_GEN_ENDSECTION;
1831 : pszLine =
1832 0 : AVCE00GenEndSection(psInfo->hGenInfo, psSect->eType, FALSE);
1833 : }
1834 : }
1835 0 : else if (psInfo->iCurStep != AVC_GEN_ENDSECTION)
1836 : {
1837 : /* We should never get here! */
1838 0 : CPLAssert(FALSE);
1839 : }
1840 :
1841 : /*=================================================================
1842 : * End of section, for all files
1843 : *================================================================*/
1844 :
1845 : /*-----------------------------------------------------------------
1846 : * Finished processing of an ARC, PAL, CNT, LAB, TOL, PRJ file ...
1847 : * continue returning the "end of section" line(s), and move the pointer
1848 : * to the next section once we're done.
1849 : *----------------------------------------------------------------*/
1850 0 : if (psInfo->iCurStep == AVC_GEN_ENDSECTION && pszLine == nullptr)
1851 : {
1852 0 : pszLine = AVCE00GenEndSection(psInfo->hGenInfo, psSect->eType, TRUE);
1853 :
1854 0 : if (pszLine == nullptr)
1855 : {
1856 : /*---------------------------------------------------------
1857 : * Finished returning the last lines of the section...
1858 : * proceed to the next section...
1859 : * OK, I don't really like recursion, but it was
1860 : * the simplest way to do this, and anyways we should never
1861 : * have more than one level of recursion.
1862 : *--------------------------------------------------------*/
1863 0 : if (psInfo->bReadAllSections)
1864 0 : psInfo->iCurSection++;
1865 : else
1866 0 : psInfo->iCurSection = psInfo->numSections;
1867 0 : psInfo->iCurStep = AVC_GEN_NOTSTARTED;
1868 :
1869 0 : pszLine = AVCE00ReadNextLine(psInfo);
1870 : }
1871 : }
1872 :
1873 0 : return pszLine;
1874 : }
1875 :
1876 : /**********************************************************************
1877 : * AVCE00ReadSectionsList()
1878 : *
1879 : * Returns an array of AVCE00Section structures that describe the
1880 : * skeleton of the whole coverage. The value of *numSect will be
1881 : * set to the number of sections in the array.
1882 : *
1883 : * You can scan the returned array, and use AVCE00ReadGotoSection() to move
1884 : * the read pointer directly to the beginning of a given section
1885 : * of the file.
1886 : *
1887 : * Sections of type AVCFileUnknown correspond to lines in the
1888 : * E00 output that are not directly linked to any coverage file, like
1889 : * the "EXP 0" line, the "IFO X", "SIN X", etc.
1890 : *
1891 : * THE RETURNED ARRAY IS AN INTERNAL STRUCTURE AND SHOULD NOT BE
1892 : * MODIFIED OR FREED BY THE CALLER... its contents will be valid
1893 : * for as long as the coverage will remain open.
1894 : **********************************************************************/
1895 0 : AVCE00Section *AVCE00ReadSectionsList(AVCE00ReadPtr psInfo, int *numSect)
1896 : {
1897 0 : CPLErrorReset();
1898 :
1899 0 : *numSect = psInfo->numSections;
1900 0 : return psInfo->pasSections;
1901 : }
1902 :
1903 : /**********************************************************************
1904 : * AVCE00ReadGotoSection()
1905 : *
1906 : * Move the read pointer to the E00 section (coverage file) described in
1907 : * the psSect structure. Call AVCE00ReadSectionsList() to get the list of
1908 : * sections for the current coverage.
1909 : *
1910 : * if bContinue=TRUE, then reading will automatically continue with the
1911 : * next sections of the file once the requested section is finished.
1912 : * Otherwise, if bContinue=FALSE then reading will stop at the end
1913 : * of this section (i.e. AVCE00ReadNextLine() will return nullptr when
1914 : * it reaches the end of this section)
1915 : *
1916 : * Sections of type AVCFileUnknown returned by AVCE00ReadSectionsList()
1917 : * correspond to lines in the E00 output that are not directly linked
1918 : * to any coverage file, like the "EXP 0" line, the "IFO X", "SIN X", etc.
1919 : * You can jump to these sections or any other one without problems.
1920 : *
1921 : * This function returns 0 on success or -1 on error.
1922 : **********************************************************************/
1923 0 : int AVCE00ReadGotoSection(AVCE00ReadPtr psInfo, AVCE00Section *psSect,
1924 : GBool bContinue)
1925 : {
1926 : int iSect;
1927 0 : GBool bFound = FALSE;
1928 :
1929 0 : CPLErrorReset();
1930 :
1931 : /*-----------------------------------------------------------------
1932 : * Locate the requested section in the array.
1933 : *----------------------------------------------------------------*/
1934 0 : for (iSect = 0; iSect < psInfo->numSections; iSect++)
1935 : {
1936 0 : if (psInfo->pasSections[iSect].eType == psSect->eType &&
1937 0 : EQUAL(psInfo->pasSections[iSect].pszName, psSect->pszName))
1938 : {
1939 0 : bFound = TRUE;
1940 0 : break;
1941 : }
1942 : }
1943 :
1944 : /*-----------------------------------------------------------------
1945 : * Not found ... generate an error...
1946 : *----------------------------------------------------------------*/
1947 0 : if (!bFound)
1948 : {
1949 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1950 : "Requested E00 section does not exist!");
1951 0 : return -1;
1952 : }
1953 :
1954 : /*-----------------------------------------------------------------
1955 : * Found it ... close current section and get ready to read
1956 : * the new one.
1957 : *----------------------------------------------------------------*/
1958 0 : if (psInfo->hFile)
1959 : {
1960 0 : AVCBinReadClose(psInfo->hFile);
1961 0 : psInfo->hFile = nullptr;
1962 : }
1963 :
1964 0 : psInfo->bReadAllSections = bContinue;
1965 0 : psInfo->iCurSection = iSect;
1966 0 : psInfo->iCurStep = AVC_GEN_NOTSTARTED;
1967 :
1968 0 : return 0;
1969 : }
1970 :
1971 : /**********************************************************************
1972 : * AVCE00ReadRewind()
1973 : *
1974 : * Rewinds the AVCE00ReadPtr just like the stdio rewind()
1975 : * function would do if you were reading an ASCII E00 file.
1976 : *
1977 : * Returns 0 on success or -1 on error.
1978 : **********************************************************************/
1979 0 : int AVCE00ReadRewind(AVCE00ReadPtr psInfo)
1980 : {
1981 0 : CPLErrorReset();
1982 :
1983 0 : return AVCE00ReadGotoSection(psInfo, &(psInfo->pasSections[0]), TRUE);
1984 : }
1985 :
1986 : /**********************************************************************
1987 : * AVCE00ReadRewindE00()
1988 : *
1989 : * Rewinds the AVCE00ReadE00Ptr just like the stdio rewind()
1990 : * function would do if you were reading an ASCII E00 file.
1991 : *
1992 : * Returns 0 on success or -1 on error.
1993 : **********************************************************************/
1994 28 : int AVCE00ReadRewindE00(AVCE00ReadE00Ptr psRead)
1995 : {
1996 28 : CPLErrorReset();
1997 :
1998 28 : psRead->bReadAllSections = TRUE;
1999 28 : psRead->eCurFileType = AVCFileUnknown;
2000 :
2001 28 : psRead->hParseInfo->nCurLineNum = 0;
2002 28 : psRead->hParseInfo->nStartLineNum = 0;
2003 28 : psRead->hParseInfo->bForceEndOfSection = TRUE;
2004 28 : psRead->hParseInfo->eSuperSectionType = AVCFileUnknown;
2005 28 : AVCE00ParseSectionEnd(psRead->hParseInfo, nullptr, 1);
2006 :
2007 28 : return VSIFSeekL(psRead->hFile, 0, SEEK_SET);
2008 : }
2009 :
2010 : /**********************************************************************
2011 : * _AVCE00ReadSeekE00()
2012 : *
2013 : * Seeks to a new location in the E00 file, keeping parse state
2014 : * appropriately.
2015 : *
2016 : * NOTE: This is a pretty slow implementation.
2017 : * NOTE: The SEEK_END is not implemented.
2018 : *
2019 : * Returns 0 on success or -1 on error.
2020 : **********************************************************************/
2021 18 : static int _AVCE00ReadSeekE00(AVCE00ReadE00Ptr psRead, int nOffset, int nWhence)
2022 : {
2023 : const char *pszLine;
2024 : /* void *obj; */
2025 :
2026 18 : switch (nWhence)
2027 : {
2028 0 : case SEEK_CUR:
2029 0 : break;
2030 :
2031 18 : case SEEK_SET:
2032 18 : AVCE00ReadRewindE00(psRead);
2033 18 : break;
2034 :
2035 0 : default:
2036 0 : CPLAssert(nWhence == SEEK_CUR || nWhence == SEEK_SET);
2037 0 : break;
2038 : }
2039 :
2040 1766 : while (nOffset-- && CPLGetLastErrorNo() == 0 &&
2041 874 : (pszLine = CPLReadLine2L(psRead->hFile, knMAX_CHARS_PER_LINE,
2042 : nullptr)) != nullptr)
2043 : {
2044 : /* obj = */
2045 : /* coverity[tainted_data] */
2046 874 : _AVCE00ReadNextLineE00(psRead, pszLine);
2047 : }
2048 :
2049 18 : return nOffset ? -1 : 0;
2050 : }
2051 :
2052 : /**********************************************************************
2053 : * AVCE00ReadNextObjectE00()
2054 : *
2055 : * Returns the next object in an E00 file or nullptr when there are no
2056 : * more objects, or if an error happened. The object type can be
2057 : * determined via the eCurFileType attribute of the
2058 : * AVCE00ReadE00Ptr object.
2059 : *
2060 : * Note that AVCE00ReadNextLine() returns a reference to an internal
2061 : * buffer whose contents will be valid only until the next call to
2062 : * this function. The caller should not attempt to free() the
2063 : * returned pointer.
2064 : **********************************************************************/
2065 218 : void *AVCE00ReadNextObjectE00(AVCE00ReadE00Ptr psRead)
2066 : {
2067 : const char *pszLine;
2068 218 : void *obj = nullptr;
2069 :
2070 223 : do
2071 : {
2072 441 : pszLine = CPLReadLine2L(psRead->hFile, knMAX_CHARS_PER_LINE, nullptr);
2073 441 : if (pszLine == nullptr)
2074 0 : break;
2075 : /* coverity[tainted_data] */
2076 441 : obj = _AVCE00ReadNextLineE00(psRead, pszLine);
2077 : } while (
2078 226 : obj == nullptr &&
2079 664 : (psRead->bReadAllSections || psRead->eCurFileType != AVCFileUnknown) &&
2080 223 : CPLGetLastErrorNo() == 0);
2081 218 : return obj;
2082 : }
2083 :
2084 : /**********************************************************************
2085 : * AVCE00ReadSectionsListE00()
2086 : *
2087 : * Returns an array of AVCE00Section structures that describe the
2088 : * sections in the E00 file. The value of *numSect will be set to the
2089 : * number of sections in the array.
2090 : *
2091 : * You can scan the returned array, and use AVCE00ReadGotoSectionE00()
2092 : * to move the read pointer directly to the beginning of a given
2093 : * section of the file.
2094 : *
2095 : * THE RETURNED ARRAY IS AN INTERNAL STRUCTURE AND SHOULD NOT BE
2096 : * MODIFIED OR FREED BY THE CALLER... its contents will be valid
2097 : * for as long as the coverage will remain open.
2098 : **********************************************************************/
2099 0 : AVCE00Section *AVCE00ReadSectionsListE00(AVCE00ReadE00Ptr psRead, int *numSect)
2100 : {
2101 0 : CPLErrorReset();
2102 :
2103 0 : *numSect = psRead->numSections;
2104 0 : return psRead->pasSections;
2105 : }
2106 :
2107 : /**********************************************************************
2108 : * AVCE00ReadGotoSectionE00()
2109 : *
2110 : * Move the read pointer to the E00 section described in the psSect
2111 : * structure. Call AVCE00ReadSectionsListE00() to get the list of
2112 : * sections for the current coverage.
2113 : *
2114 : * If bContinue is TRUE, then reading will automatically continue with
2115 : * the next section of the file once the requested section is finished.
2116 : * Otherwise, if bContinue is FALSE then reading will stop at the end
2117 : * of this section (i.e. AVCE00ReadNextObjectE00() will return nullptr
2118 : * when the end of this section is reached)
2119 : *
2120 : * This function returns 0 on success or -1 on error.
2121 : **********************************************************************/
2122 18 : int AVCE00ReadGotoSectionE00(AVCE00ReadE00Ptr psRead, AVCE00Section *psSect,
2123 : GBool bContinue)
2124 : {
2125 : int iSect;
2126 18 : GBool bFound = FALSE;
2127 :
2128 18 : CPLErrorReset();
2129 :
2130 : /*-----------------------------------------------------------------
2131 : * Locate the requested section in the array.
2132 : *----------------------------------------------------------------*/
2133 52 : for (iSect = 0; iSect < psRead->numSections; iSect++)
2134 : {
2135 52 : if (psRead->pasSections[iSect].eType == psSect->eType &&
2136 26 : EQUAL(psRead->pasSections[iSect].pszName, psSect->pszName))
2137 : {
2138 18 : bFound = TRUE;
2139 18 : break;
2140 : }
2141 : }
2142 :
2143 : /*-----------------------------------------------------------------
2144 : * Not found ... generate an error...
2145 : *----------------------------------------------------------------*/
2146 18 : if (!bFound)
2147 : {
2148 0 : CPLError(CE_Failure, CPLE_IllegalArg,
2149 : "Requested E00 section does not exist!");
2150 0 : return -1;
2151 : }
2152 :
2153 : /*-----------------------------------------------------------------
2154 : * Found it ... advance parser to line number of start of section
2155 : *----------------------------------------------------------------*/
2156 18 : _AVCE00ReadSeekE00(psRead, psRead->pasSections[iSect].nLineNum, SEEK_SET);
2157 :
2158 18 : psRead->bReadAllSections = bContinue;
2159 :
2160 18 : return 0;
2161 : }
|