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