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