/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 | } |