Line data Source code
1 : /**********************************************************************
2 : *
3 : * Name: mitab_tabfile.cpp
4 : * Project: MapInfo TAB Read/Write library
5 : * Language: C++
6 : * Purpose: Implementation of the TABFile class, the main class of the lib.
7 : * To be used by external programs to handle reading/writing of
8 : * features from/to TAB datasets.
9 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
10 : *
11 : **********************************************************************
12 : * Copyright (c) 1999-2003, Daniel Morissette
13 : * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
14 : *
15 : * SPDX-License-Identifier: MIT
16 : **********************************************************************/
17 :
18 : #include "cpl_port.h"
19 : #include "mitab.h"
20 :
21 : #include <cctype>
22 : #include <climits>
23 : #include <cstdio>
24 : #include <cstdlib>
25 : #include <cstring>
26 : #include <algorithm>
27 : #include <memory>
28 :
29 : #include "cpl_conv.h"
30 : #include "cpl_error.h"
31 : #include "cpl_minixml.h"
32 : #include "cpl_string.h"
33 : #include "cpl_vsi.h"
34 : #include "mitab_priv.h"
35 : #include "mitab_utils.h"
36 : #include "ogr_core.h"
37 : #include "ogr_feature.h"
38 : #include "ogr_geometry.h"
39 : #include "ogr_p.h"
40 : #include "ogr_spatialref.h"
41 : #include "ogrsf_frmts.h"
42 :
43 : static const char UNSUPPORTED_OP_READ_ONLY[] =
44 : "%s : unsupported operation on a read-only datasource.";
45 :
46 : constexpr const char *DESCRIPTION_KEY = "DESCRIPTION";
47 : // Tab file allow to store description longer than 255 characters.
48 : // But only 255 will shown in MapInfo layer list.
49 : constexpr int MAX_DESCRIPTION_LEN = 254 * 2;
50 :
51 131 : static char *EscapeString(const char *pszInput,
52 : bool bEscapeDoubleQuotes = false)
53 : {
54 131 : if (nullptr == pszInput)
55 : {
56 125 : return nullptr;
57 : }
58 :
59 6 : auto nLength = CPLStrnlen(pszInput, MAX_DESCRIPTION_LEN);
60 6 : char *pszOutput = static_cast<char *>(CPLMalloc(nLength * 2 + 1));
61 6 : int iOut = 0;
62 6 : int nDoubleQuotesCount = 0;
63 1394 : for (int iIn = 0; iIn < static_cast<int>(nLength + 1); ++iIn)
64 : {
65 1390 : if (pszInput[iIn] == '"')
66 : {
67 12 : if (bEscapeDoubleQuotes)
68 : {
69 6 : pszOutput[iOut++] = '"';
70 6 : pszOutput[iOut++] = '"';
71 : }
72 : else
73 : {
74 6 : nDoubleQuotesCount++;
75 6 : pszOutput[iOut++] = pszInput[iIn];
76 : }
77 : }
78 1378 : else if (pszInput[iIn] == '\n' || pszInput[iIn] == '\r')
79 : {
80 6 : pszOutput[iOut++] = ' ';
81 : }
82 : else
83 : {
84 1372 : if ((pszInput[iIn] & 0xc0) != 0x80)
85 : {
86 : // Stop at the start of the character just beyond the maximum
87 : // accepted
88 844 : if (iOut >= MAX_DESCRIPTION_LEN - nDoubleQuotesCount)
89 : {
90 2 : break;
91 : }
92 : }
93 1370 : pszOutput[iOut++] = pszInput[iIn];
94 : }
95 : }
96 :
97 6 : pszOutput[iOut] = '\0';
98 6 : return pszOutput;
99 : }
100 :
101 4 : static char *UnescapeString(const char *pszInput)
102 : {
103 4 : if (nullptr == pszInput)
104 : {
105 0 : return nullptr;
106 : }
107 :
108 4 : auto nLength = CPLStrnlen(pszInput, MAX_DESCRIPTION_LEN);
109 4 : char *pszOutput = static_cast<char *>(CPLMalloc(nLength * 2 + 1));
110 4 : int iOut = 0;
111 786 : for (int iIn = 0; iIn < static_cast<int>(nLength + 1); ++iIn)
112 : {
113 782 : if (pszInput[iIn] == '"' && pszInput[iIn + 1] == '"')
114 : {
115 6 : ++iIn;
116 6 : pszOutput[iOut++] = pszInput[iIn];
117 : }
118 : else
119 : {
120 776 : if ((pszInput[iIn] & 0xc0) != 0x80)
121 : {
122 : // Stop at the start of the character just beyond the maximum
123 : // accepted
124 482 : if (iOut >= MAX_DESCRIPTION_LEN)
125 : {
126 0 : break;
127 : }
128 : }
129 776 : pszOutput[iOut++] = pszInput[iIn];
130 : }
131 : }
132 4 : pszOutput[iOut] = '\0';
133 4 : return pszOutput;
134 : }
135 :
136 4 : static std::string GetTabDescription(const char *pszLine)
137 : {
138 8 : CPLString osDescriptionLine(pszLine);
139 4 : auto nStart = osDescriptionLine.find_first_of('"') + 1;
140 4 : if (nStart != std::string::npos)
141 : {
142 4 : auto nEnd = osDescriptionLine.find_last_of('"');
143 4 : auto nLen = nEnd == std::string::npos ? nEnd : nEnd - nStart;
144 4 : return osDescriptionLine.substr(nStart, nLen);
145 : }
146 0 : return "";
147 : }
148 :
149 : /*=====================================================================
150 : * class TABFile
151 : *====================================================================*/
152 :
153 : /**********************************************************************
154 : * TABFile::TABFile()
155 : *
156 : * Constructor.
157 : **********************************************************************/
158 1443 : TABFile::TABFile(GDALDataset *poDS)
159 : : IMapInfoFile(poDS), m_pszFname(nullptr), m_eAccessMode(TABRead),
160 : m_papszTABFile(nullptr), m_nVersion(300), m_panIndexNo(nullptr),
161 : m_eTableType(TABTableNative), m_poDATFile(nullptr), m_poMAPFile(nullptr),
162 : m_poINDFile(nullptr), m_poDefn(nullptr), m_poSpatialRef(nullptr),
163 : bUseSpatialTraversal(FALSE), m_nLastFeatureId(0),
164 : m_panMatchingFIDs(nullptr), m_iMatchingFID(0), m_bNeedTABRewrite(FALSE),
165 1443 : m_bLastOpWasRead(FALSE), m_bLastOpWasWrite(FALSE)
166 : {
167 1443 : m_poCurFeature = nullptr;
168 1443 : m_nCurFeatureId = 0;
169 1443 : }
170 :
171 : /**********************************************************************
172 : * TABFile::~TABFile()
173 : *
174 : * Destructor.
175 : **********************************************************************/
176 2886 : TABFile::~TABFile()
177 : {
178 1443 : TABFile::Close();
179 2886 : }
180 :
181 : /************************************************************************/
182 : /* GetFeatureCount() */
183 : /************************************************************************/
184 :
185 10278 : GIntBig TABFile::GetFeatureCount(int bForce)
186 : {
187 :
188 10278 : if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr || bForce)
189 10278 : return OGRLayer::GetFeatureCount(bForce);
190 : else
191 0 : return m_nLastFeatureId;
192 : }
193 :
194 : /************************************************************************/
195 : /* ResetReading() */
196 : /************************************************************************/
197 33830 : void TABFile::ResetReading()
198 : {
199 33830 : CPLFree(m_panMatchingFIDs);
200 33830 : m_panMatchingFIDs = nullptr;
201 33830 : m_iMatchingFID = 0;
202 :
203 33830 : m_nCurFeatureId = 0;
204 33830 : if (m_poMAPFile != nullptr)
205 33830 : m_poMAPFile->ResetReading();
206 :
207 : /* -------------------------------------------------------------------- */
208 : /* Decide whether to operate in spatial traversal mode or not, */
209 : /* and ensure the current spatial filter is applied to the map */
210 : /* file object. */
211 : /* -------------------------------------------------------------------- */
212 33830 : if (m_poMAPFile)
213 : {
214 33830 : bUseSpatialTraversal = FALSE;
215 :
216 33830 : m_poMAPFile->ResetCoordFilter();
217 :
218 33830 : if (m_poFilterGeom != nullptr)
219 : {
220 : // TABMAPHeaderBlock *poHeader = m_poMAPFile->GetHeaderBlock();
221 :
222 31399 : OGREnvelope sEnvelope;
223 31399 : m_poFilterGeom->getEnvelope(&sEnvelope);
224 :
225 31399 : TABVertex sMin;
226 31399 : TABVertex sMax;
227 31399 : m_poMAPFile->GetCoordFilter(sMin, sMax);
228 :
229 31399 : if (sEnvelope.MinX > sMin.x || sEnvelope.MinY > sMin.y ||
230 279 : sEnvelope.MaxX < sMax.x || sEnvelope.MaxY < sMax.y)
231 : {
232 31364 : bUseSpatialTraversal = TRUE;
233 31364 : sMin.x = sEnvelope.MinX;
234 31364 : sMin.y = sEnvelope.MinY;
235 31364 : sMax.x = sEnvelope.MaxX;
236 31364 : sMax.y = sEnvelope.MaxY;
237 31364 : m_poMAPFile->SetCoordFilter(sMin, sMax);
238 : }
239 : }
240 : }
241 :
242 33830 : m_bLastOpWasRead = FALSE;
243 33830 : m_bLastOpWasWrite = FALSE;
244 33830 : }
245 :
246 : /**********************************************************************
247 : * TABFile::Open()
248 : *
249 : * Open a .TAB dataset and the associated files, and initialize the
250 : * structures to be ready to read features from (or write to) it.
251 : *
252 : * Supported access modes are "r" (read-only) and "w" (create new dataset or
253 : * update).
254 : *
255 : * Set bTestOpenNoError=TRUE to silently return -1 with no error message
256 : * if the file cannot be opened. This is intended to be used in the
257 : * context of a TestOpen() function. The default value is FALSE which
258 : * means that an error is reported if the file cannot be opened.
259 : *
260 : * Note that dataset extents will have to be set using SetBounds() before
261 : * any feature can be written to a newly created dataset.
262 : *
263 : * In read mode, a valid dataset must have at least a .TAB and a .DAT file.
264 : * The .MAP and .ID files are optional and if they do not exist then
265 : * all features will be returned with NONE geometry.
266 : *
267 : * Returns 0 on success, -1 on error.
268 : **********************************************************************/
269 1443 : int TABFile::Open(const char *pszFname, TABAccess eAccess,
270 : GBool bTestOpenNoError /*=FALSE*/, int nBlockSizeForCreate,
271 : const char *pszCharset /* = NULL */)
272 : {
273 1443 : char *pszTmpFname = nullptr;
274 1443 : int nFnameLen = 0;
275 :
276 1443 : CPLErrorReset();
277 :
278 1443 : if (m_poMAPFile)
279 : {
280 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
281 : "Open() failed: object already contains an open file");
282 :
283 0 : return -1;
284 : }
285 :
286 1443 : m_eAccessMode = eAccess;
287 :
288 : /*-----------------------------------------------------------------
289 : * Make sure filename has a .TAB extension...
290 : *----------------------------------------------------------------*/
291 1443 : m_pszFname = CPLStrdup(pszFname);
292 1443 : nFnameLen = static_cast<int>(strlen(m_pszFname));
293 :
294 1443 : if (nFnameLen > 4 && (strcmp(m_pszFname + nFnameLen - 4, ".TAB") == 0 ||
295 1440 : strcmp(m_pszFname + nFnameLen - 4, ".MAP") == 0 ||
296 1440 : strcmp(m_pszFname + nFnameLen - 4, ".DAT") == 0))
297 3 : strcpy(m_pszFname + nFnameLen - 4, ".TAB");
298 1440 : else if (nFnameLen > 4 && (EQUAL(m_pszFname + nFnameLen - 4, ".tab") ||
299 0 : EQUAL(m_pszFname + nFnameLen - 4, ".map") ||
300 0 : EQUAL(m_pszFname + nFnameLen - 4, ".dat")))
301 1440 : strcpy(m_pszFname + nFnameLen - 4, ".tab");
302 : else
303 : {
304 0 : if (!bTestOpenNoError)
305 0 : CPLError(CE_Failure, CPLE_FileIO,
306 : "Open() failed for %s: invalid filename extension",
307 : m_pszFname);
308 : else
309 0 : CPLErrorReset();
310 :
311 0 : CPLFree(m_pszFname);
312 0 : m_pszFname = nullptr;
313 0 : return -1;
314 : }
315 :
316 1443 : pszTmpFname = CPLStrdup(m_pszFname);
317 :
318 : #ifndef _WIN32
319 : /*-----------------------------------------------------------------
320 : * On Unix, make sure extension uses the right cases
321 : * We do it even for write access because if a file with the same
322 : * extension already exists we want to overwrite it.
323 : *----------------------------------------------------------------*/
324 1443 : TABAdjustFilenameExtension(m_pszFname);
325 : #endif
326 :
327 : /*-----------------------------------------------------------------
328 : * Handle .TAB file... depends on access mode.
329 : *----------------------------------------------------------------*/
330 1443 : if (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite)
331 : {
332 : /*-------------------------------------------------------------
333 : * Open .TAB file... since it is a small text file, we will just load
334 : * it as a stringlist in memory.
335 : *------------------------------------------------------------*/
336 1316 : m_papszTABFile = TAB_CSLLoad(m_pszFname);
337 1316 : if (m_papszTABFile == nullptr)
338 : {
339 0 : if (!bTestOpenNoError)
340 : {
341 0 : CPLError(CE_Failure, CPLE_FileIO, "Failed opening %s.",
342 : m_pszFname);
343 : }
344 0 : CPLFree(m_pszFname);
345 0 : m_pszFname = nullptr;
346 0 : CSLDestroy(m_papszTABFile);
347 0 : m_papszTABFile = nullptr;
348 0 : CPLFree(pszTmpFname);
349 0 : return -1;
350 : }
351 :
352 : /*-------------------------------------------------------------
353 : * Do a first pass on the TAB header to establish the type of
354 : * dataset we have (NATIVE, DBF, etc.)... and also to know if
355 : * it is a supported type.
356 : *------------------------------------------------------------*/
357 1316 : if (ParseTABFileFirstPass(bTestOpenNoError) != 0)
358 : {
359 : // No need to produce an error... it is already been done if
360 : // necessary... just cleanup and exit.
361 :
362 0 : CPLFree(m_pszFname);
363 0 : m_pszFname = nullptr;
364 0 : CSLDestroy(m_papszTABFile);
365 0 : m_papszTABFile = nullptr;
366 0 : CPLFree(pszTmpFname);
367 :
368 0 : return -1;
369 : }
370 : }
371 : else
372 : {
373 : /*-------------------------------------------------------------
374 : * In Write access mode, the .TAB file will be written during the
375 : * Close() call... we will just set some defaults here.
376 : *------------------------------------------------------------*/
377 127 : m_nVersion = 300;
378 127 : if (pszCharset != nullptr)
379 127 : SetCharset(pszCharset);
380 : else
381 0 : SetCharset("Neutral");
382 127 : m_eTableType = TABTableNative;
383 :
384 : /*-------------------------------------------------------------
385 : * Do initial setup of feature definition.
386 : *------------------------------------------------------------*/
387 127 : char *pszFeatureClassName = TABGetBasename(m_pszFname);
388 127 : m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
389 127 : m_poDefn->Reference();
390 127 : CPLFree(pszFeatureClassName);
391 :
392 127 : m_bNeedTABRewrite = TRUE;
393 : }
394 :
395 : /*-----------------------------------------------------------------
396 : * Open .DAT file (or .DBF)
397 : *----------------------------------------------------------------*/
398 1443 : if (nFnameLen > 4 && strcmp(pszTmpFname + nFnameLen - 4, ".TAB") == 0)
399 : {
400 3 : if (m_eTableType == TABTableDBF)
401 0 : strcpy(pszTmpFname + nFnameLen - 4, ".DBF");
402 : else // Default is NATIVE
403 3 : strcpy(pszTmpFname + nFnameLen - 4, ".DAT");
404 : }
405 : else
406 : {
407 1440 : if (m_eTableType == TABTableDBF)
408 1 : strcpy(pszTmpFname + nFnameLen - 4, ".dbf");
409 : else // Default is NATIVE
410 1439 : strcpy(pszTmpFname + nFnameLen - 4, ".dat");
411 : }
412 :
413 : #ifndef _WIN32
414 1443 : TABAdjustFilenameExtension(pszTmpFname);
415 : #endif
416 :
417 2886 : CPLString oEncoding;
418 :
419 1443 : if (eAccess == TABRead || eAccess == TABReadWrite)
420 : {
421 1316 : oEncoding = CharsetToEncoding(GetCharset());
422 : }
423 127 : else if (eAccess == TABWrite)
424 : {
425 127 : oEncoding = CharsetToEncoding(pszCharset);
426 : }
427 :
428 1443 : m_poDATFile = new TABDATFile(oEncoding);
429 :
430 1443 : if (m_poDATFile->Open(pszTmpFname, eAccess, m_eTableType) != 0)
431 : {
432 : // Open Failed... an error has already been reported, just return.
433 0 : CPLFree(pszTmpFname);
434 0 : Close();
435 0 : if (bTestOpenNoError)
436 0 : CPLErrorReset();
437 :
438 0 : return -1;
439 : }
440 :
441 1443 : m_nLastFeatureId = m_poDATFile->GetNumRecords();
442 :
443 : /*-----------------------------------------------------------------
444 : * Parse .TAB file field defs and build FeatureDefn (only in read access)
445 : *----------------------------------------------------------------*/
446 2759 : if ((m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite) &&
447 1316 : ParseTABFileFields() != 0)
448 : {
449 : // Failed... an error has already been reported, just return.
450 16 : CPLFree(pszTmpFname);
451 16 : Close();
452 16 : if (bTestOpenNoError)
453 16 : CPLErrorReset();
454 :
455 16 : return -1;
456 : }
457 :
458 : /*-----------------------------------------------------------------
459 : * Open .MAP (and .ID) file
460 : * Note that the .MAP and .ID files are optional. Failure to open them
461 : * is not an error... it simply means that all features will be returned
462 : * with NONE geometry.
463 : *----------------------------------------------------------------*/
464 1427 : bool bUpperCase = false;
465 1427 : if (nFnameLen > 4 && strcmp(pszTmpFname + nFnameLen - 4, ".DAT") == 0)
466 : {
467 3 : bUpperCase = true;
468 3 : strcpy(pszTmpFname + nFnameLen - 4, ".MAP");
469 : }
470 : else
471 1424 : strcpy(pszTmpFname + nFnameLen - 4, ".map");
472 :
473 : #ifndef _WIN32
474 1427 : TABAdjustFilenameExtension(pszTmpFname);
475 : #endif
476 :
477 1427 : m_poMAPFile = new TABMAPFile(oEncoding);
478 1427 : if (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite)
479 : {
480 : /*-------------------------------------------------------------
481 : * Read access: .MAP/.ID are optional... try to open but return
482 : * no error if files do not exist.
483 : *------------------------------------------------------------*/
484 1300 : if (m_poMAPFile->Open(pszTmpFname, eAccess, TRUE) < 0)
485 : {
486 : // File exists, but Open Failed...
487 : // we have to produce an error message
488 0 : if (!bTestOpenNoError)
489 0 : CPLError(CE_Failure, CPLE_FileIO, "Open() failed for %s",
490 : pszTmpFname);
491 : else
492 0 : CPLErrorReset();
493 :
494 0 : CPLFree(pszTmpFname);
495 0 : Close();
496 0 : return -1;
497 : }
498 :
499 : /*-------------------------------------------------------------
500 : * Set geometry type if the geometry objects are uniform.
501 : *------------------------------------------------------------*/
502 1300 : int numPoints = 0, numRegions = 0, numTexts = 0, numLines = 0;
503 :
504 1300 : GetFeatureCountByType(numPoints, numLines, numRegions, numTexts);
505 :
506 1300 : if (numPoints >= 0 && numTexts >= 0 && numPoints < INT_MAX - numTexts)
507 1300 : numPoints += numTexts;
508 1300 : if (numPoints > 0 && numLines == 0 && numRegions == 0)
509 1196 : m_poDefn->SetGeomType(wkbPoint);
510 104 : else if (numPoints == 0 && numLines > 0 && numRegions == 0)
511 8 : m_poDefn->SetGeomType(wkbLineString);
512 96 : else if (m_eAccessMode == TABRead && numPoints == 0 && numLines == 0 &&
513 40 : numRegions == 0)
514 : /* No geometries present; this is an aspatial dataset */
515 22 : m_poDefn->SetGeomType(wkbNone);
516 : else
517 : {
518 : /* we leave it unknown indicating a mixture */
519 1300 : }
520 : }
521 127 : else if (m_poMAPFile->Open(pszTmpFname, eAccess, FALSE,
522 127 : nBlockSizeForCreate) != 0)
523 : {
524 : // Open Failed for write...
525 : // an error has already been reported, just return.
526 :
527 1 : m_poMAPFile->Close();
528 1 : delete m_poMAPFile;
529 1 : m_poMAPFile = nullptr;
530 :
531 1 : CPLFree(pszTmpFname);
532 1 : Close();
533 1 : if (bTestOpenNoError)
534 0 : CPLErrorReset();
535 :
536 1 : return -1;
537 : }
538 :
539 : /*-----------------------------------------------------------------
540 : * Set BOUNDS metadata
541 : *----------------------------------------------------------------*/
542 : {
543 1426 : OGREnvelope e;
544 1426 : GetBounds(e.MinX, e.MinY, e.MaxX, e.MaxY, 1);
545 1426 : const char *pszBounds = CPLSPrintf("%.17g,%.17g,%.17g,%.17g", e.MinX,
546 : e.MinY, e.MaxX, e.MaxY);
547 1426 : SetMetadataItem("BOUNDS", pszBounds);
548 : }
549 :
550 : /*-----------------------------------------------------------------
551 : * Initializing the attribute index (.IND) support
552 : *----------------------------------------------------------------*/
553 1426 : bool bHasIndex = false;
554 :
555 : CPLXMLNode *psRoot =
556 1426 : CPLCreateXMLNode(nullptr, CXT_Element, "OGRMILayerAttrIndex");
557 1426 : const OGRFeatureDefn *poLayerDefn = GetLayerDefn();
558 2958 : for (int iField = 0; iField < poLayerDefn->GetFieldCount(); iField++)
559 : {
560 1533 : int iIndexIndex = GetFieldIndexNumber(iField);
561 1533 : if (iIndexIndex > 0)
562 : {
563 8 : if (!bHasIndex)
564 : {
565 : const std::string osIndFilename =
566 14 : CPLFormCIFilenameSafe(CPLGetPathSafe(pszFname).c_str(),
567 7 : CPLGetBasenameSafe(pszFname).c_str(),
568 21 : (bUpperCase) ? "IND" : "ind");
569 : VSIStatBufL sStat;
570 7 : if (VSIStatL(osIndFilename.c_str(), &sStat) == 0)
571 : {
572 6 : CPLCreateXMLElementAndValue(psRoot, "MIIDFilename",
573 : osIndFilename.c_str());
574 : }
575 : else
576 : {
577 1 : CPLDebug("MITAB",
578 : "At least one field is supposed to be indexed, "
579 : "but index file is missing");
580 1 : break;
581 : }
582 : }
583 :
584 : CPLXMLNode *psIndex =
585 7 : CPLCreateXMLNode(psRoot, CXT_Element, "OGRMIAttrIndex");
586 7 : CPLCreateXMLElementAndValue(psIndex, "FieldIndex",
587 : CPLSPrintf("%d", iField));
588 7 : CPLCreateXMLElementAndValue(
589 : psIndex, "FieldName",
590 7 : poLayerDefn->GetFieldDefn(iField)->GetNameRef());
591 7 : CPLCreateXMLElementAndValue(psIndex, "IndexIndex",
592 : CPLSPrintf("%d", iIndexIndex));
593 7 : bHasIndex = true;
594 : }
595 : }
596 :
597 1426 : if (bHasIndex)
598 : {
599 6 : char *pszRawXML = CPLSerializeXMLTree(psRoot);
600 6 : InitializeIndexSupport(pszRawXML);
601 6 : CPLFree(pszRawXML);
602 : }
603 :
604 1426 : CPLDestroyXMLNode(psRoot);
605 :
606 1426 : CPLFree(pszTmpFname);
607 1426 : pszTmpFname = nullptr;
608 :
609 2726 : if (m_poDefn != nullptr && m_eAccessMode != TABWrite &&
610 1300 : m_poDefn->GetGeomFieldCount() != 0)
611 1278 : m_poDefn->GetGeomFieldDefn(0)->SetSpatialRef(GetSpatialRef());
612 :
613 1426 : if (m_poDefn)
614 1426 : m_poDefn->Seal(/* bSealFields = */ true);
615 :
616 1426 : return 0;
617 : }
618 :
619 : /**********************************************************************
620 : * TABFile::ParseTABFileFirstPass()
621 : *
622 : * Do a first pass in the TAB header file to establish the table type, etc.
623 : * and store any useful information into class members.
624 : *
625 : * This private method should be used only during the Open() call.
626 : *
627 : * Returns 0 on success, -1 on error.
628 : **********************************************************************/
629 1316 : int TABFile::ParseTABFileFirstPass(GBool bTestOpenNoError)
630 : {
631 1316 : int iLine, numLines, numFields = 0;
632 1316 : char **papszTok = nullptr;
633 1316 : GBool bInsideTableDef = FALSE, bFoundTableFields = FALSE;
634 :
635 1316 : if (m_eAccessMode == TABWrite)
636 : {
637 0 : CPLError(CE_Failure, CPLE_NotSupported,
638 : "ParseTABFile() can be used only with Read access.");
639 0 : return -1;
640 : }
641 :
642 1316 : numLines = CSLCount(m_papszTABFile);
643 :
644 12164 : for (iLine = 0; iLine < numLines; iLine++)
645 : {
646 : /*-------------------------------------------------------------
647 : * Tokenize the next .TAB line, and check first keyword
648 : *------------------------------------------------------------*/
649 10848 : CSLDestroy(papszTok);
650 10848 : papszTok = CSLTokenizeStringComplex(m_papszTABFile[iLine], " \t(),;",
651 : TRUE, FALSE);
652 10848 : if (CSLCount(papszTok) < 2)
653 2664 : continue; // All interesting lines have at least 2 tokens
654 :
655 8184 : if (EQUAL(papszTok[0], "!version"))
656 : {
657 1316 : m_nVersion = atoi(papszTok[1]);
658 1316 : if (m_nVersion == 100)
659 : {
660 : /* Version 100 files contain only the fields definition,
661 : * so we set default values for the other params.
662 : */
663 0 : bInsideTableDef = TRUE;
664 0 : SetCharset("Neutral");
665 0 : m_eTableType = TABTableNative;
666 : }
667 : }
668 6868 : else if (EQUAL(papszTok[0], "!edit_version"))
669 : {
670 : /* Sometimes, V450 files have version 300 + edit_version 450
671 : * for us version and edit_version are the same
672 : */
673 0 : m_nVersion = atoi(papszTok[1]);
674 : }
675 6868 : else if (EQUAL(papszTok[0], "!charset"))
676 : {
677 1316 : SetCharset(papszTok[1]);
678 : }
679 5552 : else if (EQUAL(papszTok[0], "Definition") &&
680 1316 : EQUAL(papszTok[1], "Table"))
681 : {
682 1316 : bInsideTableDef = TRUE;
683 : }
684 4236 : else if (bInsideTableDef && !bFoundTableFields &&
685 2636 : (EQUAL(papszTok[0], "Type") || EQUAL(papszTok[0], "FORMAT:")))
686 : {
687 1316 : if (EQUAL(papszTok[1], "NATIVE") || EQUAL(papszTok[1], "LINKED"))
688 1315 : m_eTableType = TABTableNative;
689 1 : else if (EQUAL(papszTok[1], "DBF"))
690 1 : m_eTableType = TABTableDBF;
691 : else
692 : {
693 : // Type=ACCESS, or other unsupported type... cannot open!
694 0 : if (!bTestOpenNoError)
695 0 : CPLError(CE_Failure, CPLE_NotSupported,
696 : "Unsupported table type '%s' in file %s. "
697 : "This type of .TAB file cannot be read by this "
698 : "library.",
699 0 : papszTok[1], m_pszFname);
700 0 : CSLDestroy(papszTok);
701 0 : return -1;
702 : }
703 : }
704 2920 : else if (bInsideTableDef && !bFoundTableFields &&
705 1320 : EQUAL(papszTok[0], "Description"))
706 : {
707 8 : auto osDescription = GetTabDescription(m_papszTABFile[iLine]);
708 4 : if (!osDescription.empty())
709 : {
710 4 : const char *pszEncoding = GetEncoding();
711 4 : if (pszEncoding == nullptr || EQUAL(pszEncoding, ""))
712 : {
713 : std::shared_ptr<char> oUnescapedDescription(
714 0 : UnescapeString(osDescription.c_str()), CPLFree);
715 0 : IMapInfoFile::SetMetadataItem(DESCRIPTION_KEY,
716 0 : oUnescapedDescription.get());
717 : }
718 : else
719 : {
720 : std::shared_ptr<char> oEncodedDescription(
721 : CPLRecode(osDescription.c_str(), pszEncoding,
722 : CPL_ENC_UTF8),
723 8 : CPLFree);
724 :
725 : std::shared_ptr<char> oUnescapedDescription(
726 8 : UnescapeString(oEncodedDescription.get()), CPLFree);
727 4 : IMapInfoFile::SetMetadataItem(DESCRIPTION_KEY,
728 4 : oUnescapedDescription.get());
729 : }
730 4 : }
731 : }
732 2916 : else if (bInsideTableDef && !bFoundTableFields &&
733 1316 : (EQUAL(papszTok[0], "Fields") ||
734 0 : EQUAL(papszTok[0], "FIELDS:")))
735 : {
736 : /*---------------------------------------------------------
737 : * We found the list of table fields
738 : * Just remember number of fields... the field types will be
739 : * parsed inside ParseTABFileFields() later...
740 : *--------------------------------------------------------*/
741 1316 : bFoundTableFields = TRUE;
742 1316 : numFields = atoi(papszTok[1]);
743 :
744 1316 : if (numFields < 1 || numFields > 2048 ||
745 1316 : iLine + numFields >= numLines)
746 : {
747 0 : if (!bTestOpenNoError)
748 0 : CPLError(
749 : CE_Failure, CPLE_FileIO,
750 : "Invalid number of fields (%s) at line %d in file %s",
751 0 : papszTok[1], iLine + 1, m_pszFname);
752 :
753 0 : CSLDestroy(papszTok);
754 0 : return -1;
755 : }
756 :
757 1316 : bInsideTableDef = FALSE;
758 : } /* end of fields section*/
759 : else
760 : {
761 : // Simply Ignore unrecognized lines
762 : }
763 : }
764 :
765 1316 : CSLDestroy(papszTok);
766 :
767 1316 : if (m_pszCharset == nullptr)
768 0 : SetCharset("Neutral");
769 :
770 1316 : if (numFields == 0)
771 : {
772 0 : if (!bTestOpenNoError)
773 0 : CPLError(CE_Failure, CPLE_NotSupported,
774 : "%s contains no table field definition. "
775 : "This type of .TAB file cannot be read by this library.",
776 : m_pszFname);
777 0 : return -1;
778 : }
779 :
780 1316 : return 0;
781 : }
782 :
783 : /**********************************************************************
784 : * TABFile::ParseTABFileFields()
785 : *
786 : * Extract the field definition from the TAB header file, validate
787 : * with what we have in the previously opened .DAT or .DBF file, and
788 : * finally build the m_poDefn OGRFeatureDefn for this dataset.
789 : *
790 : * This private method should be used only during the Open() call and after
791 : * ParseTABFileFirstPass() has been called.
792 : *
793 : * Returns 0 on success, -1 on error.
794 : **********************************************************************/
795 1316 : int TABFile::ParseTABFileFields()
796 : {
797 :
798 1316 : if (m_eAccessMode == TABWrite)
799 : {
800 0 : CPLError(CE_Failure, CPLE_NotSupported,
801 : "ParseTABFile() can be used only with Read access.");
802 0 : return -1;
803 : }
804 :
805 1316 : char *pszFeatureClassName = TABGetBasename(m_pszFname);
806 1316 : m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
807 1316 : CPLFree(pszFeatureClassName);
808 : // Ref count defaults to 0... set it to 1
809 1316 : m_poDefn->Reference();
810 :
811 : /*-------------------------------------------------------------
812 : * Scan for fields.
813 : *------------------------------------------------------------*/
814 1316 : OGRFieldDefn *poFieldDefn = nullptr;
815 1316 : char **papszTok = nullptr;
816 :
817 1316 : const int numLines = CSLCount(m_papszTABFile);
818 9216 : for (int iLine = 0; iLine < numLines; iLine++)
819 : {
820 : /*-------------------------------------------------------------
821 : * Tokenize the next .TAB line, and check first keyword
822 : *------------------------------------------------------------*/
823 9216 : const char *pszStr = m_papszTABFile[iLine];
824 14488 : while (*pszStr != '\0' && isspace(static_cast<unsigned char>(*pszStr)))
825 5272 : pszStr++;
826 :
827 9216 : if (STARTS_WITH_CI(pszStr, "Fields") && CPLStrnlen(pszStr, 7) >= 7)
828 : {
829 : /*---------------------------------------------------------
830 : * We found the list of table fields
831 : *--------------------------------------------------------*/
832 1316 : int iField = 0;
833 1316 : int numFields = atoi(pszStr + 7);
834 1316 : if (numFields < 1 || numFields > 2048 ||
835 1316 : iLine + numFields >= numLines)
836 : {
837 0 : CPLError(CE_Failure, CPLE_FileIO,
838 : "Invalid number of fields (%s) at line %d in file %s",
839 : pszStr + 7, iLine + 1, m_pszFname);
840 0 : CSLDestroy(papszTok);
841 0 : return -1;
842 : }
843 :
844 : // Alloc the array to keep track of indexed fields
845 1316 : m_panIndexNo =
846 1316 : static_cast<int *>(CPLCalloc(numFields, sizeof(int)));
847 :
848 1316 : iLine++;
849 1316 : poFieldDefn = nullptr;
850 2851 : for (iField = 0; iField < numFields; iField++, iLine++)
851 : {
852 : /*-----------------------------------------------------
853 : * For each field definition found in the .TAB:
854 : * Pass the info to the DAT file object. It will validate
855 : * the info with what is found in the .DAT header, and will
856 : * also use this info later to interpret field values.
857 : *
858 : * We also create the OGRFieldDefn at the same time to
859 : * initialize the OGRFeatureDefn
860 : *----------------------------------------------------*/
861 1551 : CSLDestroy(papszTok);
862 1551 : papszTok = CSLTokenizeStringComplex(m_papszTABFile[iLine],
863 : " \t(),;", TRUE, FALSE);
864 1551 : const int numTok = CSLCount(papszTok);
865 :
866 1551 : CPLAssert(m_poDefn);
867 1551 : poFieldDefn = nullptr;
868 :
869 1551 : CPLString osFieldName;
870 1551 : if (numTok > 0)
871 : {
872 1551 : osFieldName = papszTok[0];
873 1551 : if (strlen(GetEncoding()) > 0)
874 74 : osFieldName.Recode(GetEncoding(), CPL_ENC_UTF8);
875 : }
876 :
877 1551 : int nStatus = -1;
878 1551 : if (numTok >= 3 && EQUAL(papszTok[1], "char"))
879 : {
880 : /*-------------------------------------------------
881 : * CHAR type
882 : *------------------------------------------------*/
883 208 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
884 208 : iField, osFieldName, TABFChar, atoi(papszTok[2]), 0);
885 208 : poFieldDefn = new OGRFieldDefn(osFieldName, OFTString);
886 208 : poFieldDefn->SetWidth(atoi(papszTok[2]));
887 : }
888 1343 : else if (numTok >= 2 && EQUAL(papszTok[1], "integer"))
889 : {
890 : /*-------------------------------------------------
891 : * INTEGER type
892 : *------------------------------------------------*/
893 1250 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
894 : iField, osFieldName, TABFInteger, 0, 0);
895 1250 : poFieldDefn = new OGRFieldDefn(osFieldName, OFTInteger);
896 1250 : if (numTok > 2 && atoi(papszTok[2]) > 0)
897 5 : poFieldDefn->SetWidth(atoi(papszTok[2]));
898 : }
899 93 : else if (numTok >= 2 && EQUAL(papszTok[1], "smallint"))
900 : {
901 : /*-------------------------------------------------
902 : * SMALLINT type
903 : *------------------------------------------------*/
904 1 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
905 : iField, osFieldName, TABFSmallInt, 0, 0);
906 1 : poFieldDefn = new OGRFieldDefn(osFieldName, OFTInteger);
907 1 : if (numTok > 2 && atoi(papszTok[2]) > 0)
908 0 : poFieldDefn->SetWidth(atoi(papszTok[2]));
909 : }
910 92 : else if (numTok >= 2 && EQUAL(papszTok[1], "largeint"))
911 : {
912 : /*-------------------------------------------------
913 : * LargeInt type
914 : *------------------------------------------------*/
915 5 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
916 : iField, osFieldName, TABFLargeInt, 0, 0);
917 5 : poFieldDefn = new OGRFieldDefn(osFieldName, OFTInteger64);
918 5 : if (numTok > 2 && atoi(papszTok[2]) > 0)
919 0 : poFieldDefn->SetWidth(atoi(papszTok[2]));
920 : }
921 87 : else if (numTok >= 4 && EQUAL(papszTok[1], "decimal"))
922 : {
923 : /*-------------------------------------------------
924 : * DECIMAL type
925 : *------------------------------------------------*/
926 11 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
927 11 : iField, osFieldName, TABFDecimal, atoi(papszTok[2]),
928 11 : atoi(papszTok[3]));
929 11 : poFieldDefn = new OGRFieldDefn(osFieldName, OFTReal);
930 11 : poFieldDefn->SetWidth(atoi(papszTok[2]));
931 11 : poFieldDefn->SetPrecision(atoi(papszTok[3]));
932 : }
933 76 : else if (numTok >= 2 && EQUAL(papszTok[1], "float"))
934 : {
935 : /*-------------------------------------------------
936 : * FLOAT type
937 : *------------------------------------------------*/
938 31 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
939 : iField, osFieldName, TABFFloat, 0, 0);
940 31 : poFieldDefn = new OGRFieldDefn(osFieldName, OFTReal);
941 : }
942 45 : else if (numTok >= 2 && EQUAL(papszTok[1], "date"))
943 : {
944 : /*-------------------------------------------------
945 : * DATE type (returned as a string: "DD/MM/YYYY")
946 : *------------------------------------------------*/
947 21 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
948 : iField, osFieldName, TABFDate, 0, 0);
949 21 : poFieldDefn = new OGRFieldDefn(osFieldName,
950 : #ifdef MITAB_USE_OFTDATETIME
951 21 : OFTDate);
952 : #else
953 : OFTString);
954 : #endif
955 21 : poFieldDefn->SetWidth(10);
956 : }
957 24 : else if (numTok >= 2 && EQUAL(papszTok[1], "time"))
958 : {
959 : /*-------------------------------------------------
960 : * TIME type (returned as a string: "HH:MM:SS")
961 : *------------------------------------------------*/
962 3 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
963 : iField, osFieldName, TABFTime, 0, 0);
964 3 : poFieldDefn = new OGRFieldDefn(osFieldName,
965 : #ifdef MITAB_USE_OFTDATETIME
966 3 : OFTTime);
967 : #else
968 : OFTString);
969 : #endif
970 3 : poFieldDefn->SetWidth(9);
971 : }
972 21 : else if (numTok >= 2 && EQUAL(papszTok[1], "datetime"))
973 : {
974 : /*-------------------------------------------------
975 : * DATETIME type (returned as a string: "DD/MM/YYYY
976 : *HH:MM:SS")
977 : *------------------------------------------------*/
978 19 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
979 : iField, osFieldName, TABFDateTime, 0, 0);
980 19 : poFieldDefn = new OGRFieldDefn(osFieldName,
981 : #ifdef MITAB_USE_OFTDATETIME
982 19 : OFTDateTime);
983 : #else
984 : OFTString);
985 : #endif
986 19 : poFieldDefn->SetWidth(19);
987 : }
988 2 : else if (numTok >= 2 && EQUAL(papszTok[1], "logical"))
989 : {
990 : /*-------------------------------------------------
991 : * LOGICAL type (value "T" or "F")
992 : *------------------------------------------------*/
993 2 : nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
994 : iField, osFieldName, TABFLogical, 0, 0);
995 2 : poFieldDefn = new OGRFieldDefn(osFieldName, OFTInteger);
996 2 : poFieldDefn->SetSubType(OFSTBoolean);
997 2 : poFieldDefn->SetWidth(1);
998 : }
999 : else
1000 : {
1001 : // Unrecognized field type or line corrupt
1002 : }
1003 :
1004 1551 : if (nStatus != 0)
1005 : {
1006 16 : CPLError(CE_Failure, CPLE_FileIO,
1007 : "Failed to parse field definition at line %d in "
1008 : "file %s",
1009 : iLine + 1, m_pszFname);
1010 16 : CSLDestroy(papszTok);
1011 16 : delete poFieldDefn;
1012 16 : return -1;
1013 : }
1014 : /*-----------------------------------------------------
1015 : * Keep track of index number if present
1016 : *----------------------------------------------------*/
1017 1535 : if (numTok >= 4 && EQUAL(papszTok[numTok - 2], "index"))
1018 : {
1019 9 : m_panIndexNo[iField] = atoi(papszTok[numTok - 1]);
1020 : }
1021 : else
1022 : {
1023 1526 : m_panIndexNo[iField] = 0;
1024 : }
1025 :
1026 : /*-----------------------------------------------------
1027 : * Add the FieldDefn to the FeatureDefn and continue with
1028 : * the next one.
1029 : *----------------------------------------------------*/
1030 1535 : m_poDefn->AddFieldDefn(poFieldDefn);
1031 : m_oSetFields.insert(
1032 1535 : CPLString(poFieldDefn->GetNameRef()).toupper());
1033 : // AddFieldDenf() takes a copy, so we delete the original
1034 1535 : delete poFieldDefn;
1035 1535 : poFieldDefn = nullptr;
1036 : }
1037 :
1038 : /*---------------------------------------------------------
1039 : * OK, we're done... end the loop now.
1040 : *--------------------------------------------------------*/
1041 1300 : break;
1042 : } /* end of fields section*/
1043 : else
1044 : {
1045 : // Simply Ignore unrecognized lines
1046 : }
1047 : }
1048 :
1049 1300 : CSLDestroy(papszTok);
1050 :
1051 1300 : if (m_poDefn->GetFieldCount() == 0)
1052 : {
1053 0 : CPLError(CE_Failure, CPLE_NotSupported,
1054 : "%s contains no table field definition. "
1055 : "This type of .TAB file cannot be read by this library.",
1056 : m_pszFname);
1057 0 : return -1;
1058 : }
1059 :
1060 1300 : return 0;
1061 : }
1062 :
1063 : /**********************************************************************
1064 : * TABFile::WriteTABFile()
1065 : *
1066 : * Generate the .TAB file using mainly the attribute fields definition.
1067 : *
1068 : *
1069 : * Returns 0 on success, -1 on error.
1070 : **********************************************************************/
1071 1318 : int TABFile::WriteTABFile()
1072 : {
1073 1318 : if (!m_bNeedTABRewrite)
1074 : {
1075 1170 : return 0;
1076 : }
1077 :
1078 148 : if (m_poMAPFile == nullptr || m_eAccessMode == TABRead)
1079 : {
1080 0 : CPLError(CE_Failure, CPLE_NotSupported,
1081 : "WriteTABFile() can be used only with Write access.");
1082 0 : return -1;
1083 : }
1084 :
1085 : // First update file version number...
1086 148 : int nMapObjVersion = m_poMAPFile->GetMinTABFileVersion();
1087 148 : m_nVersion = std::max(m_nVersion, nMapObjVersion);
1088 :
1089 148 : VSILFILE *fp = VSIFOpenL(m_pszFname, "wt");
1090 148 : if (fp != nullptr)
1091 : {
1092 148 : VSIFPrintfL(fp, "!table\n");
1093 148 : VSIFPrintfL(fp, "!version %d\n", m_nVersion);
1094 148 : VSIFPrintfL(fp, "!charset %s\n", m_pszCharset);
1095 148 : VSIFPrintfL(fp, "\n");
1096 :
1097 148 : if (m_poDefn && m_poDefn->GetFieldCount() > 0)
1098 : {
1099 131 : VSIFPrintfL(fp, "Definition Table\n");
1100 131 : VSIFPrintfL(fp, " Type NATIVE Charset \"%s\"\n", m_pszCharset);
1101 131 : const char *pszDescription = GetMetadataItem(DESCRIPTION_KEY);
1102 131 : if (nullptr != pszDescription)
1103 : {
1104 : std::shared_ptr<char> oEscapedDescription(
1105 6 : EscapeString(pszDescription, true), CPLFree);
1106 3 : const char *pszEncoding = GetEncoding();
1107 3 : if (nullptr == pszEncoding || EQUAL(pszEncoding, ""))
1108 : {
1109 0 : VSIFPrintfL(fp, " Description \"%s\"\n",
1110 : oEscapedDescription.get());
1111 : }
1112 : else
1113 : {
1114 : std::shared_ptr<char> oEncodedDescription(
1115 3 : CPLRecode(oEscapedDescription.get(), CPL_ENC_UTF8,
1116 : pszEncoding),
1117 6 : CPLFree);
1118 3 : VSIFPrintfL(fp, " Description \"%s\"\n",
1119 : oEncodedDescription.get());
1120 : }
1121 : }
1122 131 : VSIFPrintfL(fp, " Fields %d\n", m_poDefn->GetFieldCount());
1123 :
1124 436 : for (int iField = 0; iField < m_poDefn->GetFieldCount(); iField++)
1125 : {
1126 305 : const char *pszFieldType = nullptr;
1127 305 : OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(iField);
1128 305 : switch (GetNativeFieldType(iField))
1129 : {
1130 152 : case TABFChar:
1131 : pszFieldType =
1132 152 : CPLSPrintf("Char (%d)", poFieldDefn->GetWidth());
1133 152 : break;
1134 6 : case TABFDecimal:
1135 6 : pszFieldType = CPLSPrintf("Decimal (%d,%d)",
1136 : poFieldDefn->GetWidth(),
1137 : poFieldDefn->GetPrecision());
1138 6 : break;
1139 80 : case TABFInteger:
1140 80 : if (poFieldDefn->GetWidth() == 0)
1141 73 : pszFieldType = "Integer";
1142 : else
1143 7 : pszFieldType = CPLSPrintf("Integer (%d)",
1144 : poFieldDefn->GetWidth());
1145 80 : break;
1146 0 : case TABFSmallInt:
1147 0 : if (poFieldDefn->GetWidth() == 0)
1148 0 : pszFieldType = "SmallInt";
1149 : else
1150 0 : pszFieldType = CPLSPrintf("SmallInt (%d)",
1151 : poFieldDefn->GetWidth());
1152 0 : break;
1153 4 : case TABFLargeInt:
1154 4 : if (poFieldDefn->GetWidth() == 0)
1155 4 : pszFieldType = "LargeInt";
1156 : else
1157 0 : pszFieldType = CPLSPrintf("LargeInt (%d)",
1158 : poFieldDefn->GetWidth());
1159 4 : break;
1160 24 : case TABFFloat:
1161 24 : pszFieldType = "Float";
1162 24 : break;
1163 1 : case TABFLogical:
1164 1 : pszFieldType = "Logical";
1165 1 : break;
1166 18 : case TABFDate:
1167 18 : pszFieldType = "Date";
1168 18 : break;
1169 2 : case TABFTime:
1170 2 : pszFieldType = "Time";
1171 2 : break;
1172 18 : case TABFDateTime:
1173 18 : pszFieldType = "DateTime";
1174 18 : break;
1175 0 : default:
1176 : // Unsupported field type!!! This should never happen.
1177 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1178 : "WriteTABFile(): Unsupported field type");
1179 0 : VSIFCloseL(fp);
1180 0 : return -1;
1181 : }
1182 :
1183 610 : CPLString osFieldName(poFieldDefn->GetNameRef());
1184 :
1185 305 : if (strlen(GetEncoding()) > 0)
1186 13 : osFieldName.Recode(CPL_ENC_UTF8, GetEncoding());
1187 :
1188 305 : char *pszCleanName = TABCleanFieldName(
1189 305 : osFieldName, GetEncoding(), m_bStrictLaundering);
1190 305 : osFieldName = pszCleanName;
1191 305 : CPLFree(pszCleanName);
1192 :
1193 305 : if (GetFieldIndexNumber(iField) == 0)
1194 : {
1195 304 : VSIFPrintfL(fp, " %s %s ;\n", osFieldName.c_str(),
1196 : pszFieldType);
1197 : }
1198 : else
1199 : {
1200 1 : VSIFPrintfL(fp, " %s %s Index %d ;\n",
1201 : osFieldName.c_str(), pszFieldType,
1202 : GetFieldIndexNumber(iField));
1203 : }
1204 : }
1205 : }
1206 : else
1207 : {
1208 17 : VSIFPrintfL(fp, "Definition Table\n");
1209 17 : VSIFPrintfL(fp, " Type NATIVE Charset \"%s\"\n", m_pszCharset);
1210 17 : VSIFPrintfL(fp, " Fields 1\n");
1211 17 : VSIFPrintfL(fp, " FID Integer ;\n");
1212 : }
1213 :
1214 148 : VSIFCloseL(fp);
1215 :
1216 148 : m_bNeedTABRewrite = FALSE;
1217 : }
1218 : else
1219 : {
1220 0 : CPLError(CE_Failure, CPLE_FileIO, "Failed to create file `%s'",
1221 : m_pszFname);
1222 0 : return -1;
1223 : }
1224 :
1225 148 : return 0;
1226 : }
1227 :
1228 : /**********************************************************************
1229 : * TABFile::Close()
1230 : *
1231 : * Close current file, and release all memory used.
1232 : *
1233 : * Returns 0 on success, -1 on error.
1234 : **********************************************************************/
1235 1460 : int TABFile::Close()
1236 : {
1237 1460 : CPLErrorReset();
1238 :
1239 : // Commit the latest changes to the file...
1240 :
1241 1460 : if (m_poMAPFile)
1242 : {
1243 : // In Write access, it is time to write the .TAB file.
1244 1426 : if (m_eAccessMode != TABRead)
1245 : {
1246 1175 : WriteTABFile();
1247 : }
1248 :
1249 1426 : m_poMAPFile->Close();
1250 1426 : delete m_poMAPFile;
1251 1426 : m_poMAPFile = nullptr;
1252 : }
1253 :
1254 1460 : if (m_poDATFile)
1255 : {
1256 1443 : m_poDATFile->Close();
1257 1443 : delete m_poDATFile;
1258 1443 : m_poDATFile = nullptr;
1259 : }
1260 :
1261 1460 : if (m_poINDFile)
1262 : {
1263 3 : m_poINDFile->Close();
1264 3 : delete m_poINDFile;
1265 3 : m_poINDFile = nullptr;
1266 : }
1267 :
1268 1460 : if (m_poCurFeature)
1269 : {
1270 12 : delete m_poCurFeature;
1271 12 : m_poCurFeature = nullptr;
1272 : }
1273 :
1274 1460 : if (m_poDefn)
1275 1443 : m_poDefn->Release();
1276 1460 : m_poDefn = nullptr;
1277 :
1278 1460 : if (m_poSpatialRef)
1279 1318 : m_poSpatialRef->Release();
1280 1460 : m_poSpatialRef = nullptr;
1281 :
1282 1460 : CSLDestroy(m_papszTABFile);
1283 1460 : m_papszTABFile = nullptr;
1284 :
1285 1460 : CPLFree(m_pszFname);
1286 1460 : m_pszFname = nullptr;
1287 :
1288 1460 : CPLFree(m_pszCharset);
1289 1460 : m_pszCharset = nullptr;
1290 :
1291 1460 : CPLFree(m_panIndexNo);
1292 1460 : m_panIndexNo = nullptr;
1293 :
1294 1460 : CPLFree(m_panMatchingFIDs);
1295 1460 : m_panMatchingFIDs = nullptr;
1296 :
1297 1460 : return 0;
1298 : }
1299 :
1300 : /**********************************************************************
1301 : * TABFile::SetQuickSpatialIndexMode()
1302 : *
1303 : * Select "quick spatial index mode".
1304 : *
1305 : * The default behavior of MITAB is to generate an optimized spatial index,
1306 : * but this results in slower write speed.
1307 : *
1308 : * Applications that want faster write speed and do not care
1309 : * about the performance of spatial queries on the resulting file can
1310 : * use SetQuickSpatialIndexMode() to require the creation of a non-optimal
1311 : * spatial index (actually emulating the type of spatial index produced
1312 : * by MITAB before version 1.6.0). In this mode writing files can be
1313 : * about 5 times faster, but spatial queries can be up to 30 times slower.
1314 : *
1315 : * Returns 0 on success, -1 on error.
1316 : **********************************************************************/
1317 0 : int TABFile::SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode /*=TRUE*/)
1318 : {
1319 0 : if (m_eAccessMode != TABWrite || m_poMAPFile == nullptr)
1320 : {
1321 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1322 : "SetQuickSpatialIndexMode() failed: file not opened for write "
1323 : "access.");
1324 0 : return -1;
1325 : }
1326 :
1327 0 : return m_poMAPFile->SetQuickSpatialIndexMode(bQuickSpatialIndexMode);
1328 : }
1329 :
1330 : /**********************************************************************
1331 : * TABFile::GetNextFeatureId()
1332 : *
1333 : * Returns feature id that follows nPrevId, or -1 if it is the
1334 : * last feature id. Pass nPrevId=-1 to fetch the first valid feature id.
1335 : **********************************************************************/
1336 582698 : GIntBig TABFile::GetNextFeatureId(GIntBig nPrevId)
1337 : {
1338 582698 : if (m_bLastOpWasWrite)
1339 0 : ResetReading();
1340 582698 : m_bLastOpWasRead = TRUE;
1341 :
1342 582698 : if (!CPL_INT64_FITS_ON_INT32(nPrevId))
1343 0 : return -1;
1344 :
1345 : /*-----------------------------------------------------------------
1346 : * Are we using spatial rather than .ID based traversal?
1347 : *----------------------------------------------------------------*/
1348 582698 : if (bUseSpatialTraversal)
1349 430364 : return m_poMAPFile->GetNextFeatureId(static_cast<int>(nPrevId));
1350 :
1351 : /*-----------------------------------------------------------------
1352 : * Should we use an attribute index traversal?
1353 : *----------------------------------------------------------------*/
1354 152334 : if (m_poAttrQuery != nullptr)
1355 : {
1356 4682 : if (m_panMatchingFIDs == nullptr)
1357 : {
1358 4680 : m_iMatchingFID = 0;
1359 4680 : m_panMatchingFIDs =
1360 4680 : m_poAttrQuery->EvaluateAgainstIndices(this, nullptr);
1361 : }
1362 4682 : if (m_panMatchingFIDs != nullptr)
1363 : {
1364 4 : if (m_panMatchingFIDs[m_iMatchingFID] == OGRNullFID)
1365 2 : return OGRNullFID;
1366 :
1367 2 : return m_panMatchingFIDs[m_iMatchingFID++] + 1;
1368 : }
1369 : }
1370 :
1371 : /*-----------------------------------------------------------------
1372 : * Establish what the next logical feature ID should be
1373 : *----------------------------------------------------------------*/
1374 152330 : int nFeatureId = -1;
1375 :
1376 152330 : if (nPrevId <= 0 && m_nLastFeatureId > 0)
1377 997 : nFeatureId = 1; // Feature Ids start at 1
1378 151333 : else if (nPrevId > 0 && nPrevId < m_nLastFeatureId)
1379 151055 : nFeatureId = static_cast<int>(nPrevId) + 1;
1380 : else
1381 : {
1382 : // This was the last feature
1383 278 : return OGRNullFID;
1384 : }
1385 :
1386 : /*-----------------------------------------------------------------
1387 : * Skip any feature with NONE geometry and a deleted attribute record
1388 : *----------------------------------------------------------------*/
1389 153056 : while (nFeatureId <= m_nLastFeatureId)
1390 : {
1391 306112 : if (m_poMAPFile->MoveToObjId(nFeatureId) != 0 ||
1392 153056 : m_poDATFile->GetRecordBlock(nFeatureId) == nullptr)
1393 : {
1394 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1395 : "GetNextFeatureId() failed: unable to set read pointer "
1396 : "to feature id %d",
1397 : nFeatureId);
1398 0 : return -1;
1399 : }
1400 :
1401 : // __TODO__ Add a test here to check if object is deleted,
1402 : // i.e. 0x40 set on object_id in object block
1403 154205 : if (m_poMAPFile->GetCurObjType() != TAB_GEOM_NONE ||
1404 1149 : m_poDATFile->IsCurrentRecordDeleted() == FALSE)
1405 : {
1406 : // This feature contains at least a geometry or some attributes...
1407 : // return its id.
1408 152052 : return nFeatureId;
1409 : }
1410 :
1411 1004 : nFeatureId++;
1412 : }
1413 :
1414 : // If we reached this point, then we kept skipping deleted features
1415 : // and stopped when EOF was reached.
1416 0 : return -1;
1417 : }
1418 :
1419 : /**********************************************************************
1420 : * TABFile::GetNextFeatureId_Spatial()
1421 : *
1422 : * Returns feature id that follows nPrevId, or -1 if it is the
1423 : * last feature id, but by traversing the spatial tree instead of the
1424 : * direct object index. Generally speaking the feature id's will be
1425 : * returned in an unordered fashion.
1426 : **********************************************************************/
1427 0 : int TABFile::GetNextFeatureId_Spatial(int nPrevId)
1428 : {
1429 0 : if (m_eAccessMode != TABRead)
1430 : {
1431 0 : CPLError(
1432 : CE_Failure, CPLE_NotSupported,
1433 : "GetNextFeatureId_Spatial() can be used only with Read access.");
1434 0 : return -1;
1435 : }
1436 :
1437 0 : if (m_poMAPFile == nullptr)
1438 : {
1439 0 : CPLError(
1440 : CE_Failure, CPLE_NotSupported,
1441 : "GetNextFeatureId_Spatial() requires availability of .MAP file.");
1442 0 : return -1;
1443 : }
1444 :
1445 0 : return m_poMAPFile->GetNextFeatureId(nPrevId);
1446 : }
1447 :
1448 : /**********************************************************************
1449 : * TABFile::GetFeatureRef()
1450 : *
1451 : * Fill and return a TABFeature object for the specified feature id.
1452 : *
1453 : * The returned pointer is a reference to an object owned and maintained
1454 : * by this TABFile object. It should not be altered or freed by the
1455 : * caller and its contents is guaranteed to be valid only until the next
1456 : * call to GetFeatureRef() or Close().
1457 : *
1458 : * Returns NULL if the specified feature id does not exist of if an
1459 : * error happened. In any case, CPLError() will have been called to
1460 : * report the reason of the failure.
1461 : *
1462 : * If an unsupported object type is encountered (likely from a newer version
1463 : * of MapInfo) then a valid feature will be returned with a NONE geometry,
1464 : * and a warning will be produced with code TAB_WarningFeatureTypeNotSupported
1465 : * CPLGetLastErrorNo() should be used to detect that case.
1466 : **********************************************************************/
1467 572570 : TABFeature *TABFile::GetFeatureRef(GIntBig nFeatureId)
1468 : {
1469 572570 : CPLErrorReset();
1470 :
1471 : /*-----------------------------------------------------------------
1472 : * Make sure file is opened and Validate feature id by positioning
1473 : * the read pointers for the .MAP and .DAT files to this feature id.
1474 : *----------------------------------------------------------------*/
1475 572570 : if (m_poMAPFile == nullptr)
1476 : {
1477 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1478 : "GetFeatureRef() failed: file is not opened!");
1479 0 : return nullptr;
1480 : }
1481 :
1482 572570 : if (m_bLastOpWasWrite)
1483 4 : ResetReading();
1484 572570 : m_bLastOpWasRead = TRUE;
1485 :
1486 572567 : if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId ||
1487 1717700 : m_poMAPFile->MoveToObjId(static_cast<int>(nFeatureId)) != 0 ||
1488 572559 : m_poDATFile->GetRecordBlock(static_cast<int>(nFeatureId)) == nullptr)
1489 : {
1490 : // CPLError(CE_Failure, CPLE_IllegalArg,
1491 : // "GetFeatureRef() failed: invalid feature id %d",
1492 : // nFeatureId);
1493 11 : return nullptr;
1494 : }
1495 :
1496 572559 : if (m_poDATFile->IsCurrentRecordDeleted())
1497 : {
1498 2 : if (m_poMAPFile->GetCurObjType() != TAB_GEOM_NONE)
1499 : {
1500 0 : CPLError(
1501 : CE_Failure, CPLE_AppDefined,
1502 : "Valid .MAP record " CPL_FRMT_GIB
1503 : " found, but .DAT is marked as deleted. File likely corrupt",
1504 : nFeatureId);
1505 : }
1506 2 : return nullptr;
1507 : }
1508 :
1509 : /*-----------------------------------------------------------------
1510 : * Flush current feature object
1511 : * __TODO__ try to reuse if it is already of the right type
1512 : *----------------------------------------------------------------*/
1513 572557 : if (m_poCurFeature)
1514 : {
1515 366917 : delete m_poCurFeature;
1516 366917 : m_poCurFeature = nullptr;
1517 : }
1518 :
1519 : /*-----------------------------------------------------------------
1520 : * Create new feature object of the right type
1521 : * Unsupported object types are returned as raw TABFeature (i.e. NONE
1522 : * geometry)
1523 : *----------------------------------------------------------------*/
1524 1717670 : m_poCurFeature = TABFeature::CreateFromMapInfoType(
1525 572557 : m_poMAPFile->GetCurObjType(), m_poDefn);
1526 :
1527 : /*-----------------------------------------------------------------
1528 : * Read fields from the .DAT file
1529 : * GetRecordBlock() has already been called above...
1530 : *----------------------------------------------------------------*/
1531 572557 : if (m_poCurFeature->ReadRecordFromDATFile(m_poDATFile) != 0)
1532 : {
1533 0 : delete m_poCurFeature;
1534 0 : m_poCurFeature = nullptr;
1535 0 : return nullptr;
1536 : }
1537 :
1538 : /*-----------------------------------------------------------------
1539 : * Read geometry from the .MAP file
1540 : * MoveToObjId() has already been called above...
1541 : *----------------------------------------------------------------*/
1542 572557 : TABMAPObjHdr *poObjHdr = TABMAPObjHdr::NewObj(m_poMAPFile->GetCurObjType(),
1543 572557 : m_poMAPFile->GetCurObjId());
1544 : // Note that poObjHdr==NULL is a valid case if geometry type is NONE
1545 :
1546 1145110 : if ((poObjHdr && poObjHdr->ReadObj(m_poMAPFile->GetCurObjBlock()) != 0) ||
1547 572557 : m_poCurFeature->ReadGeometryFromMAPFile(m_poMAPFile, poObjHdr) != 0)
1548 : {
1549 0 : delete m_poCurFeature;
1550 0 : m_poCurFeature = nullptr;
1551 0 : if (poObjHdr)
1552 0 : delete poObjHdr;
1553 0 : return nullptr;
1554 : }
1555 572557 : if (poObjHdr) // May be NULL if feature geometry type is NONE
1556 572557 : delete poObjHdr;
1557 :
1558 572557 : m_nCurFeatureId = nFeatureId;
1559 572557 : m_poCurFeature->SetFID(m_nCurFeatureId);
1560 :
1561 572557 : m_poCurFeature->SetRecordDeleted(m_poDATFile->IsCurrentRecordDeleted());
1562 :
1563 572557 : return m_poCurFeature;
1564 : }
1565 :
1566 : /**********************************************************************
1567 : * TABFile::DeleteFeature()
1568 : *
1569 : * Standard OGR DeleteFeature implementation.
1570 : **********************************************************************/
1571 723 : OGRErr TABFile::DeleteFeature(GIntBig nFeatureId)
1572 : {
1573 723 : CPLErrorReset();
1574 :
1575 723 : if (m_eAccessMode == TABRead)
1576 : {
1577 4 : CPLError(CE_Failure, CPLE_NotSupported,
1578 : "DeleteFeature() cannot be used in read-only access.");
1579 4 : return OGRERR_FAILURE;
1580 : }
1581 :
1582 : /*-----------------------------------------------------------------
1583 : * Make sure file is opened and establish new feature id.
1584 : *----------------------------------------------------------------*/
1585 719 : if (m_poMAPFile == nullptr)
1586 : {
1587 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1588 : "DeleteFeature() failed: file is not opened!");
1589 0 : return OGRERR_FAILURE;
1590 : }
1591 :
1592 719 : if (m_bLastOpWasWrite)
1593 4 : ResetReading();
1594 :
1595 718 : if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId ||
1596 2153 : m_poMAPFile->MoveToObjId(static_cast<int>(nFeatureId)) != 0 ||
1597 716 : m_poDATFile->GetRecordBlock(static_cast<int>(nFeatureId)) == nullptr)
1598 : {
1599 : /*CPLError(CE_Failure, CPLE_IllegalArg,
1600 : "DeleteFeature() failed: invalid feature id " CPL_FRMT_GIB,
1601 : nFeatureId);*/
1602 3 : return OGRERR_NON_EXISTING_FEATURE;
1603 : }
1604 :
1605 716 : if (m_poDATFile->IsCurrentRecordDeleted())
1606 : {
1607 : /*CPLError(CE_Failure, CPLE_IllegalArg,
1608 : "DeleteFeature() failed: record is already deleted!");*/
1609 1 : return OGRERR_NON_EXISTING_FEATURE;
1610 : }
1611 :
1612 715 : if (m_poCurFeature)
1613 : {
1614 0 : delete m_poCurFeature;
1615 0 : m_poCurFeature = nullptr;
1616 : }
1617 :
1618 715 : if (m_poMAPFile->MarkAsDeleted() != 0 || m_poDATFile->MarkAsDeleted() != 0)
1619 : {
1620 0 : return OGRERR_FAILURE;
1621 : }
1622 :
1623 715 : return OGRERR_NONE;
1624 : }
1625 :
1626 : /**********************************************************************
1627 : * TABFile::WriteFeature()
1628 : *
1629 : * Write a feature to this dataset.
1630 : *
1631 : * Returns 0 on success, or -1 if an error happened in which case,
1632 : * CPLError() will have been called to
1633 : * report the reason of the failure.
1634 : **********************************************************************/
1635 15128 : int TABFile::WriteFeature(TABFeature *poFeature)
1636 : {
1637 :
1638 15128 : m_bLastOpWasWrite = TRUE;
1639 :
1640 : /*-----------------------------------------------------------------
1641 : * Make sure file is opened and establish new feature id.
1642 : *----------------------------------------------------------------*/
1643 15128 : if (m_poMAPFile == nullptr)
1644 : {
1645 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1646 : "WriteFeature() failed: file is not opened!");
1647 0 : return -1;
1648 : }
1649 :
1650 15128 : int nFeatureId = 0;
1651 15128 : if (poFeature->GetFID() >= 0)
1652 : {
1653 211 : nFeatureId = static_cast<int>(poFeature->GetFID());
1654 : }
1655 14917 : else if (m_nLastFeatureId < 1)
1656 : {
1657 : /*-------------------------------------------------------------
1658 : * Special hack to write out at least one field if none are in
1659 : * OGRFeatureDefn.
1660 : *------------------------------------------------------------*/
1661 107 : if (m_poDATFile->GetNumFields() == 0)
1662 : {
1663 1 : CPLError(CE_Warning, CPLE_IllegalArg,
1664 : "MapInfo tables must contain at least 1 column, adding "
1665 : "dummy FID column.");
1666 1 : CPLErrorReset();
1667 1 : m_poDATFile->AddField("FID", TABFInteger, 10, 0);
1668 : }
1669 :
1670 107 : nFeatureId = 1;
1671 : }
1672 : else
1673 : {
1674 14810 : nFeatureId = m_nLastFeatureId + 1;
1675 : }
1676 :
1677 15128 : poFeature->SetFID(nFeatureId);
1678 :
1679 : /*-----------------------------------------------------------------
1680 : * Write fields to the .DAT file and update .IND if necessary
1681 : *----------------------------------------------------------------*/
1682 30256 : if (m_poDATFile->GetRecordBlock(nFeatureId) == nullptr ||
1683 15128 : poFeature->WriteRecordToDATFile(m_poDATFile, m_poINDFile,
1684 15128 : m_panIndexNo) != 0)
1685 : {
1686 1 : CPLError(CE_Failure, CPLE_FileIO,
1687 : "Failed writing attributes for feature id %d in %s",
1688 : nFeatureId, m_pszFname);
1689 1 : return -1;
1690 : }
1691 :
1692 : /*-----------------------------------------------------------------
1693 : * Write geometry to the .MAP file
1694 : * The call to PrepareNewObj() takes care of the .ID file.
1695 : *----------------------------------------------------------------*/
1696 : std::unique_ptr<TABMAPObjHdr> poObjHdr(TABMAPObjHdr::NewObj(
1697 30254 : poFeature->ValidateMapInfoType(m_poMAPFile), nFeatureId));
1698 :
1699 15127 : if (poObjHdr == nullptr)
1700 : {
1701 0 : CPLError(CE_Failure, CPLE_FileIO,
1702 : "Failed writing geometry for feature id %d in %s", nFeatureId,
1703 : m_pszFname);
1704 0 : return -1;
1705 : }
1706 :
1707 : /*-----------------------------------------------------------------
1708 : * ValidateMapInfoType() may have returned TAB_GEOM_NONE if feature
1709 : * contained an invalid geometry for its class. Need to catch that
1710 : * case and return the error.
1711 : *----------------------------------------------------------------*/
1712 15187 : if (poObjHdr->m_nType == TAB_GEOM_NONE &&
1713 60 : poFeature->GetFeatureClass() != TABFCNoGeomFeature)
1714 : {
1715 0 : CPLError(CE_Failure, CPLE_FileIO,
1716 : "Invalid geometry for feature id %d in %s", nFeatureId,
1717 : m_pszFname);
1718 0 : return -1;
1719 : }
1720 :
1721 : /*-----------------------------------------------------------------
1722 : * The ValidateMapInfoType() call above has forced calculation of the
1723 : * feature's IntMBR. Store that value in the ObjHdr for use by
1724 : * PrepareNewObj() to search the best node to insert the feature.
1725 : *----------------------------------------------------------------*/
1726 15127 : if (poObjHdr->m_nType != TAB_GEOM_NONE)
1727 : {
1728 15067 : poFeature->GetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY,
1729 15067 : poObjHdr->m_nMaxX, poObjHdr->m_nMaxY);
1730 : }
1731 :
1732 : /*
1733 : if( m_nCurFeatureId < m_nLastFeatureId )
1734 : {
1735 : delete GetFeatureRef(m_nLastFeatureId);
1736 : m_poCurFeature = NULL;
1737 : }*/
1738 :
1739 15127 : if (m_poMAPFile->PrepareNewObj(poObjHdr.get()) != 0 ||
1740 30254 : poFeature->WriteGeometryToMAPFile(m_poMAPFile, poObjHdr.get()) != 0 ||
1741 15127 : m_poMAPFile->CommitNewObj(poObjHdr.get()) != 0)
1742 : {
1743 0 : CPLError(CE_Failure, CPLE_FileIO,
1744 : "Failed writing geometry for feature id %d in %s", nFeatureId,
1745 : m_pszFname);
1746 0 : return -1;
1747 : }
1748 :
1749 15127 : m_nLastFeatureId = std::max(m_nLastFeatureId, nFeatureId);
1750 15127 : m_nCurFeatureId = nFeatureId;
1751 :
1752 15127 : return 0;
1753 : }
1754 :
1755 1445 : int TABFile::SetCharset(const char *pszCharset)
1756 : {
1757 1445 : if (0 != IMapInfoFile::SetCharset(pszCharset))
1758 : {
1759 0 : return -1;
1760 : }
1761 1445 : if (m_poDATFile != nullptr)
1762 : {
1763 2 : m_poDATFile->SetEncoding(CharsetToEncoding(pszCharset));
1764 : }
1765 1445 : if (m_poMAPFile != nullptr)
1766 : {
1767 2 : m_poMAPFile->SetEncoding(CharsetToEncoding(pszCharset));
1768 : }
1769 1445 : if (EQUAL(pszCharset, "UTF-8"))
1770 : {
1771 2 : m_nVersion = std::max(m_nVersion, 1520);
1772 : }
1773 1445 : return 0;
1774 : }
1775 :
1776 197 : void TABFile::SetStrictLaundering(bool bStrictLaundering)
1777 : {
1778 197 : IMapInfoFile::SetStrictLaundering(bStrictLaundering);
1779 197 : if (!bStrictLaundering)
1780 : {
1781 3 : m_nVersion = std::max(m_nVersion, 1520);
1782 : }
1783 197 : }
1784 :
1785 : /**********************************************************************
1786 : * TABFile::CreateFeature()
1787 : *
1788 : * Write a new feature to this dataset. The passed in feature is updated
1789 : * with the new feature id.
1790 : *
1791 : * Returns OGRERR_NONE on success, or an appropriate OGRERR_ code if an
1792 : * error happened in which case, CPLError() will have been called to
1793 : * report the reason of the failure.
1794 : **********************************************************************/
1795 14918 : OGRErr TABFile::CreateFeature(TABFeature *poFeature)
1796 : {
1797 14918 : CPLErrorReset();
1798 :
1799 14918 : if (m_eAccessMode == TABRead)
1800 : {
1801 0 : CPLError(CE_Failure, CPLE_NotSupported,
1802 : "CreateFeature() cannot be used in read-only access.");
1803 0 : return OGRERR_FAILURE;
1804 : }
1805 :
1806 14918 : GIntBig nFeatureId = poFeature->GetFID();
1807 14918 : if (nFeatureId != OGRNullFID)
1808 : {
1809 1 : if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId)
1810 : {
1811 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1812 : "CreateFeature() failed: invalid feature id " CPL_FRMT_GIB,
1813 : nFeatureId);
1814 0 : return OGRERR_FAILURE;
1815 : }
1816 :
1817 1 : if (m_poDATFile->GetRecordBlock(static_cast<int>(nFeatureId)) ==
1818 2 : nullptr ||
1819 1 : !m_poDATFile->IsCurrentRecordDeleted())
1820 : {
1821 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1822 : "CreateFeature() failed: cannot re-write already existing "
1823 : "feature " CPL_FRMT_GIB,
1824 : nFeatureId);
1825 0 : return OGRERR_FAILURE;
1826 : }
1827 : }
1828 :
1829 14918 : if (WriteFeature(poFeature) < 0)
1830 1 : return OGRERR_FAILURE;
1831 :
1832 14917 : return OGRERR_NONE;
1833 : }
1834 :
1835 : /**********************************************************************
1836 : * TABFile::ISetFeature()
1837 : *
1838 : * Implementation of OGRLayer's SetFeature()
1839 : **********************************************************************/
1840 222 : OGRErr TABFile::ISetFeature(OGRFeature *poFeature)
1841 :
1842 : {
1843 222 : CPLErrorReset();
1844 :
1845 222 : if (m_eAccessMode == TABRead)
1846 : {
1847 2 : CPLError(CE_Failure, CPLE_NotSupported,
1848 : "SetFeature() cannot be used in read-only access.");
1849 2 : return OGRERR_FAILURE;
1850 : }
1851 :
1852 : /*-----------------------------------------------------------------
1853 : * Make sure file is opened.
1854 : *----------------------------------------------------------------*/
1855 220 : if (m_poMAPFile == nullptr)
1856 : {
1857 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1858 : "SetFeature() failed: file is not opened!");
1859 0 : return OGRERR_FAILURE;
1860 : }
1861 :
1862 220 : GIntBig nFeatureId = poFeature->GetFID();
1863 220 : if (nFeatureId == OGRNullFID)
1864 : {
1865 1 : CPLError(CE_Failure, CPLE_NotSupported,
1866 : "SetFeature() must be used on a feature with a FID.");
1867 1 : return OGRERR_FAILURE;
1868 : }
1869 219 : if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId)
1870 : {
1871 : /*CPLError(CE_Failure, CPLE_IllegalArg,
1872 : "SetFeature() failed: invalid feature id " CPL_FRMT_GIB,
1873 : nFeatureId);*/
1874 3 : return OGRERR_NON_EXISTING_FEATURE;
1875 : }
1876 :
1877 216 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1878 429 : if (poGeom != nullptr &&
1879 213 : ((wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint) ||
1880 213 : (wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)))
1881 : {
1882 0 : CPLError(CE_Failure, CPLE_NotSupported,
1883 : "SetFeature() failed: setting MultiPoint or "
1884 : "GeometryCollection not supported");
1885 0 : return OGRERR_FAILURE;
1886 : }
1887 :
1888 216 : TABFeature *poTABFeature = CreateTABFeature(poFeature);
1889 216 : if (poTABFeature == nullptr)
1890 0 : return OGRERR_FAILURE;
1891 :
1892 216 : if (m_bLastOpWasWrite)
1893 201 : ResetReading();
1894 :
1895 216 : if (m_poDATFile->GetRecordBlock(static_cast<int>(nFeatureId)) == nullptr)
1896 : {
1897 : /*CPLError(CE_Failure, CPLE_IllegalArg,
1898 : "SetFeature() failed: invalid feature id " CPL_FRMT_GIB,
1899 : nFeatureId);*/
1900 0 : delete poTABFeature;
1901 0 : return OGRERR_NON_EXISTING_FEATURE;
1902 : }
1903 :
1904 : /* If the object is not already deleted, delete it */
1905 216 : if (!(m_poDATFile->IsCurrentRecordDeleted()))
1906 : {
1907 115 : OGRFeature *poOldFeature = GetFeature(nFeatureId);
1908 115 : if (poOldFeature != nullptr)
1909 : {
1910 : /* Optimization: if old and new features are the same, do nothing */
1911 115 : if (poOldFeature->Equal(poFeature))
1912 : {
1913 1 : CPLDebug("MITAB", "Un-modified object " CPL_FRMT_GIB,
1914 : nFeatureId);
1915 1 : delete poTABFeature;
1916 1 : delete poOldFeature;
1917 1 : return OGRERR_NONE;
1918 : }
1919 :
1920 : /* Optimization: if old and new geometries are the same, just */
1921 : /* rewrite the attributes */
1922 114 : OGRGeometry *poOldGeom = poOldFeature->GetGeometryRef();
1923 114 : OGRGeometry *poNewGeom = poFeature->GetGeometryRef();
1924 225 : if ((poOldGeom == nullptr && poNewGeom == nullptr) ||
1925 111 : (poOldGeom != nullptr && poNewGeom != nullptr &&
1926 110 : poOldGeom->Equals(poNewGeom)))
1927 : {
1928 5 : const char *pszOldStyle = poOldFeature->GetStyleString();
1929 5 : const char *pszNewStyle = poFeature->GetStyleString();
1930 5 : if ((pszOldStyle == nullptr && pszNewStyle == nullptr) ||
1931 3 : (pszOldStyle != nullptr && pszNewStyle != nullptr &&
1932 3 : EQUAL(pszOldStyle, pszNewStyle)))
1933 : {
1934 5 : CPLDebug("MITAB",
1935 : "Rewrite only attributes for object " CPL_FRMT_GIB,
1936 : nFeatureId);
1937 10 : if (poTABFeature->WriteRecordToDATFile(
1938 5 : m_poDATFile, m_poINDFile, m_panIndexNo) != 0)
1939 : {
1940 0 : CPLError(CE_Failure, CPLE_FileIO,
1941 : "Failed writing attributes for feature "
1942 : "id " CPL_FRMT_GIB " in %s",
1943 : nFeatureId, m_pszFname);
1944 0 : delete poTABFeature;
1945 0 : delete poOldFeature;
1946 0 : return OGRERR_FAILURE;
1947 : }
1948 :
1949 5 : delete poTABFeature;
1950 5 : delete poOldFeature;
1951 5 : return OGRERR_NONE;
1952 : }
1953 : }
1954 :
1955 109 : delete poOldFeature;
1956 : }
1957 :
1958 109 : if (DeleteFeature(nFeatureId) != OGRERR_NONE)
1959 : {
1960 0 : delete poTABFeature;
1961 0 : return OGRERR_FAILURE;
1962 : }
1963 : }
1964 :
1965 210 : int nStatus = WriteFeature(poTABFeature);
1966 :
1967 210 : delete poTABFeature;
1968 :
1969 210 : if (nStatus < 0)
1970 0 : return OGRERR_FAILURE;
1971 :
1972 210 : return OGRERR_NONE;
1973 : }
1974 :
1975 : /**********************************************************************
1976 : * TABFile::GetLayerDefn() const
1977 : *
1978 : * Returns a reference to the OGRFeatureDefn that will be used to create
1979 : * features in this dataset.
1980 : *
1981 : * Returns a reference to an object that is maintained by this TABFile
1982 : * object (and thus should not be modified or freed by the caller) or
1983 : * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file
1984 : * opened yet)
1985 : **********************************************************************/
1986 251147 : const OGRFeatureDefn *TABFile::GetLayerDefn() const
1987 : {
1988 251147 : return m_poDefn;
1989 : }
1990 :
1991 : /**********************************************************************
1992 : * TABFile::SetFeatureDefn()
1993 : *
1994 : * Pass a reference to the OGRFeatureDefn that will be used to create
1995 : * features in this dataset. This function should be called after
1996 : * creating a new dataset, but before writing the first feature.
1997 : * All features that will be written to this dataset must share this same
1998 : * OGRFeatureDefn.
1999 : *
2000 : * A reference to the OGRFeatureDefn will be kept and will be used to
2001 : * build the .DAT file, etc.
2002 : *
2003 : * Returns 0 on success, -1 on error.
2004 : **********************************************************************/
2005 0 : int TABFile::SetFeatureDefn(
2006 : OGRFeatureDefn *poFeatureDefn,
2007 : TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
2008 : {
2009 0 : if (m_eAccessMode != TABWrite)
2010 : {
2011 0 : CPLError(CE_Failure, CPLE_NotSupported,
2012 : "SetFeatureDefn() can be used only with Write access.");
2013 0 : return -1;
2014 : }
2015 :
2016 : /*-----------------------------------------------------------------
2017 : * Keep a reference to the OGRFeatureDefn... we'll have to take the
2018 : * reference count into account when we are done with it.
2019 : *----------------------------------------------------------------*/
2020 0 : if (m_poDefn && m_poDefn->Dereference() == 0)
2021 0 : delete m_poDefn;
2022 :
2023 0 : m_poDefn = poFeatureDefn;
2024 0 : m_poDefn->Reference();
2025 :
2026 : /*-----------------------------------------------------------------
2027 : * Pass field information to the .DAT file, after making sure that
2028 : * it has been created and that it does not contain any field
2029 : * definition yet.
2030 : *----------------------------------------------------------------*/
2031 0 : if (m_poDATFile == nullptr || m_poDATFile->GetNumFields() > 0)
2032 : {
2033 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2034 : "SetFeatureDefn() can be called only once in a newly "
2035 : "created dataset.");
2036 0 : return -1;
2037 : }
2038 :
2039 0 : const int numFields = poFeatureDefn->GetFieldCount();
2040 0 : TABFieldType eMapInfoType = TABFUnknown;
2041 0 : int nStatus = 0;
2042 0 : for (int iField = 0; nStatus == 0 && iField < numFields; iField++)
2043 : {
2044 0 : OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(iField);
2045 :
2046 0 : if (paeMapInfoNativeFieldTypes)
2047 : {
2048 0 : eMapInfoType = paeMapInfoNativeFieldTypes[iField];
2049 : }
2050 : else
2051 : {
2052 : /*---------------------------------------------------------
2053 : * Map OGRFieldTypes to MapInfo native types
2054 : *--------------------------------------------------------*/
2055 0 : switch (poFieldDefn->GetType())
2056 : {
2057 0 : case OFTInteger:
2058 0 : eMapInfoType = poFieldDefn->GetSubType() == OFSTBoolean
2059 0 : ? TABFLogical
2060 : : TABFInteger;
2061 0 : break;
2062 0 : case OFTReal:
2063 0 : if (poFieldDefn->GetWidth() > 0 ||
2064 0 : poFieldDefn->GetPrecision() > 0)
2065 0 : eMapInfoType = TABFDecimal;
2066 : else
2067 0 : eMapInfoType = TABFFloat;
2068 0 : break;
2069 0 : case OFTDateTime:
2070 0 : eMapInfoType = TABFDateTime;
2071 0 : break;
2072 0 : case OFTDate:
2073 0 : eMapInfoType = TABFDate;
2074 0 : break;
2075 0 : case OFTTime:
2076 0 : eMapInfoType = TABFTime;
2077 0 : break;
2078 0 : case OFTString:
2079 : default:
2080 0 : eMapInfoType = TABFChar;
2081 : }
2082 : }
2083 :
2084 0 : nStatus = m_poDATFile->AddField(poFieldDefn->GetNameRef(), eMapInfoType,
2085 : poFieldDefn->GetWidth(),
2086 : poFieldDefn->GetPrecision());
2087 : }
2088 :
2089 : /*-----------------------------------------------------------------
2090 : * Alloc the array to keep track of indexed fields (default=NOT indexed)
2091 : *----------------------------------------------------------------*/
2092 0 : m_panIndexNo = static_cast<int *>(CPLCalloc(numFields, sizeof(int)));
2093 :
2094 0 : return nStatus;
2095 : }
2096 :
2097 : /**********************************************************************
2098 : * TABFile::AddFieldNative()
2099 : *
2100 : * Create a new field using a native mapinfo data type... this is an
2101 : * alternative to defining fields through the OGR interface.
2102 : * This function should be called after creating a new dataset.
2103 : *
2104 : * This function will build/update the OGRFeatureDefn that will have to be
2105 : * used when writing features to this dataset.
2106 : *
2107 : * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
2108 : *
2109 : * Note: The bUnique flag has no effect on TABFiles. See the TABView class.
2110 : *
2111 : * Returns 0 on success, -1 on error.
2112 : **********************************************************************/
2113 224 : int TABFile::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
2114 : int nWidth /*=0*/, int nPrecision /*=0*/,
2115 : GBool bIndexed /*=FALSE*/, GBool /*bUnique=FALSE*/,
2116 : int /*bApproxOK*/)
2117 : {
2118 224 : if (m_eAccessMode == TABRead || m_poDATFile == nullptr)
2119 : {
2120 0 : CPLError(CE_Failure, CPLE_NotSupported,
2121 : "AddFieldNative() cannot be used only with Read access.");
2122 0 : return -1;
2123 : }
2124 :
2125 224 : m_bNeedTABRewrite = TRUE;
2126 :
2127 : /*-----------------------------------------------------------------
2128 : * Validate field width... must be <= 254
2129 : *----------------------------------------------------------------*/
2130 224 : if (nWidth > 254)
2131 : {
2132 0 : CPLError(CE_Warning, CPLE_IllegalArg,
2133 : "Invalid size (%d) for field '%s'. "
2134 : "Size must be 254 or less.",
2135 : nWidth, pszName);
2136 0 : nWidth = 254;
2137 : }
2138 :
2139 : /*-----------------------------------------------------------------
2140 : * Map fields with width=0 (variable length in OGR) to a valid default
2141 : *----------------------------------------------------------------*/
2142 224 : if (eMapInfoType == TABFDecimal && nWidth == 0)
2143 0 : nWidth = 20;
2144 224 : else if (nWidth == 0)
2145 0 : nWidth = 254; /* char fields */
2146 :
2147 448 : CPLString osName(NormalizeFieldName(pszName));
2148 :
2149 : /*-----------------------------------------------------------------
2150 : * Map MapInfo native types to OGR types
2151 : *----------------------------------------------------------------*/
2152 224 : OGRFieldDefn *poFieldDefn = nullptr;
2153 :
2154 224 : switch (eMapInfoType)
2155 : {
2156 78 : case TABFChar:
2157 : /*-------------------------------------------------
2158 : * CHAR type
2159 : *------------------------------------------------*/
2160 78 : poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTString);
2161 78 : poFieldDefn->SetWidth(nWidth);
2162 78 : break;
2163 73 : case TABFInteger:
2164 : /*-------------------------------------------------
2165 : * INTEGER type
2166 : *------------------------------------------------*/
2167 73 : poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger);
2168 73 : if (nWidth <= 10)
2169 5 : poFieldDefn->SetWidth(nWidth);
2170 73 : break;
2171 0 : case TABFSmallInt:
2172 : /*-------------------------------------------------
2173 : * SMALLINT type
2174 : *------------------------------------------------*/
2175 0 : poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger);
2176 0 : if (nWidth <= 5)
2177 0 : poFieldDefn->SetWidth(nWidth);
2178 0 : break;
2179 4 : case TABFLargeInt:
2180 : /*-------------------------------------------------
2181 : * SMALLINT type
2182 : *------------------------------------------------*/
2183 4 : poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger64);
2184 4 : break;
2185 6 : case TABFDecimal:
2186 : /*-------------------------------------------------
2187 : * DECIMAL type
2188 : *------------------------------------------------*/
2189 6 : poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTReal);
2190 6 : poFieldDefn->SetWidth(nWidth);
2191 6 : poFieldDefn->SetPrecision(nPrecision);
2192 6 : break;
2193 24 : case TABFFloat:
2194 : /*-------------------------------------------------
2195 : * FLOAT type
2196 : *------------------------------------------------*/
2197 24 : poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTReal);
2198 24 : break;
2199 18 : case TABFDate:
2200 : /*-------------------------------------------------
2201 : * DATE type (V450, returned as a string: "DD/MM/YYYY")
2202 : *------------------------------------------------*/
2203 18 : poFieldDefn = new OGRFieldDefn(osName.c_str(),
2204 : #ifdef MITAB_USE_OFTDATETIME
2205 18 : OFTDate);
2206 : #else
2207 : OFTString);
2208 : #endif
2209 18 : poFieldDefn->SetWidth(10);
2210 18 : m_nVersion = std::max(m_nVersion, 450);
2211 18 : break;
2212 2 : case TABFTime:
2213 : /*-------------------------------------------------
2214 : * TIME type (V900, returned as a string: "HH:MM:SS")
2215 : *------------------------------------------------*/
2216 2 : poFieldDefn = new OGRFieldDefn(osName.c_str(),
2217 : #ifdef MITAB_USE_OFTDATETIME
2218 2 : OFTTime);
2219 : #else
2220 : OFTString);
2221 : #endif
2222 2 : poFieldDefn->SetWidth(8);
2223 2 : m_nVersion = std::max(m_nVersion, 900);
2224 2 : break;
2225 18 : case TABFDateTime:
2226 : /*-------------------------------------------------
2227 : * DATETIME type (V900, returned as a string: "DD/MM/YYYY HH:MM:SS")
2228 : *------------------------------------------------*/
2229 18 : poFieldDefn = new OGRFieldDefn(osName.c_str(),
2230 : #ifdef MITAB_USE_OFTDATETIME
2231 18 : OFTDateTime);
2232 : #else
2233 : OFTString);
2234 : #endif
2235 18 : poFieldDefn->SetWidth(19);
2236 18 : m_nVersion = std::max(m_nVersion, 900);
2237 18 : break;
2238 1 : case TABFLogical:
2239 : /*-------------------------------------------------
2240 : * LOGICAL type (value "T" or "F")
2241 : *------------------------------------------------*/
2242 1 : poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger);
2243 1 : poFieldDefn->SetSubType(OFSTBoolean);
2244 1 : poFieldDefn->SetWidth(1);
2245 1 : break;
2246 0 : default:
2247 0 : CPLError(CE_Failure, CPLE_NotSupported,
2248 : "Unsupported type for field %s", osName.c_str());
2249 0 : return -1;
2250 : }
2251 :
2252 : /*-----------------------------------------------------
2253 : * Add the FieldDefn to the FeatureDefn
2254 : *----------------------------------------------------*/
2255 224 : whileUnsealing(m_poDefn)->AddFieldDefn(poFieldDefn);
2256 224 : m_oSetFields.insert(CPLString(poFieldDefn->GetNameRef()).toupper());
2257 224 : delete poFieldDefn;
2258 :
2259 : /*-----------------------------------------------------
2260 : * ... and pass field info to the .DAT file.
2261 : *----------------------------------------------------*/
2262 : int nStatus =
2263 224 : m_poDATFile->AddField(osName.c_str(), eMapInfoType, nWidth, nPrecision);
2264 :
2265 : /*-----------------------------------------------------------------
2266 : * Extend the array to keep track of indexed fields (default=NOT indexed)
2267 : *----------------------------------------------------------------*/
2268 224 : m_panIndexNo = static_cast<int *>(
2269 224 : CPLRealloc(m_panIndexNo, m_poDefn->GetFieldCount() * sizeof(int)));
2270 224 : m_panIndexNo[m_poDefn->GetFieldCount() - 1] = 0;
2271 :
2272 : /*-----------------------------------------------------------------
2273 : * Index the field if requested
2274 : *----------------------------------------------------------------*/
2275 224 : if (nStatus == 0 && bIndexed)
2276 0 : nStatus = SetFieldIndexed(m_poDefn->GetFieldCount() - 1);
2277 :
2278 224 : if (nStatus == 0 && m_eAccessMode == TABReadWrite)
2279 2 : nStatus = WriteTABFile();
2280 :
2281 224 : return nStatus;
2282 : }
2283 :
2284 : /**********************************************************************
2285 : * TABFile::GetNativeFieldType()
2286 : *
2287 : * Returns the native MapInfo field type for the specified field.
2288 : *
2289 : * Returns TABFUnknown if file is not opened, or if specified field index is
2290 : * invalid.
2291 : *
2292 : * Note that field ids are positive and start at 0.
2293 : **********************************************************************/
2294 310 : TABFieldType TABFile::GetNativeFieldType(int nFieldId)
2295 : {
2296 310 : if (m_poDATFile)
2297 : {
2298 310 : return m_poDATFile->GetFieldType(nFieldId);
2299 : }
2300 0 : return TABFUnknown;
2301 : }
2302 :
2303 : /**********************************************************************
2304 : * TABFile::GetFieldIndexNumber()
2305 : *
2306 : * Returns the field's index number that was specified in the .TAB header
2307 : * or 0 if the specified field is not indexed.
2308 : *
2309 : * Note that field ids are positive and start at 0
2310 : * and valid index ids are positive and start at 1.
2311 : **********************************************************************/
2312 1841 : int TABFile::GetFieldIndexNumber(int nFieldId)
2313 : {
2314 3682 : if (m_panIndexNo == nullptr || nFieldId < 0 || m_poDATFile == nullptr ||
2315 1841 : nFieldId >= m_poDefn->GetFieldCount())
2316 0 : return 0; // no index
2317 :
2318 1841 : return m_panIndexNo[nFieldId];
2319 : }
2320 :
2321 : /************************************************************************
2322 : * TABFile::SetFieldIndexed()
2323 : *
2324 : * Request that a field be indexed. This will create the .IND file if
2325 : * necessary, etc.
2326 : *
2327 : * Note that field ids are positive and start at 0.
2328 : *
2329 : * Returns 0 on success, -1 on error.
2330 : ************************************************************************/
2331 3 : int TABFile::SetFieldIndexed(int nFieldId)
2332 : {
2333 : /*-----------------------------------------------------------------
2334 : * Make sure things are OK
2335 : *----------------------------------------------------------------*/
2336 3 : if (m_pszFname == nullptr || m_eAccessMode != TABWrite ||
2337 2 : m_poDefn == nullptr)
2338 : {
2339 1 : CPLError(CE_Failure, CPLE_AssertionFailed,
2340 : "SetFieldIndexed() must be called after opening a new "
2341 : "dataset, but before writing the first feature to it.");
2342 1 : return -1;
2343 : }
2344 :
2345 4 : if (m_panIndexNo == nullptr || nFieldId < 0 || m_poDATFile == nullptr ||
2346 2 : nFieldId >= m_poDefn->GetFieldCount())
2347 : {
2348 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2349 : "Invalid field number in SetFieldIndexed().");
2350 0 : return -1;
2351 : }
2352 :
2353 : /*-----------------------------------------------------------------
2354 : * If field is already indexed then just return
2355 : *----------------------------------------------------------------*/
2356 2 : if (m_panIndexNo[nFieldId] != 0)
2357 1 : return 0; // Nothing to do
2358 :
2359 : /*-----------------------------------------------------------------
2360 : * Create .IND file if it is not done yet.
2361 : *
2362 : * Note: We can pass the .TAB's filename directly and the
2363 : * TABINDFile class will automagically adjust the extension.
2364 : *----------------------------------------------------------------*/
2365 1 : if (m_poINDFile == nullptr)
2366 : {
2367 1 : m_poINDFile = new TABINDFile;
2368 :
2369 1 : if (m_poINDFile->Open(m_pszFname, "w", TRUE) != 0)
2370 : {
2371 : // File could not be opened...
2372 0 : delete m_poINDFile;
2373 0 : m_poINDFile = nullptr;
2374 0 : return -1;
2375 : }
2376 : }
2377 :
2378 : /*-----------------------------------------------------------------
2379 : * Init new index.
2380 : *----------------------------------------------------------------*/
2381 1 : OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(nFieldId);
2382 :
2383 1 : if (poFieldDefn == nullptr)
2384 0 : return -1;
2385 1 : const int nNewIndexNo = m_poINDFile->CreateIndex(
2386 : GetNativeFieldType(nFieldId), poFieldDefn->GetWidth());
2387 1 : if (nNewIndexNo < 1)
2388 : {
2389 : // Failed... an error has already been reported.
2390 0 : return -1;
2391 : }
2392 :
2393 1 : m_panIndexNo[nFieldId] = nNewIndexNo;
2394 :
2395 1 : return 0;
2396 : }
2397 :
2398 : /************************************************************************
2399 : * TABFile::IsFieldIndexed()
2400 : *
2401 : * Returns TRUE if field is indexed, or FALSE otherwise.
2402 : ************************************************************************/
2403 0 : GBool TABFile::IsFieldIndexed(int nFieldId)
2404 : {
2405 0 : return GetFieldIndexNumber(nFieldId) > 0 ? TRUE : FALSE;
2406 : }
2407 :
2408 : /**********************************************************************
2409 : * TABFile::GetINDFileRef()
2410 : *
2411 : * Opens the .IND file for this dataset and returns a reference to
2412 : * the handle.
2413 : * If the .IND file has already been opened then the same handle is
2414 : * returned directly.
2415 : * If the .IND file does not exist then the function silently returns NULL.
2416 : *
2417 : * Note that the returned TABINDFile handle is only a reference to an
2418 : * object that is owned by this class. Callers can use it but cannot
2419 : * destroy the object. The object will remain valid for as long as
2420 : * the TABFile will remain open.
2421 : **********************************************************************/
2422 2 : TABINDFile *TABFile::GetINDFileRef()
2423 : {
2424 2 : if (m_pszFname == nullptr)
2425 0 : return nullptr;
2426 :
2427 2 : if (m_eAccessMode == TABRead && m_poINDFile == nullptr)
2428 : {
2429 : /*-------------------------------------------------------------
2430 : * File is not opened yet... do it now.
2431 : *
2432 : * Note: We can pass the .TAB's filename directly and the
2433 : * TABINDFile class will automagically adjust the extension.
2434 : *------------------------------------------------------------*/
2435 2 : m_poINDFile = new TABINDFile;
2436 :
2437 2 : if (m_poINDFile->Open(m_pszFname, "r", TRUE) != 0)
2438 : {
2439 : // File could not be opened... probably does not exist
2440 0 : delete m_poINDFile;
2441 0 : m_poINDFile = nullptr;
2442 : }
2443 2 : else if (m_panIndexNo && m_poDATFile)
2444 : {
2445 : /*---------------------------------------------------------
2446 : * Pass type information for each indexed field.
2447 : *--------------------------------------------------------*/
2448 6 : for (int i = 0; i < m_poDefn->GetFieldCount(); i++)
2449 : {
2450 4 : if (m_panIndexNo[i] > 0)
2451 : {
2452 2 : m_poINDFile->SetIndexFieldType(m_panIndexNo[i],
2453 : GetNativeFieldType(i));
2454 : }
2455 : }
2456 : }
2457 : }
2458 :
2459 2 : return m_poINDFile;
2460 : }
2461 :
2462 : /**********************************************************************
2463 : * TABFile::SetBounds()
2464 : *
2465 : * Set projection coordinates bounds of the newly created dataset.
2466 : *
2467 : * This function must be called after creating a new dataset and before any
2468 : * feature can be written to it.
2469 : *
2470 : * Returns 0 on success, -1 on error.
2471 : **********************************************************************/
2472 128 : int TABFile::SetBounds(double dXMin, double dYMin, double dXMax, double dYMax)
2473 : {
2474 128 : if (m_eAccessMode != TABWrite)
2475 : {
2476 0 : CPLError(CE_Failure, CPLE_NotSupported,
2477 : "SetBounds() can be used only with Write access.");
2478 0 : return -1;
2479 : }
2480 :
2481 : /*-----------------------------------------------------------------
2482 : * Check that dataset has been created but no feature set yet.
2483 : *----------------------------------------------------------------*/
2484 128 : if (m_poMAPFile && m_nLastFeatureId < 1)
2485 : {
2486 128 : m_poMAPFile->SetCoordsysBounds(dXMin, dYMin, dXMax, dYMax);
2487 :
2488 : /*-----------------------------------------------------------------
2489 : * Set BOUNDS metadata
2490 : *----------------------------------------------------------------*/
2491 : {
2492 128 : OGREnvelope e;
2493 128 : GetBounds(e.MinX, e.MinY, e.MaxX, e.MaxY, 1);
2494 128 : const char *pszBounds = CPLSPrintf("%.17g,%.17g,%.17g,%.17g",
2495 : e.MinX, e.MinY, e.MaxX, e.MaxY);
2496 128 : SetMetadataItem("BOUNDS", pszBounds);
2497 : }
2498 :
2499 128 : m_bBoundsSet = TRUE;
2500 : }
2501 : else
2502 : {
2503 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2504 : "SetBounds() can be called only after dataset has been "
2505 : "created and before any feature is set.");
2506 0 : return -1;
2507 : }
2508 :
2509 128 : return 0;
2510 : }
2511 :
2512 : /**********************************************************************
2513 : * TABFile::GetBounds()
2514 : *
2515 : * Fetch projection coordinates bounds of a dataset.
2516 : *
2517 : * The bForce flag has no effect on TAB files since the bounds are
2518 : * always in the header.
2519 : *
2520 : * Returns 0 on success, -1 on error.
2521 : **********************************************************************/
2522 1554 : int TABFile::GetBounds(double &dXMin, double &dYMin, double &dXMax,
2523 : double &dYMax, GBool /*bForce = TRUE*/)
2524 : {
2525 1554 : if (m_poMAPFile)
2526 : {
2527 1554 : TABMAPHeaderBlock *poHeader = m_poMAPFile->GetHeaderBlock();
2528 1554 : if (poHeader != nullptr)
2529 : {
2530 : /*-------------------------------------------------------------
2531 : * Projection bounds correspond to the +/- 1e9 integer coord. limits
2532 : *------------------------------------------------------------*/
2533 1554 : double dX0 = 0.0;
2534 1554 : double dX1 = 0.0;
2535 1554 : double dY0 = 0.0;
2536 1554 : double dY1 = 0.0;
2537 1554 : m_poMAPFile->Int2Coordsys(-1000000000, -1000000000, dX0, dY0);
2538 1554 : m_poMAPFile->Int2Coordsys(1000000000, 1000000000, dX1, dY1);
2539 : /*-------------------------------------------------------------
2540 : * ... and make sure that Min < Max
2541 : *------------------------------------------------------------*/
2542 1554 : dXMin = std::min(dX0, dX1);
2543 1554 : dXMax = std::max(dX0, dX1);
2544 1554 : dYMin = std::min(dY0, dY1);
2545 1554 : dYMax = std::max(dY0, dY1);
2546 1554 : return 0;
2547 : }
2548 : }
2549 :
2550 0 : CPLError(CE_Failure, CPLE_AppDefined,
2551 : "GetBounds() can be called only after dataset has been opened.");
2552 0 : return -1;
2553 : }
2554 :
2555 : /**********************************************************************
2556 : * TABFile::IGetExtent()
2557 : *
2558 : * Fetch extent of the data currently stored in the dataset.
2559 : *
2560 : * The bForce flag has no effect on TAB files since that value is
2561 : * always in the header.
2562 : *
2563 : * Returns OGRERR_NONE/OGRRERR_FAILURE.
2564 : **********************************************************************/
2565 14 : OGRErr TABFile::IGetExtent(int /*iGeomField*/, OGREnvelope *psExtent,
2566 : bool /* bForce */)
2567 : {
2568 14 : TABMAPHeaderBlock *poHeader = nullptr;
2569 :
2570 28 : if (m_poMAPFile && (poHeader = m_poMAPFile->GetHeaderBlock()) != nullptr &&
2571 14 : GetGeomType() != wkbNone)
2572 : {
2573 14 : double dX0 = 0.0;
2574 14 : double dX1 = 0.0;
2575 14 : double dY0 = 0.0;
2576 14 : double dY1 = 0.0;
2577 : /*-------------------------------------------------------------
2578 : * Fetch extent of the data from the .map header block
2579 : * this value is different from the projection bounds.
2580 : *------------------------------------------------------------*/
2581 14 : m_poMAPFile->Int2Coordsys(poHeader->m_nXMin, poHeader->m_nYMin, dX0,
2582 : dY0);
2583 14 : m_poMAPFile->Int2Coordsys(poHeader->m_nXMax, poHeader->m_nYMax, dX1,
2584 : dY1);
2585 :
2586 : /*-------------------------------------------------------------
2587 : * ... and make sure that Min < Max
2588 : *------------------------------------------------------------*/
2589 14 : psExtent->MinX = std::min(dX0, dX1);
2590 14 : psExtent->MaxX = std::max(dX0, dX1);
2591 14 : psExtent->MinY = std::min(dY0, dY1);
2592 14 : psExtent->MaxY = std::max(dY0, dY1);
2593 :
2594 14 : return OGRERR_NONE;
2595 : }
2596 :
2597 0 : return OGRERR_FAILURE;
2598 : }
2599 :
2600 : /**********************************************************************
2601 : * TABFile::GetFeatureCountByType()
2602 : *
2603 : * Return number of features of each type.
2604 : *
2605 : * Note that the sum of the 4 returned values may be different from
2606 : * the total number of features since features with NONE geometry
2607 : * are not taken into account here.
2608 : *
2609 : * Note: the bForce flag has nmo effect on .TAB files since the info
2610 : * is always in the header.
2611 : *
2612 : * Returns 0 on success, or silently returns -1 (with no error) if this
2613 : * information is not available.
2614 : **********************************************************************/
2615 1300 : int TABFile::GetFeatureCountByType(int &numPoints, int &numLines,
2616 : int &numRegions, int &numTexts,
2617 : GBool /* bForce = TRUE*/)
2618 : {
2619 1300 : TABMAPHeaderBlock *poHeader = nullptr;
2620 :
2621 1300 : if (m_poMAPFile && (poHeader = m_poMAPFile->GetHeaderBlock()) != nullptr)
2622 : {
2623 1300 : numPoints = poHeader->m_numPointObjects;
2624 1300 : numLines = poHeader->m_numLineObjects;
2625 1300 : numRegions = poHeader->m_numRegionObjects;
2626 1300 : numTexts = poHeader->m_numTextObjects;
2627 : }
2628 : else
2629 : {
2630 0 : numPoints = numLines = numRegions = numTexts = 0;
2631 0 : return -1;
2632 : }
2633 :
2634 1300 : return 0;
2635 : }
2636 :
2637 : /**********************************************************************
2638 : * TABFile::SetMIFCoordSys()
2639 : *
2640 : * Set projection for a new file using a MIF coordsys string.
2641 : *
2642 : * This function must be called after creating a new dataset and before any
2643 : * feature can be written to it.
2644 : *
2645 : * Returns 0 on success, -1 on error.
2646 : **********************************************************************/
2647 0 : int TABFile::SetMIFCoordSys(const char *pszMIFCoordSys)
2648 : {
2649 0 : if (m_eAccessMode != TABWrite)
2650 : {
2651 0 : CPLError(CE_Failure, CPLE_NotSupported,
2652 : "SetMIFCoordSys() can be used only with Write access.");
2653 0 : return -1;
2654 : }
2655 :
2656 : /*-----------------------------------------------------------------
2657 : * Check that dataset has been created but no feature set yet.
2658 : *----------------------------------------------------------------*/
2659 0 : if (m_poMAPFile && m_nLastFeatureId < 1)
2660 : {
2661 : OGRSpatialReference *poSpatialRef =
2662 0 : MITABCoordSys2SpatialRef(pszMIFCoordSys);
2663 :
2664 0 : if (poSpatialRef)
2665 : {
2666 0 : double dXMin = 0.0;
2667 0 : double dYMin = 0.0;
2668 0 : double dXMax = 0.0;
2669 0 : double dYMax = 0.0;
2670 0 : if (SetSpatialRef(poSpatialRef) == 0)
2671 : {
2672 0 : if (MITABExtractCoordSysBounds(pszMIFCoordSys, dXMin, dYMin,
2673 : dXMax, dYMax))
2674 : {
2675 : // If the coordsys string contains bounds, then use them
2676 0 : if (SetBounds(dXMin, dYMin, dXMax, dYMax) != 0)
2677 : {
2678 : // Failed Setting Bounds... an error should have
2679 : // been already reported.
2680 0 : return -1;
2681 : }
2682 : }
2683 : }
2684 : else
2685 : {
2686 : // Failed setting poSpatialRef... and error should have
2687 : // been reported.
2688 0 : return -1;
2689 : }
2690 :
2691 : // Release our handle on poSpatialRef
2692 0 : if (poSpatialRef->Dereference() == 0)
2693 0 : delete poSpatialRef;
2694 0 : }
2695 : }
2696 : else
2697 : {
2698 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2699 : "SetMIFCoordSys() can be called only after dataset has been "
2700 : "created and before any feature is set.");
2701 0 : return -1;
2702 : }
2703 :
2704 0 : return 0;
2705 : }
2706 :
2707 : /**********************************************************************
2708 : * TABFile::SetProjInfo()
2709 : *
2710 : * Set projection for a new file using a TABProjInfo structure.
2711 : *
2712 : * This function must be called after creating a new dataset and before any
2713 : * feature can be written to it.
2714 : *
2715 : * This call will also trigger a lookup of default bounds for the specified
2716 : * projection (except nonearth), and reset the m_bBoundsValid flag.
2717 : *
2718 : * Returns 0 on success, -1 on error.
2719 : **********************************************************************/
2720 33 : int TABFile::SetProjInfo(TABProjInfo *poPI)
2721 : {
2722 33 : if (m_eAccessMode != TABWrite)
2723 : {
2724 0 : CPLError(CE_Failure, CPLE_NotSupported,
2725 : "SetProjInfo() can be used only with Write access.");
2726 0 : return -1;
2727 : }
2728 :
2729 : /*-----------------------------------------------------------------
2730 : * Lookup default bounds and reset m_bBoundsSet flag
2731 : *----------------------------------------------------------------*/
2732 : double dXMin;
2733 : double dYMin;
2734 : double dXMax;
2735 : double dYMax;
2736 :
2737 33 : m_bBoundsSet = FALSE;
2738 33 : if (MITABLookupCoordSysBounds(poPI, dXMin, dYMin, dXMax, dYMax))
2739 : {
2740 18 : SetBounds(dXMin, dYMin, dXMax, dYMax);
2741 : }
2742 :
2743 : /*-----------------------------------------------------------------
2744 : * Check that dataset has been created but no feature set yet.
2745 : *----------------------------------------------------------------*/
2746 33 : if (m_poMAPFile && m_nLastFeatureId < 1)
2747 : {
2748 33 : if (m_poMAPFile->GetHeaderBlock()->SetProjInfo(poPI) != 0)
2749 0 : return -1;
2750 : }
2751 : else
2752 : {
2753 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
2754 : "SetProjInfo() can be called only after dataset has been "
2755 : "created and before any feature is set.");
2756 0 : return -1;
2757 : }
2758 :
2759 33 : return 0;
2760 : }
2761 :
2762 : /************************************************************************/
2763 : /* DeleteField() */
2764 : /************************************************************************/
2765 :
2766 4 : OGRErr TABFile::DeleteField(int iField)
2767 : {
2768 4 : if (m_poDATFile == nullptr || !TestCapability(OLCDeleteField))
2769 : {
2770 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
2771 : "DeleteField");
2772 0 : return OGRERR_FAILURE;
2773 : }
2774 :
2775 4 : if (iField < 0 || iField >= m_poDefn->GetFieldCount())
2776 : {
2777 2 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
2778 2 : return OGRERR_FAILURE;
2779 : }
2780 :
2781 2 : if (m_poDATFile->DeleteField(iField) == 0)
2782 : {
2783 2 : m_bNeedTABRewrite = TRUE;
2784 4 : m_oSetFields.erase(
2785 2 : CPLString(m_poDefn->GetFieldDefn(iField)->GetNameRef()).toupper());
2786 :
2787 : /* Delete from the array of indexed fields */
2788 2 : if (iField < m_poDefn->GetFieldCount() - 1)
2789 : {
2790 1 : memmove(m_panIndexNo + iField, m_panIndexNo + iField + 1,
2791 1 : (m_poDefn->GetFieldCount() - 1 - iField) * sizeof(int));
2792 : }
2793 :
2794 2 : whileUnsealing(m_poDefn)->DeleteFieldDefn(iField);
2795 :
2796 2 : if (m_eAccessMode == TABReadWrite)
2797 2 : WriteTABFile();
2798 :
2799 2 : return OGRERR_NONE;
2800 : }
2801 : else
2802 0 : return OGRERR_FAILURE;
2803 : }
2804 :
2805 : /************************************************************************/
2806 : /* ReorderFields() */
2807 : /************************************************************************/
2808 :
2809 15 : OGRErr TABFile::ReorderFields(int *panMap)
2810 : {
2811 15 : if (m_poDATFile == nullptr || !TestCapability(OLCDeleteField))
2812 : {
2813 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
2814 : "ReorderFields");
2815 0 : return OGRERR_FAILURE;
2816 : }
2817 15 : if (m_poDefn->GetFieldCount() == 0)
2818 4 : return OGRERR_NONE;
2819 :
2820 11 : OGRErr eErr = OGRCheckPermutation(panMap, m_poDefn->GetFieldCount());
2821 11 : if (eErr != OGRERR_NONE)
2822 1 : return eErr;
2823 :
2824 10 : if (m_poDATFile->ReorderFields(panMap) == 0)
2825 : {
2826 10 : m_bNeedTABRewrite = TRUE;
2827 :
2828 : int *panNewIndexedField = static_cast<int *>(
2829 10 : CPLMalloc(sizeof(int) * m_poDefn->GetFieldCount()));
2830 52 : for (int i = 0; i < m_poDefn->GetFieldCount(); i++)
2831 : {
2832 42 : panNewIndexedField[i] = m_panIndexNo[panMap[i]];
2833 : }
2834 10 : CPLFree(m_panIndexNo);
2835 10 : m_panIndexNo = panNewIndexedField;
2836 :
2837 10 : whileUnsealing(m_poDefn)->ReorderFieldDefns(panMap);
2838 :
2839 10 : if (m_eAccessMode == TABReadWrite)
2840 10 : WriteTABFile();
2841 :
2842 10 : return OGRERR_NONE;
2843 : }
2844 : else
2845 0 : return OGRERR_FAILURE;
2846 : }
2847 :
2848 : /************************************************************************/
2849 : /* AlterFieldDefn() */
2850 : /************************************************************************/
2851 :
2852 13 : OGRErr TABFile::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
2853 : int nFlagsIn)
2854 : {
2855 13 : if (m_poDATFile == nullptr || !TestCapability(OLCDeleteField))
2856 : {
2857 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
2858 : "AlterFieldDefn");
2859 0 : return OGRERR_FAILURE;
2860 : }
2861 :
2862 13 : if (iField < 0 || iField >= m_poDefn->GetFieldCount())
2863 : {
2864 2 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
2865 2 : return OGRERR_FAILURE;
2866 : }
2867 :
2868 11 : OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(iField);
2869 11 : if (m_poDATFile->AlterFieldDefn(iField, poFieldDefn, poNewFieldDefn,
2870 11 : nFlagsIn) == 0)
2871 : {
2872 9 : m_bNeedTABRewrite = TRUE;
2873 :
2874 9 : auto oTemporaryUnsealer(poFieldDefn->GetTemporaryUnsealer());
2875 18 : if ((nFlagsIn & ALTER_TYPE_FLAG) &&
2876 9 : poNewFieldDefn->GetType() != poFieldDefn->GetType())
2877 : {
2878 5 : poFieldDefn->SetType(poNewFieldDefn->GetType());
2879 : }
2880 9 : if (nFlagsIn & ALTER_NAME_FLAG)
2881 : {
2882 9 : m_oSetFields.erase(CPLString(poFieldDefn->GetNameRef()).toupper());
2883 9 : poFieldDefn->SetName(poNewFieldDefn->GetNameRef());
2884 : m_oSetFields.insert(
2885 9 : CPLString(poNewFieldDefn->GetNameRef()).toupper());
2886 : }
2887 9 : if (poFieldDefn->GetType() == OFTString)
2888 : {
2889 5 : poFieldDefn->SetWidth(m_poDATFile->GetFieldWidth(iField));
2890 : }
2891 4 : else if (nFlagsIn & ALTER_WIDTH_PRECISION_FLAG)
2892 : {
2893 4 : poFieldDefn->SetWidth(poNewFieldDefn->GetWidth());
2894 4 : poFieldDefn->SetPrecision(poNewFieldDefn->GetPrecision());
2895 : }
2896 :
2897 : // Take into account .dat limitations on width & precision to clamp
2898 : // what user might have specify
2899 9 : int nWidth = 0;
2900 9 : int nPrecision = 0;
2901 9 : GetTABType(poFieldDefn, nullptr, &nWidth, &nPrecision);
2902 9 : poFieldDefn->SetWidth(nWidth);
2903 9 : poFieldDefn->SetPrecision(nPrecision);
2904 :
2905 9 : if (m_eAccessMode == TABReadWrite)
2906 6 : WriteTABFile();
2907 :
2908 9 : return OGRERR_NONE;
2909 : }
2910 : else
2911 2 : return OGRERR_FAILURE;
2912 : }
2913 :
2914 : /************************************************************************/
2915 : /* SyncToDisk() */
2916 : /************************************************************************/
2917 :
2918 123 : OGRErr TABFile::SyncToDisk()
2919 : {
2920 : /* Silently return */
2921 123 : if (m_eAccessMode == TABRead)
2922 0 : return OGRERR_NONE;
2923 :
2924 123 : OGRErr eErr = OGRERR_NONE;
2925 :
2926 : // This is a hack for Windows and VSIFFlushL() issue. See
2927 : // http://trac.osgeo.org/gdal/ticket/5556
2928 123 : CPLSetConfigOption("VSI_FLUSH", "TRUE");
2929 :
2930 123 : if (WriteTABFile() != 0)
2931 0 : eErr = OGRERR_FAILURE;
2932 :
2933 123 : if (m_poMAPFile->SyncToDisk() != 0)
2934 0 : eErr = OGRERR_FAILURE;
2935 :
2936 123 : if (m_poDATFile->SyncToDisk() != 0)
2937 0 : eErr = OGRERR_FAILURE;
2938 :
2939 123 : CPLSetConfigOption("VSI_FLUSH", nullptr);
2940 :
2941 123 : return eErr;
2942 : }
2943 :
2944 : /************************************************************************/
2945 : /* TestCapability() */
2946 : /************************************************************************/
2947 :
2948 2451 : int TABFile::TestCapability(const char *pszCap) const
2949 :
2950 : {
2951 2451 : if (EQUAL(pszCap, OLCRandomRead))
2952 2 : return TRUE;
2953 :
2954 2449 : else if (EQUAL(pszCap, OLCSequentialWrite))
2955 19 : return m_eAccessMode != TABRead;
2956 :
2957 2430 : else if (EQUAL(pszCap, OLCRandomWrite))
2958 5 : return m_eAccessMode != TABRead;
2959 :
2960 2425 : else if (EQUAL(pszCap, OLCDeleteFeature))
2961 3 : return m_eAccessMode != TABRead;
2962 :
2963 2422 : else if (EQUAL(pszCap, OLCFastFeatureCount))
2964 0 : return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr;
2965 :
2966 2422 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
2967 0 : return TRUE;
2968 :
2969 2422 : else if (EQUAL(pszCap, OLCFastGetExtent))
2970 8 : return TRUE;
2971 :
2972 2414 : else if (EQUAL(pszCap, OLCCreateField))
2973 22 : return m_eAccessMode != TABRead;
2974 :
2975 2392 : else if (EQUAL(pszCap, OLCDeleteField))
2976 37 : return m_eAccessMode != TABRead;
2977 :
2978 2355 : else if (EQUAL(pszCap, OLCReorderFields))
2979 5 : return m_eAccessMode != TABRead;
2980 :
2981 2350 : else if (EQUAL(pszCap, OLCAlterFieldDefn))
2982 5 : return m_eAccessMode != TABRead;
2983 :
2984 2345 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
2985 48 : return TestUtf8Capability();
2986 :
2987 : else
2988 2297 : return FALSE;
2989 : }
2990 :
2991 : /**********************************************************************
2992 : * TABFile::Dump()
2993 : *
2994 : * Dump block contents... available only in DEBUG mode.
2995 : **********************************************************************/
2996 : #ifdef DEBUG
2997 :
2998 0 : void TABFile::Dump(FILE *fpOut /*=NULL*/)
2999 : {
3000 0 : if (fpOut == nullptr)
3001 0 : fpOut = stdout;
3002 :
3003 0 : fprintf(fpOut, "----- TABFile::Dump() -----\n");
3004 :
3005 0 : if (m_poMAPFile == nullptr)
3006 : {
3007 0 : fprintf(fpOut, "File is not opened.\n");
3008 : }
3009 : else
3010 : {
3011 0 : fprintf(fpOut, "File is opened: %s\n", m_pszFname);
3012 0 : fprintf(fpOut, "Associated TABLE file ...\n\n");
3013 0 : m_poDATFile->Dump(fpOut);
3014 0 : fprintf(fpOut, "... end of TABLE file dump.\n\n");
3015 0 : if (GetSpatialRef() != nullptr)
3016 : {
3017 0 : char *pszWKT = nullptr;
3018 :
3019 0 : GetSpatialRef()->exportToWkt(&pszWKT);
3020 0 : fprintf(fpOut, "SRS = %s\n", pszWKT);
3021 0 : CPLFree(pszWKT);
3022 : }
3023 0 : fprintf(fpOut, "Associated .MAP file ...\n\n");
3024 0 : m_poMAPFile->Dump(fpOut);
3025 0 : fprintf(fpOut, "... end of .MAP file dump.\n\n");
3026 : }
3027 :
3028 0 : fflush(fpOut);
3029 0 : }
3030 :
3031 : #endif // DEBUG
3032 :
3033 : /*
3034 : * SetMetadataItem()
3035 : */
3036 1682 : CPLErr TABFile::SetMetadataItem(const char *pszName, const char *pszValue,
3037 : const char *pszDomain)
3038 : {
3039 1682 : if (EQUAL(DESCRIPTION_KEY, pszName) && EQUAL(pszDomain, ""))
3040 : {
3041 128 : if (m_eAccessMode == TABRead)
3042 : {
3043 0 : CPLError(CE_Warning, CPLE_AppDefined,
3044 : "Description will not save in TAB file in readonly mode.");
3045 : }
3046 :
3047 128 : m_bNeedTABRewrite = TRUE;
3048 128 : std::shared_ptr<char> oEscapedString(EscapeString(pszValue), CPLFree);
3049 128 : auto result = IMapInfoFile::SetMetadataItem(DESCRIPTION_KEY,
3050 128 : oEscapedString.get());
3051 128 : if (oEscapedString)
3052 : {
3053 3 : CPLDebug("MITAB", "Set description to '%s'", oEscapedString.get());
3054 : }
3055 128 : return result;
3056 : }
3057 1554 : return IMapInfoFile::SetMetadataItem(pszName, pszValue, pszDomain);
3058 : }
3059 :
3060 : /**********************************************************************
3061 : * TABFile::GetSpatialRef()
3062 : *
3063 : * Returns a reference to an OGRSpatialReference for this dataset.
3064 : * If the projection parameters have not been parsed yet, then we will
3065 : * parse them before returning.
3066 : *
3067 : * The returned object is owned and maintained by this TABFile and
3068 : * should not be modified or freed by the caller.
3069 : *
3070 : * Returns NULL if the SpatialRef cannot be accessed.
3071 : **********************************************************************/
3072 206568 : const OGRSpatialReference *TABFile::GetSpatialRef() const
3073 : {
3074 206568 : if (m_poMAPFile == nullptr)
3075 : {
3076 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3077 : "GetSpatialRef() failed: file has not been opened yet.");
3078 0 : return nullptr;
3079 : }
3080 :
3081 206568 : if (GetGeomType() == wkbNone)
3082 1 : return nullptr;
3083 :
3084 : /*-----------------------------------------------------------------
3085 : * If projection params have already been processed, just use them.
3086 : *----------------------------------------------------------------*/
3087 206567 : if (m_poSpatialRef != nullptr)
3088 205282 : return m_poSpatialRef;
3089 :
3090 : /*-----------------------------------------------------------------
3091 : * Fetch the parameters from the header.
3092 : *----------------------------------------------------------------*/
3093 : TABProjInfo sTABProj;
3094 :
3095 1285 : TABMAPHeaderBlock *poHeader = nullptr;
3096 2570 : if ((poHeader = m_poMAPFile->GetHeaderBlock()) == nullptr ||
3097 1285 : poHeader->GetProjInfo(&sTABProj) != 0)
3098 : {
3099 0 : CPLError(CE_Failure, CPLE_FileIO,
3100 : "GetSpatialRef() failed reading projection parameters.");
3101 0 : return nullptr;
3102 : }
3103 :
3104 1285 : m_poSpatialRef = TABFileGetSpatialRefFromTABProj(sTABProj);
3105 1285 : return m_poSpatialRef;
3106 : }
3107 :
3108 : /**********************************************************************
3109 : * TABFile::SetSpatialRef()
3110 : *
3111 : * Set the OGRSpatialReference for this dataset.
3112 : * A reference to the OGRSpatialReference will be kept, and it will also
3113 : * be converted into a TABProjInfo to be stored in the .MAP header.
3114 : *
3115 : * Returns 0 on success, and -1 on error.
3116 : **********************************************************************/
3117 33 : int TABFile::SetSpatialRef(OGRSpatialReference *poSpatialRef)
3118 : {
3119 33 : if (m_eAccessMode != TABWrite)
3120 : {
3121 0 : CPLError(CE_Failure, CPLE_NotSupported,
3122 : "SetSpatialRef() can be used only with Write access.");
3123 0 : return -1;
3124 : }
3125 :
3126 33 : if (m_poMAPFile == nullptr)
3127 : {
3128 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3129 : "SetSpatialRef() failed: file has not been opened yet.");
3130 0 : return -1;
3131 : }
3132 :
3133 33 : if (poSpatialRef == nullptr)
3134 : {
3135 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
3136 : "SetSpatialRef() failed: Called with NULL poSpatialRef.");
3137 0 : return -1;
3138 : }
3139 :
3140 : /*-----------------------------------------------------------------
3141 : * Keep a copy of the OGRSpatialReference...
3142 : * Note: we have to take the reference count into account...
3143 : *----------------------------------------------------------------*/
3144 33 : if (m_poSpatialRef && m_poSpatialRef->Dereference() == 0)
3145 0 : delete m_poSpatialRef;
3146 :
3147 33 : m_poSpatialRef = poSpatialRef->Clone();
3148 :
3149 : TABProjInfo sTABProj;
3150 33 : int nParamCount = 0;
3151 33 : TABFileGetTABProjFromSpatialRef(poSpatialRef, sTABProj, nParamCount);
3152 :
3153 : /*-----------------------------------------------------------------
3154 : * Set the new parameters in the .MAP header.
3155 : * This will also trigger lookup of default bounds for the projection.
3156 : *----------------------------------------------------------------*/
3157 33 : if (SetProjInfo(&sTABProj) != 0)
3158 : {
3159 0 : CPLError(CE_Failure, CPLE_FileIO,
3160 : "SetSpatialRef() failed setting projection parameters.");
3161 0 : return -1;
3162 : }
3163 :
3164 33 : return 0;
3165 : }
|