1 /**********************************************************************
2  *
3  * Name:     mitab_datfile.cpp
4  * Project:  MapInfo TAB Read/Write library
5  * Language: C++
6  * Purpose:  Implementation of the MIDDATAFile class used to handle
7  *           reading/writing of the MID/MIF files
8  * Author:   Stephane Villeneuve, stephane.v@videotron.ca
9  *
10  **********************************************************************
11  * Copyright (c) 1999, 2000, Stephane Villeneuve
12  *
13  * Permission is hereby granted, free of charge, to any person obtaining a
14  * copy of this software and associated documentation files (the "Software"),
15  * to deal in the Software without restriction, including without limitation
16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17  * and/or sell copies of the Software, and to permit persons to whom the
18  * Software is furnished to do so, subject to the following conditions:
19  *
20  * The above copyright notice and this permission notice shall be included
21  * in all copies or substantial portions of the Software.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
26  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29  * DEALINGS IN THE SOFTWARE.
30  **********************************************************************/
31 
32 #include "cpl_port.h"
33 #include "mitab.h"
34 
35 #include <cstdarg>
36 #include <cstddef>
37 
38 #include "cpl_conv.h"
39 #include "cpl_error.h"
40 #include "cpl_string.h"
41 #include "cpl_vsi.h"
42 #include "mitab_priv.h"
43 
44 CPL_CVSID("$Id: mitab_middatafile.cpp 7355f684f451b09efde7b4c59aae57454fa85541 2019-03-17 12:47:10 +0100 Even Rouault $")
45 
46 /*=====================================================================
47  *                      class MIDDATAFile
48  *
49  *====================================================================*/
50 
MIDDATAFile(const char * pszEncoding)51 MIDDATAFile::MIDDATAFile( const char* pszEncoding ) :
52     m_fp(nullptr),
53     m_pszDelimiter("\t"),  // Encom 2003 (was NULL).
54     m_pszFname(nullptr),
55     m_eAccessMode(TABRead),
56     // TODO(schwehr): m_szLastRead({}),
57     // TODO(schwehr): m_szSavedLine({}),
58     m_dfXMultiplier(1.0),
59     m_dfYMultiplier(1.0),
60     m_dfXDisplacement(0.0),
61     m_dfYDisplacement(0.0),
62     m_bEof(FALSE),
63     m_osEncoding(pszEncoding)
64 {
65     m_szLastRead[0] = '\0';
66     m_szSavedLine[0] = '\0';
67 }
68 
~MIDDATAFile()69 MIDDATAFile::~MIDDATAFile() { Close(); }
70 
SaveLine(const char * pszLine)71 void MIDDATAFile::SaveLine(const char *pszLine)
72 {
73     if(pszLine == nullptr)
74     {
75         m_szSavedLine[0] = '\0';
76     }
77     else
78     {
79         CPLStrlcpy(m_szSavedLine, pszLine, MIDMAXCHAR);
80     }
81 }
82 
GetSavedLine()83 const char *MIDDATAFile::GetSavedLine() { return m_szSavedLine; }
84 
Open(const char * pszFname,const char * pszAccess)85 int MIDDATAFile::Open(const char *pszFname, const char *pszAccess)
86 {
87     if(m_fp)
88     {
89         return -1;
90     }
91 
92     // Validate access mode and make sure we use Text access.
93     if(STARTS_WITH_CI(pszAccess, "r"))
94     {
95         m_eAccessMode = TABRead;
96         pszAccess = "rt";
97     }
98     else if(STARTS_WITH_CI(pszAccess, "w"))
99     {
100         m_eAccessMode = TABWrite;
101         pszAccess = "wt";
102     }
103     else
104     {
105         return -1;
106     }
107 
108     // Open file for reading.
109     m_pszFname = CPLStrdup(pszFname);
110     m_fp = VSIFOpenL(m_pszFname, pszAccess);
111 
112     if(m_fp == nullptr)
113     {
114         CPLFree(m_pszFname);
115         m_pszFname = nullptr;
116         return -1;
117     }
118 
119     SetEof(FALSE);
120     return 0;
121 }
122 
Rewind()123 int MIDDATAFile::Rewind()
124 {
125     if(m_fp == nullptr || m_eAccessMode == TABWrite)
126         return -1;
127 
128     else
129     {
130         VSIRewindL(m_fp);
131         SetEof(FALSE);
132     }
133     return 0;
134 }
135 
Close()136 int MIDDATAFile::Close()
137 {
138     if(m_fp == nullptr)
139         return 0;
140 
141     // Close file
142     VSIFCloseL(m_fp);
143     m_fp = nullptr;
144 
145     // clear readline buffer.
146     CPLReadLineL(nullptr);
147 
148     CPLFree(m_pszFname);
149     m_pszFname = nullptr;
150 
151     return 0;
152 }
153 
GetLine()154 const char *MIDDATAFile::GetLine()
155 {
156     if(m_eAccessMode != TABRead)
157     {
158         CPLAssert(false);
159         return nullptr;
160     }
161 
162     const char *pszLine = CPLReadLine2L(m_fp, MIDMAXCHAR, nullptr);
163 
164     if(pszLine == nullptr)
165     {
166         SetEof(TRUE);
167         m_szLastRead[0] = '\0';
168     }
169     else
170     {
171         // Skip leading spaces and tabs except if the delimiter is tab.
172         while(pszLine && (*pszLine == ' ' ||
173                           (*m_pszDelimiter != '\t' && *pszLine == '\t')))
174             pszLine++;
175 
176         CPLStrlcpy(m_szLastRead, pszLine, MIDMAXCHAR);
177     }
178 
179 #if DEBUG_VERBOSE
180     if(pszLine)
181         CPLDebug("MITAB", "pszLine: %s", pszLine);
182 #endif
183 
184     return pszLine;
185 }
186 
GetLastLine()187 const char *MIDDATAFile::GetLastLine()
188 {
189     // Return NULL if EOF.
190     if(GetEof())
191     {
192         return nullptr;
193     }
194     if(m_eAccessMode == TABRead)
195     {
196 #if DEBUG_VERBOSE
197         CPLDebug("MITAB", "m_szLastRead: %s", m_szLastRead);
198 #endif
199         return m_szLastRead;
200     }
201 
202     // We should never get here.  Read/Write mode not implemented.
203     CPLAssert(false);
204     return nullptr;
205 }
206 
WriteLine(const char * pszFormat,...)207 void MIDDATAFile::WriteLine(const char *pszFormat, ...)
208 {
209     va_list args;
210 
211     if(m_eAccessMode == TABWrite && m_fp)
212     {
213         va_start(args, pszFormat);
214         CPLString osStr;
215         osStr.vPrintf(pszFormat, args);
216         VSIFWriteL(osStr.c_str(), 1, osStr.size(), m_fp);
217         va_end(args);
218     }
219     else
220     {
221         CPLAssert(false);
222     }
223 }
224 
SetTranslation(double dfXMul,double dfYMul,double dfXTran,double dfYTran)225 void MIDDATAFile::SetTranslation( double dfXMul,double dfYMul,
226                                   double dfXTran, double dfYTran )
227 {
228     m_dfXMultiplier = dfXMul;
229     m_dfYMultiplier = dfYMul;
230     m_dfXDisplacement = dfXTran;
231     m_dfYDisplacement = dfYTran;
232 }
233 
GetXTrans(double dfX)234 double MIDDATAFile::GetXTrans(double dfX)
235 {
236     return (dfX * m_dfXMultiplier) + m_dfXDisplacement;
237 }
238 
GetYTrans(double dfY)239 double MIDDATAFile::GetYTrans(double dfY)
240 {
241     return (dfY * m_dfYMultiplier) + m_dfYDisplacement;
242 }
243 
IsValidFeature(const char * pszString)244 GBool MIDDATAFile::IsValidFeature(const char *pszString)
245 {
246     char **papszToken = CSLTokenizeString(pszString);
247 
248     if(CSLCount(papszToken) == 0)
249     {
250         CSLDestroy(papszToken);
251         return FALSE;
252     }
253 
254     if(EQUAL(papszToken[0], "NONE") || EQUAL(papszToken[0], "POINT") ||
255        EQUAL(papszToken[0], "LINE") || EQUAL(papszToken[0], "PLINE") ||
256        EQUAL(papszToken[0], "REGION") || EQUAL(papszToken[0], "ARC") ||
257        EQUAL(papszToken[0], "TEXT") || EQUAL(papszToken[0], "RECT") ||
258        EQUAL(papszToken[0], "ROUNDRECT") || EQUAL(papszToken[0], "ELLIPSE") ||
259        EQUAL(papszToken[0], "MULTIPOINT") || EQUAL(papszToken[0], "COLLECTION"))
260     {
261         CSLDestroy(papszToken);
262         return TRUE;
263     }
264 
265     CSLDestroy(papszToken);
266     return FALSE;
267 }
268 
GetEof()269 GBool MIDDATAFile::GetEof() { return m_bEof; }
270 
GetEncoding() const271 const CPLString& MIDDATAFile::GetEncoding() const
272 {
273     return m_osEncoding;
274 }
275 
SetEncoding(const CPLString & osEncoding)276 void MIDDATAFile::SetEncoding( const CPLString& osEncoding )
277 {
278     m_osEncoding = osEncoding;
279 }
280 
SetEof(GBool bEof)281 void MIDDATAFile::SetEof(GBool bEof) { m_bEof = bEof; }
282