Line data Source code
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 55 : int _AVCE00ComputeRecSize(int numFields, AVCFieldInfo *pasDef,
59 : GBool bMapType40ToDouble)
60 : {
61 55 : int i, nType, nBufSize = 0;
62 :
63 : /*-------------------------------------------------------------
64 : * Add up the nbr of chars used by each field
65 : *------------------------------------------------------------*/
66 338 : for (i = 0; i < numFields; i++)
67 : {
68 283 : nType = pasDef[i].nType1 * 10;
69 283 : if (nType == AVC_FT_DATE || nType == AVC_FT_CHAR ||
70 : nType == AVC_FT_FIXINT)
71 : {
72 22 : nBufSize += pasDef[i].nSize;
73 : }
74 261 : else if (nType == AVC_FT_BININT && pasDef[i].nSize == 4)
75 87 : nBufSize += 11;
76 174 : else if (nType == AVC_FT_BININT && pasDef[i].nSize == 2)
77 0 : nBufSize += 6;
78 174 : else if (bMapType40ToDouble && nType == AVC_FT_FIXNUM &&
79 0 : pasDef[i].nSize > 8)
80 : {
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 : }
86 174 : else if ((nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 4) ||
87 : nType == AVC_FT_FIXNUM)
88 174 : nBufSize += 14;
89 0 : else if (nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 8)
90 0 : nBufSize += 24;
91 : else
92 : {
93 : /*-----------------------------------------------------
94 : * Hummm... unsupported field type...
95 : *----------------------------------------------------*/
96 0 : CPLError(CE_Failure, CPLE_NotSupported,
97 : "_AVCE00ComputeRecSize(): Unsupported field type: "
98 : "(type=%d, size=%d)",
99 0 : nType, pasDef[i].nSize);
100 0 : return -1;
101 : }
102 : }
103 :
104 55 : return nBufSize;
105 : }
106 :
107 : /**********************************************************************
108 : * _AVCDestroyTableFields()
109 : *
110 : * Release all memory associated with an array of AVCField structures.
111 : **********************************************************************/
112 62 : void _AVCDestroyTableFields(AVCTableDef *psTableDef, AVCField *pasFields)
113 : {
114 : int i, nFieldType;
115 :
116 62 : if (pasFields)
117 : {
118 376 : for (i = 0; i < psTableDef->numFields; i++)
119 : {
120 314 : nFieldType = psTableDef->pasFieldDef[i].nType1 * 10;
121 314 : if (nFieldType == AVC_FT_DATE || nFieldType == AVC_FT_CHAR ||
122 289 : nFieldType == AVC_FT_FIXINT || nFieldType == AVC_FT_FIXNUM)
123 : {
124 25 : CPLFree(pasFields[i].pszStr);
125 : }
126 : }
127 62 : CPLFree(pasFields);
128 : }
129 62 : }
130 :
131 : /**********************************************************************
132 : * _AVCDestroyTableDef()
133 : *
134 : * Release all memory associated with a AVCTableDef structure.
135 : *
136 : **********************************************************************/
137 62 : void _AVCDestroyTableDef(AVCTableDef *psTableDef)
138 : {
139 62 : if (psTableDef)
140 : {
141 62 : CPLFree(psTableDef->pasFieldDef);
142 62 : CPLFree(psTableDef);
143 : }
144 62 : }
145 :
146 : /**********************************************************************
147 : * _AVCDupTableDef()
148 : *
149 : * Create a new copy of a AVCTableDef structure.
150 : **********************************************************************/
151 0 : AVCTableDef *_AVCDupTableDef(AVCTableDef *psSrcDef)
152 : {
153 : 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 : }
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 10 : GBool AVCFileExists(const char *pszPath, const char *pszName)
181 : {
182 : char *pszBuf;
183 10 : GBool bFileExists = FALSE;
184 : VSILFILE *fp;
185 :
186 10 : pszBuf = (char *)CPLMalloc(strlen(pszPath) + strlen(pszName) + 1);
187 10 : snprintf(pszBuf, strlen(pszPath) + strlen(pszName) + 1, "%s%s", pszPath,
188 : pszName);
189 :
190 10 : AVCAdjustCaseSensitiveFilename(pszBuf);
191 :
192 10 : if ((fp = VSIFOpenL(pszBuf, "rb")) != nullptr)
193 : {
194 10 : bFileExists = TRUE;
195 10 : VSIFCloseL(fp);
196 : }
197 :
198 10 : CPLFree(pszBuf);
199 :
200 10 : return bFileExists;
201 : }
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 105 : char *AVCAdjustCaseSensitiveFilename(char *pszFname)
219 : {
220 : VSIStatBufL sStatBuf;
221 105 : char *pszTmpPath = nullptr;
222 : int nTotalLen, iTmpPtr;
223 : GBool bValidPath;
224 :
225 : /*-----------------------------------------------------------------
226 : * First check if the filename is OK as is.
227 : *----------------------------------------------------------------*/
228 105 : if (VSIStatL(pszFname, &sStatBuf) == 0)
229 : {
230 61 : return pszFname;
231 : }
232 :
233 44 : pszTmpPath = CPLStrdup(pszFname);
234 44 : nTotalLen = (int)strlen(pszTmpPath);
235 :
236 : /*-----------------------------------------------------------------
237 : * Remap '\\' to '/'
238 : *----------------------------------------------------------------*/
239 2336 : for (iTmpPtr = 0; iTmpPtr < nTotalLen; iTmpPtr++)
240 : {
241 2292 : if (pszTmpPath[iTmpPtr] == '\\')
242 0 : pszTmpPath[iTmpPtr] = '/';
243 : }
244 :
245 : /*-----------------------------------------------------------------
246 : * Try all lower case, check if the filename is OK as that.
247 : *----------------------------------------------------------------*/
248 2336 : for (iTmpPtr = 0; iTmpPtr < nTotalLen; iTmpPtr++)
249 : {
250 2292 : if (pszTmpPath[iTmpPtr] >= 'A' && pszTmpPath[iTmpPtr] <= 'Z')
251 132 : pszTmpPath[iTmpPtr] += 32;
252 : }
253 :
254 44 : if (VSIStatL(pszTmpPath, &sStatBuf) == 0)
255 : {
256 44 : strcpy(pszFname, pszTmpPath);
257 44 : CPLFree(pszTmpPath);
258 44 : return pszFname;
259 : }
260 :
261 : /*-----------------------------------------------------------------
262 : * Try all upper case, check if the filename is OK as that.
263 : *----------------------------------------------------------------*/
264 0 : for (iTmpPtr = 0; iTmpPtr < nTotalLen; iTmpPtr++)
265 : {
266 0 : if (pszTmpPath[iTmpPtr] >= 'a' && pszTmpPath[iTmpPtr] <= 'z')
267 0 : pszTmpPath[iTmpPtr] -= 32;
268 : }
269 :
270 0 : if (VSIStatL(pszTmpPath, &sStatBuf) == 0)
271 : {
272 0 : strcpy(pszFname, pszTmpPath);
273 0 : CPLFree(pszTmpPath);
274 0 : return pszFname;
275 : }
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 0 : strcpy(pszTmpPath, pszFname);
282 :
283 : /*-----------------------------------------------------------------
284 : * Remap '\\' to '/'
285 : *----------------------------------------------------------------*/
286 0 : for (iTmpPtr = 0; iTmpPtr < nTotalLen; iTmpPtr++)
287 : {
288 0 : if (pszTmpPath[iTmpPtr] == '\\')
289 0 : pszTmpPath[iTmpPtr] = '/';
290 : }
291 :
292 0 : bValidPath = FALSE;
293 0 : while (iTmpPtr > 0 && !bValidPath)
294 : {
295 : /*-------------------------------------------------------------
296 : * Move back to the previous '/' separator
297 : *------------------------------------------------------------*/
298 0 : pszTmpPath[--iTmpPtr] = '\0';
299 0 : while (iTmpPtr > 0 && pszTmpPath[iTmpPtr - 1] != '/')
300 : {
301 0 : pszTmpPath[--iTmpPtr] = '\0';
302 : }
303 :
304 0 : if (iTmpPtr > 0 && VSIStatL(pszTmpPath, &sStatBuf) == 0)
305 0 : bValidPath = TRUE;
306 : }
307 :
308 0 : CPLAssert(iTmpPtr >= 0);
309 :
310 : /*-----------------------------------------------------------------
311 : * Assume that CWD is valid... so an empty path is a valid path
312 : *----------------------------------------------------------------*/
313 0 : 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 0 : while (bValidPath && strlen(pszTmpPath) < (size_t)nTotalLen)
323 : {
324 0 : char **papszDir = VSIReadDir(pszTmpPath);
325 : int iEntry, iLastPartStart;
326 :
327 0 : iLastPartStart = iTmpPtr;
328 :
329 : /*-------------------------------------------------------------
330 : * Add one component to the current path
331 : *------------------------------------------------------------*/
332 0 : pszTmpPath[iTmpPtr] = pszFname[iTmpPtr];
333 0 : iTmpPtr++;
334 0 : for (; pszFname[iTmpPtr] != '\0' && pszFname[iTmpPtr] != '/'; iTmpPtr++)
335 : {
336 0 : pszTmpPath[iTmpPtr] = pszFname[iTmpPtr];
337 : }
338 :
339 0 : while (iLastPartStart < iTmpPtr && pszTmpPath[iLastPartStart] == '/')
340 0 : iLastPartStart++;
341 :
342 : /*-------------------------------------------------------------
343 : * And do a case insensitive search in the current dir...
344 : *------------------------------------------------------------*/
345 0 : for (iEntry = 0; papszDir && papszDir[iEntry]; iEntry++)
346 : {
347 0 : if (EQUAL(pszTmpPath + iLastPartStart, papszDir[iEntry]))
348 : {
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 0 : strcpy(pszTmpPath + iLastPartStart, papszDir[iEntry]);
356 : #endif
357 0 : break;
358 : }
359 : }
360 :
361 0 : if (iTmpPtr > 0 && VSIStatL(pszTmpPath, &sStatBuf) != 0)
362 0 : bValidPath = FALSE;
363 :
364 0 : CSLDestroy(papszDir);
365 : }
366 :
367 : /*-----------------------------------------------------------------
368 : * We reached the last valid path component... just copy the rest
369 : * of the path as is.
370 : *----------------------------------------------------------------*/
371 0 : if (iTmpPtr < nTotalLen - 1)
372 : {
373 0 : strncpy(pszTmpPath + iTmpPtr, pszFname + iTmpPtr, nTotalLen - iTmpPtr);
374 : }
375 :
376 : /*-----------------------------------------------------------------
377 : * Update the source buffer and return.
378 : *----------------------------------------------------------------*/
379 0 : strcpy(pszFname, pszTmpPath);
380 0 : CPLFree(pszTmpPath);
381 :
382 0 : return pszFname;
383 : }
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 0 : int AVCPrintRealValue(char *pszBuf, size_t nBufLen, int nPrecision,
395 : AVCFileType eType, double dValue)
396 : {
397 : 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 : {
408 : char szBuf[50];
409 : 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 : {
415 0 : if (szBuf[i] == '+' || szBuf[i] == '-')
416 : break;
417 0 : numExpDigits++;
418 : }
419 : }
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 : {
428 0 : *pszBuf = '-';
429 0 : dValue = -1.0 * dValue;
430 : }
431 : 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 : {
440 : /* Float stored in DBF table in PC coverages */
441 0 : CPLsnprintf(pszBuf + 1, nBufLen - 1, "%9.6E", dValue);
442 0 : nLen = 13;
443 : }
444 0 : else if (nPrecision == AVC_DOUBLE_PREC && eType == AVCFileTABLE)
445 : {
446 0 : CPLsnprintf(pszBuf + 1, nBufLen - 1, "%20.17E", dValue);
447 0 : nLen = 24;
448 : }
449 0 : else if (nPrecision == AVC_DOUBLE_PREC)
450 : {
451 0 : CPLsnprintf(pszBuf + 1, nBufLen - 1, "%17.14E", dValue);
452 0 : nLen = 21;
453 : }
454 : else
455 : {
456 0 : CPLsnprintf(pszBuf + 1, nBufLen - 1, "%10.7E", dValue);
457 0 : nLen = 14;
458 : }
459 :
460 : /* Adjust number of exponent digits if necessary
461 : */
462 0 : if (numExpDigits > 2)
463 : {
464 : 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 : }
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 : }
|