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