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