Line data Source code
1 : /**********************************************************************
2 : *
3 : * Name: mitab_idfile.cpp
4 : * Project: MapInfo TAB Read/Write library
5 : * Language: C++
6 : * Purpose: Implementation of the TABIDFile class used to handle
7 : * reading/writing of the .ID file attached to a .MAP file
8 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
9 : *
10 : **********************************************************************
11 : * Copyright (c) 1999, 2000, Daniel Morissette
12 : * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
13 : *
14 : * Permission is hereby granted, free of charge, to any person obtaining a
15 : * copy of this software and associated documentation files (the "Software"),
16 : * to deal in the Software without restriction, including without limitation
17 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 : * and/or sell copies of the Software, and to permit persons to whom the
19 : * Software is furnished to do so, subject to the following conditions:
20 : *
21 : * The above copyright notice and this permission notice shall be included
22 : * in all copies or substantial portions of the Software.
23 : *
24 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 : * DEALINGS IN THE SOFTWARE.
31 : **********************************************************************/
32 :
33 : #include "cpl_port.h"
34 : #include "mitab.h"
35 :
36 : #include <algorithm>
37 : #include <limits.h>
38 : #include <string.h>
39 :
40 : #include "cpl_conv.h"
41 : #include "cpl_error.h"
42 : #include "cpl_vsi.h"
43 : #include "mitab_priv.h"
44 : #include "mitab_utils.h"
45 :
46 : /*=====================================================================
47 : * class TABIDFile
48 : *====================================================================*/
49 :
50 : /**********************************************************************
51 : * TABIDFile::TABIDFile()
52 : *
53 : * Constructor.
54 : **********************************************************************/
55 1425 : TABIDFile::TABIDFile()
56 : : m_pszFname(nullptr), m_fp(nullptr), m_eAccessMode(TABRead),
57 1425 : m_poIDBlock(nullptr), m_nBlockSize(0), m_nMaxId(-1)
58 : {
59 1425 : }
60 :
61 : /**********************************************************************
62 : * TABIDFile::~TABIDFile()
63 : *
64 : * Destructor.
65 : **********************************************************************/
66 2850 : TABIDFile::~TABIDFile()
67 : {
68 1425 : Close();
69 1425 : }
70 :
71 : /**********************************************************************
72 : * TABIDFile::Open()
73 : *
74 : * Compatibility layer with new interface.
75 : * Return 0 on success, -1 in case of failure.
76 : **********************************************************************/
77 :
78 0 : int TABIDFile::Open(const char *pszFname, const char *pszAccess)
79 : {
80 : // cppcheck-suppress nullPointer
81 0 : if (STARTS_WITH_CI(pszAccess, "r"))
82 0 : return Open(pszFname, TABRead);
83 0 : if (STARTS_WITH_CI(pszAccess, "w"))
84 0 : return Open(pszFname, TABWrite);
85 :
86 0 : CPLError(CE_Failure, CPLE_FileIO,
87 : "Open() failed: access mode \"%s\" not supported", pszAccess);
88 0 : return -1;
89 : }
90 :
91 : /**********************************************************************
92 : * TABIDFile::Open()
93 : *
94 : * Open a .ID file, and initialize the structures to be ready to read
95 : * objects from it.
96 : *
97 : * If the filename that is passed in contains a .MAP extension then
98 : * the extension will be changed to .ID before trying to open the file.
99 : *
100 : * Returns 0 on success, -1 on error.
101 : **********************************************************************/
102 1425 : int TABIDFile::Open(const char *pszFname, TABAccess eAccess)
103 : {
104 1425 : if (m_fp)
105 : {
106 0 : CPLError(CE_Failure, CPLE_FileIO,
107 : "Open() failed: object already contains an open file");
108 0 : return -1;
109 : }
110 :
111 : // Validate access mode and make sure we use binary access.
112 : // Note that in Write mode we need TABReadWrite since we do random
113 : // updates in the index as data blocks are split.
114 1425 : const char *pszAccess = nullptr;
115 1425 : if (eAccess == TABRead)
116 : {
117 236 : m_eAccessMode = TABRead;
118 236 : pszAccess = "rb";
119 : }
120 1189 : else if (eAccess == TABWrite)
121 : {
122 116 : m_eAccessMode = TABReadWrite;
123 116 : pszAccess = "wb+";
124 : }
125 1073 : else if (eAccess == TABReadWrite)
126 : {
127 1073 : m_eAccessMode = TABReadWrite;
128 1073 : pszAccess = "rb+";
129 : }
130 : else
131 : {
132 0 : CPLError(CE_Failure, CPLE_FileIO,
133 : "Open() failed: access mode \"%d\" not supported", eAccess);
134 0 : return -1;
135 : }
136 :
137 : // Change .MAP extension to .ID if necessary.
138 1425 : m_pszFname = CPLStrdup(pszFname);
139 :
140 1425 : const int nLen = static_cast<int>(strlen(m_pszFname));
141 1425 : if (nLen > 4 && strcmp(m_pszFname + nLen - 4, ".MAP") == 0)
142 3 : strcpy(m_pszFname + nLen - 4, ".ID");
143 1422 : else if (nLen > 4 && strcmp(m_pszFname + nLen - 4, ".map") == 0)
144 1422 : strcpy(m_pszFname + nLen - 4, ".id");
145 :
146 : #ifndef _WIN32
147 : // Change .MAP extension to .ID if necessary.
148 1425 : TABAdjustFilenameExtension(m_pszFname);
149 : #endif
150 :
151 : // Open file.
152 1425 : m_fp = VSIFOpenL(m_pszFname, pszAccess);
153 :
154 1425 : if (m_fp == nullptr)
155 : {
156 0 : CPLError(CE_Failure, CPLE_FileIO, "Open() failed for %s", m_pszFname);
157 0 : CPLFree(m_pszFname);
158 0 : m_pszFname = nullptr;
159 0 : return -1;
160 : }
161 :
162 1425 : if (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite)
163 : {
164 : // READ access:
165 : // Establish the number of object IDs from the size of the file.
166 : VSIStatBufL sStatBuf;
167 1425 : if (VSIStatL(m_pszFname, &sStatBuf) == -1)
168 : {
169 0 : CPLError(CE_Failure, CPLE_FileIO, "stat() failed for %s",
170 : m_pszFname);
171 0 : Close();
172 0 : return -1;
173 : }
174 :
175 1425 : if (static_cast<vsi_l_offset>(sStatBuf.st_size) >
176 : static_cast<vsi_l_offset>(INT_MAX / 4))
177 0 : m_nMaxId = INT_MAX / 4;
178 : else
179 1425 : m_nMaxId = static_cast<int>(sStatBuf.st_size / 4);
180 1425 : m_nBlockSize = std::min(1024, m_nMaxId * 4);
181 :
182 : // Read the first block from the file.
183 1425 : m_poIDBlock = new TABRawBinBlock(m_eAccessMode, FALSE);
184 :
185 1425 : if (m_nMaxId == 0)
186 : {
187 : // .ID file size = 0 ... just allocate a blank block but
188 : // it won't get really used anyways.
189 127 : m_nBlockSize = 512;
190 127 : m_poIDBlock->InitNewBlock(m_fp, m_nBlockSize, 0);
191 : }
192 1298 : else if (m_poIDBlock->ReadFromFile(m_fp, 0, m_nBlockSize) != 0)
193 : {
194 : // CPLError() has already been called.
195 0 : Close();
196 0 : return -1;
197 1425 : }
198 : }
199 : else
200 : {
201 : // WRITE access:
202 : // Get ready to write to the file.
203 0 : m_poIDBlock = new TABRawBinBlock(m_eAccessMode, FALSE);
204 0 : m_nMaxId = 0;
205 0 : m_nBlockSize = 1024;
206 0 : m_poIDBlock->InitNewBlock(m_fp, m_nBlockSize, 0);
207 : }
208 :
209 1425 : return 0;
210 : }
211 :
212 : /**********************************************************************
213 : * TABIDFile::Close()
214 : *
215 : * Close current file, and release all memory used.
216 : *
217 : * Returns 0 on success, -1 on error.
218 : **********************************************************************/
219 2850 : int TABIDFile::Close()
220 : {
221 2850 : if (m_fp == nullptr)
222 1425 : return 0;
223 :
224 : // Write access: commit latest changes to the file.
225 1425 : if (m_eAccessMode != TABRead)
226 1189 : SyncToDisk();
227 :
228 : // Delete all structures
229 1425 : delete m_poIDBlock;
230 1425 : m_poIDBlock = nullptr;
231 :
232 : // Close file
233 1425 : VSIFCloseL(m_fp);
234 1425 : m_fp = nullptr;
235 :
236 1425 : CPLFree(m_pszFname);
237 1425 : m_pszFname = nullptr;
238 :
239 1425 : return 0;
240 : }
241 :
242 : /************************************************************************/
243 : /* SyncToDisk() */
244 : /************************************************************************/
245 :
246 2384 : int TABIDFile::SyncToDisk()
247 : {
248 2384 : if (m_eAccessMode == TABRead)
249 : {
250 0 : CPLError(CE_Failure, CPLE_NotSupported,
251 : "SyncToDisk() can be used only with Write access.");
252 0 : return -1;
253 : }
254 :
255 2384 : if (m_poIDBlock == nullptr)
256 0 : return 0;
257 :
258 2384 : return m_poIDBlock->CommitToFile();
259 : }
260 :
261 : /**********************************************************************
262 : * TABIDFile::GetObjPtr()
263 : *
264 : * Return the offset in the .MAP file where the map object with the
265 : * specified id is located.
266 : *
267 : * Note that object ids are positive and start at 1.
268 : *
269 : * An object Id of '0' means that object has no geometry.
270 : *
271 : * Returns a value >= 0 on success, -1 on error.
272 : **********************************************************************/
273 153043 : GInt32 TABIDFile::GetObjPtr(GInt32 nObjId)
274 : {
275 153043 : if (m_poIDBlock == nullptr)
276 0 : return -1;
277 :
278 153043 : if (nObjId < 1 || nObjId > m_nMaxId)
279 : {
280 0 : CPLError(CE_Failure, CPLE_IllegalArg,
281 : "GetObjPtr(): Invalid object ID %d (valid range is [1..%d])",
282 : nObjId, m_nMaxId);
283 0 : return -1;
284 : }
285 :
286 153043 : if (m_poIDBlock->GotoByteInFile((nObjId - 1) * 4) != 0)
287 0 : return -1;
288 :
289 153043 : return m_poIDBlock->ReadInt32();
290 : }
291 :
292 : /**********************************************************************
293 : * TABIDFile::SetObjPtr()
294 : *
295 : * Set the offset in the .MAP file where the map object with the
296 : * specified id is located.
297 : *
298 : * Note that object ids are positive and start at 1.
299 : *
300 : * An object Id of '0' means that object has no geometry.
301 : *
302 : * Returns a value of 0 on success, -1 on error.
303 : **********************************************************************/
304 27218 : int TABIDFile::SetObjPtr(GInt32 nObjId, GInt32 nObjPtr)
305 : {
306 27218 : if (m_poIDBlock == nullptr)
307 0 : return -1;
308 :
309 27218 : if (m_eAccessMode == TABRead)
310 : {
311 0 : CPLError(CE_Failure, CPLE_NotSupported,
312 : "SetObjPtr() can be used only with Write access.");
313 0 : return -1;
314 : }
315 :
316 27218 : if (nObjId < 1)
317 : {
318 0 : CPLError(
319 : CE_Failure, CPLE_IllegalArg,
320 : "SetObjPtr(): Invalid object ID %d (must be greater than zero)",
321 : nObjId);
322 0 : return -1;
323 : }
324 :
325 : // GotoByteInFile() will automagically commit current block and init
326 : // a new one if necessary.
327 27218 : const GInt32 nLastIdBlock = ((m_nMaxId - 1) * 4) / m_nBlockSize;
328 27218 : const GInt32 nTargetIdBlock = ((nObjId - 1) * 4) / m_nBlockSize;
329 27218 : if (m_nMaxId > 0 && nTargetIdBlock <= nLastIdBlock)
330 : {
331 : // Pass second arg to GotoByteInFile() to force reading from file
332 : // when going back to blocks already committed.
333 26798 : if (m_poIDBlock->GotoByteInFile((nObjId - 1) * 4, TRUE) != 0)
334 0 : return -1;
335 : }
336 : else
337 : {
338 : // If we reach EOF then a new empty block will have to be allocated.
339 420 : if (m_poIDBlock->GotoByteInFile((nObjId - 1) * 4) != 0)
340 0 : return -1;
341 : }
342 :
343 27218 : m_nMaxId = std::max(m_nMaxId, nObjId);
344 :
345 27218 : return m_poIDBlock->WriteInt32(nObjPtr);
346 : }
347 :
348 : /**********************************************************************
349 : * TABIDFile::GetMaxObjId()
350 : *
351 : * Return the value of the biggest valid object id.
352 : *
353 : * Note that object ids are positive and start at 1.
354 : *
355 : * Returns a value >= 0 on success, -1 on error.
356 : **********************************************************************/
357 0 : GInt32 TABIDFile::GetMaxObjId()
358 : {
359 0 : return m_nMaxId;
360 : }
361 :
362 : /**********************************************************************
363 : * TABIDFile::Dump()
364 : *
365 : * Dump block contents... available only in DEBUG mode.
366 : **********************************************************************/
367 : #ifdef DEBUG
368 :
369 0 : void TABIDFile::Dump(FILE *fpOut /*=NULL*/)
370 : {
371 0 : if (fpOut == nullptr)
372 0 : fpOut = stdout;
373 :
374 0 : fprintf(fpOut, "----- TABIDFile::Dump() -----\n");
375 :
376 0 : if (m_fp == nullptr)
377 : {
378 0 : fprintf(fpOut, "File is not opened.\n");
379 : }
380 : else
381 : {
382 0 : fprintf(fpOut, "File is opened: %s\n", m_pszFname);
383 0 : fprintf(fpOut, "Current index block follows ...\n\n");
384 0 : m_poIDBlock->Dump(fpOut);
385 0 : fprintf(fpOut, "... end of index block.\n\n");
386 : }
387 :
388 0 : fflush(fpOut);
389 0 : }
390 :
391 : #endif // DEBUG
|