Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: TIGER/Line Translator
4 : * Purpose: Implements TigerBaseFile class, providing common services to all
5 : * the tiger file readers.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 1999, Frank Warmerdam
10 : * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "ogr_tiger.h"
16 : #include "cpl_conv.h"
17 : #include "cpl_error.h"
18 : #include "cpl_string.h"
19 :
20 : #include <cinttypes>
21 :
22 : /************************************************************************/
23 : /* TigerFileBase() */
24 : /************************************************************************/
25 :
26 0 : TigerFileBase::TigerFileBase(const TigerRecordInfo *psRTInfoIn,
27 0 : const char *m_pszFileCodeIn)
28 : : poDS(nullptr), pszModule(nullptr), pszShortModule(nullptr),
29 : fpPrimary(nullptr), poFeatureDefn(nullptr), nFeatures(0),
30 : nRecordLength(0), nVersionCode(0), nVersion(TIGER_Unknown),
31 0 : psRTInfo(psRTInfoIn), m_pszFileCode(m_pszFileCodeIn)
32 : {
33 0 : }
34 :
35 : /************************************************************************/
36 : /* ~TigerFileBase() */
37 : /************************************************************************/
38 :
39 0 : TigerFileBase::~TigerFileBase()
40 :
41 : {
42 0 : CPLFree(pszModule);
43 0 : CPLFree(pszShortModule);
44 :
45 0 : if (poFeatureDefn != nullptr)
46 : {
47 0 : poFeatureDefn->Release();
48 0 : poFeatureDefn = nullptr;
49 : }
50 :
51 0 : if (fpPrimary != nullptr)
52 : {
53 0 : VSIFCloseL(fpPrimary);
54 0 : fpPrimary = nullptr;
55 : }
56 0 : }
57 :
58 : /************************************************************************/
59 : /* OpenFile() */
60 : /************************************************************************/
61 :
62 0 : int TigerFileBase::OpenFile(const char *pszModuleToOpen,
63 : const char *pszExtension)
64 :
65 : {
66 :
67 0 : CPLFree(pszModule);
68 0 : pszModule = nullptr;
69 0 : CPLFree(pszShortModule);
70 0 : pszShortModule = nullptr;
71 :
72 0 : if (fpPrimary != nullptr)
73 : {
74 0 : VSIFCloseL(fpPrimary);
75 0 : fpPrimary = nullptr;
76 : }
77 :
78 0 : if (pszModuleToOpen == nullptr)
79 0 : return TRUE;
80 :
81 0 : char *pszFilename = poDS->BuildFilename(pszModuleToOpen, pszExtension);
82 :
83 0 : fpPrimary = VSIFOpenL(pszFilename, "rb");
84 :
85 0 : CPLFree(pszFilename);
86 :
87 0 : if (fpPrimary == nullptr)
88 0 : return FALSE;
89 :
90 0 : pszModule = CPLStrdup(pszModuleToOpen);
91 0 : pszShortModule = CPLStrdup(pszModuleToOpen);
92 0 : for (int i = 0; pszShortModule[i] != '\0'; i++)
93 : {
94 0 : if (pszShortModule[i] == '.')
95 0 : pszShortModule[i] = '\0';
96 : }
97 :
98 0 : SetupVersion();
99 :
100 0 : return TRUE;
101 : }
102 :
103 : /************************************************************************/
104 : /* SetupVersion() */
105 : /************************************************************************/
106 :
107 0 : void TigerFileBase::SetupVersion()
108 :
109 : {
110 : char aszRecordHead[6];
111 :
112 0 : VSIFSeekL(fpPrimary, 0, SEEK_SET);
113 0 : VSIFReadL(aszRecordHead, 1, 5, fpPrimary);
114 0 : aszRecordHead[5] = '\0';
115 0 : nVersionCode = atoi(aszRecordHead + 1);
116 0 : VSIFSeekL(fpPrimary, 0, SEEK_SET);
117 :
118 0 : nVersion = TigerClassifyVersion(nVersionCode);
119 0 : }
120 :
121 : /************************************************************************/
122 : /* EstablishRecordLength() */
123 : /************************************************************************/
124 :
125 0 : int TigerFileBase::EstablishRecordLength(VSILFILE *fp)
126 :
127 : {
128 0 : if (fp == nullptr || VSIFSeekL(fp, 0, SEEK_SET) != 0)
129 0 : return -1;
130 :
131 : /* -------------------------------------------------------------------- */
132 : /* Read through to the end of line. */
133 : /* -------------------------------------------------------------------- */
134 0 : int nRecLen = 0;
135 0 : char chCurrent = '\0';
136 0 : while (VSIFReadL(&chCurrent, 1, 1, fp) == 1 && chCurrent != 10 &&
137 0 : chCurrent != 13)
138 : {
139 0 : nRecLen++;
140 : }
141 :
142 : /* -------------------------------------------------------------------- */
143 : /* Is the file zero length? */
144 : /* -------------------------------------------------------------------- */
145 0 : if (nRecLen == 0)
146 : {
147 0 : return -1;
148 : }
149 :
150 0 : nRecLen++; /* for the 10 or 13 we encountered */
151 :
152 : /* -------------------------------------------------------------------- */
153 : /* Read through line terminator characters. We are trying to */
154 : /* handle cases of CR, CR/LF and LF/CR gracefully. */
155 : /* -------------------------------------------------------------------- */
156 0 : while (VSIFReadL(&chCurrent, 1, 1, fp) == 1 &&
157 0 : (chCurrent == 10 || chCurrent == 13))
158 : {
159 0 : nRecLen++;
160 : }
161 :
162 0 : VSIFSeekL(fp, 0, SEEK_SET);
163 :
164 0 : return nRecLen;
165 : }
166 :
167 : /************************************************************************/
168 : /* EstablishFeatureCount() */
169 : /************************************************************************/
170 :
171 0 : void TigerFileBase::EstablishFeatureCount()
172 :
173 : {
174 0 : if (fpPrimary == nullptr)
175 0 : return;
176 :
177 0 : nRecordLength = EstablishRecordLength(fpPrimary);
178 :
179 0 : if (nRecordLength == -1)
180 : {
181 0 : nRecordLength = 1;
182 0 : nFeatures = 0;
183 0 : return;
184 : }
185 :
186 : /* -------------------------------------------------------------------- */
187 : /* Now we think we know the fixed record length for the file */
188 : /* (including line terminators). Get the total file size, and */
189 : /* divide by this length to get the presumed number of records. */
190 : /* -------------------------------------------------------------------- */
191 :
192 0 : VSIFSeekL(fpPrimary, 0, SEEK_END);
193 0 : const vsi_l_offset nFileSize = VSIFTellL(fpPrimary);
194 :
195 0 : if ((nFileSize % (vsi_l_offset)nRecordLength) != 0)
196 : {
197 0 : CPLError(CE_Warning, CPLE_FileIO,
198 : "TigerFileBase::EstablishFeatureCount(): "
199 : "File length %d doesn't divide by record length %d.\n",
200 0 : (int)nFileSize, (int)nRecordLength);
201 : }
202 :
203 0 : if (nFileSize / (vsi_l_offset)nRecordLength > (vsi_l_offset)INT_MAX)
204 0 : nFeatures = INT_MAX;
205 : else
206 0 : nFeatures = static_cast<int>(nFileSize / (vsi_l_offset)nRecordLength);
207 : }
208 :
209 : /************************************************************************/
210 : /* GetField() */
211 : /************************************************************************/
212 :
213 0 : const char *TigerFileBase::GetField(const char *pachRawDataRecord,
214 : int nStartChar, int nEndChar)
215 :
216 : {
217 : char aszField[128];
218 0 : int nLength = nEndChar - nStartChar + 1;
219 :
220 0 : CPLAssert(nEndChar - nStartChar + 2 < (int)sizeof(aszField));
221 :
222 0 : strncpy(aszField, pachRawDataRecord + nStartChar - 1, nLength);
223 :
224 0 : aszField[nLength] = '\0';
225 0 : while (nLength > 0 && aszField[nLength - 1] == ' ')
226 0 : aszField[--nLength] = '\0';
227 :
228 0 : return CPLSPrintf("%s", aszField);
229 : }
230 :
231 : /************************************************************************/
232 : /* SetField() */
233 : /* */
234 : /* Set a field on an OGRFeature from a tiger record, or leave */
235 : /* NULL if the value isn't found. */
236 : /************************************************************************/
237 :
238 0 : void TigerFileBase::SetField(OGRFeature *poFeature, const char *pszField,
239 : const char *pachRecord, int nStart, int nEnd)
240 :
241 : {
242 0 : const char *pszFieldValue = GetField(pachRecord, nStart, nEnd);
243 :
244 0 : if (pszFieldValue[0] == '\0')
245 0 : return;
246 :
247 0 : poFeature->SetField(pszField, pszFieldValue);
248 : }
249 :
250 : /************************************************************************/
251 : /* AddFieldDefns() */
252 : /************************************************************************/
253 0 : void TigerFileBase::AddFieldDefns(const TigerRecordInfo *psRTInfoIn,
254 : OGRFeatureDefn *poFeatureDefnIn)
255 : {
256 0 : OGRFieldDefn oField("", OFTInteger);
257 : int i, bLFieldHack;
258 :
259 0 : bLFieldHack =
260 0 : CPLTestBool(CPLGetConfigOption("TIGER_LFIELD_AS_STRING", "NO"));
261 :
262 0 : for (i = 0; i < psRTInfoIn->nFieldCount; ++i)
263 : {
264 0 : if (psRTInfoIn->pasFields[i].bDefine)
265 : {
266 0 : OGRFieldType eFT = (OGRFieldType)psRTInfoIn->pasFields[i].OGRtype;
267 :
268 0 : if (bLFieldHack && psRTInfoIn->pasFields[i].cFmt == 'L' &&
269 0 : psRTInfoIn->pasFields[i].cType == 'N')
270 0 : eFT = OFTString;
271 :
272 0 : oField.Set(psRTInfoIn->pasFields[i].pszFieldName, eFT,
273 0 : psRTInfoIn->pasFields[i].nLen);
274 0 : poFeatureDefnIn->AddFieldDefn(&oField);
275 : }
276 : }
277 0 : }
278 :
279 : /************************************************************************/
280 : /* SetFields() */
281 : /************************************************************************/
282 :
283 0 : void TigerFileBase::SetFields(const TigerRecordInfo *psRTInfoIn,
284 : OGRFeature *poFeature, char *achRecord)
285 : {
286 0 : for (int i = 0; i < psRTInfoIn->nFieldCount; ++i)
287 : {
288 0 : if (psRTInfoIn->pasFields[i].bSet)
289 : {
290 0 : SetField(poFeature, psRTInfoIn->pasFields[i].pszFieldName,
291 0 : achRecord, psRTInfoIn->pasFields[i].nBeg,
292 0 : psRTInfoIn->pasFields[i].nEnd);
293 : }
294 : }
295 0 : }
296 :
297 : /************************************************************************/
298 : /* SetModule() */
299 : /************************************************************************/
300 :
301 0 : bool TigerFileBase::SetModule(const char *pszModuleIn)
302 :
303 : {
304 0 : if (m_pszFileCode == nullptr)
305 0 : return false;
306 :
307 0 : if (!OpenFile(pszModuleIn, m_pszFileCode))
308 0 : return false;
309 :
310 0 : EstablishFeatureCount();
311 :
312 0 : return true;
313 : }
314 :
315 : /************************************************************************/
316 : /* GetFeature() */
317 : /************************************************************************/
318 :
319 0 : OGRFeature *TigerFileBase::GetFeature(int nRecordId)
320 :
321 : {
322 : char achRecord[OGR_TIGER_RECBUF_LEN];
323 :
324 0 : if (psRTInfo == nullptr)
325 0 : return nullptr;
326 :
327 0 : if (nRecordId < 0 || nRecordId >= nFeatures)
328 : {
329 0 : CPLError(CE_Failure, CPLE_FileIO,
330 : "Request for out-of-range feature %d of %s", nRecordId,
331 : pszModule);
332 0 : return nullptr;
333 : }
334 :
335 : /* -------------------------------------------------------------------- */
336 : /* Read the raw record data from the file. */
337 : /* -------------------------------------------------------------------- */
338 0 : if (fpPrimary == nullptr)
339 0 : return nullptr;
340 :
341 : {
342 0 : const auto nOffset = static_cast<uint64_t>(nRecordId) * nRecordLength;
343 0 : if (VSIFSeekL(fpPrimary, nOffset, SEEK_SET) != 0)
344 : {
345 0 : CPLError(CE_Failure, CPLE_FileIO,
346 : "Failed to seek to %" PRIu64 " of %s", nOffset, pszModule);
347 0 : return nullptr;
348 : }
349 : }
350 :
351 : // Overflow cannot happen since psRTInfo->nRecordLength is unsigned
352 : // char and sizeof(achRecord) == OGR_TIGER_RECBUF_LEN > 255
353 0 : if (VSIFReadL(achRecord, psRTInfo->nRecordLength, 1, fpPrimary) != 1)
354 : {
355 0 : CPLError(CE_Failure, CPLE_FileIO, "Failed to read record %d of %s",
356 : nRecordId, pszModule);
357 0 : return nullptr;
358 : }
359 :
360 : /* -------------------------------------------------------------------- */
361 : /* Set fields. */
362 : /* -------------------------------------------------------------------- */
363 0 : OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
364 :
365 0 : SetFields(psRTInfo, poFeature, achRecord);
366 :
367 0 : return poFeature;
368 : }
|