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