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