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