1 : /**********************************************************************
2 : * $Id: mitab_utils.cpp,v 1.22 2008/07/21 16:04:58 dmorissette Exp $
3 : *
4 : * Name: mitab_utils.cpp
5 : * Project: MapInfo TAB Read/Write library
6 : * Language: C++
7 : * Purpose: Misc. util. functions for the library
8 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
9 : *
10 : **********************************************************************
11 : * Copyright (c) 1999-2001, 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: mitab_utils.cpp,v $
33 : * Revision 1.22 2008/07/21 16:04:58 dmorissette
34 : * Fixed const char * warnings with GCC 4.3 (GDAL ticket #2325)
35 : *
36 : * Revision 1.21 2006/12/01 16:53:15 dmorissette
37 : * Wrapped <mbctype.h> stuff with !defined(WIN32CE) (done by mloskot in OGR)
38 : *
39 : * Revision 1.20 2005/08/07 21:02:14 fwarmerdam
40 : * avoid warnings about testing for characters > 255.
41 : *
42 : * Revision 1.19 2004/06/30 20:29:04 dmorissette
43 : * Fixed refs to old address danmo@videotron.ca
44 : *
45 : * Revision 1.18 2002/08/28 14:19:22 warmerda
46 : * fix TABGetBasename() for mixture of path divider types like 'mi/abc\def.tab'
47 : *
48 : * Revision 1.17 2001/06/27 19:52:54 warmerda
49 : * avoid multi byte support if _WIN32 and unix defined for cygwin support
50 : *
51 : * Revision 1.16 2001/01/23 21:23:42 daniel
52 : * Added projection bounds lookup table, called from TABFile::SetProjInfo()
53 : *
54 : * Revision 1.15 2001/01/19 06:06:18 daniel
55 : * Don't filter chars in TABCleanFieldName() if we're on a DBCS system
56 : *
57 : * Revision 1.14 2000/09/28 16:39:44 warmerda
58 : * avoid warnings for unused, and unitialized variables
59 : *
60 : * Revision 1.13 2000/09/20 18:35:51 daniel
61 : * Fixed TABAdjustFilenameExtension() to also handle basename and path
62 : * using TABAdjustCaseSensitiveFilename()
63 : *
64 : * Revision 1.12 2000/04/18 04:19:22 daniel
65 : * Now accept extended chars with accents in TABCleanFieldName()
66 : *
67 : * Revision 1.11 2000/02/28 17:08:56 daniel
68 : * Avoid using isalnum() in TABCleanFieldName
69 : *
70 : * Revision 1.10 2000/02/18 20:46:35 daniel
71 : * Added TABCleanFieldName()
72 : *
73 : * Revision 1.9 2000/01/15 22:30:45 daniel
74 : * Switch to MIT/X-Consortium OpenSource license
75 : *
76 : * Revision 1.8 2000/01/14 23:46:59 daniel
77 : * Added TABEscapeString()/TABUnEscapeString()
78 : *
79 : * Revision 1.7 1999/12/16 06:10:24 daniel
80 : * TABGetBasename(): make sure last '/' of path is removed
81 : *
82 : * Revision 1.6 1999/12/14 02:08:37 daniel
83 : * Added TABGetBasename() + TAB_CSLLoad()
84 : *
85 : * Revision 1.5 1999/11/08 04:30:59 stephane
86 : * Modify TABGenerateArc()
87 : *
88 : * Revision 1.4 1999/09/29 17:59:21 daniel
89 : * Definition for PI was gone on Windows
90 : *
91 : * Revision 1.3 1999/09/16 02:39:17 daniel
92 : * Completed read support for most feature types
93 : *
94 : * Revision 1.2 1999/07/12 05:44:59 daniel
95 : * Added include math.h for VC++
96 : *
97 : * Revision 1.1 1999/07/12 04:18:25 daniel
98 : * Initial checkin
99 : *
100 : **********************************************************************/
101 :
102 : #include "mitab.h"
103 : #include "mitab_utils.h"
104 : #include "cpl_conv.h"
105 :
106 : #include <math.h> /* sin()/cos() */
107 : #include <ctype.h> /* toupper()/tolower() */
108 :
109 : #if defined(_WIN32) && !defined(unix) && !defined(WIN32CE)
110 : # include <mbctype.h> /* Multibyte chars stuff */
111 : #endif
112 :
113 :
114 : /**********************************************************************
115 : * TABGenerateArc()
116 : *
117 : * Generate the coordinates for an arc and ADD the coordinates to the
118 : * geometry object. If the geometry already contains some points then
119 : * these won't be lost.
120 : *
121 : * poLine can be a OGRLineString or one of its derived classes, such as
122 : * OGRLinearRing
123 : * numPoints is the number of points to generate.
124 : * Angles are specified in radians, valid values are in the range [0..2*PI]
125 : *
126 : * Arcs are always generated counterclockwise, even if StartAngle > EndAngle
127 : *
128 : * Returns 0 on success, -1 on error.
129 : **********************************************************************/
130 : int TABGenerateArc(OGRLineString *poLine, int numPoints,
131 : double dCenterX, double dCenterY,
132 : double dXRadius, double dYRadius,
133 0 : double dStartAngle, double dEndAngle)
134 : {
135 0 : double dX, dY, dAngleStep, dAngle=0.0;
136 : int i;
137 :
138 : // Adjust angles to go counterclockwise
139 0 : if (dEndAngle < dStartAngle)
140 0 : dEndAngle += 2.0*PI;
141 :
142 0 : dAngleStep = (dEndAngle-dStartAngle)/(numPoints-1.0);
143 :
144 0 : for(i=0; i<numPoints; i++)
145 : {
146 0 : dAngle = (dStartAngle + (double)i*dAngleStep);
147 0 : dX = dCenterX + dXRadius*cos(dAngle);
148 0 : dY = dCenterY + dYRadius*sin(dAngle);
149 0 : poLine->addPoint(dX, dY);
150 : }
151 :
152 : // Complete the arc with the last EndAngle, to make sure that
153 : // the arc is correcly close.
154 :
155 0 : dX = dCenterX + dXRadius*cos(dAngle);
156 0 : dY = dCenterY + dYRadius*sin(dAngle);
157 0 : poLine->addPoint(dX,dY);
158 :
159 :
160 0 : return 0;
161 : }
162 :
163 :
164 : /**********************************************************************
165 : * TABCloseRing()
166 : *
167 : * Check if a ring is closed, and add a point to close it if necessary.
168 : *
169 : * Returns 0 on success, -1 on error.
170 : **********************************************************************/
171 0 : int TABCloseRing(OGRLineString *poRing)
172 : {
173 0 : if ( poRing->getNumPoints() > 0 && !poRing->get_IsClosed() )
174 : {
175 0 : poRing->addPoint(poRing->getX(0), poRing->getY(0));
176 : }
177 :
178 0 : return 0;
179 : }
180 :
181 : /**********************************************************************
182 : * TABAdjustCaseSensitiveFilename()
183 : *
184 : * Scan a filename and its path, adjust uppercase/lowercases if
185 : * necessary.
186 : *
187 : * Returns TRUE if file found, or FALSE if it could not be located with
188 : * a case-insensitive search.
189 : *
190 : * This function works on the original buffer and returns a reference to it.
191 : * It does nothing on Windows systems where filenames are not case sensitive.
192 : **********************************************************************/
193 24 : GBool TABAdjustCaseSensitiveFilename(char *pszFname)
194 : {
195 :
196 : #ifdef _WIN32
197 : /*-----------------------------------------------------------------
198 : * Nothing to do on Windows
199 : *----------------------------------------------------------------*/
200 : return TRUE;
201 :
202 : #else
203 : /*-----------------------------------------------------------------
204 : * Unix case.
205 : *----------------------------------------------------------------*/
206 : VSIStatBuf sStatBuf;
207 24 : char *pszTmpPath = NULL;
208 : int nTotalLen, iTmpPtr;
209 : GBool bValidPath;
210 :
211 : /*-----------------------------------------------------------------
212 : * First check if the filename is OK as is.
213 : *----------------------------------------------------------------*/
214 24 : if (VSIStat(pszFname, &sStatBuf) == 0)
215 : {
216 0 : return TRUE;
217 : }
218 :
219 : /*-----------------------------------------------------------------
220 : * OK, file either does not exist or has the wrong cases... we'll
221 : * go backwards until we find a portion of the path that is valid.
222 : *----------------------------------------------------------------*/
223 24 : pszTmpPath = CPLStrdup(pszFname);
224 24 : nTotalLen = strlen(pszTmpPath);
225 24 : iTmpPtr = nTotalLen;
226 24 : bValidPath = FALSE;
227 :
228 72 : while(iTmpPtr > 0 && !bValidPath)
229 : {
230 : /*-------------------------------------------------------------
231 : * Move back to the previous '/' separator
232 : *------------------------------------------------------------*/
233 24 : pszTmpPath[--iTmpPtr] = '\0';
234 314 : while( iTmpPtr > 0 && pszTmpPath[iTmpPtr-1] != '/' )
235 : {
236 266 : pszTmpPath[--iTmpPtr] = '\0';
237 : }
238 :
239 24 : if (iTmpPtr > 0 && VSIStat(pszTmpPath, &sStatBuf) == 0)
240 20 : bValidPath = TRUE;
241 : }
242 :
243 24 : CPLAssert(iTmpPtr >= 0);
244 :
245 : /*-----------------------------------------------------------------
246 : * Assume that CWD is valid... so an empty path is a valid path
247 : *----------------------------------------------------------------*/
248 24 : if (iTmpPtr == 0)
249 4 : bValidPath = TRUE;
250 :
251 : /*-----------------------------------------------------------------
252 : * OK, now that we have a valid base, reconstruct the whole path
253 : * by scanning all the sub-directories.
254 : * If we get to a point where a path component does not exist then
255 : * we simply return the rest of the path as is.
256 : *----------------------------------------------------------------*/
257 72 : while(bValidPath && (int)strlen(pszTmpPath) < nTotalLen)
258 : {
259 24 : char **papszDir=NULL;
260 : int iEntry, iLastPartStart;
261 :
262 24 : iLastPartStart = iTmpPtr;
263 24 : papszDir = CPLReadDir(pszTmpPath);
264 :
265 : /*-------------------------------------------------------------
266 : * Add one component to the current path
267 : *------------------------------------------------------------*/
268 24 : pszTmpPath[iTmpPtr] = pszFname[iTmpPtr];
269 24 : iTmpPtr++;
270 290 : for( ; pszFname[iTmpPtr] != '\0' && pszFname[iTmpPtr]!='/'; iTmpPtr++)
271 : {
272 266 : pszTmpPath[iTmpPtr] = pszFname[iTmpPtr];
273 : }
274 :
275 48 : while(iLastPartStart < iTmpPtr && pszTmpPath[iLastPartStart] == '/')
276 0 : iLastPartStart++;
277 :
278 : /*-------------------------------------------------------------
279 : * And do a case insensitive search in the current dir...
280 : *------------------------------------------------------------*/
281 553 : for(iEntry=0; papszDir && papszDir[iEntry]; iEntry++)
282 : {
283 529 : if (EQUAL(pszTmpPath+iLastPartStart, papszDir[iEntry]))
284 : {
285 : /* Fount it! */
286 0 : strcpy(pszTmpPath+iLastPartStart, papszDir[iEntry]);
287 0 : break;
288 : }
289 : }
290 :
291 24 : if (iTmpPtr > 0 && VSIStat(pszTmpPath, &sStatBuf) != 0)
292 24 : bValidPath = FALSE;
293 :
294 24 : CSLDestroy(papszDir);
295 : }
296 :
297 : /*-----------------------------------------------------------------
298 : * We reached the last valid path component... just copy the rest
299 : * of the path as is.
300 : *----------------------------------------------------------------*/
301 24 : if (iTmpPtr < nTotalLen-1)
302 : {
303 0 : strncpy(pszTmpPath+iTmpPtr, pszFname+iTmpPtr, nTotalLen-iTmpPtr);
304 : }
305 :
306 : /*-----------------------------------------------------------------
307 : * Update the source buffer and return.
308 : *----------------------------------------------------------------*/
309 24 : strcpy(pszFname, pszTmpPath);
310 24 : CPLFree(pszTmpPath);
311 :
312 24 : return bValidPath;
313 :
314 : #endif
315 : }
316 :
317 :
318 :
319 :
320 : /**********************************************************************
321 : * TABAdjustFilenameExtension()
322 : *
323 : * Because Unix filenames are case sensitive and MapInfo datasets often have
324 : * mixed cases filenames, we use this function to find the right filename
325 : * to use ot open a specific file.
326 : *
327 : * This function works directly on the source string, so the filename it
328 : * contains at the end of the call is the one that should be used.
329 : *
330 : * Returns TRUE if one of the extensions worked, and FALSE otherwise.
331 : * If none of the extensions worked then the original extension will NOT be
332 : * restored.
333 : **********************************************************************/
334 65 : GBool TABAdjustFilenameExtension(char *pszFname)
335 : {
336 : VSIStatBuf sStatBuf;
337 : int i;
338 :
339 : /*-----------------------------------------------------------------
340 : * First try using filename as provided
341 : *----------------------------------------------------------------*/
342 65 : if (VSIStat(pszFname, &sStatBuf) == 0)
343 : {
344 41 : return TRUE;
345 : }
346 :
347 : /*-----------------------------------------------------------------
348 : * Try using uppercase extension (we assume that fname contains a '.')
349 : *----------------------------------------------------------------*/
350 94 : for(i = strlen(pszFname)-1; i >= 0 && pszFname[i] != '.'; i--)
351 : {
352 70 : pszFname[i] = toupper(pszFname[i]);
353 : }
354 :
355 24 : if (VSIStat(pszFname, &sStatBuf) == 0)
356 : {
357 0 : return TRUE;
358 : }
359 :
360 : /*-----------------------------------------------------------------
361 : * Try using lowercase extension
362 : *----------------------------------------------------------------*/
363 94 : for(i = strlen(pszFname)-1; i >= 0 && pszFname[i] != '.'; i--)
364 : {
365 70 : pszFname[i] = tolower(pszFname[i]);
366 : }
367 :
368 24 : if (VSIStat(pszFname, &sStatBuf) == 0)
369 : {
370 0 : return TRUE;
371 : }
372 :
373 : /*-----------------------------------------------------------------
374 : * None of the extensions worked!
375 : * Try adjusting cases in the whole path and filename
376 : *----------------------------------------------------------------*/
377 24 : return TABAdjustCaseSensitiveFilename(pszFname);
378 : }
379 :
380 :
381 :
382 : /**********************************************************************
383 : * TABGetBasename()
384 : *
385 : * Extract the basename part of a complete file path.
386 : *
387 : * Returns a newly allocated string without the leading path (dirs) and
388 : * the extenstion. The returned string should be freed using CPLFree().
389 : **********************************************************************/
390 22 : char *TABGetBasename(const char *pszFname)
391 : {
392 22 : const char *pszTmp = NULL;
393 :
394 : /*-----------------------------------------------------------------
395 : * Skip leading path or use whole name if no path dividers are
396 : * encountered.
397 : *----------------------------------------------------------------*/
398 22 : pszTmp = pszFname + strlen(pszFname) - 1;
399 313 : while ( pszTmp != pszFname
400 : && *pszTmp != '/' && *pszTmp != '\\' )
401 269 : pszTmp--;
402 :
403 22 : if( pszTmp != pszFname )
404 20 : pszTmp++;
405 :
406 : /*-----------------------------------------------------------------
407 : * Now allocate our own copy and remove extension
408 : *----------------------------------------------------------------*/
409 22 : char *pszBasename = CPLStrdup(pszTmp);
410 : int i;
411 205 : for(i=0; pszBasename[i] != '\0'; i++)
412 : {
413 205 : if (pszBasename[i] == '.')
414 : {
415 22 : pszBasename[i] = '\0';
416 22 : break;
417 : }
418 : }
419 :
420 22 : return pszBasename;
421 : }
422 :
423 :
424 :
425 : /**********************************************************************
426 : * TAB_CSLLoad()
427 : *
428 : * Same as CSLLoad(), but does not produce an error if it fails... it
429 : * just returns NULL silently instead.
430 : *
431 : * Load a test file into a stringlist.
432 : *
433 : * Lines are limited in length by the size fo the CPLReadLine() buffer.
434 : **********************************************************************/
435 2 : char **TAB_CSLLoad(const char *pszFname)
436 : {
437 : FILE *fp;
438 : const char *pszLine;
439 2 : char **papszStrList=NULL;
440 :
441 2 : fp = VSIFOpen(pszFname, "rt");
442 :
443 2 : if (fp)
444 : {
445 24 : while(!VSIFEof(fp))
446 : {
447 20 : if ( (pszLine = CPLReadLine(fp)) != NULL )
448 : {
449 18 : papszStrList = CSLAddString(papszStrList, pszLine);
450 : }
451 : }
452 :
453 2 : VSIFClose(fp);
454 : }
455 :
456 2 : return papszStrList;
457 : }
458 :
459 :
460 :
461 : /**********************************************************************
462 : * TABUnEscapeString()
463 : *
464 : * Convert a string that can possibly contain escaped "\n" chars in
465 : * into into a new one with binary newlines in it.
466 : *
467 : * Tries to work on hte original buffer unless bSrcIsConst=TRUE, in
468 : * which case the original is always untouched and a copy is allocated
469 : * ONLY IF NECESSARY. This means that the caller should compare the
470 : * return value and the source (pszString) to see if a copy was returned,
471 : * in which case the caller becomes responsible of freeing both the
472 : * source and the copy.
473 : **********************************************************************/
474 0 : char *TABUnEscapeString(char *pszString, GBool bSrcIsConst)
475 : {
476 :
477 : /*-----------------------------------------------------------------
478 : * First check if we need to do any replacement
479 : *----------------------------------------------------------------*/
480 0 : if (pszString == NULL || strstr(pszString, "\\n") == NULL)
481 : {
482 0 : return pszString;
483 : }
484 :
485 : /*-----------------------------------------------------------------
486 : * Yes, we need to replace at least one "\n"
487 : * We try to work on the original buffer unless we have bSrcIsConst=TRUE
488 : *
489 : * Note that we do not worry about freeing the source buffer when we
490 : * return a copy... it is up to the caller to decide if he needs to
491 : * free the source based on context and by comparing pszString with
492 : * the returned pointer (pszWorkString) to see if they are identical.
493 : *----------------------------------------------------------------*/
494 0 : char *pszWorkString = NULL;
495 0 : int i =0;
496 0 : int j =0;
497 :
498 0 : if (bSrcIsConst)
499 : {
500 : // We have to create a copy to work on.
501 : pszWorkString = (char *)CPLMalloc(sizeof(char) *
502 0 : (strlen(pszString) +1));
503 : }
504 : else
505 : {
506 : // We'll work on the original.
507 0 : pszWorkString = pszString;
508 : }
509 :
510 :
511 0 : while (pszString[i])
512 : {
513 0 : if (pszString[i] =='\\' &&
514 : pszString[i+1] == 'n')
515 : {
516 0 : pszWorkString[j++] = '\n';
517 0 : i+= 2;
518 : }
519 0 : else if (pszString[i] =='\\' &&
520 : pszString[i+1] == '\\')
521 : {
522 0 : pszWorkString[j++] = '\\';
523 0 : i+= 2;
524 : }
525 : else
526 : {
527 0 : pszWorkString[j++] = pszString[i++];
528 : }
529 : }
530 0 : pszWorkString[j++] = '\0';
531 :
532 0 : return pszWorkString;
533 : }
534 :
535 : /**********************************************************************
536 : * TABEscapeString()
537 : *
538 : * Convert a string that can possibly contain binary "\n" chars in
539 : * into into a new one with escaped newlines ("\\" + "n") in it.
540 : *
541 : * The function returns the original string pointer if it did not need to
542 : * be modified, or a copy that has to be freed by the caller if the
543 : * string had to be modified.
544 : *
545 : * It is up to the caller to decide if he needs to free the returned
546 : * string by comparing the source (pszString) pointer with the returned
547 : * pointer (pszWorkString) to see if they are identical.
548 : **********************************************************************/
549 0 : char *TABEscapeString(char *pszString)
550 : {
551 : /*-----------------------------------------------------------------
552 : * First check if we need to do any replacement
553 : *----------------------------------------------------------------*/
554 0 : if (pszString == NULL || strchr(pszString, '\n') == NULL)
555 : {
556 0 : return pszString;
557 : }
558 :
559 : /*-----------------------------------------------------------------
560 : * OK, we need to do some replacements... alloc a copy big enough
561 : * to hold the worst possible case
562 : *----------------------------------------------------------------*/
563 : char *pszWorkString = (char *)CPLMalloc(2*sizeof(char) *
564 0 : (strlen(pszString) +1));
565 :
566 0 : int i =0;
567 0 : int j =0;
568 :
569 0 : while (pszString[i])
570 : {
571 0 : if (pszString[i] =='\n')
572 : {
573 0 : pszWorkString[j++] = '\\';
574 0 : pszWorkString[j++] = 'n';
575 0 : i++;
576 : }
577 0 : else if (pszString[i] =='\\')
578 : {
579 0 : pszWorkString[j++] = '\\';
580 0 : pszWorkString[j++] = '\\';
581 0 : i++;
582 : }
583 : else
584 : {
585 0 : pszWorkString[j++] = pszString[i++];
586 : }
587 : }
588 0 : pszWorkString[j++] = '\0';
589 :
590 0 : return pszWorkString;
591 : }
592 :
593 : /**********************************************************************
594 : * TABCleanFieldName()
595 : *
596 : * Return a copy of pszSrcName that contains only valid characters for a
597 : * TAB field name. All invalid characters are replaced by '_'.
598 : *
599 : * The returned string should be freed by the caller.
600 : **********************************************************************/
601 64 : char *TABCleanFieldName(const char *pszSrcName)
602 : {
603 : char *pszNewName;
604 64 : int numInvalidChars = 0;
605 :
606 64 : pszNewName = CPLStrdup(pszSrcName);
607 :
608 64 : if (strlen(pszNewName) > 31)
609 : {
610 0 : pszNewName[31] = '\0';
611 : CPLError(CE_Warning, TAB_WarningInvalidFieldName,
612 : "Field name '%s' is longer than the max of 31 characters. "
613 0 : "'%s' will be used instead.", pszSrcName, pszNewName);
614 : }
615 :
616 : #if defined(_WIN32) && !defined(unix) && !defined(WIN32CE)
617 : /*-----------------------------------------------------------------
618 : * On Windows, check if we're using a double-byte codepage, and
619 : * if so then just keep the field name as is...
620 : *----------------------------------------------------------------*/
621 : if (_getmbcp() != 0)
622 : return pszNewName;
623 : #endif
624 :
625 : /*-----------------------------------------------------------------
626 : * According to the MapInfo User's Guide (p. 240, v5.5)
627 : * New Table Command:
628 : * Name:
629 : * Displays the field name in the name box. You can also enter new field
630 : * names here. Defaults are Field1, Field2, etc. A field name can contain
631 : * up to 31 alphanumeric characters. Use letters, numbers, and the
632 : * underscore. Do not use spaces; instead, use the underscore character
633 : * (_) to separate words in a field name. Use upper and lower case for
634 : * legibility, but MapInfo is not case-sensitive.
635 : *
636 : * It was also verified that extended chars with accents are also
637 : * accepted.
638 : *----------------------------------------------------------------*/
639 448 : for(int i=0; pszSrcName && pszSrcName[i] != '\0'; i++)
640 : {
641 384 : if ( !( pszSrcName[i] == '_' ||
642 : (pszSrcName[i]>='0' && pszSrcName[i]<='9') ||
643 : (pszSrcName[i]>='a' && pszSrcName[i]<='z') ||
644 : (pszSrcName[i]>='A' && pszSrcName[i]<='Z') ||
645 : (GByte)pszSrcName[i]>=192 ) )
646 : {
647 0 : pszNewName[i] = '_';
648 0 : numInvalidChars++;
649 : }
650 : }
651 :
652 64 : if (numInvalidChars > 0)
653 : {
654 : CPLError(CE_Warning, TAB_WarningInvalidFieldName,
655 : "Field name '%s' contains invalid characters. "
656 0 : "'%s' will be used instead.", pszSrcName, pszNewName);
657 : }
658 :
659 64 : return pszNewName;
660 : }
661 :
662 :
663 : /**********************************************************************
664 : * MapInfo Units string to numeric ID conversion
665 : **********************************************************************/
666 : typedef struct
667 : {
668 : int nUnitId;
669 : const char *pszAbbrev;
670 : } MapInfoUnitsInfo;
671 :
672 : static MapInfoUnitsInfo gasUnitsList[] =
673 : {
674 : {0, "mi"},
675 : {1, "km"},
676 : {2, "in"},
677 : {3, "ft"},
678 : {4, "yd"},
679 : {5, "mm"},
680 : {6, "cm"},
681 : {7, "m"},
682 : {8, "survey ft"},
683 : {9, "nmi"},
684 : {30, "li"},
685 : {31, "ch"},
686 : {32, "rd"},
687 : {-1, NULL}
688 : };
689 :
690 :
691 : /**********************************************************************
692 : * TABUnitIdToString()
693 : *
694 : * Return the MIF units name for specified units id.
695 : * Return "" if no match found.
696 : *
697 : * The returned string should not be freed by the caller.
698 : **********************************************************************/
699 0 : const char *TABUnitIdToString(int nId)
700 : {
701 : MapInfoUnitsInfo *psList;
702 :
703 0 : psList = gasUnitsList;
704 :
705 0 : while(psList->nUnitId != -1)
706 : {
707 0 : if (psList->nUnitId == nId)
708 0 : return psList->pszAbbrev;
709 0 : psList++;
710 : }
711 :
712 0 : return "";
713 : }
714 :
715 : /**********************************************************************
716 : * TABUnitIdFromString()
717 : *
718 : * Return the units ID for specified MIF units name
719 : *
720 : * Returns -1 if no match found.
721 : **********************************************************************/
722 0 : int TABUnitIdFromString(const char *pszName)
723 : {
724 : MapInfoUnitsInfo *psList;
725 :
726 0 : psList = gasUnitsList;
727 :
728 0 : while(psList->nUnitId != -1)
729 : {
730 0 : if (EQUAL(psList->pszAbbrev, pszName))
731 0 : return psList->nUnitId;
732 0 : psList++;
733 : }
734 :
735 0 : return -1;
736 : }
737 :
|