Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: DXF Translator
4 : * Purpose: Implements low level DXF reading with caching and parsing of
5 : * of the code/value pairs.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "ogr_dxf.h"
15 : #include "cpl_conv.h"
16 : #include "cpl_string.h"
17 : #include "cpl_csv.h"
18 :
19 : /************************************************************************/
20 : /* OGRDXFReader() */
21 : /************************************************************************/
22 :
23 281 : OGRDXFReader::OGRDXFReader()
24 : : fp(nullptr), iSrcBufferOffset(0), nSrcBufferBytes(0),
25 281 : iSrcBufferFileOffset(0), achSrcBuffer{}, nLastValueSize(0), nLineNumber(0)
26 : {
27 281 : }
28 :
29 : /************************************************************************/
30 : /* ~OGRDXFReader() */
31 : /************************************************************************/
32 :
33 281 : OGRDXFReader::~OGRDXFReader()
34 :
35 : {
36 281 : }
37 :
38 : /************************************************************************/
39 : /* Initialize() */
40 : /************************************************************************/
41 :
42 281 : void OGRDXFReader::Initialize(VSILFILE *fpIn)
43 :
44 : {
45 281 : fp = fpIn;
46 281 : }
47 :
48 : /************************************************************************/
49 : /* ResetReadPointer() */
50 : /************************************************************************/
51 :
52 202 : void OGRDXFReader::ResetReadPointer(unsigned int iNewOffset,
53 : int nNewLineNumber /* = 0 */)
54 :
55 : {
56 202 : nSrcBufferBytes = 0;
57 202 : iSrcBufferOffset = 0;
58 202 : iSrcBufferFileOffset = iNewOffset;
59 202 : nLastValueSize = 0;
60 202 : nLineNumber = nNewLineNumber;
61 :
62 202 : VSIFSeekL(fp, iNewOffset, SEEK_SET);
63 202 : }
64 :
65 : /************************************************************************/
66 : /* LoadDiskChunk() */
67 : /* */
68 : /* Load another block (512 bytes) of input from the source */
69 : /* file. */
70 : /************************************************************************/
71 :
72 16599 : void OGRDXFReader::LoadDiskChunk()
73 :
74 : {
75 16599 : if (nSrcBufferBytes - iSrcBufferOffset > 511)
76 0 : return;
77 :
78 16599 : if (iSrcBufferOffset > 0)
79 : {
80 16055 : CPLAssert(nSrcBufferBytes <= 1024);
81 16055 : CPLAssert(iSrcBufferOffset <= nSrcBufferBytes);
82 :
83 16055 : memmove(achSrcBuffer, achSrcBuffer + iSrcBufferOffset,
84 16055 : nSrcBufferBytes - iSrcBufferOffset);
85 16055 : iSrcBufferFileOffset += iSrcBufferOffset;
86 16055 : nSrcBufferBytes -= iSrcBufferOffset;
87 16055 : iSrcBufferOffset = 0;
88 : }
89 :
90 16599 : nSrcBufferBytes +=
91 16599 : static_cast<int>(VSIFReadL(achSrcBuffer + nSrcBufferBytes, 1, 512, fp));
92 16599 : achSrcBuffer[nSrcBufferBytes] = '\0';
93 :
94 16599 : CPLAssert(nSrcBufferBytes <= 1024);
95 16599 : CPLAssert(iSrcBufferOffset <= nSrcBufferBytes);
96 : }
97 :
98 : /************************************************************************/
99 : /* ReadValue() */
100 : /* */
101 : /* Read one type code and value line pair from the DXF file. */
102 : /************************************************************************/
103 :
104 153895 : int OGRDXFReader::ReadValueRaw(char *pszValueBuf, int nValueBufSize)
105 :
106 : {
107 : /* -------------------------------------------------------------------- */
108 : /* Make sure we have lots of data in our buffer for one value. */
109 : /* -------------------------------------------------------------------- */
110 153895 : if (nSrcBufferBytes - iSrcBufferOffset < 512)
111 16596 : LoadDiskChunk();
112 :
113 : /* -------------------------------------------------------------------- */
114 : /* Capture the value code, and skip past it. */
115 : /* -------------------------------------------------------------------- */
116 153895 : unsigned int iStartSrcBufferOffset = iSrcBufferOffset;
117 153895 : int nValueCode = atoi(achSrcBuffer + iSrcBufferOffset);
118 :
119 153895 : nLineNumber++;
120 :
121 : // proceed to newline.
122 1544300 : while (achSrcBuffer[iSrcBufferOffset] != '\n' &&
123 615361 : achSrcBuffer[iSrcBufferOffset] != '\r' &&
124 461648 : achSrcBuffer[iSrcBufferOffset] != '\0')
125 461466 : iSrcBufferOffset++;
126 :
127 153895 : if (achSrcBuffer[iSrcBufferOffset] == '\0')
128 182 : return -1;
129 :
130 : // skip past newline. CR, CRLF, or LFCR
131 153713 : if ((achSrcBuffer[iSrcBufferOffset] == '\r' &&
132 5820 : achSrcBuffer[iSrcBufferOffset + 1] == '\n') ||
133 147909 : (achSrcBuffer[iSrcBufferOffset] == '\n' &&
134 147893 : achSrcBuffer[iSrcBufferOffset + 1] == '\r'))
135 5805 : iSrcBufferOffset += 2;
136 : else
137 147908 : iSrcBufferOffset += 1;
138 :
139 153713 : if (achSrcBuffer[iSrcBufferOffset] == '\0')
140 0 : return -1;
141 :
142 : /* -------------------------------------------------------------------- */
143 : /* Capture the value string. */
144 : /* -------------------------------------------------------------------- */
145 153713 : unsigned int iEOL = iSrcBufferOffset;
146 307426 : CPLString osValue;
147 :
148 153713 : nLineNumber++;
149 :
150 : // proceed to newline.
151 1184440 : while (achSrcBuffer[iEOL] != '\n' && achSrcBuffer[iEOL] != '\r' &&
152 1030730 : achSrcBuffer[iEOL] != '\0')
153 1030720 : iEOL++;
154 :
155 153713 : bool bLongLine = false;
156 153715 : while (achSrcBuffer[iEOL] == '\0' ||
157 153712 : (achSrcBuffer[iEOL] == '\r' && achSrcBuffer[iEOL + 1] == '\0'))
158 : {
159 : // The line is longer than the buffer (or the line ending is split at
160 : // end of buffer). Let's copy what we have so far into our string, and
161 : // read more
162 3 : const auto nValueLength = osValue.length();
163 :
164 3 : if (nValueLength + iEOL - iSrcBufferOffset > 1048576)
165 : {
166 0 : CPLError(CE_Failure, CPLE_AppDefined, "Line %d is too long",
167 : nLineNumber);
168 0 : return -1;
169 : }
170 :
171 3 : osValue.resize(nValueLength + iEOL - iSrcBufferOffset, '\0');
172 3 : std::copy(achSrcBuffer + iSrcBufferOffset, achSrcBuffer + iEOL,
173 3 : osValue.begin() + nValueLength);
174 :
175 3 : iSrcBufferOffset = iEOL;
176 3 : LoadDiskChunk();
177 3 : iEOL = iSrcBufferOffset;
178 3 : bLongLine = true;
179 :
180 : // Have we prematurely reached the end of the file?
181 3 : if (achSrcBuffer[iEOL] == '\0')
182 1 : return -1;
183 :
184 : // Proceed to newline again
185 437 : while (achSrcBuffer[iEOL] != '\n' && achSrcBuffer[iEOL] != '\r' &&
186 435 : achSrcBuffer[iEOL] != '\0')
187 435 : iEOL++;
188 : }
189 :
190 153712 : size_t nValueBufLen = 0;
191 :
192 : // If this was an extremely long line, copy from osValue into the buffer
193 153712 : if (!osValue.empty())
194 : {
195 2 : strncpy(pszValueBuf, osValue.c_str(), nValueBufSize - 1);
196 2 : pszValueBuf[nValueBufSize - 1] = '\0';
197 :
198 2 : nValueBufLen = strlen(pszValueBuf);
199 :
200 2 : if (static_cast<int>(osValue.length()) > nValueBufSize - 1)
201 : {
202 2 : CPLDebug("DXF", "Long line truncated to %d characters.\n%s...",
203 : nValueBufSize - 1, pszValueBuf);
204 : }
205 : }
206 :
207 : // Copy the last (normally, the only) section of this line into the buffer
208 153712 : if (static_cast<int>(iEOL - iSrcBufferOffset) >
209 153712 : nValueBufSize - static_cast<int>(nValueBufLen) - 1)
210 : {
211 2 : strncpy(pszValueBuf + nValueBufLen, achSrcBuffer + iSrcBufferOffset,
212 2 : nValueBufSize - static_cast<int>(nValueBufLen) - 1);
213 2 : pszValueBuf[nValueBufSize - 1] = '\0';
214 :
215 2 : CPLDebug("DXF", "Long line truncated to %d characters.\n%s...",
216 : nValueBufSize - 1, pszValueBuf);
217 : }
218 : else
219 : {
220 153710 : strncpy(pszValueBuf + nValueBufLen, achSrcBuffer + iSrcBufferOffset,
221 153710 : iEOL - iSrcBufferOffset);
222 153710 : pszValueBuf[nValueBufLen + iEOL - iSrcBufferOffset] = '\0';
223 : }
224 :
225 153712 : iSrcBufferOffset = iEOL;
226 :
227 : // skip past newline. CR, CRLF, or LFCR
228 153712 : if ((achSrcBuffer[iSrcBufferOffset] == '\r' &&
229 5816 : achSrcBuffer[iSrcBufferOffset + 1] == '\n') ||
230 147908 : (achSrcBuffer[iSrcBufferOffset] == '\n' &&
231 147896 : achSrcBuffer[iSrcBufferOffset + 1] == '\r'))
232 5804 : iSrcBufferOffset += 2;
233 : else
234 147908 : iSrcBufferOffset += 1;
235 :
236 : /* -------------------------------------------------------------------- */
237 : /* Record how big this value was, so it can be unread safely. */
238 : /* -------------------------------------------------------------------- */
239 153712 : if (bLongLine)
240 2 : nLastValueSize = 0;
241 : else
242 : {
243 153710 : nLastValueSize = iSrcBufferOffset - iStartSrcBufferOffset;
244 153710 : CPLAssert(nLastValueSize > 0);
245 : }
246 :
247 153712 : return nValueCode;
248 : }
249 :
250 153895 : int OGRDXFReader::ReadValue(char *pszValueBuf, int nValueBufSize)
251 : {
252 : int nValueCode;
253 : while (true)
254 : {
255 153895 : nValueCode = ReadValueRaw(pszValueBuf, nValueBufSize);
256 153895 : if (nValueCode == 999)
257 : {
258 : // Skip comments
259 13 : continue;
260 : }
261 153882 : break;
262 : }
263 153882 : return nValueCode;
264 : }
265 :
266 : /************************************************************************/
267 : /* UnreadValue() */
268 : /* */
269 : /* Unread the last value read, accomplished by resetting the */
270 : /* read pointer. */
271 : /************************************************************************/
272 :
273 1958 : void OGRDXFReader::UnreadValue()
274 :
275 : {
276 1958 : if (nLastValueSize == 0)
277 : {
278 1 : CPLError(CE_Failure, CPLE_AppDefined,
279 : "Cannot UnreadValue(), likely due to a previous long line");
280 1 : return;
281 : }
282 1957 : CPLAssert(iSrcBufferOffset >= nLastValueSize);
283 1957 : CPLAssert(nLastValueSize > 0);
284 :
285 1957 : iSrcBufferOffset -= nLastValueSize;
286 1957 : nLineNumber -= 2;
287 1957 : nLastValueSize = 0;
288 : }
|