1 /**********************************************************************
2  * $Id: avc_bin.cpp 6ef13199b493973da285decbfcd5e2a763954b97 2018-06-07 05:46:42 -0400 luzpaz $
3  *
4  * Name:     avc_bin.c
5  * Project:  Arc/Info vector coverage (AVC)  BIN->E00 conversion library
6  * Language: ANSI C
7  * Purpose:  Binary files access functions.
8  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
9  *
10  **********************************************************************
11  * Copyright (c) 1999-2005, Daniel Morissette
12  *
13  * Permission is hereby granted, free of charge, to any person obtaining a
14  * copy of this software and associated documentation files (the "Software"),
15  * to deal in the Software without restriction, including without limitation
16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17  * and/or sell copies of the Software, and to permit persons to whom the
18  * Software is furnished to do so, subject to the following conditions:
19  *
20  * The above copyright notice and this permission notice shall be included
21  * in all copies or substantial portions of the Software.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
26  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29  * DEALINGS IN THE SOFTWARE.
30  **********************************************************************
31  *
32  * $Log: avc_bin.c,v $
33  * Revision 1.30  2008/07/23 20:51:38  dmorissette
34  * Fixed GCC 4.1.x compile warnings related to use of char vs unsigned char
35  * (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2495)
36  *
37  * Revision 1.29  2006/08/17 18:56:42  dmorissette
38  * Support for reading standalone info tables (just tables, no coverage
39  * data) by pointing AVCE00ReadOpen() to the info directory (bug 1549).
40  *
41  * Revision 1.28  2006/06/14 16:31:28  daniel
42  * Added support for AVCCoverPC2 type (bug 1491)
43  *
44  * Revision 1.27  2005/06/03 03:49:58  daniel
45  * Update email address, website url, and copyright dates
46  *
47  * Revision 1.26  2004/02/28 06:35:49  warmerda
48  * Fixed AVCBinReadObject() index support to use 'x' or 'X' for index
49  * depending on the case of the original name.
50  * Fixed so that PC Arc/Info coverages with the extra 256 byte header work
51  * properly when using indexes to read them.
52  *   http://bugzilla.remotesensing.org/show_bug.cgi?id=493
53  *
54  * Revision 1.25  2004/02/11 05:49:44  daniel
55  * Added support for deleted flag in arc.dir (bug 2332)
56  *
57  * Revision 1.24  2002/08/27 15:26:06  daniel
58  * Removed C++ style comments for IRIX compiler (GDAL bug 192)
59  *
60  * Revision 1.23  2002/04/16 20:04:24  daniel
61  * Use record size while reading ARC, PAL, CNT to skip junk bytes. (bug940)
62  *
63  * Revision 1.22  2002/03/18 19:03:37  daniel
64  * Fixed AVCBinReadObject() for PAL objects (bug 848)
65  *
66  * Revision 1.21  2002/02/14 22:54:13  warmerda
67  * added polygon and table support for random reading
68  *
69  * Revision 1.20  2002/02/13 20:35:24  warmerda
70  * added AVCBinReadObject
71  *
72  * Revision 1.19  2001/11/25 22:01:23  daniel
73  * Fixed order of args to AVCRawBinFSeek() in _AVCBinReadNextTableRec()
74  *
75  * Revision 1.18  2000/10/16 16:16:20  daniel
76  * Accept TXT files in AVCCoverWeird that use both PC or V7 TXT structure
77  *
78  * Revision 1.17  2000/09/26 20:21:04  daniel
79  * Added AVCCoverPC write
80  *
81  * Revision 1.16  2000/09/22 19:45:20  daniel
82  * Switch to MIT-style license
83  *
84  * Revision 1.15  2000/09/20 15:09:34  daniel
85  * Check for DAT/NIT fnames sometimes truncated to 8 chars in weird coverages
86  *
87  * Revision 1.14  2000/06/05 21:38:53  daniel
88  * Handle precision field > 1000 in cover file header as meaning double prec.
89  *
90  * Revision 1.13  2000/05/29 15:31:30  daniel
91  * Added Japanese DBCS support
92  *
93  * Revision 1.12  2000/02/14 17:22:36  daniel
94  * Check file signature (9993 or 9994) when reading header.
95  *
96  * Revision 1.11  2000/02/02 04:24:52  daniel
97  * Support double precision "weird" coverages
98  *
99  * Revision 1.10  2000/01/10 02:54:10  daniel
100  * Added read support for "weird" coverages
101  *
102  * Revision 1.9  2000/01/07 07:11:51  daniel
103  * Added support for reading PC Coverage TXT files
104  *
105  * Revision 1.8  1999/12/24 07:38:10  daniel
106  * Added missing DBFClose()
107  *
108  * Revision 1.7  1999/12/24 07:18:34  daniel
109  * Added PC Arc/Info coverages support
110  *
111  * Revision 1.6  1999/08/23 18:17:16  daniel
112  * Modified AVCBinReadListTables() to return INFO fnames for DeleteCoverage()
113  *
114  * Revision 1.5  1999/05/11 01:49:08  daniel
115  * Simple changes required by addition of coverage write support
116  *
117  * Revision 1.4  1999/03/03 18:42:53  daniel
118  * Fixed problem with INFO table headers (arc.dir) that sometimes contain an
119  * invalid number of records.
120  *
121  * Revision 1.3  1999/02/25 17:01:53  daniel
122  * Added support for 16 bit integers in INFO tables (type=50, size=2)
123  *
124  * Revision 1.2  1999/02/25 03:41:28  daniel
125  * Added TXT, TX6/TX7, RXP and RPL support
126  *
127  * Revision 1.1  1999/01/29 16:28:52  daniel
128  * Initial revision
129  *
130  **********************************************************************/
131 
132 #include "avc.h"
133 
134 #include <ctype.h>      /* for isspace() */
135 
136 
137 /* Used by avc_binwr.c */
138 extern int _AVCBinReadNextArcDir(AVCRawBinFile *psFile, AVCTableDef *psArcDir);
139 
140 /*=====================================================================
141  * Prototypes for some static functions
142  *====================================================================*/
143 
144 static AVCBinFile *_AVCBinReadOpenTable(const char *pszInfoPath,
145                                         const char *pszTableName,
146                                         AVCCoverType eCoverType,
147                                         AVCDBCSInfo *psDBCSInfo);
148 static AVCBinFile *_AVCBinReadOpenDBFTable(const char *pszInfoPath,
149                                            const char *pszTableName);
150 static AVCBinFile *_AVCBinReadOpenPrj(const char *pszPath,const char *pszName);
151 
152 static int _AVCBinReadNextTableRec(AVCRawBinFile *psFile, int nFields,
153                                    AVCFieldInfo *pasDef, AVCField *pasFields,
154                                    int nRecordSize);
155 static int _AVCBinReadNextDBFTableRec(DBFHandle hDBFFile, int *piRecordIndex,
156                                       int nFields, AVCFieldInfo *pasDef,
157                                       AVCField *pasFields);
158 
159 /*=====================================================================
160  * Stuff related to reading the binary coverage files
161  *====================================================================*/
162 
163 /**********************************************************************
164  *                          AVCBinReadOpen()
165  *
166  * Open a coverage file for reading, read the file header if applicable,
167  * and initialize a temp. storage structure to be ready to read objects
168  * from the file.
169  *
170  * pszPath is the coverage (or info directory) path, terminated by
171  *         a '/' or a '\\'
172  * pszName is the name of the file to open relative to this directory.
173  *
174  * Note: For most file types except tables, passing pszPath="" and
175  * including the coverage path as part of pszName instead would work.
176  *
177  * Returns a valid AVCBinFile handle, or nullptr if the file could
178  * not be opened.
179  *
180  * AVCBinClose() will eventually have to be called to release the
181  * resources used by the AVCBinFile structure.
182  **********************************************************************/
AVCBinReadOpen(const char * pszPath,const char * pszName,AVCCoverType eCoverType,AVCFileType eFileType,AVCDBCSInfo * psDBCSInfo)183 AVCBinFile *AVCBinReadOpen(const char *pszPath, const char *pszName,
184                            AVCCoverType eCoverType, AVCFileType eFileType,
185                            AVCDBCSInfo *psDBCSInfo)
186 {
187     AVCBinFile   *psFile;
188 
189     /*-----------------------------------------------------------------
190      * The case of INFO tables is a bit more complicated...
191      * pass the control to a separate function.
192      *----------------------------------------------------------------*/
193     if (eFileType == AVCFileTABLE)
194     {
195         if (eCoverType == AVCCoverPC || eCoverType == AVCCoverPC2)
196             return _AVCBinReadOpenDBFTable(pszPath, pszName);
197         else
198             return _AVCBinReadOpenTable(pszPath, pszName,
199                                         eCoverType, psDBCSInfo);
200     }
201 
202     /*-----------------------------------------------------------------
203      * PRJ files are text files... we won't use the AVCRawBin*()
204      * functions for them...
205      *----------------------------------------------------------------*/
206     if (eFileType == AVCFilePRJ)
207     {
208         return _AVCBinReadOpenPrj(pszPath, pszName);
209     }
210 
211     /*-----------------------------------------------------------------
212      * All other file types share a very similar opening method.
213      *----------------------------------------------------------------*/
214     psFile = (AVCBinFile*)CPLCalloc(1, sizeof(AVCBinFile));
215 
216     psFile->eFileType = eFileType;
217     psFile->eCoverType = eCoverType;
218 
219     psFile->pszFilename = (char*)CPLMalloc(strlen(pszPath)+strlen(pszName)+1);
220     snprintf(psFile->pszFilename,
221              strlen(pszPath)+strlen(pszName)+1, "%s%s", pszPath, pszName);
222 
223     AVCAdjustCaseSensitiveFilename(psFile->pszFilename);
224 
225     psFile->psRawBinFile = AVCRawBinOpen(psFile->pszFilename, "r",
226                                          AVC_COVER_BYTE_ORDER(eCoverType),
227                                          psDBCSInfo);
228 
229     if (psFile->psRawBinFile == nullptr)
230     {
231         /* Failed to open file... just return nullptr since an error message
232          * has already been issued by AVCRawBinOpen()
233          */
234         CPLFree(psFile->pszFilename);
235         CPLFree(psFile);
236         return nullptr;
237     }
238 
239     /*-----------------------------------------------------------------
240      * Read the header, and set the precision field if applicable
241      *----------------------------------------------------------------*/
242     if (AVCBinReadRewind(psFile) != 0)
243     {
244         AVCRawBinClose(psFile->psRawBinFile);
245         CPLFree(psFile->pszFilename);
246         CPLFree(psFile);
247         return nullptr;
248     }
249 
250     /*-----------------------------------------------------------------
251      * Allocate a temp. structure to use to read objects from the file
252      * (Using Calloc() will automatically initialize the struct contents
253      *  to nullptr... this is very important for ARCs and PALs)
254      *----------------------------------------------------------------*/
255     if (psFile->eFileType == AVCFileARC)
256     {
257         psFile->cur.psArc = (AVCArc*)CPLCalloc(1, sizeof(AVCArc));
258     }
259     else if (psFile->eFileType == AVCFilePAL ||
260              psFile->eFileType == AVCFileRPL )
261     {
262         psFile->cur.psPal = (AVCPal*)CPLCalloc(1, sizeof(AVCPal));
263     }
264     else if (psFile->eFileType == AVCFileCNT)
265     {
266         psFile->cur.psCnt = (AVCCnt*)CPLCalloc(1, sizeof(AVCCnt));
267     }
268     else if (psFile->eFileType == AVCFileLAB)
269     {
270         psFile->cur.psLab = (AVCLab*)CPLCalloc(1, sizeof(AVCLab));
271     }
272     else if (psFile->eFileType == AVCFileTOL)
273     {
274         psFile->cur.psTol = (AVCTol*)CPLCalloc(1, sizeof(AVCTol));
275     }
276     else if (psFile->eFileType == AVCFileTXT ||
277              psFile->eFileType == AVCFileTX6)
278     {
279         psFile->cur.psTxt = (AVCTxt*)CPLCalloc(1, sizeof(AVCTxt));
280     }
281     else if (psFile->eFileType == AVCFileRXP)
282     {
283         psFile->cur.psRxp = (AVCRxp*)CPLCalloc(1, sizeof(AVCRxp));
284     }
285     else
286     {
287         CPLError(CE_Failure, CPLE_IllegalArg,
288                  "%s: Unsupported file type or corrupted file.",
289                  psFile->pszFilename);
290         AVCRawBinClose(psFile->psRawBinFile);
291         CPLFree(psFile->pszFilename);
292         CPLFree(psFile);
293         psFile = nullptr;
294     }
295 
296     return psFile;
297 }
298 
299 /**********************************************************************
300  *                          AVCBinReadClose()
301  *
302  * Close a coverage file, and release all memory (object strcut., buffers,
303  * etc.) associated with this file.
304  **********************************************************************/
AVCBinReadClose(AVCBinFile * psFile)305 void    AVCBinReadClose(AVCBinFile *psFile)
306 {
307     AVCRawBinClose(psFile->psRawBinFile);
308     psFile->psRawBinFile = nullptr;
309 
310     CPLFree(psFile->pszFilename);
311     psFile->pszFilename = nullptr;
312 
313     if (psFile->hDBFFile)
314         DBFClose(psFile->hDBFFile);
315 
316     if( psFile->psIndexFile != nullptr )
317         AVCRawBinClose( psFile->psIndexFile );
318 
319     if (psFile->eFileType == AVCFileARC)
320     {
321         if (psFile->cur.psArc)
322             CPLFree(psFile->cur.psArc->pasVertices);
323         CPLFree(psFile->cur.psArc);
324     }
325     else if (psFile->eFileType == AVCFilePAL ||
326              psFile->eFileType == AVCFileRPL )
327     {
328         if (psFile->cur.psPal)
329             CPLFree(psFile->cur.psPal->pasArcs);
330         CPLFree(psFile->cur.psPal);
331     }
332     else if (psFile->eFileType == AVCFileCNT)
333     {
334         if (psFile->cur.psCnt)
335             CPLFree(psFile->cur.psCnt->panLabelIds);
336         CPLFree(psFile->cur.psCnt);
337     }
338     else if (psFile->eFileType == AVCFileLAB)
339     {
340         CPLFree(psFile->cur.psLab);
341     }
342     else if (psFile->eFileType == AVCFileTOL)
343     {
344         CPLFree(psFile->cur.psTol);
345     }
346     else if (psFile->eFileType == AVCFilePRJ)
347     {
348         CSLDestroy(psFile->cur.papszPrj);
349     }
350     else if (psFile->eFileType == AVCFileTXT ||
351              psFile->eFileType == AVCFileTX6)
352     {
353         if (psFile->cur.psTxt)
354         {
355             CPLFree(psFile->cur.psTxt->pasVertices);
356             CPLFree(psFile->cur.psTxt->pszText);
357         }
358         CPLFree(psFile->cur.psTxt);
359     }
360     else if (psFile->eFileType == AVCFileRXP)
361     {
362         CPLFree(psFile->cur.psRxp);
363     }
364     else if (psFile->eFileType == AVCFileTABLE)
365     {
366         _AVCDestroyTableFields(psFile->hdr.psTableDef, psFile->cur.pasFields);
367         _AVCDestroyTableDef(psFile->hdr.psTableDef);
368     }
369     else
370     {
371         CPLError(CE_Failure, CPLE_IllegalArg,
372                  "Unsupported file type or invalid file handle!");
373     }
374 
375     CPLFree(psFile);
376 }
377 
378 /**********************************************************************
379  *                          _AVCBinReadHeader()
380  *
381  * (This function is for internal library use... external calls should
382  * go to AVCBinReadRewind() instead)
383  *
384  * Read the first 100 bytes header of the file and fill the AVCHeader
385  * structure.
386  *
387  * Returns 0 on success or -1 on error.
388  **********************************************************************/
389 static
_AVCBinReadHeader(AVCRawBinFile * psFile,AVCBinHeader * psHeader,AVCCoverType eCoverType)390 int _AVCBinReadHeader(AVCRawBinFile *psFile, AVCBinHeader *psHeader,
391                       AVCCoverType eCoverType)
392 {
393     int nStatus = 0;
394 
395     /*-----------------------------------------------------------------
396      * For AVCCoverPC coverages (files without the .adf extension),
397      * there is a first 256 bytes header that we just skip and that
398      * precedes the 100 bytes header block.
399      *
400      * In AVCCoverV7, we only have the 100 bytes header.
401      *----------------------------------------------------------------*/
402     if (eCoverType == AVCCoverPC)
403         AVCRawBinFSeek(psFile, 256, SEEK_SET);
404     else
405         AVCRawBinFSeek(psFile, 0, SEEK_SET);
406 
407     psHeader->nSignature = AVCRawBinReadInt32(psFile);
408 
409     if (AVCRawBinEOF(psFile))
410         nStatus = -1;
411 
412     psHeader->nPrecision = AVCRawBinReadInt32(psFile);
413     psHeader->nRecordSize= AVCRawBinReadInt32(psFile);
414 
415     /* Jump to 24th byte in header */
416     AVCRawBinFSeek(psFile, 12, SEEK_CUR);
417     psHeader->nLength    = AVCRawBinReadInt32(psFile);
418     if( psHeader->nLength < 0 || psHeader->nLength > (INT_MAX - 256) / 2 )
419     {
420         return -1;
421     }
422 
423     /*-----------------------------------------------------------------
424      * File length, in words (16 bits)... pass the info to the RawBinFile
425      * to prevent it from trying to read junk bytes at the end of files...
426      * this problem happens specially with PC Arc/Info files.
427      *----------------------------------------------------------------*/
428     if (eCoverType == AVCCoverPC)
429         AVCRawBinSetFileDataSize(psFile, psHeader->nLength*2 + 256);
430     else
431         AVCRawBinSetFileDataSize(psFile, psHeader->nLength*2 );
432 
433     /* Move the pointer at the end of the 100 bytes header
434      */
435     AVCRawBinFSeek(psFile, 72, SEEK_CUR);
436 
437     return nStatus;
438 }
439 
440 
441 /**********************************************************************
442  *                          AVCBinReadRewind()
443  *
444  * Rewind the read pointer, and read/skip the header if necessary so
445  * that we are ready to read the data objects from the file after
446  * this call.
447  *
448  * Returns 0 on success, -1 on error, and -2 if file has an invalid
449  * signature and is possibly corrupted.
450  **********************************************************************/
AVCBinReadRewind(AVCBinFile * psFile)451 int AVCBinReadRewind(AVCBinFile *psFile)
452 {
453     AVCBinHeader sHeader;
454     int          nStatus=0;
455 
456     /*-----------------------------------------------------------------
457      * For AVCCoverPC coverages, there is a first 256 bytes header
458      * that we just skip and that precedes the 100 bytes header block.
459      *
460      * In AVCCoverV7, AVCCoverPC2 and AVCCoverWeird, we only find the
461      * 100 bytes header.
462      *
463      * Note: it is the call to _AVCBinReadHeader() that takes care
464      * of skipping the first 256 bytes header if necessary.
465      *----------------------------------------------------------------*/
466 
467     AVCRawBinFSeek(psFile->psRawBinFile, 0, SEEK_SET);
468 
469     if ( psFile->eFileType == AVCFileARC ||
470          psFile->eFileType == AVCFilePAL ||
471          psFile->eFileType == AVCFileRPL ||
472          psFile->eFileType == AVCFileCNT ||
473          psFile->eFileType == AVCFileLAB ||
474          psFile->eFileType == AVCFileTXT ||
475          psFile->eFileType == AVCFileTX6  )
476     {
477         nStatus = _AVCBinReadHeader(psFile->psRawBinFile, &sHeader,
478                                     psFile->eCoverType);
479 
480         /* Store the precision information inside the file handle.
481          *
482          * Of course, there had to be an exception...
483          * At least PAL and TXT files in PC Arc/Info coverages sometimes
484          * have a negative precision flag even if they contain single
485          * precision data... why is that????  A PC Arc bug?
486          *
487          * 2000-06-05: Found a double-precision PAL file with a signature
488          *             of 1011 (should have been -11).  So we'll assume
489          *             that signature > 1000 also means double precision.
490          */
491         if ((sHeader.nPrecision < 0 || sHeader.nPrecision > 1000) &&
492             psFile->eCoverType != AVCCoverPC)
493             psFile->nPrecision = AVC_DOUBLE_PREC;
494         else
495             psFile->nPrecision = AVC_SINGLE_PREC;
496 
497         /* Validate the signature value... this will allow us to detect
498          * corrupted files or files that do not belong in the coverage.
499          */
500         if (sHeader.nSignature != 9993 && sHeader.nSignature != 9994)
501         {
502             CPLError(CE_Warning, CPLE_AssertionFailed,
503                      "%s appears to have an invalid file header.",
504                      psFile->pszFilename);
505             return -2;
506         }
507 
508         /* In Weird coverages, TXT files can be stored in the PC or the V7
509          * format.  Look at the 'precision' field in the header to tell which
510          * type we have.
511          *   Weird TXT in PC format: nPrecision = 16
512          *   Weird TXT in V7 format: nPrecision = +/-67
513          * Use AVCFileTXT for PC type, and AVCFileTX6 for V7 type.
514          */
515         if (psFile->eCoverType == AVCCoverWeird &&
516             psFile->eFileType == AVCFileTXT &&
517             (sHeader.nPrecision == 67 || sHeader.nPrecision == -67) )
518         {
519             /* TXT file will be processed as V7 TXT/TX6/TX7 */
520             psFile->eFileType = AVCFileTX6;
521         }
522     }
523     else if (psFile->eFileType == AVCFileTOL)
524     {
525         /*-------------------------------------------------------------
526          * For some reason, the tolerance files do not follow the
527          * general rules!
528          * Single precision "tol.adf" have no header
529          * Double precision "par.adf" have the usual 100 bytes header,
530          *  but the 3rd field, which usually defines the precision has
531          *  a positive value, even if the file is double precision!
532          *
533          * Also, we have a problem with PC Arc/Info TOL files since they
534          * do not contain the first 256 bytes header either... so we will
535          * just assume that double precision TOL files cannot exist in
536          * PC Arc/Info coverages... this should be OK.
537          *------------------------------------------------------------*/
538         int nSignature = 0;
539         nSignature = AVCRawBinReadInt32(psFile->psRawBinFile);
540 
541         if (nSignature == 9993)
542         {
543             /* We have a double precision par.adf... read the 100 bytes
544              * header and set the precision information inside the file
545              * handle.
546              */
547             nStatus = _AVCBinReadHeader(psFile->psRawBinFile, &sHeader,
548                                         psFile->eCoverType);
549 
550             psFile->nPrecision = AVC_DOUBLE_PREC;
551         }
552         else
553         {
554             /* It's a single precision tol.adf ... just set the
555              * precision field.
556              */
557             AVCRawBinFSeek(psFile->psRawBinFile, 0, SEEK_SET);
558             psFile->nPrecision = AVC_SINGLE_PREC;
559         }
560     }
561 
562     return nStatus;
563 }
564 
565 /**********************************************************************
566  *                          AVCBinReadObject()
567  *
568  * Read the object with a particular index.  For fixed length record
569  * files we seek directly to the object.  For variable files we try to
570  * get the offset from the corresponding index file.
571  *
572  * NOTE: Currently only implemented for ARC, PAL and TABLE files.
573  *
574  * Returns the read object on success or nullptr on error.
575  **********************************************************************/
AVCBinReadObject(AVCBinFile * psFile,int iObjIndex)576 void *AVCBinReadObject(AVCBinFile *psFile, int iObjIndex )
577 {
578     int  bIndexed = FALSE;
579     int  nObjectOffset, nRecordSize=0, nRecordStart = 0, nLen;
580     /* cppcheck-suppress unreadVariable */
581     char szExt[4] = {0,0,0,0};
582     char *pszExt = szExt;
583 
584     if( iObjIndex < 0 )
585         return nullptr;
586 
587     /*-----------------------------------------------------------------
588      * Determine some information from based on the coverage type.
589      *----------------------------------------------------------------*/
590     nLen = (int)strlen(psFile->pszFilename);
591     if( psFile->eFileType == AVCFileARC &&
592         ((nLen>=3 && STARTS_WITH_CI((pszExt=psFile->pszFilename+nLen-3), "arc")) ||
593          (nLen>=7 && STARTS_WITH_CI((pszExt=psFile->pszFilename+nLen-7), "arc.adf"))))
594     {
595         bIndexed = TRUE;
596     }
597     else if( psFile->eFileType == AVCFilePAL &&
598         ((nLen>=3 && STARTS_WITH_CI((pszExt=psFile->pszFilename+nLen-3), "pal")) ||
599          (nLen>=7 && STARTS_WITH_CI((pszExt=psFile->pszFilename+nLen-7), "pal.adf"))))
600     {
601         bIndexed = TRUE;
602     }
603     else if( psFile->eFileType == AVCFileTABLE )
604     {
605         bIndexed = FALSE;
606         nRecordSize = psFile->hdr.psTableDef->nRecSize;
607         nRecordStart = 0;
608     }
609     else
610         return nullptr;
611 
612     /*-----------------------------------------------------------------
613      * Ensure the index file is opened if an index file is required.
614      *----------------------------------------------------------------*/
615 
616     if( bIndexed && psFile->psIndexFile == nullptr )
617     {
618         char chOrig;
619 
620         chOrig = pszExt[2];
621         if( chOrig > 'A' && chOrig < 'Z' )
622             pszExt[2] = 'X';
623         else
624             pszExt[2] = 'x';
625 
626         psFile->psIndexFile =
627             AVCRawBinOpen( psFile->pszFilename, "rb",
628                            psFile->psRawBinFile->eByteOrder,
629                            psFile->psRawBinFile->psDBCSInfo);
630         pszExt[2] = chOrig;
631 
632         if( psFile->psIndexFile == nullptr )
633             return nullptr;
634     }
635 
636     /*-----------------------------------------------------------------
637      * Establish the offset to read the object from.
638      *----------------------------------------------------------------*/
639     if( bIndexed )
640     {
641         GIntBig nIndexOffsetBig;
642 
643         if (psFile->eCoverType == AVCCoverPC)
644             nIndexOffsetBig = 356 + static_cast<GIntBig>(iObjIndex-1)*8;
645         else
646             nIndexOffsetBig = 100 + static_cast<GIntBig>(iObjIndex-1)*8;
647         if( nIndexOffsetBig < INT_MIN || nIndexOffsetBig > INT_MAX )
648             return nullptr;
649 
650         const int nIndexOffset = static_cast<int>(nIndexOffsetBig);
651         AVCRawBinFSeek( psFile->psIndexFile, nIndexOffset, SEEK_SET );
652         if( AVCRawBinEOF( psFile->psIndexFile ) )
653             return nullptr;
654 
655         nObjectOffset = AVCRawBinReadInt32( psFile->psIndexFile );
656         if( nObjectOffset < INT_MIN / 2 || nObjectOffset > (INT_MAX - 256)/ 2 )
657             return nullptr;
658         nObjectOffset *= 2;
659 
660         if (psFile->eCoverType == AVCCoverPC)
661             nObjectOffset += 256;
662     }
663     else
664     {
665         GIntBig nObjectOffsetBig = nRecordStart +
666             nRecordSize * static_cast<GIntBig>(iObjIndex-1);
667         if( nObjectOffsetBig < INT_MIN || nObjectOffsetBig > INT_MAX )
668             return nullptr;
669         nObjectOffset = static_cast<int>(nObjectOffsetBig);
670     }
671 
672     /*-----------------------------------------------------------------
673      * Seek to the start of the object in the data file.
674      *----------------------------------------------------------------*/
675     AVCRawBinFSeek( psFile->psRawBinFile, nObjectOffset, SEEK_SET );
676     if( AVCRawBinEOF( psFile->psRawBinFile ) )
677         return nullptr;
678 
679     /*-----------------------------------------------------------------
680      * Read and return the object.
681      *----------------------------------------------------------------*/
682     return AVCBinReadNextObject( psFile );
683 }
684 
685 
686 /**********************************************************************
687  *                          AVCBinReadNextObject()
688  *
689  * Read the next structure from the file.  This function is just a generic
690  * cover on top of the AVCBinReadNextArc/Lab/Pal/Cnt() functions.
691  *
692  * Returns a (void*) to a static structure with the contents of the object
693  * that was read.  The contents of the structure will be valid only until
694  * the next call.
695  * If you use the returned value, then make sure that you cast it to
696  * the right type for the current file! (AVCArc, AVCPal, AVCCnt, ...)
697  *
698  * Returns nullptr if an error happened or if EOF was reached.
699  **********************************************************************/
AVCBinReadNextObject(AVCBinFile * psFile)700 void *AVCBinReadNextObject(AVCBinFile *psFile)
701 {
702     void *psObj = nullptr;
703 
704     switch(psFile->eFileType)
705     {
706       case AVCFileARC:
707         psObj = (void*)AVCBinReadNextArc(psFile);
708         break;
709       case AVCFilePAL:
710       case AVCFileRPL:
711         psObj = (void*)AVCBinReadNextPal(psFile);
712         break;
713       case AVCFileCNT:
714         psObj = (void*)AVCBinReadNextCnt(psFile);
715         break;
716       case AVCFileLAB:
717         psObj = (void*)AVCBinReadNextLab(psFile);
718         break;
719       case AVCFileTOL:
720         psObj = (void*)AVCBinReadNextTol(psFile);
721         break;
722       case AVCFileTXT:
723       case AVCFileTX6:
724         psObj = (void*)AVCBinReadNextTxt(psFile);
725         break;
726       case AVCFileRXP:
727         psObj = (void*)AVCBinReadNextRxp(psFile);
728         break;
729       case AVCFileTABLE:
730         psObj = (void*)AVCBinReadNextTableRec(psFile);
731         break;
732       default:
733         CPLError(CE_Failure, CPLE_IllegalArg,
734                  "AVCBinReadNextObject(): Unsupported file type!");
735     }
736 
737     return psObj;
738 }
739 
740 
741 
742 /**********************************************************************
743  *                          AVCBinReadNextTableRec()
744  *
745  * Reads the next record from an attribute table.
746  *
747  * Returns a pointer to an array of static AVCField structure whose
748  * contents will be valid only until the next call,
749  * or nullptr if an error happened or if EOF was reached.
750  **********************************************************************/
AVCBinReadNextTableRec(AVCBinFile * psFile)751 AVCField *AVCBinReadNextTableRec(AVCBinFile *psFile)
752 {
753     if (psFile->eCoverType != AVCCoverPC &&
754         psFile->eCoverType != AVCCoverPC2 &&
755         psFile->eFileType == AVCFileTABLE &&
756         psFile->hdr.psTableDef->numRecords > 0 &&
757         ! AVCRawBinEOF(psFile->psRawBinFile) &&
758         _AVCBinReadNextTableRec(psFile->psRawBinFile,
759                                     psFile->hdr.psTableDef->numFields,
760                                     psFile->hdr.psTableDef->pasFieldDef,
761                                     psFile->cur.pasFields,
762                                     psFile->hdr.psTableDef->nRecSize) == 0 )
763     {
764         return psFile->cur.pasFields;
765     }
766     else if ((psFile->eCoverType == AVCCoverPC ||
767               psFile->eCoverType == AVCCoverPC2 ) &&
768              psFile->eFileType == AVCFileTABLE &&
769              psFile->hdr.psTableDef->numRecords > 0 &&
770              _AVCBinReadNextDBFTableRec(psFile->hDBFFile,
771                                         &(psFile->nCurDBFRecord),
772                                         psFile->hdr.psTableDef->numFields,
773                                         psFile->hdr.psTableDef->pasFieldDef,
774                                         psFile->cur.pasFields) == 0)
775     {
776         return psFile->cur.pasFields;
777     }
778 
779     return nullptr;
780 }
781 
782 
783 
784 
785 /*=====================================================================
786  *                              ARC
787  *====================================================================*/
788 
789 /**********************************************************************
790  *                          _AVCBinReadNextArc()
791  *
792  * (This function is for internal library use... external calls should
793  * go to AVCBinReadNextArc() instead)
794  *
795  * Read the next Arc structure from the file.
796  *
797  * The contents of the psArc structure is assumed to be valid, and the
798  * psArc->pasVertices buffer may be reallocated or free()'d if it is not
799  * nullptr.
800  *
801  * Returns 0 on success or -1 on error.
802  **********************************************************************/
803 static
_AVCBinReadNextArc(AVCRawBinFile * psFile,AVCArc * psArc,int nPrecision)804 int _AVCBinReadNextArc(AVCRawBinFile *psFile, AVCArc *psArc,
805                               int nPrecision)
806 {
807     int i, numVertices;
808     int nRecordSize, nStartPos, nBytesRead;
809 
810     psArc->nArcId  = AVCRawBinReadInt32(psFile);
811     if (AVCRawBinEOF(psFile))
812         return -1;
813 
814     nRecordSize    = AVCRawBinReadInt32(psFile);
815     if( nRecordSize < 0 || nRecordSize > 100 * 1024 * 1024 )
816         return -1;
817     nRecordSize *= 2;
818     nStartPos      = psFile->nCurPos+psFile->nOffset;
819     psArc->nUserId = AVCRawBinReadInt32(psFile);
820     psArc->nFNode  = AVCRawBinReadInt32(psFile);
821     psArc->nTNode  = AVCRawBinReadInt32(psFile);
822     psArc->nLPoly  = AVCRawBinReadInt32(psFile);
823     psArc->nRPoly  = AVCRawBinReadInt32(psFile);
824     numVertices    = AVCRawBinReadInt32(psFile);
825     if( numVertices < 0 || numVertices > 100 * 1024 * 1024 )
826         return -1;
827     if( numVertices > 10 * 1024 * 1024 &&
828         !AVCRawBinIsFileGreaterThan(psFile,
829                 numVertices * ((nPrecision == AVC_SINGLE_PREC) ? 8 : 16)) )
830     {
831         return -1;
832     }
833 
834     /* Realloc the vertices array only if it needs to grow...
835      * do not realloc to a smaller size.
836      * Note that for simplicity reasons, we always store the vertices as
837      * double values in memory, even for single precision coverages.
838      */
839     if (psArc->pasVertices == nullptr || numVertices > psArc->numVertices)
840     {
841         AVCVertex* pasNewVertices = (AVCVertex*)VSIRealloc(psArc->pasVertices,
842                                                 numVertices*sizeof(AVCVertex));
843         if( pasNewVertices == nullptr )
844             return -1;
845         psArc->pasVertices = pasNewVertices;
846     }
847 
848     psArc->numVertices = numVertices;
849 
850     if (nPrecision == AVC_SINGLE_PREC)
851     {
852         for(i=0; i<numVertices; i++)
853         {
854             psArc->pasVertices[i].x = AVCRawBinReadFloat(psFile);
855             psArc->pasVertices[i].y = AVCRawBinReadFloat(psFile);
856             if( psFile->nCurSize == 0 )
857                 return -1;
858         }
859     }
860     else
861     {
862         for(i=0; i<numVertices; i++)
863         {
864             psArc->pasVertices[i].x = AVCRawBinReadDouble(psFile);
865             psArc->pasVertices[i].y = AVCRawBinReadDouble(psFile);
866             if( psFile->nCurSize == 0 )
867                 return -1;
868         }
869 
870     }
871 
872     /*-----------------------------------------------------------------
873      * Record size may be larger than number of vertices.  Skip up to
874      * start of next object.
875      *----------------------------------------------------------------*/
876     nBytesRead = (psFile->nCurPos + psFile->nOffset) - nStartPos;
877     if ( nBytesRead < nRecordSize)
878         AVCRawBinFSeek(psFile, nRecordSize - nBytesRead, SEEK_CUR);
879 
880     return 0;
881 }
882 
883 /**********************************************************************
884  *                          AVCBinReadNextArc()
885  *
886  * Read the next Arc structure from the file.
887  *
888  * Returns a pointer to a static AVCArc structure whose contents will be
889  * valid only until the next call or nullptr if an error happened or if EOF
890  * was reached.
891  **********************************************************************/
AVCBinReadNextArc(AVCBinFile * psFile)892 AVCArc *AVCBinReadNextArc(AVCBinFile *psFile)
893 {
894     if (psFile->eFileType != AVCFileARC ||
895         AVCRawBinEOF(psFile->psRawBinFile) ||
896         _AVCBinReadNextArc(psFile->psRawBinFile, psFile->cur.psArc,
897                            psFile->nPrecision) !=0)
898     {
899         return nullptr;
900     }
901 
902     return psFile->cur.psArc;
903 }
904 
905 
906 /*=====================================================================
907  *                              PAL
908  *====================================================================*/
909 
910 /**********************************************************************
911  *                          _AVCBinReadNextPal()
912  *
913  * (This function is for internal library use... external calls should
914  * go to AVCBinReadNextPal() instead)
915  *
916  * Read the next PAL (Polygon Arc List) structure from the file.
917  *
918  * The contents of the psPal structure is assumed to be valid, and the
919  * psPal->paVertices buffer may be reallocated or free()'d if it is not
920  * nullptr.
921  *
922  * Returns 0 on success or -1 on error.
923  **********************************************************************/
924 static
_AVCBinReadNextPal(AVCRawBinFile * psFile,AVCPal * psPal,int nPrecision)925 int _AVCBinReadNextPal(AVCRawBinFile *psFile, AVCPal *psPal,
926                               int nPrecision)
927 {
928     int i, numArcs;
929     int nRecordSize, nStartPos, nBytesRead;
930 
931     psPal->nPolyId = AVCRawBinReadInt32(psFile);
932     nRecordSize    = AVCRawBinReadInt32(psFile);
933     if( nRecordSize < 0 || nRecordSize > 100 * 1024 * 1024 )
934         return -1;
935     nRecordSize *= 2;
936     nStartPos      = psFile->nCurPos+psFile->nOffset;
937 
938     if (AVCRawBinEOF(psFile))
939         return -1;
940 
941     if (nPrecision == AVC_SINGLE_PREC)
942     {
943         psPal->sMin.x  = AVCRawBinReadFloat(psFile);
944         psPal->sMin.y  = AVCRawBinReadFloat(psFile);
945         psPal->sMax.x  = AVCRawBinReadFloat(psFile);
946         psPal->sMax.y  = AVCRawBinReadFloat(psFile);
947     }
948     else
949     {
950         psPal->sMin.x  = AVCRawBinReadDouble(psFile);
951         psPal->sMin.y  = AVCRawBinReadDouble(psFile);
952         psPal->sMax.x  = AVCRawBinReadDouble(psFile);
953         psPal->sMax.y  = AVCRawBinReadDouble(psFile);
954     }
955 
956     numArcs            = AVCRawBinReadInt32(psFile);
957     if( numArcs < 0 || numArcs > 100 * 1024 * 1024 )
958         return -1;
959     if( numArcs > 10 * 1024 * 1024 &&
960         !AVCRawBinIsFileGreaterThan(psFile, numArcs * sizeof(int) * 3) )
961     {
962         return -1;
963     }
964 
965     /* Realloc the arc list array only if it needs to grow...
966      * do not realloc to a smaller size.
967      */
968     if (psPal->pasArcs == nullptr || numArcs > psPal->numArcs)
969     {
970         AVCPalArc* pasNewArcs = (AVCPalArc*)VSIRealloc(psPal->pasArcs,
971                                                  numArcs*sizeof(AVCPalArc));
972         if( pasNewArcs == nullptr )
973             return -1;
974         psPal->pasArcs = pasNewArcs;
975     }
976 
977     psPal->numArcs = numArcs;
978 
979     for(i=0; i<numArcs; i++)
980     {
981         psPal->pasArcs[i].nArcId = AVCRawBinReadInt32(psFile);
982         psPal->pasArcs[i].nFNode = AVCRawBinReadInt32(psFile);
983         psPal->pasArcs[i].nAdjPoly = AVCRawBinReadInt32(psFile);
984         if( psFile->nCurSize == 0 )
985             return -1;
986     }
987 
988     /*-----------------------------------------------------------------
989      * Record size may be larger than number of vertices.  Skip up to
990      * start of next object.
991      *----------------------------------------------------------------*/
992     nBytesRead = (psFile->nCurPos + psFile->nOffset) - nStartPos;
993     if ( nBytesRead < nRecordSize)
994         AVCRawBinFSeek(psFile, nRecordSize - nBytesRead, SEEK_CUR);
995 
996     return 0;
997 }
998 
999 /**********************************************************************
1000  *                          AVCBinReadNextPal()
1001  *
1002  * Read the next PAL structure from the file.
1003  *
1004  * Returns a pointer to a static AVCPal structure whose contents will be
1005  * valid only until the next call or nullptr if an error happened or if EOF
1006  * was reached.
1007  **********************************************************************/
AVCBinReadNextPal(AVCBinFile * psFile)1008 AVCPal *AVCBinReadNextPal(AVCBinFile *psFile)
1009 {
1010     if ((psFile->eFileType!=AVCFilePAL && psFile->eFileType!=AVCFileRPL) ||
1011         AVCRawBinEOF(psFile->psRawBinFile) ||
1012         _AVCBinReadNextPal(psFile->psRawBinFile, psFile->cur.psPal,
1013                            psFile->nPrecision) !=0)
1014     {
1015         return nullptr;
1016     }
1017 
1018     return psFile->cur.psPal;
1019 }
1020 
1021 
1022 /*=====================================================================
1023  *                              CNT
1024  *====================================================================*/
1025 
1026 /**********************************************************************
1027  *                          _AVCBinReadNextCnt()
1028  *
1029  * (This function is for internal library use... external calls should
1030  * go to AVCBinReadNextCnt() instead)
1031  *
1032  * Read the next CNT (Polygon Centroid) structure from the file.
1033  *
1034  * Returns 0 on success or -1 on error.
1035  **********************************************************************/
1036 static
_AVCBinReadNextCnt(AVCRawBinFile * psFile,AVCCnt * psCnt,int nPrecision)1037 int _AVCBinReadNextCnt(AVCRawBinFile *psFile, AVCCnt *psCnt,
1038                               int nPrecision)
1039 {
1040     int i, numLabels;
1041     int nRecordSize, nStartPos, nBytesRead;
1042 
1043     psCnt->nPolyId = AVCRawBinReadInt32(psFile);
1044     nRecordSize    = AVCRawBinReadInt32(psFile);
1045     if( nRecordSize < 0 || nRecordSize > 100 * 1024 * 1024 )
1046         return -1;
1047     nRecordSize *= 2;
1048     nStartPos      = psFile->nCurPos+psFile->nOffset;
1049 
1050     if (AVCRawBinEOF(psFile))
1051         return -1;
1052 
1053     if (nPrecision == AVC_SINGLE_PREC)
1054     {
1055         psCnt->sCoord.x  = AVCRawBinReadFloat(psFile);
1056         psCnt->sCoord.y  = AVCRawBinReadFloat(psFile);
1057     }
1058     else
1059     {
1060         psCnt->sCoord.x  = AVCRawBinReadDouble(psFile);
1061         psCnt->sCoord.y  = AVCRawBinReadDouble(psFile);
1062     }
1063 
1064     numLabels      = AVCRawBinReadInt32(psFile);
1065     if( numLabels < 0 || numLabels > 100 * 1024 * 1024 )
1066         return -1;
1067     if( numLabels > 10 * 1024 * 1024 &&
1068         !AVCRawBinIsFileGreaterThan(psFile, numLabels * sizeof(int)) )
1069     {
1070         return -1;
1071     }
1072 
1073     /* Realloc the LabelIds array only if it needs to grow...
1074      * do not realloc to a smaller size.
1075      */
1076     if (psCnt->panLabelIds == nullptr || numLabels > psCnt->numLabels)
1077     {
1078         GInt32* panIds = (GInt32 *)VSIRealloc(psCnt->panLabelIds,
1079                                                   numLabels*sizeof(GInt32));
1080         if( panIds == nullptr )
1081             return -1;
1082         psCnt->panLabelIds = panIds;
1083     }
1084 
1085     psCnt->numLabels = numLabels;
1086 
1087     for(i=0; i<numLabels; i++)
1088     {
1089         psCnt->panLabelIds[i] = AVCRawBinReadInt32(psFile);
1090         if( psFile->nCurSize == 0 )
1091             return -1;
1092     }
1093 
1094     /*-----------------------------------------------------------------
1095      * Record size may be larger than number of vertices.  Skip up to
1096      * start of next object.
1097      *----------------------------------------------------------------*/
1098     nBytesRead = (psFile->nCurPos + psFile->nOffset) - nStartPos;
1099     if ( nBytesRead < nRecordSize)
1100         AVCRawBinFSeek(psFile, nRecordSize - nBytesRead, SEEK_CUR);
1101 
1102     return 0;
1103 }
1104 
1105 /**********************************************************************
1106  *                          AVCBinReadNextCnt()
1107  *
1108  * Read the next CNT structure from the file.
1109  *
1110  * Returns a pointer to a static AVCCnt structure whose contents will be
1111  * valid only until the next call or nullptr if an error happened or if EOF
1112  * was reached.
1113  **********************************************************************/
AVCBinReadNextCnt(AVCBinFile * psFile)1114 AVCCnt *AVCBinReadNextCnt(AVCBinFile *psFile)
1115 {
1116     if (psFile->eFileType != AVCFileCNT ||
1117         AVCRawBinEOF(psFile->psRawBinFile) ||
1118         _AVCBinReadNextCnt(psFile->psRawBinFile, psFile->cur.psCnt,
1119                            psFile->nPrecision) !=0)
1120     {
1121         return nullptr;
1122     }
1123 
1124     return psFile->cur.psCnt;
1125 }
1126 
1127 
1128 /*=====================================================================
1129  *                              LAB
1130  *====================================================================*/
1131 
1132 /**********************************************************************
1133  *                          _AVCBinReadNextLab()
1134  *
1135  * (This function is for internal library use... external calls should
1136  * go to AVCBinReadNextLab() instead)
1137  *
1138  * Read the next LAB (Centroid Label) structure from the file.
1139  *
1140  * Returns 0 on success or -1 on error.
1141  **********************************************************************/
1142 static
_AVCBinReadNextLab(AVCRawBinFile * psFile,AVCLab * psLab,int nPrecision)1143 int _AVCBinReadNextLab(AVCRawBinFile *psFile, AVCLab *psLab,
1144                               int nPrecision)
1145 {
1146 
1147     psLab->nValue  = AVCRawBinReadInt32(psFile);
1148     psLab->nPolyId = AVCRawBinReadInt32(psFile);
1149 
1150     if (AVCRawBinEOF(psFile))
1151         return -1;
1152 
1153     if (nPrecision == AVC_SINGLE_PREC)
1154     {
1155         psLab->sCoord1.x  = AVCRawBinReadFloat(psFile);
1156         psLab->sCoord1.y  = AVCRawBinReadFloat(psFile);
1157         psLab->sCoord2.x  = AVCRawBinReadFloat(psFile);
1158         psLab->sCoord2.y  = AVCRawBinReadFloat(psFile);
1159         psLab->sCoord3.x  = AVCRawBinReadFloat(psFile);
1160         psLab->sCoord3.y  = AVCRawBinReadFloat(psFile);
1161     }
1162     else
1163     {
1164         psLab->sCoord1.x  = AVCRawBinReadDouble(psFile);
1165         psLab->sCoord1.y  = AVCRawBinReadDouble(psFile);
1166         psLab->sCoord2.x  = AVCRawBinReadDouble(psFile);
1167         psLab->sCoord2.y  = AVCRawBinReadDouble(psFile);
1168         psLab->sCoord3.x  = AVCRawBinReadDouble(psFile);
1169         psLab->sCoord3.y  = AVCRawBinReadDouble(psFile);
1170     }
1171 
1172     return 0;
1173 }
1174 
1175 /**********************************************************************
1176  *                          AVCBinReadNextLab()
1177  *
1178  * Read the next LAB structure from the file.
1179  *
1180  * Returns a pointer to a static AVCLab structure whose contents will be
1181  * valid only until the next call or nullptr if an error happened or if EOF
1182  * was reached.
1183  **********************************************************************/
AVCBinReadNextLab(AVCBinFile * psFile)1184 AVCLab *AVCBinReadNextLab(AVCBinFile *psFile)
1185 {
1186     if (psFile->eFileType != AVCFileLAB ||
1187         AVCRawBinEOF(psFile->psRawBinFile) ||
1188         _AVCBinReadNextLab(psFile->psRawBinFile, psFile->cur.psLab,
1189                            psFile->nPrecision) !=0)
1190     {
1191         return nullptr;
1192     }
1193 
1194     return psFile->cur.psLab;
1195 }
1196 
1197 /*=====================================================================
1198  *                              TOL
1199  *====================================================================*/
1200 
1201 /**********************************************************************
1202  *                          _AVCBinReadNextTol()
1203  *
1204  * (This function is for internal library use... external calls should
1205  * go to AVCBinReadNextTol() instead)
1206  *
1207  * Read the next TOL (tolerance) structure from the file.
1208  *
1209  * Returns 0 on success or -1 on error.
1210  **********************************************************************/
1211 static
_AVCBinReadNextTol(AVCRawBinFile * psFile,AVCTol * psTol,int nPrecision)1212 int _AVCBinReadNextTol(AVCRawBinFile *psFile, AVCTol *psTol,
1213                        int nPrecision)
1214 {
1215 
1216     psTol->nIndex  = AVCRawBinReadInt32(psFile);
1217     psTol->nFlag   = AVCRawBinReadInt32(psFile);
1218 
1219     if (AVCRawBinEOF(psFile))
1220         return -1;
1221 
1222     if (nPrecision == AVC_SINGLE_PREC)
1223     {
1224         psTol->dValue  = AVCRawBinReadFloat(psFile);
1225     }
1226     else
1227     {
1228         psTol->dValue  = AVCRawBinReadDouble(psFile);
1229     }
1230 
1231     return 0;
1232 }
1233 
1234 /**********************************************************************
1235  *                          AVCBinReadNextTol()
1236  *
1237  * Read the next TOL structure from the file.
1238  *
1239  * Returns a pointer to a static AVCTol structure whose contents will be
1240  * valid only until the next call or nullptr if an error happened or if EOF
1241  * was reached.
1242  **********************************************************************/
AVCBinReadNextTol(AVCBinFile * psFile)1243 AVCTol *AVCBinReadNextTol(AVCBinFile *psFile)
1244 {
1245     if (psFile->eFileType != AVCFileTOL ||
1246         AVCRawBinEOF(psFile->psRawBinFile) ||
1247         _AVCBinReadNextTol(psFile->psRawBinFile, psFile->cur.psTol,
1248                            psFile->nPrecision) !=0)
1249     {
1250         return nullptr;
1251     }
1252 
1253     return psFile->cur.psTol;
1254 }
1255 
1256 /*=====================================================================
1257  *                              PRJ
1258  *====================================================================*/
1259 
1260 /**********************************************************************
1261  *                          _AVCBinReadOpenPrj()
1262  *
1263  * (This function is for internal library use... external calls should
1264  * go to AVCBinReadOpen() with type AVCFilePRJ instead)
1265  *
1266  * Open a PRJ file.
1267  *
1268  * This call will actually read the whole PRJ file in memory since PRJ
1269  * files are small text files.
1270  **********************************************************************/
_AVCBinReadOpenPrj(const char * pszPath,const char * pszName)1271 AVCBinFile *_AVCBinReadOpenPrj(const char *pszPath, const char *pszName)
1272 {
1273     AVCBinFile   *psFile;
1274     char         *pszFname, **papszPrj;
1275 
1276     /*-----------------------------------------------------------------
1277      * Load the PRJ file contents into a stringlist.
1278      *----------------------------------------------------------------*/
1279     pszFname = (char*)CPLMalloc(strlen(pszPath)+strlen(pszName)+1);
1280     snprintf(pszFname, strlen(pszPath)+strlen(pszName)+1, "%s%s", pszPath, pszName);
1281 
1282     papszPrj = CSLLoad2(pszFname, 50, 160, nullptr);
1283 
1284     CPLFree(pszFname);
1285 
1286     if (papszPrj == nullptr)
1287     {
1288         /* Failed to open file... just return nullptr since an error message
1289          * has already been issued by CSLLoad()
1290          */
1291         return nullptr;
1292     }
1293 
1294     /*-----------------------------------------------------------------
1295      * Alloc and init the AVCBinFile handle.
1296      *----------------------------------------------------------------*/
1297     psFile = (AVCBinFile*)CPLCalloc(1, sizeof(AVCBinFile));
1298 
1299     psFile->eFileType = AVCFilePRJ;
1300     psFile->psRawBinFile = nullptr;
1301     psFile->cur.papszPrj = papszPrj;
1302     psFile->pszFilename = nullptr;
1303 
1304 
1305     return psFile;
1306 }
1307 
1308 /**********************************************************************
1309  *                          AVCBinReadPrj()
1310  *
1311  * Return the contents of the previously opened PRJ (projection) file.
1312  *
1313  * PRJ files are simple text files with variable length lines, so we
1314  * don't use the AVCRawBin*() functions for this case.
1315  *
1316  * Returns a reference to a static stringlist with the whole file
1317  * contents, or nullptr in case of error.
1318  *
1319  * The returned stringlist should NOT be freed by the caller.
1320  **********************************************************************/
AVCBinReadNextPrj(AVCBinFile * psFile)1321 char **AVCBinReadNextPrj(AVCBinFile *psFile)
1322 {
1323     /*-----------------------------------------------------------------
1324      * The file should have already been loaded by AVCBinFileOpen(),
1325      * so there is not much to do here!
1326      *----------------------------------------------------------------*/
1327     return psFile->cur.papszPrj;
1328 }
1329 
1330 /*=====================================================================
1331  *                              TXT/TX6/TX7
1332  *====================================================================*/
1333 
1334 /**********************************************************************
1335  *                          _AVCBinReadNextTxt()
1336  *
1337  * (This function is for internal library use... external calls should
1338  * go to AVCBinReadNextTxt() instead)
1339  *
1340  * Read the next TXT/TX6/TX7 (Annotation) structure from the file.
1341  *
1342  * Returns 0 on success or -1 on error.
1343  **********************************************************************/
1344 static
_AVCBinReadNextTxt(AVCRawBinFile * psFile,AVCTxt * psTxt,int nPrecision)1345 int _AVCBinReadNextTxt(AVCRawBinFile *psFile, AVCTxt *psTxt,
1346                               int nPrecision)
1347 {
1348     int i, numVerticesBefore, numVertices, numCharsToRead, nRecordSize;
1349     int numBytesRead;
1350 
1351     numVerticesBefore = ABS(psTxt->numVerticesLine) +
1352                         ABS(psTxt->numVerticesArrow);
1353 
1354     psTxt->nTxtId  = AVCRawBinReadInt32(psFile);
1355     if (AVCRawBinEOF(psFile))
1356         return -1;
1357 
1358     nRecordSize    = AVCRawBinReadInt32(psFile);
1359     if( nRecordSize < 0 || nRecordSize > 100 * 1024 * 1024 )
1360         return -1;
1361     nRecordSize = nRecordSize*2+8;
1362 
1363     psTxt->nUserId = AVCRawBinReadInt32(psFile);
1364     psTxt->nLevel  = AVCRawBinReadInt32(psFile);
1365 
1366     psTxt->f_1e2    = AVCRawBinReadFloat(psFile);
1367     psTxt->nSymbol  = AVCRawBinReadInt32(psFile);
1368     psTxt->numVerticesLine  = AVCRawBinReadInt32(psFile);
1369     psTxt->n28      = AVCRawBinReadInt32(psFile);
1370     psTxt->numChars = AVCRawBinReadInt32(psFile);
1371     if( psTxt->numChars < 0 || psTxt->numChars > 10 * 1024 * 1024 )
1372         return -1;
1373     psTxt->numVerticesArrow = AVCRawBinReadInt32(psFile);
1374 
1375     for(i=0; i<20; i++)
1376     {
1377         psTxt->anJust1[i] = AVCRawBinReadInt16(psFile);
1378     }
1379     for(i=0; i<20; i++)
1380     {
1381         psTxt->anJust2[i] = AVCRawBinReadInt16(psFile);
1382     }
1383 
1384     if (nPrecision == AVC_SINGLE_PREC)
1385     {
1386         psTxt->dHeight = AVCRawBinReadFloat(psFile);
1387         psTxt->dV2     = AVCRawBinReadFloat(psFile);
1388         psTxt->dV3     = AVCRawBinReadFloat(psFile);
1389     }
1390     else
1391     {
1392         psTxt->dHeight = AVCRawBinReadDouble(psFile);
1393         psTxt->dV2     = AVCRawBinReadDouble(psFile);
1394         psTxt->dV3     = AVCRawBinReadDouble(psFile);
1395     }
1396 
1397     numCharsToRead = ((int)(psTxt->numChars + 3)/4)*4;
1398     if (psTxt->pszText == nullptr ||
1399         ((int)(strlen((char*)psTxt->pszText)+3)/4)*4 < numCharsToRead )
1400     {
1401         GByte* pszNewText = (GByte*)VSIRealloc(psTxt->pszText,
1402                                             (numCharsToRead+1)*sizeof(char));
1403         if( pszNewText == nullptr )
1404             return -1;
1405         psTxt->pszText = pszNewText;
1406     }
1407 
1408     AVCRawBinReadString(psFile, numCharsToRead, psTxt->pszText);
1409     psTxt->pszText[psTxt->numChars] = '\0';
1410 
1411     /* Realloc the vertices array only if it needs to grow...
1412      * do not realloc to a smaller size.
1413      */
1414     if( psTxt->numVerticesLine == INT_MIN ||
1415         psTxt->numVerticesArrow == INT_MIN ||
1416         ABS(psTxt->numVerticesLine) >
1417                 100 * 1024 * 1024 - ABS(psTxt->numVerticesArrow) )
1418         return -1;
1419     numVertices = ABS(psTxt->numVerticesLine) + ABS(psTxt->numVerticesArrow);
1420     if( numVertices > 10 * 1024 * 1024 &&
1421         !AVCRawBinIsFileGreaterThan(psFile,
1422                 numVertices * ((nPrecision == AVC_SINGLE_PREC) ? 8 : 16)) )
1423     {
1424         return -1;
1425     }
1426 
1427     if (psTxt->pasVertices == nullptr || numVertices > numVerticesBefore)
1428         psTxt->pasVertices = (AVCVertex*)CPLRealloc(psTxt->pasVertices,
1429                                               numVertices*sizeof(AVCVertex));
1430 
1431     if (nPrecision == AVC_SINGLE_PREC)
1432     {
1433         for(i=0; i<numVertices; i++)
1434         {
1435             psTxt->pasVertices[i].x = AVCRawBinReadFloat(psFile);
1436             psTxt->pasVertices[i].y = AVCRawBinReadFloat(psFile);
1437             if( psFile->nCurSize == 0 )
1438                 return -1;
1439         }
1440     }
1441     else
1442     {
1443         for(i=0; i<numVertices; i++)
1444         {
1445             psTxt->pasVertices[i].x = AVCRawBinReadDouble(psFile);
1446             psTxt->pasVertices[i].y = AVCRawBinReadDouble(psFile);
1447             if( psFile->nCurSize == 0 )
1448                 return -1;
1449         }
1450     }
1451 
1452     /* In V7 Coverages, we always have 8 bytes of junk at end of record.
1453      * In Weird coverages, these 8 bytes are sometimes present, and
1454      * sometimes not!!! (Probably another AI "random feature"! ;-)
1455      * So we use the record size to establish if there is any junk to skip
1456      */
1457     if (nPrecision == AVC_SINGLE_PREC)
1458         numBytesRead = 132 + numCharsToRead + numVertices * 2 * 4;
1459     else
1460         numBytesRead = 144 + numCharsToRead + numVertices * 2 * 8;
1461 
1462     if (numBytesRead < nRecordSize)
1463         AVCRawBinFSeek(psFile, nRecordSize - numBytesRead, SEEK_CUR);
1464 
1465     return 0;
1466 }
1467 
1468 /**********************************************************************
1469  *                          _AVCBinReadNextPCCoverageTxt()
1470  *
1471  * (This function is for internal library use... external calls should
1472  * go to AVCBinReadNextTxt() instead)
1473  *
1474  * Read the next TXT (Annotation) structure from a PC Coverage file.
1475  * Note that it is assumed that PC Coverage files are always single
1476  * precision.
1477  *
1478  * Returns 0 on success or -1 on error.
1479  **********************************************************************/
1480 static
_AVCBinReadNextPCCoverageTxt(AVCRawBinFile * psFile,AVCTxt * psTxt,int nPrecision)1481 int _AVCBinReadNextPCCoverageTxt(AVCRawBinFile *psFile, AVCTxt *psTxt,
1482                                  int nPrecision)
1483 {
1484     int i, numVerticesBefore, numVertices, numCharsToRead, nRecordSize;
1485 
1486     numVerticesBefore = ABS(psTxt->numVerticesLine) +
1487                         ABS(psTxt->numVerticesArrow);
1488 
1489     psTxt->nTxtId  = AVCRawBinReadInt32(psFile);
1490     if (AVCRawBinEOF(psFile))
1491         return -1;
1492 
1493     nRecordSize    = AVCRawBinReadInt32(psFile);
1494     if( nRecordSize < 0 || nRecordSize > 100 * 1024 * 1024 )
1495         return -1;
1496     nRecordSize = nRecordSize*2+8;
1497 
1498     psTxt->nUserId = 0;
1499     psTxt->nLevel  = AVCRawBinReadInt32(psFile);
1500 
1501     psTxt->numVerticesLine  = AVCRawBinReadInt32(psFile);
1502     /* We are not expecting more than 4 vertices */
1503     psTxt->numVerticesLine = MIN(psTxt->numVerticesLine, 4);
1504 
1505     psTxt->numVerticesArrow = 0;
1506 
1507     /* Realloc the vertices array only if it needs to grow...
1508      * do not realloc to a smaller size.
1509      *
1510      * Note that because of the way V7 binary TXT files work, the rest of the
1511      * lib expects to receive duplicate coords for the first vertex, so
1512      * we have to include an additional vertex for that.
1513      */
1514     psTxt->numVerticesLine += 1;
1515     if( psTxt->numVerticesLine == INT_MIN ||
1516         ABS(psTxt->numVerticesLine) > 100 * 1024 * 1024 )
1517         return -1;
1518     numVertices = ABS(psTxt->numVerticesLine);
1519     if( numVertices < 2 )
1520         return -1;
1521     if( numVertices > 10 * 1024 * 1024 &&
1522         !AVCRawBinIsFileGreaterThan(psFile,
1523                 numVertices * ((nPrecision == AVC_SINGLE_PREC) ? 8 : 16)) )
1524     {
1525         return -1;
1526     }
1527 
1528     if (psTxt->pasVertices == nullptr || numVertices > numVerticesBefore)
1529         psTxt->pasVertices = (AVCVertex*)CPLRealloc(psTxt->pasVertices,
1530                                               numVertices*sizeof(AVCVertex));
1531 
1532     for(i=1; i<numVertices; i++)
1533     {
1534         if (nPrecision == AVC_SINGLE_PREC)
1535         {
1536             psTxt->pasVertices[i].x = AVCRawBinReadFloat(psFile);
1537             psTxt->pasVertices[i].y = AVCRawBinReadFloat(psFile);
1538             if( psFile->nCurSize == 0 )
1539                 return -1;
1540         }
1541         else
1542         {
1543             psTxt->pasVertices[i].x = AVCRawBinReadDouble(psFile);
1544             psTxt->pasVertices[i].y = AVCRawBinReadDouble(psFile);
1545             if( psFile->nCurSize == 0 )
1546                 return -1;
1547         }
1548     }
1549     /* Duplicate the first vertex because that's the way the other binary TXT
1550      * files work and that's what the lib expects to generate the E00.
1551      */
1552     psTxt->pasVertices[0].x = psTxt->pasVertices[1].x;
1553     psTxt->pasVertices[0].y = psTxt->pasVertices[1].y;
1554 
1555     /* Skip the other floats (vertices) that are unused */
1556     if (nPrecision == AVC_SINGLE_PREC)
1557         AVCRawBinFSeek(psFile, 4*(15-2*(numVertices-1)) , SEEK_CUR);
1558     else
1559         AVCRawBinFSeek(psFile, 8*(15-2*(numVertices-1)) , SEEK_CUR);
1560 
1561     if (nPrecision == AVC_SINGLE_PREC)
1562     {
1563         psTxt->dHeight  = AVCRawBinReadFloat(psFile);
1564     }
1565     else
1566     {
1567         psTxt->dHeight  = AVCRawBinReadDouble(psFile);
1568     }
1569     psTxt->f_1e2    = AVCRawBinReadFloat(psFile);
1570     psTxt->nSymbol  = AVCRawBinReadInt32(psFile);
1571     psTxt->numChars = AVCRawBinReadInt32(psFile);
1572     if( psTxt->numChars < 0 )
1573         return -1;
1574 
1575     /* In some cases, we may need to skip additional spaces after the
1576      * text string... more than should be required to simply align with
1577      * a 4 bytes boundary... include that in numCharsToRead
1578      */
1579     if (nPrecision == AVC_SINGLE_PREC)
1580     {
1581         numCharsToRead = nRecordSize - (28 + 16*4);
1582     }
1583     else
1584     {
1585         numCharsToRead = nRecordSize - (28 + 16*8);
1586     }
1587     if( numCharsToRead < 0 )
1588         return -1;
1589 
1590     /* Do a quick check in case file is corrupt! */
1591     psTxt->numChars = MIN(psTxt->numChars, numCharsToRead);
1592 
1593     if (psTxt->pszText == nullptr ||
1594         ((int)(strlen((char*)psTxt->pszText)+3)/4)*4 < numCharsToRead )
1595     {
1596         psTxt->pszText = (GByte*)CPLRealloc(psTxt->pszText,
1597                                             (numCharsToRead+5)*sizeof(char));
1598     }
1599 
1600     AVCRawBinReadString(psFile, numCharsToRead, psTxt->pszText);
1601     psTxt->pszText[psTxt->numChars] = '\0';
1602 
1603     /* Set unused members to default values...
1604      */
1605     psTxt->dV2     = 0.0;
1606     psTxt->dV3     = 0.0;
1607     psTxt->n28      = 0;
1608     for(i=0; i<20; i++)
1609     {
1610         psTxt->anJust1[i] = 0;
1611         psTxt->anJust2[i] = 0;
1612     }
1613 
1614     return 0;
1615 }
1616 
1617 /**********************************************************************
1618  *                          AVCBinReadNextTxt()
1619  *
1620  * Read the next TXT/TX6/TX7 structure from the file.
1621  *
1622  * Returns a pointer to a static AVCTxt structure whose contents will be
1623  * valid only until the next call or nullptr if an error happened or if EOF
1624  * was reached.
1625  **********************************************************************/
AVCBinReadNextTxt(AVCBinFile * psFile)1626 AVCTxt *AVCBinReadNextTxt(AVCBinFile *psFile)
1627 {
1628     int nStatus = 0;
1629 
1630     if ((psFile->eFileType != AVCFileTXT && psFile->eFileType != AVCFileTX6) ||
1631         AVCRawBinEOF(psFile->psRawBinFile) )
1632     {
1633         return nullptr;
1634     }
1635 
1636     /* AVCCoverPC have a different TXT format than AVCCoverV7
1637      *
1638      * Note: Some Weird coverages use the PC TXT structure, and some use the
1639      *       V7 structure.  We distinguish them using the header's precision
1640      *       field in AVCBinReadRewind().
1641      */
1642     if (psFile->eFileType == AVCFileTXT &&
1643         (psFile->eCoverType == AVCCoverPC ||
1644          psFile->eCoverType == AVCCoverWeird) )
1645     {
1646         /* TXT file in PC Coverages (and some Weird Coverages)
1647          */
1648         nStatus = _AVCBinReadNextPCCoverageTxt(psFile->psRawBinFile,
1649                                                psFile->cur.psTxt,
1650                                                psFile->nPrecision);
1651     }
1652     else
1653     {
1654         /* TXT in V7 Coverages (and some Weird Coverages), and TX6/TX7 in
1655          * all coverage types
1656          */
1657         nStatus = _AVCBinReadNextTxt(psFile->psRawBinFile, psFile->cur.psTxt,
1658                                      psFile->nPrecision);
1659     }
1660 
1661     if (nStatus != 0)
1662     {
1663         return nullptr;
1664     }
1665 
1666     return psFile->cur.psTxt;
1667 }
1668 
1669 
1670 /*=====================================================================
1671  *                              RXP
1672  *====================================================================*/
1673 
1674 /**********************************************************************
1675  *                          _AVCBinReadNextRxp()
1676  *
1677  * (This function is for internal library use... external calls should
1678  * go to AVCBinReadNextRxp() instead)
1679  *
1680  * Read the next RXP (Region something...) structure from the file.
1681  *
1682  * Returns 0 on success or -1 on error.
1683  **********************************************************************/
1684 static
_AVCBinReadNextRxp(AVCRawBinFile * psFile,AVCRxp * psRxp,CPL_UNUSED int nPrecision)1685 int _AVCBinReadNextRxp(AVCRawBinFile *psFile,
1686                        AVCRxp *psRxp,
1687                        CPL_UNUSED int nPrecision)
1688 {
1689 
1690     psRxp->n1  = AVCRawBinReadInt32(psFile);
1691     if (AVCRawBinEOF(psFile))
1692         return -1;
1693     psRxp->n2  = AVCRawBinReadInt32(psFile);
1694 
1695     return 0;
1696 }
1697 
1698 /**********************************************************************
1699  *                          AVCBinReadNextRxp()
1700  *
1701  * Read the next RXP structure from the file.
1702  *
1703  * Returns a pointer to a static AVCRxp structure whose contents will be
1704  * valid only until the next call or nullptr if an error happened or if EOF
1705  * was reached.
1706  **********************************************************************/
AVCBinReadNextRxp(AVCBinFile * psFile)1707 AVCRxp *AVCBinReadNextRxp(AVCBinFile *psFile)
1708 {
1709     if (psFile->eFileType != AVCFileRXP ||
1710         AVCRawBinEOF(psFile->psRawBinFile) ||
1711         _AVCBinReadNextRxp(psFile->psRawBinFile, psFile->cur.psRxp,
1712                            psFile->nPrecision) !=0)
1713     {
1714         return nullptr;
1715     }
1716 
1717     return psFile->cur.psRxp;
1718 }
1719 
1720 /*=====================================================================
1721  *                         NATIVE (V7.x) TABLEs
1722  *
1723  * Note: Also applies to AVCCoverWeird
1724  *====================================================================*/
1725 
1726 /**********************************************************************
1727  *                          _AVCBinReadNextArcDir()
1728  *
1729  * (This function is for internal library use... external calls should
1730  * go to AVCBinReadOpen() with type AVCFileTABLE instead)
1731  *
1732  * Read the next record from an arc.dir (or "arcdr9") file.
1733  *
1734  * Note that arc.dir files have no header... they start with the
1735  * first record immediately.
1736  *
1737  * Returns 0 on success or -1 on error.
1738  **********************************************************************/
1739 
_AVCBinReadNextArcDir(AVCRawBinFile * psFile,AVCTableDef * psArcDir)1740 int _AVCBinReadNextArcDir(AVCRawBinFile *psFile, AVCTableDef *psArcDir)
1741 {
1742     int i;
1743 
1744     /* Arc/Info Table name
1745      */
1746     AVCRawBinReadString(psFile, 32, (GByte *)psArcDir->szTableName);
1747     psArcDir->szTableName[32] = '\0';
1748 
1749     if (AVCRawBinEOF(psFile))
1750         return -1;
1751 
1752     /* "ARC####" basename for .DAT and .NIT files
1753      */
1754     AVCRawBinReadString(psFile, 8, (GByte *)psArcDir->szInfoFile);
1755     psArcDir->szInfoFile[7] = '\0';
1756     for (i=6; i>0 && psArcDir->szInfoFile[i]==' '; i--)
1757         psArcDir->szInfoFile[i] = '\0';
1758 
1759     psArcDir->numFields = AVCRawBinReadInt16(psFile);
1760     psArcDir->nRecSize  = AVCRawBinReadInt16(psFile);
1761 
1762     AVCRawBinFSeek(psFile, 18, SEEK_CUR);     /* Skip 18 bytes */
1763 
1764     psArcDir->bDeletedFlag = AVCRawBinReadInt16(psFile);
1765     psArcDir->numRecords = AVCRawBinReadInt32(psFile);
1766 
1767     AVCRawBinFSeek(psFile, 10, SEEK_CUR);     /* Skip 10 bytes */
1768 
1769     AVCRawBinReadBytes(psFile, 2, (GByte *)psArcDir->szExternal);
1770     psArcDir->szExternal[2] = '\0';
1771 
1772     AVCRawBinFSeek(psFile, 300, SEEK_CUR);  /* Skip the remaining 300 bytes */
1773 
1774     return 0;
1775 }
1776 
1777 /**********************************************************************
1778  *                          _AVCBinReadNextNit()
1779  *
1780  * (This function is for internal library use... external calls should
1781  * go to AVCBinReadOpen() with type AVCFileTABLE instead)
1782  *
1783  * Read the next record from an arc####.nit file.
1784  *
1785  * Note that arc####.nit files have no header... they start with the
1786  * first record immediately.
1787  *
1788  * Returns 0 on success or -1 on error.
1789  **********************************************************************/
1790 static
_AVCBinReadNextArcNit(AVCRawBinFile * psFile,AVCFieldInfo * psField)1791 int _AVCBinReadNextArcNit(AVCRawBinFile *psFile, AVCFieldInfo *psField)
1792 {
1793     AVCRawBinReadString(psFile, 16, (GByte *)psField->szName);
1794     psField->szName[16] = '\0';
1795 
1796     if (AVCRawBinEOF(psFile))
1797         return -1;
1798 
1799     psField->nSize     = AVCRawBinReadInt16(psFile);
1800     if( psField->nSize < 0 )
1801         return -1;
1802     psField->v2        = AVCRawBinReadInt16(psFile);  /* Always -1 ? */
1803     psField->nOffset   = AVCRawBinReadInt16(psFile);
1804     psField->v4        = AVCRawBinReadInt16(psFile);  /* Always 4 ?  */
1805     psField->v5        = AVCRawBinReadInt16(psFile);  /* Always -1 ? */
1806     psField->nFmtWidth = AVCRawBinReadInt16(psFile);
1807     psField->nFmtPrec  = AVCRawBinReadInt16(psFile);
1808     psField->nType1    = AVCRawBinReadInt16(psFile);
1809     psField->nType2    = AVCRawBinReadInt16(psFile);  /* Always 0 ? */
1810     psField->v10       = AVCRawBinReadInt16(psFile);  /* Always -1 ? */
1811     psField->v11       = AVCRawBinReadInt16(psFile);  /* Always -1 ? */
1812     psField->v12       = AVCRawBinReadInt16(psFile);  /* Always -1 ? */
1813     psField->v13       = AVCRawBinReadInt16(psFile);  /* Always -1 ? */
1814 
1815     AVCRawBinReadString(psFile, 16, (GByte *)psField->szAltName);   /* Always Blank ? */
1816     psField->szAltName[16] = '\0';
1817 
1818     AVCRawBinFSeek(psFile, 56, SEEK_CUR);             /* Skip 56 bytes */
1819 
1820     psField->nIndex    = AVCRawBinReadInt16(psFile);
1821 
1822     AVCRawBinFSeek(psFile, 28, SEEK_CUR);  /* Skip the remaining 28 bytes */
1823 
1824     return 0;
1825 }
1826 
1827 /**********************************************************************
1828  *                          _AVCBinReadGetInfoFilename()
1829  *
1830  * Look for the DAT or NIT files for a given table... returns TRUE if
1831  * they exist, or FALSE otherwise.
1832  *
1833  * If pszRetFnmae/pszRetNitFile != nullptr then the filename with full path
1834  * will be copied to the specified buffer.
1835  **********************************************************************/
1836 static
_AVCBinReadGetInfoFilename(const char * pszInfoPath,const char * pszBasename,const char * pszDatOrNit,AVCCoverType eCoverType,char * pszRetFname,size_t nRetFnameLen)1837 GBool _AVCBinReadGetInfoFilename(const char *pszInfoPath,
1838                                  const char *pszBasename,
1839                                  const char *pszDatOrNit,
1840                                  AVCCoverType eCoverType,
1841                                  char *pszRetFname,
1842                                  size_t nRetFnameLen)
1843 {
1844     GBool       bFilesExist = FALSE;
1845     char        *pszBuf = nullptr;
1846     VSIStatBufL  sStatBuf;
1847     size_t      nBufLen;
1848 
1849     if (pszRetFname)
1850     {
1851         pszBuf = pszRetFname;
1852         nBufLen = nRetFnameLen;
1853     }
1854     else
1855     {
1856         nBufLen = strlen(pszInfoPath)+strlen(pszBasename)+10;
1857         pszBuf = (char*)CPLMalloc(nBufLen);
1858     }
1859 
1860     if (eCoverType == AVCCoverWeird)
1861     {
1862         snprintf(pszBuf, nBufLen, "%s%s%s", pszInfoPath, pszBasename, pszDatOrNit);
1863     }
1864     else
1865     {
1866         snprintf(pszBuf, nBufLen, "%s%s.%s", pszInfoPath, pszBasename, pszDatOrNit);
1867     }
1868 
1869     AVCAdjustCaseSensitiveFilename(pszBuf);
1870 
1871     if (VSIStatL(pszBuf, &sStatBuf) == 0)
1872         bFilesExist = TRUE;
1873 
1874     if (eCoverType == AVCCoverWeird && !bFilesExist)
1875     {
1876         /* In some cases, the filename can be truncated to 8 chars
1877          * and we end up with "ARC000DA"... check that possibility.
1878          */
1879         pszBuf[strlen(pszBuf)-1] = '\0';
1880 
1881         AVCAdjustCaseSensitiveFilename(pszBuf);
1882 
1883         if (VSIStatL(pszBuf, &sStatBuf) == 0)
1884             bFilesExist = TRUE;
1885     }
1886 
1887     if (pszRetFname == nullptr)
1888         CPLFree(pszBuf);
1889 
1890     return bFilesExist;
1891 
1892 }
1893 
1894 /**********************************************************************
1895  *                          _AVCBinReadInfoFilesExist()
1896  *
1897  * Look for the DAT and NIT files for a given table... returns TRUE if
1898  * they exist, or FALSE otherwise.
1899  *
1900  * If pszRetDatFile/pszRetNitFile != nullptr then the .DAT and .NIT filename
1901  * without the info path will be copied to the specified buffers.
1902  **********************************************************************/
1903 static
_AVCBinReadInfoFileExists(const char * pszInfoPath,const char * pszBasename,AVCCoverType eCoverType)1904 GBool _AVCBinReadInfoFileExists(const char *pszInfoPath,
1905                                 const char *pszBasename,
1906                                 AVCCoverType eCoverType)
1907 {
1908 
1909     return (_AVCBinReadGetInfoFilename(pszInfoPath, pszBasename,
1910                                        "dat", eCoverType, nullptr, 0) == TRUE &&
1911             _AVCBinReadGetInfoFilename(pszInfoPath, pszBasename,
1912                                        "nit", eCoverType, nullptr, 0) == TRUE);
1913 
1914 }
1915 
1916 /**********************************************************************
1917  *                          AVCBinReadListTables()
1918  *
1919  * Scan the arc.dir file and return stringlist with one entry for the
1920  * Arc/Info name of each table that belongs to the specified coverage.
1921  * Pass pszCoverName = nullptr to get the list of all tables.
1922  *
1923  * ppapszArcDatFiles if not nullptr will be set to point to a stringlist
1924  * with the corresponding "ARC????" info file basenames corresponding
1925  * to each table found.
1926  *
1927  * Note that arc.dir files have no header... they start with the
1928  * first record immediately.
1929  *
1930  * In AVCCoverWeird, the file is called "arcdr9"
1931  *
1932  * Returns a stringlist that should be deallocated by the caller
1933  * with CSLDestroy(), or nullptr on error.
1934  **********************************************************************/
AVCBinReadListTables(const char * pszInfoPath,const char * pszCoverName,char *** ppapszArcDatFiles,AVCCoverType eCoverType,AVCDBCSInfo * psDBCSInfo)1935 char **AVCBinReadListTables(const char *pszInfoPath, const char *pszCoverName,
1936                             char ***ppapszArcDatFiles, AVCCoverType eCoverType,
1937                             AVCDBCSInfo *psDBCSInfo)
1938 {
1939     char              **papszList = nullptr;
1940     char               *pszFname;
1941     char                szNameToFind[33] = "";
1942     int                 nLen;
1943     AVCRawBinFile      *hFile;
1944     AVCTableDef         sEntry;
1945 
1946     if (ppapszArcDatFiles)
1947         *ppapszArcDatFiles = nullptr;
1948 
1949     /*-----------------------------------------------------------------
1950      * For AVCCoverV7Tables type we do not look for tables for a specific
1951      * coverage, we return all tables from the info dir.
1952      *----------------------------------------------------------------*/
1953     if (eCoverType == AVCCoverV7Tables)
1954         pszCoverName = nullptr;
1955 
1956     /*-----------------------------------------------------------------
1957      * All tables that belong to a given coverage have their name starting
1958      * with the coverage name (in uppercase letters), followed by a 3
1959      * letters extension.
1960      *----------------------------------------------------------------*/
1961     if (pszCoverName != nullptr)
1962         // cppcheck-suppress bufferAccessOutOfBounds
1963         snprintf(szNameToFind, sizeof(szNameToFind), "%-.28s.", pszCoverName);
1964     nLen = (int)strlen(szNameToFind);
1965 
1966     /*-----------------------------------------------------------------
1967      * Open the arc.dir and add all entries that match the criteria
1968      * to our list.
1969      * In AVCCoverWeird, the file is called "arcdr9"
1970      *----------------------------------------------------------------*/
1971     pszFname = (char*)CPLMalloc(strlen(pszInfoPath)+9);
1972     if (eCoverType == AVCCoverWeird)
1973         snprintf(pszFname, strlen(pszInfoPath)+9, "%sarcdr9", pszInfoPath);
1974     else
1975         snprintf(pszFname, strlen(pszInfoPath)+9, "%sarc.dir", pszInfoPath);
1976 
1977     AVCAdjustCaseSensitiveFilename(pszFname);
1978 
1979     hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType),
1980                           psDBCSInfo);
1981 
1982     if (hFile)
1983     {
1984         while (!AVCRawBinEOF(hFile) &&
1985                _AVCBinReadNextArcDir(hFile, &sEntry) == 0)
1986         {
1987             if (/* sEntry.numRecords > 0 && (DO NOT skip empty tables) */
1988                 !sEntry.bDeletedFlag &&
1989                 (pszCoverName == nullptr ||
1990                  EQUALN(szNameToFind, sEntry.szTableName, nLen)) &&
1991                 _AVCBinReadInfoFileExists(pszInfoPath,
1992                                           sEntry.szInfoFile,
1993                                           eCoverType) )
1994             {
1995                 papszList = CSLAddString(papszList, sEntry.szTableName);
1996 
1997                 if (ppapszArcDatFiles)
1998                     *ppapszArcDatFiles = CSLAddString(*ppapszArcDatFiles,
1999                                                       sEntry.szInfoFile);
2000             }
2001         }
2002         AVCRawBinClose(hFile);
2003 
2004     }
2005 
2006     CPLFree(pszFname);
2007 
2008     return papszList;
2009 }
2010 
2011 /**********************************************************************
2012  *                         _AVCBinReadOpenTable()
2013  *
2014  * (This function is for internal library use... external calls should
2015  * go to AVCBinReadOpen() with type AVCFileTABLE instead)
2016  *
2017  * Open a INFO table, read the header file (.NIT), and finally open
2018  * the associated data file to be ready to read records from it.
2019  *
2020  * Returns a valid AVCBinFile handle, or nullptr if the file could
2021  * not be opened.
2022  *
2023  * _AVCBinReadCloseTable() will eventually have to be called to release the
2024  * resources used by the AVCBinFile structure.
2025  **********************************************************************/
_AVCBinReadOpenTable(const char * pszInfoPath,const char * pszTableName,AVCCoverType eCoverType,AVCDBCSInfo * psDBCSInfo)2026 AVCBinFile *_AVCBinReadOpenTable(const char *pszInfoPath,
2027                                  const char *pszTableName,
2028                                  AVCCoverType eCoverType,
2029                                  AVCDBCSInfo *psDBCSInfo)
2030 {
2031     AVCBinFile    *psFile;
2032     AVCRawBinFile *hFile;
2033     AVCTableDef    sTableDef;
2034     AVCFieldInfo  *pasFieldDef;
2035     char          *pszFname;
2036     GBool          bFound;
2037     int            i;
2038     size_t         nFnameLen;
2039 
2040     memset(&sTableDef, 0, sizeof(sTableDef));
2041     sTableDef.numFields = 0;
2042     sTableDef.pasFieldDef = nullptr;
2043 
2044     /* Alloc a buffer big enough for the longest possible filename...
2045      */
2046     nFnameLen = strlen(pszInfoPath)+81;
2047     pszFname = (char*)CPLMalloc(nFnameLen);
2048 
2049     /*-----------------------------------------------------------------
2050      * Fetch info about this table from the "arc.dir"
2051      *----------------------------------------------------------------*/
2052     if (eCoverType == AVCCoverWeird)
2053         snprintf(pszFname, nFnameLen, "%sarcdr9", pszInfoPath);
2054     else
2055         snprintf(pszFname, nFnameLen, "%sarc.dir", pszInfoPath);
2056 
2057     AVCAdjustCaseSensitiveFilename(pszFname);
2058 
2059     hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType),
2060                           psDBCSInfo);
2061     bFound = FALSE;
2062 
2063     if (hFile)
2064     {
2065         while(!bFound && _AVCBinReadNextArcDir(hFile, &sTableDef) == 0)
2066         {
2067             if (!sTableDef.bDeletedFlag &&
2068                 EQUALN(sTableDef.szTableName, pszTableName,
2069                        strlen(pszTableName)) &&                _AVCBinReadInfoFileExists(pszInfoPath,
2070                                           sTableDef.szInfoFile,
2071                                           eCoverType))
2072             {
2073                 bFound = TRUE;
2074             }
2075         }
2076         AVCRawBinClose(hFile);
2077     }
2078 
2079     /* Hummm... quite likely that this table does not exist!
2080      */
2081     if (!bFound)
2082     {
2083         CPLError(CE_Failure, CPLE_OpenFailed,
2084                  "Failed to open table %s", pszTableName);
2085         CPLFree(pszFname);
2086         return nullptr;
2087     }
2088     /* To please Coverity */
2089     if( sTableDef.numFields < 0 || sTableDef.numFields >= 32767 )
2090     {
2091         CPLError(CE_Failure, CPLE_OpenFailed,
2092                  "Invalid numFields in %s", pszTableName);
2093         CPLFree(pszFname);
2094         return nullptr;
2095     }
2096 
2097     /*-----------------------------------------------------------------
2098      * Establish the location of the data file... depends on the
2099      * szExternal[] field.
2100      *----------------------------------------------------------------*/
2101     if (EQUAL(sTableDef.szExternal, "XX"))
2102     {
2103         /*-------------------------------------------------------------
2104          * The data file is located outside of the INFO directory.
2105          * Read the path to the data file from the arc####.dat file
2106          *------------------------------------------------------------*/
2107         _AVCBinReadGetInfoFilename(pszInfoPath, sTableDef.szInfoFile,
2108                                    "dat", eCoverType, pszFname, nFnameLen);
2109         AVCAdjustCaseSensitiveFilename(pszFname);
2110 
2111         hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType),
2112                               psDBCSInfo);
2113 
2114         if (hFile)
2115         {
2116             /* Read the relative file path, and remove trailing spaces.
2117              */
2118             AVCRawBinReadBytes(hFile, 80, (GByte *)sTableDef.szDataFile);
2119             sTableDef.szDataFile[80] = '\0';
2120 
2121             for(i = (int)strlen(sTableDef.szDataFile)-1;
2122                 i >= 0 && isspace((unsigned char)sTableDef.szDataFile[i]);
2123                 i--)
2124             {
2125                 sTableDef.szDataFile[i] = '\0';
2126             }
2127 
2128             AVCRawBinClose(hFile);
2129         }
2130         else
2131         {
2132             CPLError(CE_Failure, CPLE_OpenFailed,
2133                      "Failed to open file %s", pszFname);
2134             CPLFree(pszFname);
2135             return nullptr;
2136         }
2137     }
2138     else
2139     {
2140         /*-------------------------------------------------------------
2141          * The data file IS the arc####.dat file
2142          * Note: sTableDef.szDataFile must be relative to info directory
2143          *------------------------------------------------------------*/
2144         _AVCBinReadGetInfoFilename(pszInfoPath, sTableDef.szInfoFile,
2145                                    "dat", eCoverType, pszFname, nFnameLen);
2146         snprintf(sTableDef.szDataFile, sizeof(sTableDef.szDataFile), "%s", pszFname+strlen(pszInfoPath));
2147    }
2148 
2149     /*-----------------------------------------------------------------
2150      * Read the table field definitions from the "arc####.nit" file.
2151      *----------------------------------------------------------------*/
2152     _AVCBinReadGetInfoFilename(pszInfoPath, sTableDef.szInfoFile,
2153                                "nit", eCoverType, pszFname, nFnameLen);
2154     AVCAdjustCaseSensitiveFilename(pszFname);
2155 
2156     hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType),
2157                           psDBCSInfo);
2158 
2159     if (hFile)
2160     {
2161         int iField;
2162 
2163         pasFieldDef = (AVCFieldInfo*)CPLCalloc(sTableDef.numFields,
2164                                                sizeof(AVCFieldInfo));
2165 
2166         /*-------------------------------------------------------------
2167          * There must be at least sTableDef.numFields valid entries
2168          * in the .NIT file...
2169          *
2170          * Note that we ignore any deleted field entries (entries with
2171          * index=-1)... I don't see any use for these deleted fields...
2172          * and I don't understand why Arc/Info includes them in their
2173          * E00 table headers...
2174          *------------------------------------------------------------*/
2175         for(i=0, iField=0; iField<sTableDef.numFields; i++)
2176         {
2177             if (_AVCBinReadNextArcNit(hFile, &(pasFieldDef[iField])) != 0)
2178             {
2179                 /* Problems.... is the NIT file corrupt???
2180                  */
2181                 AVCRawBinClose(hFile);
2182                 CPLFree(pszFname);
2183                 CPLFree(pasFieldDef);
2184                 CPLError(CE_Failure, CPLE_FileIO,
2185                          "Failed reading table field info for table %s "
2186                          "File may be corrupt?",  pszTableName);
2187                 return nullptr;
2188             }
2189 
2190             /*---------------------------------------------------------
2191              * Check if the field has been deleted (nIndex == -1).
2192              * We just ignore deleted fields
2193              *--------------------------------------------------------*/
2194             if (pasFieldDef[iField].nIndex > 0)
2195                 iField++;
2196         }
2197 
2198         AVCRawBinClose(hFile);
2199     }
2200     else
2201     {
2202         CPLError(CE_Failure, CPLE_OpenFailed,
2203                  "Failed to open file %s", pszFname);
2204         CPLFree(pszFname);
2205         return nullptr;
2206     }
2207 
2208 
2209     /*-----------------------------------------------------------------
2210      * Open the data file... ready to read records from it.
2211      * If the header says that table has 0 records, then we don't
2212      * try to open the file... but we don't consider that as an error.
2213      *----------------------------------------------------------------*/
2214     if (sTableDef.numRecords > 0 &&
2215         AVCFileExists(pszInfoPath, sTableDef.szDataFile))
2216     {
2217         VSIStatBufL      sStatBuf;
2218 
2219         snprintf(pszFname, nFnameLen, "%s%s", pszInfoPath, sTableDef.szDataFile);
2220         AVCAdjustCaseSensitiveFilename(pszFname);
2221 
2222         hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType),
2223                               psDBCSInfo);
2224 
2225         /* OOPS... data file does not exist!
2226          */
2227         if (hFile == nullptr)
2228         {
2229             CPLError(CE_Failure, CPLE_OpenFailed,
2230                      "Failed to open file %s", pszFname);
2231             CPLFree(pszFname);
2232             return nullptr;
2233         }
2234 
2235         /*-------------------------------------------------------------
2236          * In some cases, the number of records field for a table in the
2237          * arc.dir does not correspond to the real number of records
2238          * in the data file.  In this kind of situation, the number of
2239          * records returned by Arc/Info in an E00 file will be based
2240          * on the real data file size, and not on the value from the arc.dir.
2241          *
2242          * Fetch the data file size, and correct the number of record
2243          * field in the table header if necessary.
2244          *------------------------------------------------------------*/
2245         if ( VSIStatL(pszFname, &sStatBuf) != -1 &&
2246              sTableDef.nRecSize > 0 &&
2247              sStatBuf.st_size/sTableDef.nRecSize != sTableDef.numRecords)
2248         {
2249             sTableDef.numRecords = (int)(sStatBuf.st_size/sTableDef.nRecSize);
2250         }
2251 
2252     }
2253     else
2254     {
2255         hFile = nullptr;
2256         sTableDef.numRecords = 0;
2257     }
2258 
2259     /*-----------------------------------------------------------------
2260      * Alloc. and init. the AVCBinFile structure.
2261      *----------------------------------------------------------------*/
2262     psFile = (AVCBinFile*)CPLCalloc(1, sizeof(AVCBinFile));
2263 
2264     psFile->psRawBinFile = hFile;
2265     psFile->eCoverType = AVCCoverV7;
2266     psFile->eFileType = AVCFileTABLE;
2267     psFile->pszFilename = pszFname;
2268 
2269     psFile->hdr.psTableDef = (AVCTableDef*)CPLMalloc(sizeof(AVCTableDef));
2270     *(psFile->hdr.psTableDef) = sTableDef;
2271 
2272     psFile->hdr.psTableDef->pasFieldDef = pasFieldDef;
2273 
2274     /* We can't really tell the precision from a Table header...
2275      * just set an arbitrary value... it probably won't be used anyways!
2276      */
2277     psFile->nPrecision = AVC_SINGLE_PREC;
2278 
2279     /*-----------------------------------------------------------------
2280      * Allocate temp. structures to use to read records from the file
2281      * And allocate buffers for those fields that are stored as strings.
2282      *----------------------------------------------------------------*/
2283     psFile->cur.pasFields = (AVCField*)CPLCalloc(sTableDef.numFields,
2284                                                  sizeof(AVCField));
2285 
2286     for(i=0; i<sTableDef.numFields; i++)
2287     {
2288         if (pasFieldDef[i].nType1*10 == AVC_FT_DATE ||
2289             pasFieldDef[i].nType1*10 == AVC_FT_CHAR ||
2290             pasFieldDef[i].nType1*10 == AVC_FT_FIXINT ||
2291             pasFieldDef[i].nType1*10 == AVC_FT_FIXNUM )
2292         {
2293             psFile->cur.pasFields[i].pszStr =
2294                 (GByte*)CPLCalloc(pasFieldDef[i].nSize+1, sizeof(char));
2295         }
2296     }
2297 
2298     return psFile;
2299 }
2300 
2301 
2302 /**********************************************************************
2303  *                         _AVCBinReadNextTableRec()
2304  *
2305  * (This function is for internal library use... external calls should
2306  * go to AVCBinReadNextTableRec() instead)
2307  *
2308  * Reads the next record from an attribute table and fills the
2309  * pasFields[] array.
2310  *
2311  * Note that it is assumed that the pasFields[] array has been properly
2312  * initialized, re the allocation of buffers for fields stored as
2313  * strings.
2314  *
2315  * Returns 0 on success or -1 on error.
2316  **********************************************************************/
2317 static
_AVCBinReadNextTableRec(AVCRawBinFile * psFile,int nFields,AVCFieldInfo * pasDef,AVCField * pasFields,int nRecordSize)2318 int _AVCBinReadNextTableRec(AVCRawBinFile *psFile, int nFields,
2319                             AVCFieldInfo *pasDef, AVCField *pasFields,
2320                             int nRecordSize)
2321 {
2322     int i, nType, nBytesRead=0;
2323 
2324     if (psFile == nullptr)
2325         return -1;
2326 
2327     for(i=0; i<nFields; i++)
2328     {
2329         if (AVCRawBinEOF(psFile))
2330             return -1;
2331 
2332         nType = pasDef[i].nType1*10;
2333 
2334         if (nType ==  AVC_FT_DATE || nType == AVC_FT_CHAR ||
2335             nType == AVC_FT_FIXINT || nType == AVC_FT_FIXNUM)
2336         {
2337             /*---------------------------------------------------------
2338              * Values stored as strings
2339              *--------------------------------------------------------*/
2340             AVCRawBinReadString(psFile, pasDef[i].nSize, pasFields[i].pszStr);
2341             pasFields[i].pszStr[pasDef[i].nSize] = '\0';
2342         }
2343         else if (nType == AVC_FT_BININT && pasDef[i].nSize == 4)
2344         {
2345             /*---------------------------------------------------------
2346              * 32 bit binary integers
2347              *--------------------------------------------------------*/
2348             pasFields[i].nInt32 = AVCRawBinReadInt32(psFile);
2349         }
2350         else if (nType == AVC_FT_BININT && pasDef[i].nSize == 2)
2351         {
2352             /*---------------------------------------------------------
2353              * 16 bit binary integers
2354              *--------------------------------------------------------*/
2355             pasFields[i].nInt16 = AVCRawBinReadInt16(psFile);
2356         }
2357         else if (nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 4)
2358         {
2359             /*---------------------------------------------------------
2360              * Single precision floats
2361              *--------------------------------------------------------*/
2362             pasFields[i].fFloat = AVCRawBinReadFloat(psFile);
2363         }
2364         else if (nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 8)
2365         {
2366             /*---------------------------------------------------------
2367              * Double precision floats
2368              *--------------------------------------------------------*/
2369             pasFields[i].dDouble = AVCRawBinReadDouble(psFile);
2370         }
2371         else
2372         {
2373             /*---------------------------------------------------------
2374              * Hummm... unsupported field type...
2375              *--------------------------------------------------------*/
2376             CPLError(CE_Failure, CPLE_NotSupported,
2377                      "Unsupported field type: (type=%d, size=%d)",
2378                      nType, pasDef[i].nSize);
2379             return -1;
2380         }
2381 
2382         nBytesRead += pasDef[i].nSize;
2383     }
2384 
2385     /*-----------------------------------------------------------------
2386      * Record size is rounded to a multiple of 2 bytes.
2387      * Check the number of bytes read, and move the read pointer if
2388      * necessary.
2389      *----------------------------------------------------------------*/
2390     if (nBytesRead < nRecordSize)
2391         AVCRawBinFSeek(psFile, nRecordSize - nBytesRead, SEEK_CUR);
2392 
2393     return 0;
2394 }
2395 
2396 /*=====================================================================
2397  *                         PC Arc/Info DBF TABLEs
2398  *====================================================================*/
2399 
2400 void _AVCBinReadRepairDBFFieldName(char *pszFieldName);
2401 
2402 /**********************************************************************
2403  *                         _AVCBinReadOpenDBFTable()
2404  *
2405  * (This function is for internal library use... external calls should
2406  * go to AVCBinReadOpen() with type AVCCoverPC/AVCFileTABLE instead)
2407  *
2408  * Open the DBF table, reads the header information and inits the
2409  * AVCBinFile handle to be ready to read records from it.
2410  *
2411  * Returns a valid AVCBinFile handle, or nullptr if the file could
2412  * not be opened.
2413  *
2414  * _AVCBinReadCloseDBFTable() will eventually have to be called to release the
2415  * resources used by the AVCBinFile structure.
2416  **********************************************************************/
_AVCBinReadOpenDBFTable(const char * pszDBFFilename,const char * pszArcInfoTableName)2417 AVCBinFile *_AVCBinReadOpenDBFTable(const char *pszDBFFilename,
2418                                     const char *pszArcInfoTableName)
2419 {
2420     AVCBinFile    *psFile;
2421     DBFHandle     hDBFFile = nullptr;
2422     int            iField;
2423     AVCTableDef   *psTableDef;
2424     AVCFieldInfo  *pasFieldDef;
2425 
2426     /*-----------------------------------------------------------------
2427      * Try to open the DBF file
2428      *----------------------------------------------------------------*/
2429     if ( (hDBFFile = DBFOpen(pszDBFFilename, "rb")) == nullptr)
2430     {
2431         CPLError(CE_Failure, CPLE_OpenFailed,
2432                  "Failed to open table %s", pszDBFFilename);
2433         return nullptr;
2434     }
2435 
2436     /*-----------------------------------------------------------------
2437      * Alloc. and init. the AVCBinFile structure.
2438      *----------------------------------------------------------------*/
2439     psFile = (AVCBinFile*)CPLCalloc(1, sizeof(AVCBinFile));
2440 
2441     psFile->hDBFFile = hDBFFile;
2442 
2443     psFile->eCoverType = AVCCoverPC;
2444     psFile->eFileType = AVCFileTABLE;
2445     psFile->pszFilename = CPLStrdup(pszDBFFilename);
2446 
2447     psFile->hdr.psTableDef = nullptr;
2448 
2449     /* nCurDBFRecord is used to keep track of the 0-based index of the
2450      * last record we read from the DBF file... this is to emulate
2451      * sequential access which is assumed by the rest of the lib.
2452      * Since the first record (record 0) has not been read yet, then
2453      * we init the index at -1.
2454      */
2455     psFile->nCurDBFRecord = -1;
2456 
2457     /* We can't really tell the precision from a Table header...
2458      * just set an arbitrary value... it probably won't be used anyways!
2459      */
2460     psFile->nPrecision = AVC_SINGLE_PREC;
2461 
2462     /*-----------------------------------------------------------------
2463      * Build TableDef from the info in the DBF header
2464      *----------------------------------------------------------------*/
2465     /* Use calloc() to init some unused struct members */
2466     psTableDef = (AVCTableDef*)CPLCalloc(1, sizeof(AVCTableDef));
2467     psFile->hdr.psTableDef = psTableDef;
2468 
2469     snprintf(psTableDef->szTableName, sizeof(psTableDef->szTableName), "%-32.32s", pszArcInfoTableName);
2470 
2471     psTableDef->numFields = (GInt16)DBFGetFieldCount(hDBFFile);
2472 
2473     /* We'll compute nRecSize value when we read fields info later */
2474     psTableDef->nRecSize = 0;
2475 
2476     psTableDef->numRecords = DBFGetRecordCount(hDBFFile);
2477 
2478     /* All DBF tables are considered External */
2479     strcpy(psTableDef->szExternal, "XX");
2480 
2481     /*-----------------------------------------------------------------
2482      * Build Field definitions
2483      *----------------------------------------------------------------*/
2484     pasFieldDef = (AVCFieldInfo*)CPLCalloc(psTableDef->numFields,
2485                                            sizeof(AVCFieldInfo));
2486 
2487     psTableDef->pasFieldDef = pasFieldDef;
2488 
2489     for(iField=0; iField< psTableDef->numFields; iField++)
2490     {
2491         int nWidth, nDecimals;
2492         /* DBFFieldType eDBFType; */
2493         char         cNativeType;
2494 
2495         /*-------------------------------------------------------------
2496          * Fetch DBF Field info and convert to Arc/Info type...
2497          * Note that since DBF fields names are limited to 10 chars,
2498          * we do not have to worry about field name length in the process.
2499          *------------------------------------------------------------*/
2500         /* eDBFType = */
2501         DBFGetFieldInfo(hDBFFile, iField,
2502                         pasFieldDef[iField].szName,
2503                         &nWidth, &nDecimals);
2504         cNativeType = DBFGetNativeFieldType(hDBFFile, iField);
2505 
2506         pasFieldDef[iField].nFmtWidth = (GInt16)nWidth;
2507         pasFieldDef[iField].nFmtPrec = (GInt16)nDecimals;
2508 
2509         /* nIndex is the 1-based field index that we see in the E00 header */
2510         pasFieldDef[iField].nIndex = (GInt16)(iField+1);
2511 
2512         if (cNativeType == 'F' || (cNativeType == 'N' && nDecimals > 0) )
2513         {
2514             /*---------------------------------------------------------
2515              * BINARY FLOAT
2516              *--------------------------------------------------------*/
2517             pasFieldDef[iField].nType1 = AVC_FT_BINFLOAT/10;
2518             pasFieldDef[iField].nSize = 4;
2519             pasFieldDef[iField].nFmtWidth = 12; /* PC Arc/Info ignores the */
2520             pasFieldDef[iField].nFmtPrec = 3;   /* DBF width/precision     */
2521         }
2522         else if (cNativeType == 'N')
2523         {
2524             /*---------------------------------------------------------
2525              * BINARY INTEGER
2526              *--------------------------------------------------------*/
2527             pasFieldDef[iField].nType1 = AVC_FT_BININT/10;
2528             pasFieldDef[iField].nSize = 4;
2529             pasFieldDef[iField].nFmtWidth = 5;  /* PC Arc/Info ignores the */
2530             pasFieldDef[iField].nFmtPrec = -1;  /* DBF width/precision     */
2531 
2532             /*---------------------------------------------------------
2533              * Some special integer fields need to have their names
2534              * repaired because DBF does not support special characters.
2535              *--------------------------------------------------------*/
2536             _AVCBinReadRepairDBFFieldName(pasFieldDef[iField].szName);
2537         }
2538         else if (cNativeType == 'D')
2539         {
2540             /*---------------------------------------------------------
2541              * DATE - Actually handled as a string internally
2542              *--------------------------------------------------------*/
2543             pasFieldDef[iField].nType1 = AVC_FT_DATE/10;
2544             pasFieldDef[iField].nSize = (GInt16)nWidth;
2545             pasFieldDef[iField].nFmtPrec = -1;
2546 
2547         }
2548         else /* (cNativeType == 'C' || cNativeType == 'L') */
2549         {
2550             /*---------------------------------------------------------
2551              * CHAR STRINGS ... and all unknown types also handled as strings
2552              *--------------------------------------------------------*/
2553             pasFieldDef[iField].nType1 = AVC_FT_CHAR/10;
2554             pasFieldDef[iField].nSize = (GInt16)nWidth;
2555             pasFieldDef[iField].nFmtPrec = -1;
2556 
2557         }
2558 
2559         /*---------------------------------------------------------
2560          * Keep track of position of field in record... first one always
2561          * starts at offset=1
2562          *--------------------------------------------------------*/
2563         if (iField == 0)
2564             pasFieldDef[iField].nOffset = 1;
2565         else
2566             pasFieldDef[iField].nOffset = (pasFieldDef[iField-1].nOffset +
2567                                             pasFieldDef[iField-1].nSize );
2568 
2569         /*---------------------------------------------------------
2570          * Set default values for all other unused members in the struct
2571          *--------------------------------------------------------*/
2572         pasFieldDef[iField].v2     = -1;  /* Always -1 ? */
2573         pasFieldDef[iField].v4     = 4;   /* Always 4 ?  */
2574         pasFieldDef[iField].v5     = -1;  /* Always -1 ? */
2575         pasFieldDef[iField].nType2 = 0;   /* Always 0 ?  */
2576         pasFieldDef[iField].v10    = -1;  /* Always -1 ? */
2577         pasFieldDef[iField].v11    = -1;  /* Always -1 ? */
2578         pasFieldDef[iField].v12    = -1;  /* Always -1 ? */
2579         pasFieldDef[iField].v13    = -1;  /* Always -1 ? */
2580 
2581     }
2582 
2583     /*-----------------------------------------------------------------
2584      * Compute record size...
2585      * Record size has to be rounded to a multiple of 2 bytes.
2586      *----------------------------------------------------------------*/
2587     if (psTableDef->numFields > 0)
2588     {
2589         psTableDef->nRecSize = (pasFieldDef[psTableDef->numFields-1].nOffset-1+
2590                                 pasFieldDef[psTableDef->numFields-1].nSize);
2591         psTableDef->nRecSize = ((psTableDef->nRecSize+1)/2)*2;
2592     }
2593     else
2594         psTableDef->nRecSize = 0;
2595 
2596     /*-----------------------------------------------------------------
2597      * Allocate temp. structures to use to read records from the file
2598      * And allocate buffers for those fields that are stored as strings.
2599      *----------------------------------------------------------------*/
2600     psFile->cur.pasFields = (AVCField*)CPLCalloc(psTableDef->numFields,
2601                                                  sizeof(AVCField));
2602 
2603     for(iField=0; iField<psTableDef->numFields; iField++)
2604     {
2605         if (pasFieldDef[iField].nType1*10 == AVC_FT_DATE ||
2606             pasFieldDef[iField].nType1*10 == AVC_FT_CHAR ||
2607             pasFieldDef[iField].nType1*10 == AVC_FT_FIXINT ||
2608             pasFieldDef[iField].nType1*10 == AVC_FT_FIXNUM )
2609         {
2610             psFile->cur.pasFields[iField].pszStr =
2611                 (GByte*)CPLCalloc(pasFieldDef[iField].nSize+1, sizeof(GByte));
2612         }
2613     }
2614 
2615     return psFile;
2616 }
2617 
2618 
2619 /**********************************************************************
2620  *                         _AVCBinReadNextDBFTableRec()
2621  *
2622  * (This function is for internal library use... external calls should
2623  * go to AVCBinReadNextTableRec() instead)
2624  *
2625  * Reads the next record from a AVCCoverPC DBF attribute table and fills the
2626  * pasFields[] array.
2627  *
2628  * Note that it is assumed that the pasFields[] array has been properly
2629  * initialized, re the allocation of buffers for fields stored as
2630  * strings.
2631  *
2632  * Returns 0 on success or -1 on error.
2633  **********************************************************************/
2634 static
_AVCBinReadNextDBFTableRec(DBFHandle hDBFFile,int * piRecordIndex,int nFields,AVCFieldInfo * pasDef,AVCField * pasFields)2635 int _AVCBinReadNextDBFTableRec(DBFHandle hDBFFile, int *piRecordIndex,
2636                                       int nFields, AVCFieldInfo *pasDef,
2637                                       AVCField *pasFields)
2638 {
2639     int         i, nType;
2640 
2641     /*-----------------------------------------------------------------
2642      * Increment current record index.
2643      * We use nCurDBFRecord to keep track of the 0-based index of the
2644      * last record we read from the DBF file... this is to emulate
2645      * sequential access which is assumed by the rest of the lib.
2646      *----------------------------------------------------------------*/
2647     if (hDBFFile == nullptr || piRecordIndex == nullptr ||
2648         pasDef == nullptr || pasFields == nullptr)
2649         return -1;
2650 
2651     (*piRecordIndex)++;
2652 
2653     if (*piRecordIndex >= DBFGetRecordCount(hDBFFile))
2654         return -1;  /* Reached EOF */
2655 
2656     /*-----------------------------------------------------------------
2657      * Read/convert each field based on type
2658      *----------------------------------------------------------------*/
2659     for(i=0; i<nFields; i++)
2660     {
2661         nType = pasDef[i].nType1*10;
2662 
2663         if (nType ==  AVC_FT_DATE || nType == AVC_FT_CHAR ||
2664             nType == AVC_FT_FIXINT || nType == AVC_FT_FIXNUM)
2665         {
2666             /*---------------------------------------------------------
2667              * Values stored as strings
2668              *--------------------------------------------------------*/
2669             const char *pszValue;
2670             pszValue = DBFReadStringAttribute(hDBFFile,
2671                                               *piRecordIndex, i);
2672             strncpy((char*)pasFields[i].pszStr, pszValue, pasDef[i].nSize);
2673             pasFields[i].pszStr[pasDef[i].nSize] = '\0';
2674         }
2675         else if (nType == AVC_FT_BININT && pasDef[i].nSize == 4)
2676         {
2677             /*---------------------------------------------------------
2678              * 32 bit binary integers
2679              *--------------------------------------------------------*/
2680             pasFields[i].nInt32 = DBFReadIntegerAttribute(hDBFFile,
2681                                                           *piRecordIndex, i);
2682         }
2683         else if (nType == AVC_FT_BININT && pasDef[i].nSize == 2)
2684         {
2685             /*---------------------------------------------------------
2686              * 16 bit binary integers
2687              *--------------------------------------------------------*/
2688             pasFields[i].nInt16 = (GInt16)DBFReadIntegerAttribute(hDBFFile,
2689                                                                *piRecordIndex,
2690                                                                   i);
2691         }
2692         else if (nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 4)
2693         {
2694             /*---------------------------------------------------------
2695              * Single precision floats
2696              *--------------------------------------------------------*/
2697             pasFields[i].fFloat = (float)DBFReadDoubleAttribute(hDBFFile,
2698                                                                 *piRecordIndex,
2699                                                                 i);
2700         }
2701         else if (nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 8)
2702         {
2703             /*---------------------------------------------------------
2704              * Double precision floats
2705              *--------------------------------------------------------*/
2706             pasFields[i].dDouble = DBFReadDoubleAttribute(hDBFFile,
2707                                                           *piRecordIndex,
2708                                                           i);
2709         }
2710         else
2711         {
2712             /*---------------------------------------------------------
2713              * Hummm... unsupported field type...
2714              *--------------------------------------------------------*/
2715             CPLError(CE_Failure, CPLE_NotSupported,
2716                      "Unsupported field type: (type=%d, size=%d)",
2717                      nType, pasDef[i].nSize);
2718             return -1;
2719         }
2720 
2721     }
2722 
2723     return 0;
2724 }
2725 
2726 
2727 /**********************************************************************
2728  *                         _AVCBinReadRepairDBFFieldName()
2729  *
2730  * Attempt to repair some special integer field names that usually
2731  * carry special chars such as '#' or '-' but that are lost because of
2732  * DBF limitations and are replaced by '_'.
2733  *
2734  **********************************************************************/
_AVCBinReadRepairDBFFieldName(char * pszFieldName)2735 void _AVCBinReadRepairDBFFieldName(char *pszFieldName)
2736 {
2737     char *pszTmp;
2738 
2739     if ((pszTmp = strrchr(pszFieldName, '_')) == nullptr)
2740         return;  /* No special char to process */
2741 
2742     /*-----------------------------------------------------------------
2743      * Replace '_' at end of field name by a '#', as in:
2744      *   COVER# , FNODE#, TNODE#, LPOLY#, RPOLY#
2745      *
2746      * and replace names that end with "_ID" with "-ID" as in COVER-ID
2747      *----------------------------------------------------------------*/
2748     if (EQUAL(pszTmp, "_"))
2749         *pszTmp = '#';
2750     else if (EQUAL(pszTmp, "_ID"))
2751         *pszTmp = '-';
2752 
2753 }
2754