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 355 : OGRDXFReader::OGRDXFReader()
24 : : fp(nullptr), iSrcBufferOffset(0), nSrcBufferBytes(0),
25 355 : iSrcBufferFileOffset(0), achSrcBuffer{}, nLastValueSize(0), nLineNumber(0)
26 : {
27 355 : }
28 :
29 : /************************************************************************/
30 : /* ~OGRDXFReader() */
31 : /************************************************************************/
32 :
33 355 : OGRDXFReader::~OGRDXFReader()
34 :
35 : {
36 355 : }
37 :
38 : /************************************************************************/
39 : /* Initialize() */
40 : /************************************************************************/
41 :
42 355 : void OGRDXFReader::Initialize(VSILFILE *fpIn)
43 :
44 : {
45 355 : fp = fpIn;
46 355 : }
47 :
48 : /************************************************************************/
49 : /* ResetReadPointer() */
50 : /************************************************************************/
51 :
52 241 : void OGRDXFReader::ResetReadPointer(unsigned int iNewOffset,
53 : int nNewLineNumber /* = 0 */)
54 :
55 : {
56 241 : nSrcBufferBytes = 0;
57 241 : iSrcBufferOffset = 0;
58 241 : iSrcBufferFileOffset = iNewOffset;
59 241 : nLastValueSize = 0;
60 241 : nLineNumber = nNewLineNumber;
61 :
62 241 : VSIFSeekL(fp, iNewOffset, SEEK_SET);
63 241 : }
64 :
65 : /************************************************************************/
66 : /* LoadDiskChunk() */
67 : /* */
68 : /* Load another block (512 bytes) of input from the source */
69 : /* file. */
70 : /************************************************************************/
71 :
72 20695 : void OGRDXFReader::LoadDiskChunk()
73 :
74 : {
75 20695 : if (nSrcBufferBytes - iSrcBufferOffset > 511)
76 0 : return;
77 :
78 20695 : if (iSrcBufferOffset > 0)
79 : {
80 20050 : CPLAssert(nSrcBufferBytes <= 1024);
81 20050 : CPLAssert(iSrcBufferOffset <= nSrcBufferBytes);
82 :
83 20050 : memmove(achSrcBuffer, achSrcBuffer + iSrcBufferOffset,
84 20050 : nSrcBufferBytes - iSrcBufferOffset);
85 20050 : iSrcBufferFileOffset += iSrcBufferOffset;
86 20050 : nSrcBufferBytes -= iSrcBufferOffset;
87 20050 : iSrcBufferOffset = 0;
88 : }
89 :
90 20695 : nSrcBufferBytes +=
91 20695 : static_cast<int>(VSIFReadL(achSrcBuffer + nSrcBufferBytes, 1, 512, fp));
92 20695 : achSrcBuffer[nSrcBufferBytes] = '\0';
93 :
94 20695 : CPLAssert(nSrcBufferBytes <= 1024);
95 20695 : 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 193971 : 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 193971 : if (nSrcBufferBytes - iSrcBufferOffset < 512)
111 20692 : LoadDiskChunk();
112 :
113 : /* -------------------------------------------------------------------- */
114 : /* Capture the value code, and skip past it. */
115 : /* -------------------------------------------------------------------- */
116 193971 : unsigned int iStartSrcBufferOffset = iSrcBufferOffset;
117 193971 : int nValueCode = atoi(achSrcBuffer + iSrcBufferOffset);
118 :
119 193971 : nLineNumber++;
120 :
121 : // proceed to newline.
122 1945900 : while (achSrcBuffer[iSrcBufferOffset] != '\n' &&
123 775495 : achSrcBuffer[iSrcBufferOffset] != '\r' &&
124 581754 : achSrcBuffer[iSrcBufferOffset] != '\0')
125 581524 : iSrcBufferOffset++;
126 :
127 193971 : if (achSrcBuffer[iSrcBufferOffset] == '\0')
128 230 : return -1;
129 :
130 : // skip past newline. CR, CRLF, or LFCR
131 193741 : if ((achSrcBuffer[iSrcBufferOffset] == '\r' &&
132 7131 : achSrcBuffer[iSrcBufferOffset + 1] == '\n') ||
133 186626 : (achSrcBuffer[iSrcBufferOffset] == '\n' &&
134 186610 : achSrcBuffer[iSrcBufferOffset + 1] == '\r'))
135 7116 : iSrcBufferOffset += 2;
136 : else
137 186625 : iSrcBufferOffset += 1;
138 :
139 193741 : if (achSrcBuffer[iSrcBufferOffset] == '\0')
140 0 : return -1;
141 :
142 : /* -------------------------------------------------------------------- */
143 : /* Capture the value string. */
144 : /* -------------------------------------------------------------------- */
145 193741 : unsigned int iEOL = iSrcBufferOffset;
146 387482 : CPLString osValue;
147 :
148 193741 : nLineNumber++;
149 :
150 : // proceed to newline.
151 1488090 : while (achSrcBuffer[iEOL] != '\n' && achSrcBuffer[iEOL] != '\r' &&
152 1294350 : achSrcBuffer[iEOL] != '\0')
153 1294350 : iEOL++;
154 :
155 193741 : bool bLongLine = false;
156 193743 : while (achSrcBuffer[iEOL] == '\0' ||
157 193740 : (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 193740 : size_t nValueBufLen = 0;
191 :
192 : // If this was an extremely long line, copy from osValue into the buffer
193 193740 : 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 193740 : if (static_cast<int>(iEOL - iSrcBufferOffset) >
209 193740 : 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 193738 : strncpy(pszValueBuf + nValueBufLen, achSrcBuffer + iSrcBufferOffset,
221 193738 : iEOL - iSrcBufferOffset);
222 193738 : pszValueBuf[nValueBufLen + iEOL - iSrcBufferOffset] = '\0';
223 : }
224 :
225 193740 : iSrcBufferOffset = iEOL;
226 :
227 : // skip past newline. CR, CRLF, or LFCR
228 193740 : if ((achSrcBuffer[iSrcBufferOffset] == '\r' &&
229 7127 : achSrcBuffer[iSrcBufferOffset + 1] == '\n') ||
230 186625 : (achSrcBuffer[iSrcBufferOffset] == '\n' &&
231 186613 : achSrcBuffer[iSrcBufferOffset + 1] == '\r'))
232 7115 : iSrcBufferOffset += 2;
233 : else
234 186625 : iSrcBufferOffset += 1;
235 :
236 : /* -------------------------------------------------------------------- */
237 : /* Record how big this value was, so it can be unread safely. */
238 : /* -------------------------------------------------------------------- */
239 193740 : if (bLongLine)
240 2 : nLastValueSize = 0;
241 : else
242 : {
243 193738 : nLastValueSize = iSrcBufferOffset - iStartSrcBufferOffset;
244 193738 : CPLAssert(nLastValueSize > 0);
245 : }
246 :
247 193740 : return nValueCode;
248 : }
249 :
250 193971 : int OGRDXFReader::ReadValue(char *pszValueBuf, int nValueBufSize)
251 : {
252 : int nValueCode;
253 : while (true)
254 : {
255 193971 : nValueCode = ReadValueRaw(pszValueBuf, nValueBufSize);
256 193971 : if (nValueCode == 999)
257 : {
258 : // Skip comments
259 13 : continue;
260 : }
261 193958 : break;
262 : }
263 193958 : return nValueCode;
264 : }
265 :
266 : /************************************************************************/
267 : /* UnreadValue() */
268 : /* */
269 : /* Unread the last value read, accomplished by resetting the */
270 : /* read pointer. */
271 : /************************************************************************/
272 :
273 2281 : void OGRDXFReader::UnreadValue()
274 :
275 : {
276 2281 : 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 2280 : CPLAssert(iSrcBufferOffset >= nLastValueSize);
283 2280 : CPLAssert(nLastValueSize > 0);
284 :
285 2280 : iSrcBufferOffset -= nLastValueSize;
286 2280 : nLineNumber -= 2;
287 2280 : nLastValueSize = 0;
288 : }
|