Coverage Report

Created: 2025-06-09 08:44

/src/gdal/ogr/ogrsf_frmts/avc/avc_misc.cpp
Line
Count
Source (jump to first uncovered line)
1
/**********************************************************************
2
 *
3
 * Name:     avc_misc.c
4
 * Project:  Arc/Info vector coverage (AVC)  BIN<->E00 conversion library
5
 * Language: ANSI C
6
 * Purpose:  Misc. functions used by several parts of the library
7
 * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
8
 *
9
 **********************************************************************
10
 * Copyright (c) 1999-2005, Daniel Morissette
11
 *
12
 * SPDX-License-Identifier: MIT
13
 **********************************************************************
14
 *
15
 * $Log: avc_misc.c,v $
16
 * Revision 1.9  2005/06/03 03:49:59  daniel
17
 * Update email address, website url, and copyright dates
18
 *
19
 * Revision 1.8  2004/08/31 21:00:20  warmerda
20
 * Applied Carl Anderson's patch to reduce the amount of stating while
21
 * trying to discover filename "case" on Unix in AVCAdjustCaseSensitiveFilename.
22
 * http://bugzilla.remotesensing.org/show_bug.cgi?id=314
23
 *
24
 * Revision 1.7  2001/11/25 21:38:01  daniel
25
 * Remap '\\' to '/' in AVCAdjustCaseSensitiveFilename() on Unix.
26
 *
27
 * Revision 1.6  2001/11/25 21:15:23  daniel
28
 * Added hack (AVC_MAP_TYPE40_TO_DOUBLE) to map type 40 fields bigger than 8
29
 * digits to double precision as we generate E00 output (bug599)
30
 *
31
 * Revision 1.5  2000/09/26 20:21:04  daniel
32
 * Added AVCCoverPC write
33
 *
34
 * Revision 1.4  2000/09/22 19:45:21  daniel
35
 * Switch to MIT-style license
36
 *
37
 * Revision 1.3  2000/01/10 02:53:21  daniel
38
 * Added AVCAdjustCaseSensitiveFilename() and AVCFileExists()
39
 *
40
 * Revision 1.2  1999/08/23 18:24:27  daniel
41
 * Fixed support for attribute fields of type 40
42
 *
43
 * Revision 1.1  1999/05/11 02:34:46  daniel
44
 * Initial revision
45
 *
46
 **********************************************************************/
47
48
#include "avc.h"
49
50
/**********************************************************************
51
 *                          AVCE00ComputeRecSize()
52
 *
53
 * Computes the number of chars required to generate a E00 attribute
54
 * table record.
55
 *
56
 * Returns -1 on error, i.e. if it encounters an unsupported field type.
57
 **********************************************************************/
58
int _AVCE00ComputeRecSize(int numFields, AVCFieldInfo *pasDef,
59
                          GBool bMapType40ToDouble)
60
1.81M
{
61
1.81M
    int i, nType, nBufSize = 0;
62
63
    /*-------------------------------------------------------------
64
     * Add up the nbr of chars used by each field
65
     *------------------------------------------------------------*/
66
3.66M
    for (i = 0; i < numFields; i++)
67
1.85M
    {
68
1.85M
        nType = pasDef[i].nType1 * 10;
69
1.85M
        if (nType == AVC_FT_DATE || nType == AVC_FT_CHAR ||
70
1.85M
            nType == AVC_FT_FIXINT)
71
1.50M
        {
72
1.50M
            nBufSize += pasDef[i].nSize;
73
1.50M
        }
74
349k
        else if (nType == AVC_FT_BININT && pasDef[i].nSize == 4)
75
6.90k
            nBufSize += 11;
76
343k
        else if (nType == AVC_FT_BININT && pasDef[i].nSize == 2)
77
6.31k
            nBufSize += 6;
78
336k
        else if (bMapType40ToDouble && nType == AVC_FT_FIXNUM &&
79
336k
                 pasDef[i].nSize > 8)
80
0
        {
81
            /* See explanation in AVCE00GenTableHdr() about this hack to remap
82
             * type 40 fields to double precision floats.
83
             */
84
0
            nBufSize += 24; /* Remap to double float */
85
0
        }
86
336k
        else if ((nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 4) ||
87
336k
                 nType == AVC_FT_FIXNUM)
88
331k
            nBufSize += 14;
89
5.61k
        else if (nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 8)
90
5.57k
            nBufSize += 24;
91
41
        else
92
41
        {
93
            /*-----------------------------------------------------
94
             * Hummm... unsupported field type...
95
             *----------------------------------------------------*/
96
41
            CPLError(CE_Failure, CPLE_NotSupported,
97
41
                     "_AVCE00ComputeRecSize(): Unsupported field type: "
98
41
                     "(type=%d, size=%d)",
99
41
                     nType, pasDef[i].nSize);
100
41
            return -1;
101
41
        }
102
1.85M
    }
103
104
1.81M
    return nBufSize;
105
1.81M
}
106
107
/**********************************************************************
108
 *                          _AVCDestroyTableFields()
109
 *
110
 * Release all memory associated with an array of AVCField structures.
111
 **********************************************************************/
112
void _AVCDestroyTableFields(AVCTableDef *psTableDef, AVCField *pasFields)
113
1.89M
{
114
1.89M
    int i, nFieldType;
115
116
1.89M
    if (pasFields)
117
1.81M
    {
118
3.74M
        for (i = 0; i < psTableDef->numFields; i++)
119
1.92M
        {
120
1.92M
            nFieldType = psTableDef->pasFieldDef[i].nType1 * 10;
121
1.92M
            if (nFieldType == AVC_FT_DATE || nFieldType == AVC_FT_CHAR ||
122
1.92M
                nFieldType == AVC_FT_FIXINT || nFieldType == AVC_FT_FIXNUM)
123
1.89M
            {
124
1.89M
                CPLFree(pasFields[i].pszStr);
125
1.89M
            }
126
1.92M
        }
127
1.81M
        CPLFree(pasFields);
128
1.81M
    }
129
1.89M
}
130
131
/**********************************************************************
132
 *                          _AVCDestroyTableDef()
133
 *
134
 * Release all memory associated with a AVCTableDef structure.
135
 *
136
 **********************************************************************/
137
void _AVCDestroyTableDef(AVCTableDef *psTableDef)
138
1.89M
{
139
1.89M
    if (psTableDef)
140
1.89M
    {
141
1.89M
        CPLFree(psTableDef->pasFieldDef);
142
1.89M
        CPLFree(psTableDef);
143
1.89M
    }
144
1.89M
}
145
146
/**********************************************************************
147
 *                          _AVCDupTableDef()
148
 *
149
 * Create a new copy of a AVCTableDef structure.
150
 **********************************************************************/
151
AVCTableDef *_AVCDupTableDef(AVCTableDef *psSrcDef)
152
0
{
153
0
    AVCTableDef *psNewDef;
154
155
0
    if (psSrcDef == nullptr)
156
0
        return nullptr;
157
158
0
    psNewDef = (AVCTableDef *)CPLMalloc(1 * sizeof(AVCTableDef));
159
160
0
    memcpy(psNewDef, psSrcDef, sizeof(AVCTableDef));
161
162
0
    psNewDef->pasFieldDef =
163
0
        (AVCFieldInfo *)CPLMalloc(psSrcDef->numFields * sizeof(AVCFieldInfo));
164
165
0
    memcpy(psNewDef->pasFieldDef, psSrcDef->pasFieldDef,
166
0
           psSrcDef->numFields * sizeof(AVCFieldInfo));
167
168
0
    return psNewDef;
169
0
}
170
171
/**********************************************************************
172
 *                          AVCFileExists()
173
 *
174
 * Returns TRUE if a file with the specified name exists in the
175
 * specified directory.
176
 *
177
 * For now I simply try to fopen() the file ... would it be more
178
 * efficient to use stat() ???
179
 **********************************************************************/
180
GBool AVCFileExists(const char *pszPath, const char *pszName)
181
15.2k
{
182
15.2k
    char *pszBuf;
183
15.2k
    GBool bFileExists = FALSE;
184
15.2k
    VSILFILE *fp;
185
186
15.2k
    pszBuf = (char *)CPLMalloc(strlen(pszPath) + strlen(pszName) + 1);
187
15.2k
    snprintf(pszBuf, strlen(pszPath) + strlen(pszName) + 1, "%s%s", pszPath,
188
15.2k
             pszName);
189
190
15.2k
    AVCAdjustCaseSensitiveFilename(pszBuf);
191
192
15.2k
    if ((fp = VSIFOpenL(pszBuf, "rb")) != nullptr)
193
13.4k
    {
194
13.4k
        bFileExists = TRUE;
195
13.4k
        VSIFCloseL(fp);
196
13.4k
    }
197
198
15.2k
    CPLFree(pszBuf);
199
200
15.2k
    return bFileExists;
201
15.2k
}
202
203
/**********************************************************************
204
 *                     AVCAdjustCaseSensitiveFilename()
205
 *
206
 * Scan a filename and its path, adjust uppercase/lowercases if
207
 * necessary, and return a reference to that filename.
208
 *
209
 * This function works on the original buffer and returns a reference to it.
210
 *
211
 * NFW: It seems like this could be made somewhat more efficient by
212
 * getting a directory listing and doing a case insensitive search in
213
 * that list rather than all this stating that can be very expensive
214
 * in some circumstances.  However, at least with Carl's fix this is
215
 * somewhat faster.
216
 * see: http://bugzilla.remotesensing.org/show_bug.cgi?id=314
217
 **********************************************************************/
218
char *AVCAdjustCaseSensitiveFilename(char *pszFname)
219
141k
{
220
141k
    VSIStatBufL sStatBuf;
221
141k
    char *pszTmpPath = nullptr;
222
141k
    int nTotalLen, iTmpPtr;
223
141k
    GBool bValidPath;
224
225
    /*-----------------------------------------------------------------
226
     * First check if the filename is OK as is.
227
     *----------------------------------------------------------------*/
228
141k
    if (VSIStatL(pszFname, &sStatBuf) == 0)
229
86.4k
    {
230
86.4k
        return pszFname;
231
86.4k
    }
232
233
54.9k
    pszTmpPath = CPLStrdup(pszFname);
234
54.9k
    nTotalLen = (int)strlen(pszTmpPath);
235
236
    /*-----------------------------------------------------------------
237
     * Remap '\\' to '/'
238
     *----------------------------------------------------------------*/
239
4.17M
    for (iTmpPtr = 0; iTmpPtr < nTotalLen; iTmpPtr++)
240
4.11M
    {
241
4.11M
        if (pszTmpPath[iTmpPtr] == '\\')
242
53.7k
            pszTmpPath[iTmpPtr] = '/';
243
4.11M
    }
244
245
    /*-----------------------------------------------------------------
246
     * Try all lower case, check if the filename is OK as that.
247
     *----------------------------------------------------------------*/
248
4.17M
    for (iTmpPtr = 0; iTmpPtr < nTotalLen; iTmpPtr++)
249
4.11M
    {
250
4.11M
        if (pszTmpPath[iTmpPtr] >= 'A' && pszTmpPath[iTmpPtr] <= 'Z')
251
210k
            pszTmpPath[iTmpPtr] += 32;
252
4.11M
    }
253
254
54.9k
    if (VSIStatL(pszTmpPath, &sStatBuf) == 0)
255
17.5k
    {
256
17.5k
        strcpy(pszFname, pszTmpPath);
257
17.5k
        CPLFree(pszTmpPath);
258
17.5k
        return pszFname;
259
17.5k
    }
260
261
    /*-----------------------------------------------------------------
262
     * Try all upper case, check if the filename is OK as that.
263
     *----------------------------------------------------------------*/
264
3.20M
    for (iTmpPtr = 0; iTmpPtr < nTotalLen; iTmpPtr++)
265
3.17M
    {
266
3.17M
        if (pszTmpPath[iTmpPtr] >= 'a' && pszTmpPath[iTmpPtr] <= 'z')
267
2.15M
            pszTmpPath[iTmpPtr] -= 32;
268
3.17M
    }
269
270
37.4k
    if (VSIStatL(pszTmpPath, &sStatBuf) == 0)
271
0
    {
272
0
        strcpy(pszFname, pszTmpPath);
273
0
        CPLFree(pszTmpPath);
274
0
        return pszFname;
275
0
    }
276
277
    /*-----------------------------------------------------------------
278
     * OK, file either does not exist or has the wrong cases... we'll
279
     * go backwards until we find a portion of the path that is valid.
280
     *----------------------------------------------------------------*/
281
37.4k
    strcpy(pszTmpPath, pszFname);
282
283
    /*-----------------------------------------------------------------
284
     * Remap '\\' to '/'
285
     *----------------------------------------------------------------*/
286
3.20M
    for (iTmpPtr = 0; iTmpPtr < nTotalLen; iTmpPtr++)
287
3.17M
    {
288
3.17M
        if (pszTmpPath[iTmpPtr] == '\\')
289
53.6k
            pszTmpPath[iTmpPtr] = '/';
290
3.17M
    }
291
292
37.4k
    bValidPath = FALSE;
293
126k
    while (iTmpPtr > 0 && !bValidPath)
294
88.8k
    {
295
        /*-------------------------------------------------------------
296
         * Move back to the previous '/' separator
297
         *------------------------------------------------------------*/
298
88.8k
        pszTmpPath[--iTmpPtr] = '\0';
299
1.47M
        while (iTmpPtr > 0 && pszTmpPath[iTmpPtr - 1] != '/')
300
1.38M
        {
301
1.38M
            pszTmpPath[--iTmpPtr] = '\0';
302
1.38M
        }
303
304
88.8k
        if (iTmpPtr > 0 && VSIStatL(pszTmpPath, &sStatBuf) == 0)
305
37.4k
            bValidPath = TRUE;
306
88.8k
    }
307
308
37.4k
    CPLAssert(iTmpPtr >= 0);
309
310
    /*-----------------------------------------------------------------
311
     * Assume that CWD is valid... so an empty path is a valid path
312
     *----------------------------------------------------------------*/
313
37.4k
    if (iTmpPtr == 0)
314
0
        bValidPath = TRUE;
315
316
    /*-----------------------------------------------------------------
317
     * OK, now that we have a valid base, reconstruct the whole path
318
     * by scanning all the sub-directories.
319
     * If we get to a point where a path component does not exist then
320
     * we simply return the rest of the path as is.
321
     *----------------------------------------------------------------*/
322
82.8k
    while (bValidPath && strlen(pszTmpPath) < (size_t)nTotalLen)
323
45.3k
    {
324
45.3k
        char **papszDir = VSIReadDir(pszTmpPath);
325
45.3k
        int iEntry, iLastPartStart;
326
327
45.3k
        iLastPartStart = iTmpPtr;
328
329
        /*-------------------------------------------------------------
330
         * Add one component to the current path
331
         *------------------------------------------------------------*/
332
45.3k
        pszTmpPath[iTmpPtr] = pszFname[iTmpPtr];
333
45.3k
        iTmpPtr++;
334
335k
        for (; pszFname[iTmpPtr] != '\0' && pszFname[iTmpPtr] != '/'; iTmpPtr++)
335
289k
        {
336
289k
            pszTmpPath[iTmpPtr] = pszFname[iTmpPtr];
337
289k
        }
338
339
53.3k
        while (iLastPartStart < iTmpPtr && pszTmpPath[iLastPartStart] == '/')
340
7.98k
            iLastPartStart++;
341
342
        /*-------------------------------------------------------------
343
         * And do a case insensitive search in the current dir...
344
         *------------------------------------------------------------*/
345
203k
        for (iEntry = 0; papszDir && papszDir[iEntry]; iEntry++)
346
176k
        {
347
176k
            if (EQUAL(pszTmpPath + iLastPartStart, papszDir[iEntry]))
348
18.2k
            {
349
                /* Fount it! */
350
#ifdef CSA_BUILD
351
                // Silence false positive warning about overlapping buffers
352
                memmove(pszTmpPath + iLastPartStart, papszDir[iEntry],
353
                        strlen(papszDir[iEntry]) + 1);
354
#else
355
18.2k
                strcpy(pszTmpPath + iLastPartStart, papszDir[iEntry]);
356
18.2k
#endif
357
18.2k
                break;
358
18.2k
            }
359
176k
        }
360
361
45.3k
        if (iTmpPtr > 0 && VSIStatL(pszTmpPath, &sStatBuf) != 0)
362
20.8k
            bValidPath = FALSE;
363
364
45.3k
        CSLDestroy(papszDir);
365
45.3k
    }
366
367
    /*-----------------------------------------------------------------
368
     * We reached the last valid path component... just copy the rest
369
     * of the path as is.
370
     *----------------------------------------------------------------*/
371
37.4k
    if (iTmpPtr < nTotalLen - 1)
372
2.33k
    {
373
2.33k
        strncpy(pszTmpPath + iTmpPtr, pszFname + iTmpPtr, nTotalLen - iTmpPtr);
374
2.33k
    }
375
376
    /*-----------------------------------------------------------------
377
     * Update the source buffer and return.
378
     *----------------------------------------------------------------*/
379
37.4k
    strcpy(pszFname, pszTmpPath);
380
37.4k
    CPLFree(pszTmpPath);
381
382
37.4k
    return pszFname;
383
37.4k
}
384
385
/**********************************************************************
386
 *                          AVCPrintRealValue()
387
 *
388
 * Format a floating point value according to the specified coverage
389
 * precision (AVC_SINGLE/DOUBLE_PREC),  and append the formatted value
390
 * to the end of the pszBuf buffer.
391
 *
392
 * The function returns the number of characters added to the buffer.
393
 **********************************************************************/
394
int AVCPrintRealValue(char *pszBuf, size_t nBufLen, int nPrecision,
395
                      AVCFileType eType, double dValue)
396
0
{
397
0
    static int numExpDigits = -1;
398
0
    int nLen = 0;
399
400
    /* WIN32 systems' printf() for floating point output generates 3
401
     * digits exponents (ex: 1.23E+012), but E00 files must have 2 digits
402
     * exponents (ex: 1.23E+12).
403
     * Run a test (only once per prg execution) to establish the number
404
     * of exponent digits on the current platform.
405
     */
406
0
    if (numExpDigits == -1)
407
0
    {
408
0
        char szBuf[50];
409
0
        int i;
410
411
0
        CPLsnprintf(szBuf, sizeof(szBuf), "%10.7E", 123.45);
412
0
        numExpDigits = 0;
413
0
        for (i = (int)strlen(szBuf) - 1; i > 0; i--)
414
0
        {
415
0
            if (szBuf[i] == '+' || szBuf[i] == '-')
416
0
                break;
417
0
            numExpDigits++;
418
0
        }
419
0
    }
420
421
    /* We will append the value at the end of the current buffer contents.
422
     */
423
0
    nBufLen -= strlen(pszBuf);
424
0
    pszBuf = pszBuf + strlen(pszBuf);
425
426
0
    if (dValue < 0.0)
427
0
    {
428
0
        *pszBuf = '-';
429
0
        dValue = -1.0 * dValue;
430
0
    }
431
0
    else
432
0
        *pszBuf = ' ';
433
434
    /* Just to make things more complicated, double values are
435
     * output in a different format in attribute tables than in
436
     * the other files!
437
     */
438
0
    if (nPrecision == AVC_FORMAT_DBF_FLOAT)
439
0
    {
440
        /* Float stored in DBF table in PC coverages */
441
0
        CPLsnprintf(pszBuf + 1, nBufLen - 1, "%9.6E", dValue);
442
0
        nLen = 13;
443
0
    }
444
0
    else if (nPrecision == AVC_DOUBLE_PREC && eType == AVCFileTABLE)
445
0
    {
446
0
        CPLsnprintf(pszBuf + 1, nBufLen - 1, "%20.17E", dValue);
447
0
        nLen = 24;
448
0
    }
449
0
    else if (nPrecision == AVC_DOUBLE_PREC)
450
0
    {
451
0
        CPLsnprintf(pszBuf + 1, nBufLen - 1, "%17.14E", dValue);
452
0
        nLen = 21;
453
0
    }
454
0
    else
455
0
    {
456
0
        CPLsnprintf(pszBuf + 1, nBufLen - 1, "%10.7E", dValue);
457
0
        nLen = 14;
458
0
    }
459
460
    /* Adjust number of exponent digits if necessary
461
     */
462
0
    if (numExpDigits > 2)
463
0
    {
464
0
        int n;
465
0
        n = (int)strlen(pszBuf);
466
467
0
        pszBuf[n - numExpDigits] = pszBuf[n - 2];
468
0
        pszBuf[n - numExpDigits + 1] = pszBuf[n - 1];
469
0
        pszBuf[n - numExpDigits + 2] = '\0';
470
0
    }
471
472
    /* Just make sure that the actual output length is what we expected.
473
     */
474
0
    CPLAssert(strlen(pszBuf) == (size_t)nLen);
475
476
0
    return nLen;
477
0
}