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