/src/MapServer/src/mapxbase.c
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * $Id$ |
3 | | * |
4 | | * Project: MapServer |
5 | | * Purpose: .dbf access API. Derived from shapelib, and relicensed with |
6 | | * permission of Frank Warmerdam (shapelib author). |
7 | | * Author: Steve Lime and the MapServer team. |
8 | | * |
9 | | ****************************************************************************** |
10 | | * Copyright (c) 1996-2005 Regents of the University of Minnesota. |
11 | | * |
12 | | * Permission is hereby granted, free of charge, to any person obtaining a |
13 | | * copy of this software and associated documentation files (the "Software"), |
14 | | * to deal in the Software without restriction, including without limitation |
15 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
16 | | * and/or sell copies of the Software, and to permit persons to whom the |
17 | | * Software is furnished to do so, subject to the following conditions: |
18 | | * |
19 | | * The above copyright notice and this permission notice shall be included in |
20 | | * all copies of this Software or works derived from this Software. |
21 | | * |
22 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
23 | | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
24 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
25 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
26 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
27 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
28 | | * DEALINGS IN THE SOFTWARE. |
29 | | ****************************************************************************/ |
30 | | |
31 | | #include "mapserver.h" |
32 | | #include <stdlib.h> /* for atof() and atoi() */ |
33 | | #include <math.h> |
34 | | |
35 | | #include "cpl_vsi.h" |
36 | | |
37 | 0 | static inline void IGUR_sizet(size_t ignored) { |
38 | 0 | (void)ignored; |
39 | 0 | } /* Ignore GCC Unused Result */ |
40 | | |
41 | | /************************************************************************/ |
42 | | /* SfRealloc() */ |
43 | | /* */ |
44 | | /* A realloc cover function that will access a NULL pointer as */ |
45 | | /* a valid input. */ |
46 | | /************************************************************************/ |
47 | | static void *SfRealloc(void *pMem, int nNewSize) |
48 | | |
49 | 1.26k | { |
50 | 1.26k | return ((void *)realloc(pMem, nNewSize)); |
51 | 1.26k | } |
52 | | |
53 | | /************************************************************************/ |
54 | | /* writeHeader() */ |
55 | | /* */ |
56 | | /* This is called to write out the file header, and field */ |
57 | | /* descriptions before writing any actual data records. This */ |
58 | | /* also computes all the DBFDataSet field offset/size/decimals */ |
59 | | /* and so forth values. */ |
60 | | /************************************************************************/ |
61 | | static void writeHeader(DBFHandle psDBF) |
62 | | |
63 | 0 | { |
64 | 0 | uchar abyHeader[32]; |
65 | 0 | int i; |
66 | |
|
67 | 0 | if (!psDBF->bNoHeader) |
68 | 0 | return; |
69 | | |
70 | 0 | psDBF->bNoHeader = MS_FALSE; |
71 | | |
72 | | /* -------------------------------------------------------------------- */ |
73 | | /* Initialize the file header information. */ |
74 | | /* -------------------------------------------------------------------- */ |
75 | 0 | for (i = 0; i < 32; i++) |
76 | 0 | abyHeader[i] = 0; |
77 | |
|
78 | 0 | abyHeader[0] = 0x03; /* memo field? - just copying */ |
79 | | |
80 | | /* date updated on close, record count preset at zero */ |
81 | |
|
82 | 0 | abyHeader[8] = psDBF->nHeaderLength % 256; |
83 | 0 | abyHeader[9] = psDBF->nHeaderLength / 256; |
84 | |
|
85 | 0 | abyHeader[10] = psDBF->nRecordLength % 256; |
86 | 0 | abyHeader[11] = psDBF->nRecordLength / 256; |
87 | | |
88 | | /* -------------------------------------------------------------------- */ |
89 | | /* Write the initial 32 byte file header, and all the field */ |
90 | | /* descriptions. */ |
91 | | /* -------------------------------------------------------------------- */ |
92 | 0 | VSIFSeekL(psDBF->fp, 0, 0); |
93 | 0 | VSIFWriteL(abyHeader, 32, 1, psDBF->fp); |
94 | 0 | VSIFWriteL(psDBF->pszHeader, 32, psDBF->nFields, psDBF->fp); |
95 | | |
96 | | /* -------------------------------------------------------------------- */ |
97 | | /* Write out the newline character if there is room for it. */ |
98 | | /* -------------------------------------------------------------------- */ |
99 | 0 | if (psDBF->nHeaderLength > 32 * psDBF->nFields + 32) { |
100 | 0 | char cNewline; |
101 | |
|
102 | 0 | cNewline = 0x0d; |
103 | 0 | VSIFWriteL(&cNewline, 1, 1, psDBF->fp); |
104 | 0 | } |
105 | 0 | } |
106 | | |
107 | | /************************************************************************/ |
108 | | /* flushRecord() */ |
109 | | /* */ |
110 | | /* Write out the current record if there is one. */ |
111 | | /************************************************************************/ |
112 | | static void flushRecord(DBFHandle psDBF) |
113 | | |
114 | 1.22k | { |
115 | 1.22k | unsigned int nRecordOffset; |
116 | | |
117 | 1.22k | if (psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1) { |
118 | 0 | psDBF->bCurrentRecordModified = MS_FALSE; |
119 | |
|
120 | 0 | nRecordOffset = |
121 | 0 | psDBF->nRecordLength * psDBF->nCurrentRecord + psDBF->nHeaderLength; |
122 | |
|
123 | 0 | VSIFSeekL(psDBF->fp, nRecordOffset, 0); |
124 | 0 | VSIFWriteL(psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp); |
125 | 0 | } |
126 | 1.22k | } |
127 | | |
128 | 1.31k | DBFHandle msDBFOpenVirtualFile(VSILFILE *fp) { |
129 | 1.31k | DBFHandle psDBF; |
130 | 1.31k | uchar *pabyBuf; |
131 | 1.31k | int nFields, nHeadLen, nRecLen, iField; |
132 | | |
133 | | /* -------------------------------------------------------------------- */ |
134 | | /* Open the file. */ |
135 | | /* -------------------------------------------------------------------- */ |
136 | 1.31k | psDBF = (DBFHandle)calloc(1, sizeof(DBFInfo)); |
137 | 1.31k | MS_CHECK_ALLOC(psDBF, sizeof(DBFInfo), NULL); |
138 | 1.31k | psDBF->fp = fp; |
139 | | |
140 | 1.31k | psDBF->bNoHeader = MS_FALSE; |
141 | 1.31k | psDBF->nCurrentRecord = -1; |
142 | 1.31k | psDBF->bCurrentRecordModified = MS_FALSE; |
143 | | |
144 | 1.31k | psDBF->pszStringField = NULL; |
145 | 1.31k | psDBF->nStringFieldLen = 0; |
146 | | |
147 | 1.31k | psDBF->pszEncoding = NULL; |
148 | | |
149 | | /* -------------------------------------------------------------------- */ |
150 | | /* Read Table Header info */ |
151 | | /* -------------------------------------------------------------------- */ |
152 | 1.31k | pabyBuf = (uchar *)msSmallMalloc(500); |
153 | 1.31k | if (VSIFReadL(pabyBuf, 32, 1, psDBF->fp) != 1) { |
154 | 42 | VSIFCloseL(psDBF->fp); |
155 | 42 | msFree(psDBF); |
156 | 42 | msFree(pabyBuf); |
157 | 42 | return (NULL); |
158 | 42 | } |
159 | | |
160 | 1.27k | if (pabyBuf[7] < 128) |
161 | 1.08k | psDBF->nRecords = pabyBuf[4] + pabyBuf[5] * 256 + pabyBuf[6] * 256 * 256 + |
162 | 1.08k | pabyBuf[7] * 256 * 256 * 256; |
163 | 187 | else |
164 | 187 | psDBF->nRecords = 0; |
165 | | |
166 | 1.27k | psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9] * 256; |
167 | 1.27k | psDBF->nRecordLength = nRecLen = pabyBuf[10] + pabyBuf[11] * 256; |
168 | | |
169 | 1.27k | if (nHeadLen <= 32) { |
170 | 8 | VSIFCloseL(psDBF->fp); |
171 | 8 | msFree(psDBF); |
172 | 8 | msFree(pabyBuf); |
173 | 8 | return (NULL); |
174 | 8 | } |
175 | | |
176 | 1.26k | psDBF->nFields = nFields = (nHeadLen - 32) / 32; |
177 | | |
178 | 1.26k | psDBF->pszCurrentRecord = (char *)msSmallMalloc(nRecLen); |
179 | | |
180 | | /* -------------------------------------------------------------------- */ |
181 | | /* Read in Field Definitions */ |
182 | | /* -------------------------------------------------------------------- */ |
183 | 1.26k | pabyBuf = (uchar *)SfRealloc(pabyBuf, nHeadLen); |
184 | 1.26k | psDBF->pszHeader = (char *)pabyBuf; |
185 | | |
186 | 1.26k | VSIFSeekL(psDBF->fp, 32, 0); |
187 | 1.26k | if (VSIFReadL(pabyBuf, nHeadLen - 32, 1, psDBF->fp) != 1) { |
188 | 41 | msFree(psDBF->pszCurrentRecord); |
189 | 41 | VSIFCloseL(psDBF->fp); |
190 | 41 | msFree(psDBF); |
191 | 41 | msFree(pabyBuf); |
192 | 41 | return (NULL); |
193 | 41 | } |
194 | | |
195 | 1.22k | psDBF->panFieldOffset = (int *)msSmallMalloc(sizeof(int) * nFields); |
196 | 1.22k | psDBF->panFieldSize = (int *)msSmallMalloc(sizeof(int) * nFields); |
197 | 1.22k | psDBF->panFieldDecimals = (int *)msSmallMalloc(sizeof(int) * nFields); |
198 | 1.22k | psDBF->pachFieldType = (char *)msSmallMalloc(sizeof(char) * nFields); |
199 | | |
200 | 13.0k | for (iField = 0; iField < nFields; iField++) { |
201 | 11.8k | uchar *pabyFInfo; |
202 | | |
203 | 11.8k | pabyFInfo = pabyBuf + iField * 32; |
204 | | |
205 | 11.8k | if (pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F') { |
206 | 575 | psDBF->panFieldSize[iField] = pabyFInfo[16]; |
207 | 575 | psDBF->panFieldDecimals[iField] = pabyFInfo[17]; |
208 | 11.2k | } else { |
209 | 11.2k | psDBF->panFieldSize[iField] = pabyFInfo[16] + pabyFInfo[17] * 256; |
210 | 11.2k | psDBF->panFieldDecimals[iField] = 0; |
211 | 11.2k | } |
212 | | |
213 | 11.8k | psDBF->pachFieldType[iField] = (char)pabyFInfo[11]; |
214 | 11.8k | if (iField == 0) |
215 | 92 | psDBF->panFieldOffset[iField] = 1; |
216 | 11.7k | else |
217 | 11.7k | psDBF->panFieldOffset[iField] = |
218 | 11.7k | psDBF->panFieldOffset[iField - 1] + psDBF->panFieldSize[iField - 1]; |
219 | 11.8k | } |
220 | | |
221 | | /* The field offsets and sizes come straight from the header and are trusted |
222 | | by msDBFReadAttribute() when it copies panFieldSize bytes out of the |
223 | | nRecLen-byte record buffer. Reject a header whose fields do not fit within |
224 | | the record length, otherwise that copy reads past pszCurrentRecord. */ |
225 | 1.22k | if (nFields > 0 && |
226 | 92 | psDBF->panFieldOffset[nFields - 1] + psDBF->panFieldSize[nFields - 1] > |
227 | 92 | nRecLen) { |
228 | 65 | msDBFClose(psDBF); |
229 | 65 | return (NULL); |
230 | 65 | } |
231 | | |
232 | 1.15k | return (psDBF); |
233 | 1.22k | } |
234 | | |
235 | | /** |
236 | | * Attempt to read character encoding from a .CPG-file |
237 | | */ |
238 | 0 | char *msReadCPGEncoding(char *cpgFilename) { |
239 | 0 | VSILFILE *fpCPG = VSIFOpenL(cpgFilename, "rb"); |
240 | 0 | if (fpCPG == NULL) |
241 | 0 | return NULL; |
242 | | |
243 | 0 | char szEncoding[100] = ""; |
244 | 0 | size_t nRead = VSIFReadL(szEncoding, 1, sizeof(szEncoding) - 1, fpCPG); |
245 | 0 | VSIFCloseL(fpCPG); |
246 | |
|
247 | 0 | if (nRead == 0) |
248 | 0 | return NULL; |
249 | | |
250 | | // Null terminate and remove any line break |
251 | 0 | szEncoding[nRead] = '\0'; |
252 | 0 | szEncoding[strcspn(szEncoding, "\r\n")] = 0; |
253 | |
|
254 | 0 | if (szEncoding[0] == '\0') { |
255 | 0 | return NULL; |
256 | 0 | } |
257 | | |
258 | | /* See |
259 | | * https://github.com/OSGeo/gdal/blob/release/3.11/ogr/ogrsf_frmts/shape/ogrshapelayer.cpp#L503 |
260 | | */ |
261 | 0 | const int nCPG = atoi(szEncoding); |
262 | 0 | if ((nCPG >= 437 && nCPG <= 950) || (nCPG >= 1250 && nCPG <= 1258)) { |
263 | 0 | char szResult[20]; |
264 | 0 | snprintf(szResult, sizeof(szResult), "CP%d", nCPG); |
265 | 0 | return msStrdup(szResult); |
266 | 0 | } |
267 | | |
268 | 0 | if (strncasecmp(szEncoding, "8859", 4) == 0) { |
269 | 0 | const char *suffix = szEncoding + 4; |
270 | 0 | if (*suffix == '-' || *suffix == '_') |
271 | 0 | suffix++; |
272 | |
|
273 | 0 | if (!isdigit((unsigned char)*suffix)) |
274 | 0 | return msStrdup(szEncoding); |
275 | | |
276 | 0 | char szResult[40]; |
277 | 0 | snprintf(szResult, sizeof(szResult), "ISO-8859-%d", atoi(suffix)); |
278 | 0 | return msStrdup(szResult); |
279 | 0 | } |
280 | | |
281 | 0 | if (strncasecmp(szEncoding, "ANSI 1251", 9) == 0) { |
282 | 0 | return msStrdup("CP1251"); |
283 | 0 | } |
284 | | |
285 | 0 | return msStrdup(szEncoding); |
286 | 0 | } |
287 | | |
288 | | /************************************************************************/ |
289 | | /* msDBFOpen() */ |
290 | | /* */ |
291 | | /* Open a .dbf file. */ |
292 | | /************************************************************************/ |
293 | | DBFHandle msDBFOpen(const char *pszFilename, const char *pszAccess) |
294 | | |
295 | 0 | { |
296 | | /* -------------------------------------------------------------------- */ |
297 | | /* We only allow the access strings "rb" and "r+". */ |
298 | | /* -------------------------------------------------------------------- */ |
299 | 0 | if (strcmp(pszAccess, "r") != 0 && strcmp(pszAccess, "r+") != 0 && |
300 | 0 | strcmp(pszAccess, "rb") != 0 && strcmp(pszAccess, "r+b") != 0) |
301 | 0 | return (NULL); |
302 | | |
303 | | /* -------------------------------------------------------------------- */ |
304 | | /* Ensure the extension is converted to dbf or DBF if it is */ |
305 | | /* currently .shp or .shx. */ |
306 | | /* -------------------------------------------------------------------- */ |
307 | 0 | char *pszDBFFilename = (char *)msSmallMalloc(strlen(pszFilename) + 1); |
308 | 0 | strcpy(pszDBFFilename, pszFilename); |
309 | |
|
310 | 0 | if (strcmp(pszFilename + strlen(pszFilename) - 4, ".shp") == 0 || |
311 | 0 | strcmp(pszFilename + strlen(pszFilename) - 4, ".shx") == 0) { |
312 | 0 | strcpy(pszDBFFilename + strlen(pszDBFFilename) - 4, ".dbf"); |
313 | 0 | } else if (strcmp(pszFilename + strlen(pszFilename) - 4, ".SHP") == 0 || |
314 | 0 | strcmp(pszFilename + strlen(pszFilename) - 4, ".SHX") == 0) { |
315 | 0 | strcpy(pszDBFFilename + strlen(pszDBFFilename) - 4, ".DBF"); |
316 | 0 | } |
317 | | |
318 | | /* -------------------------------------------------------------------- */ |
319 | | /* Open the file. */ |
320 | | /* -------------------------------------------------------------------- */ |
321 | 0 | VSILFILE *fp = VSIFOpenL(pszDBFFilename, pszAccess); |
322 | 0 | if (fp == NULL) { |
323 | 0 | if (strcmp(pszDBFFilename + strlen(pszDBFFilename) - 4, ".dbf") == 0) { |
324 | 0 | strcpy(pszDBFFilename + strlen(pszDBFFilename) - 4, ".DBF"); |
325 | 0 | fp = VSIFOpenL(pszDBFFilename, pszAccess); |
326 | 0 | } |
327 | 0 | } |
328 | |
|
329 | 0 | if (fp == NULL) { |
330 | 0 | msFree(pszDBFFilename); |
331 | 0 | return (NULL); |
332 | 0 | } |
333 | | |
334 | 0 | DBFHandle dbfHandle = msDBFOpenVirtualFile(fp); |
335 | 0 | if (dbfHandle) { |
336 | 0 | char *pszCPGFilename = (char *)msSmallMalloc(strlen(pszDBFFilename) + 1); |
337 | 0 | strcpy(pszCPGFilename, pszDBFFilename); |
338 | |
|
339 | 0 | if (strcmp(pszDBFFilename + strlen(pszDBFFilename) - 4, ".dbf") == 0) { |
340 | 0 | strcpy(pszCPGFilename + strlen(pszCPGFilename) - 4, ".cpg"); |
341 | 0 | } else { |
342 | 0 | strcpy(pszCPGFilename + strlen(pszCPGFilename) - 4, ".CPG"); |
343 | 0 | } |
344 | |
|
345 | 0 | dbfHandle->pszEncoding = msReadCPGEncoding(pszCPGFilename); |
346 | |
|
347 | 0 | msFree(pszCPGFilename); |
348 | 0 | } |
349 | |
|
350 | 0 | msFree(pszDBFFilename); |
351 | 0 | return dbfHandle; |
352 | 0 | } |
353 | | |
354 | | /************************************************************************/ |
355 | | /* msDBFClose() */ |
356 | | /************************************************************************/ |
357 | | |
358 | 1.22k | void msDBFClose(DBFHandle psDBF) { |
359 | | /* -------------------------------------------------------------------- */ |
360 | | /* Write out header if not already written. */ |
361 | | /* -------------------------------------------------------------------- */ |
362 | 1.22k | if (psDBF->bNoHeader) |
363 | 0 | writeHeader(psDBF); |
364 | | |
365 | 1.22k | flushRecord(psDBF); |
366 | | |
367 | | /* -------------------------------------------------------------------- */ |
368 | | /* Update last access date, and number of records if we have */ |
369 | | /* write access. */ |
370 | | /* -------------------------------------------------------------------- */ |
371 | 1.22k | if (psDBF->bUpdated) { |
372 | 0 | uchar abyFileHeader[32]; |
373 | |
|
374 | 0 | VSIFSeekL(psDBF->fp, 0, 0); |
375 | 0 | IGUR_sizet(VSIFReadL(abyFileHeader, 32, 1, psDBF->fp)); |
376 | |
|
377 | 0 | abyFileHeader[1] = 95; /* YY */ |
378 | 0 | abyFileHeader[2] = 7; /* MM */ |
379 | 0 | abyFileHeader[3] = 26; /* DD */ |
380 | |
|
381 | 0 | abyFileHeader[4] = psDBF->nRecords % 256; |
382 | 0 | abyFileHeader[5] = (psDBF->nRecords / 256) % 256; |
383 | 0 | abyFileHeader[6] = (psDBF->nRecords / (256 * 256)) % 256; |
384 | 0 | abyFileHeader[7] = (psDBF->nRecords / (256 * 256 * 256)) % 256; |
385 | |
|
386 | 0 | VSIFSeekL(psDBF->fp, 0, 0); |
387 | 0 | VSIFWriteL(abyFileHeader, 32, 1, psDBF->fp); |
388 | 0 | } |
389 | | |
390 | | /* -------------------------------------------------------------------- */ |
391 | | /* Close, and free resources. */ |
392 | | /* -------------------------------------------------------------------- */ |
393 | 1.22k | VSIFCloseL(psDBF->fp); |
394 | | |
395 | 1.22k | if (psDBF->panFieldOffset != NULL) { |
396 | 92 | free(psDBF->panFieldOffset); |
397 | 92 | free(psDBF->panFieldSize); |
398 | 92 | free(psDBF->panFieldDecimals); |
399 | 92 | free(psDBF->pachFieldType); |
400 | 92 | } |
401 | | |
402 | 1.22k | free(psDBF->pszHeader); |
403 | 1.22k | free(psDBF->pszCurrentRecord); |
404 | | |
405 | 1.22k | free(psDBF->pszStringField); |
406 | 1.22k | free(psDBF->pszEncoding); |
407 | | |
408 | 1.22k | free(psDBF); |
409 | 1.22k | } |
410 | | |
411 | | /************************************************************************/ |
412 | | /* msDBFCreate() */ |
413 | | /* */ |
414 | | /* Create a new .dbf file. */ |
415 | | /************************************************************************/ |
416 | | DBFHandle msDBFCreate(const char *pszFilename) |
417 | | |
418 | 0 | { |
419 | 0 | DBFHandle psDBF; |
420 | 0 | VSILFILE *fp; |
421 | | |
422 | | /* -------------------------------------------------------------------- */ |
423 | | /* Create the file. */ |
424 | | /* -------------------------------------------------------------------- */ |
425 | 0 | fp = VSIFOpenL(pszFilename, "wb"); |
426 | 0 | if (fp == NULL) |
427 | 0 | return (NULL); |
428 | | |
429 | 0 | { |
430 | 0 | char ch = 0; |
431 | 0 | VSIFWriteL(&ch, 1, 1, fp); |
432 | 0 | } |
433 | 0 | VSIFCloseL(fp); |
434 | |
|
435 | 0 | fp = VSIFOpenL(pszFilename, "rb+"); |
436 | 0 | if (fp == NULL) |
437 | 0 | return (NULL); |
438 | | |
439 | | /* -------------------------------------------------------------------- */ |
440 | | /* Create the info structure. */ |
441 | | /* -------------------------------------------------------------------- */ |
442 | 0 | psDBF = (DBFHandle)malloc(sizeof(DBFInfo)); |
443 | 0 | if (psDBF == NULL) { |
444 | 0 | msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n", |
445 | 0 | "msDBFCreate()", __FILE__, __LINE__, |
446 | 0 | (unsigned int)sizeof(DBFInfo)); |
447 | 0 | VSIFCloseL(fp); |
448 | 0 | return NULL; |
449 | 0 | } |
450 | | |
451 | 0 | psDBF->fp = fp; |
452 | 0 | psDBF->nRecords = 0; |
453 | 0 | psDBF->nFields = 0; |
454 | 0 | psDBF->nRecordLength = 1; |
455 | 0 | psDBF->nHeaderLength = 33; |
456 | |
|
457 | 0 | psDBF->panFieldOffset = NULL; |
458 | 0 | psDBF->panFieldSize = NULL; |
459 | 0 | psDBF->panFieldDecimals = NULL; |
460 | 0 | psDBF->pachFieldType = NULL; |
461 | 0 | psDBF->pszHeader = NULL; |
462 | |
|
463 | 0 | psDBF->nCurrentRecord = -1; |
464 | 0 | psDBF->bCurrentRecordModified = MS_FALSE; |
465 | 0 | psDBF->pszCurrentRecord = NULL; |
466 | |
|
467 | 0 | psDBF->pszStringField = NULL; |
468 | 0 | psDBF->nStringFieldLen = 0; |
469 | |
|
470 | 0 | psDBF->bNoHeader = MS_TRUE; |
471 | 0 | psDBF->bUpdated = MS_FALSE; |
472 | |
|
473 | 0 | return (psDBF); |
474 | 0 | } |
475 | | |
476 | | /************************************************************************/ |
477 | | /* msDBFAddField() */ |
478 | | /* */ |
479 | | /* Add a field to a newly created .dbf file before any records */ |
480 | | /* are written. */ |
481 | | /************************************************************************/ |
482 | | int msDBFAddField(DBFHandle psDBF, const char *pszFieldName, DBFFieldType eType, |
483 | 0 | int nWidth, int nDecimals) { |
484 | 0 | char *pszFInfo; |
485 | 0 | int i; |
486 | | |
487 | | /* -------------------------------------------------------------------- */ |
488 | | /* Do some checking to ensure we can add records to this file. */ |
489 | | /* -------------------------------------------------------------------- */ |
490 | 0 | if (psDBF->nRecords > 0) |
491 | 0 | return (MS_FALSE); |
492 | | |
493 | 0 | if (!psDBF->bNoHeader) |
494 | 0 | return (MS_FALSE); |
495 | | |
496 | 0 | if (eType != FTDouble && nDecimals != 0) |
497 | 0 | return (MS_FALSE); |
498 | | |
499 | | /* -------------------------------------------------------------------- */ |
500 | | /* SfRealloc all the arrays larger to hold the additional field */ |
501 | | /* information. */ |
502 | | /* -------------------------------------------------------------------- */ |
503 | 0 | psDBF->nFields++; |
504 | |
|
505 | 0 | psDBF->panFieldOffset = |
506 | 0 | (int *)SfRealloc(psDBF->panFieldOffset, sizeof(int) * psDBF->nFields); |
507 | |
|
508 | 0 | psDBF->panFieldSize = |
509 | 0 | (int *)SfRealloc(psDBF->panFieldSize, sizeof(int) * psDBF->nFields); |
510 | |
|
511 | 0 | psDBF->panFieldDecimals = |
512 | 0 | (int *)SfRealloc(psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields); |
513 | |
|
514 | 0 | psDBF->pachFieldType = |
515 | 0 | (char *)SfRealloc(psDBF->pachFieldType, sizeof(char) * psDBF->nFields); |
516 | | |
517 | | /* -------------------------------------------------------------------- */ |
518 | | /* Assign the new field information fields. */ |
519 | | /* -------------------------------------------------------------------- */ |
520 | 0 | psDBF->panFieldOffset[psDBF->nFields - 1] = psDBF->nRecordLength; |
521 | 0 | psDBF->nRecordLength += nWidth; |
522 | 0 | psDBF->panFieldSize[psDBF->nFields - 1] = nWidth; |
523 | 0 | psDBF->panFieldDecimals[psDBF->nFields - 1] = nDecimals; |
524 | |
|
525 | 0 | if (eType == FTString) |
526 | 0 | psDBF->pachFieldType[psDBF->nFields - 1] = 'C'; |
527 | 0 | else |
528 | 0 | psDBF->pachFieldType[psDBF->nFields - 1] = 'N'; |
529 | | |
530 | | /* -------------------------------------------------------------------- */ |
531 | | /* Extend the required header information. */ |
532 | | /* -------------------------------------------------------------------- */ |
533 | 0 | psDBF->nHeaderLength += 32; |
534 | 0 | psDBF->bUpdated = MS_FALSE; |
535 | |
|
536 | 0 | psDBF->pszHeader = (char *)SfRealloc(psDBF->pszHeader, psDBF->nFields * 32); |
537 | |
|
538 | 0 | pszFInfo = psDBF->pszHeader + 32 * (psDBF->nFields - 1); |
539 | |
|
540 | 0 | for (i = 0; i < 32; i++) |
541 | 0 | pszFInfo[i] = '\0'; |
542 | |
|
543 | 0 | strncpy(pszFInfo, pszFieldName, 10); |
544 | |
|
545 | 0 | pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields - 1]; |
546 | |
|
547 | 0 | if (eType == FTString) { |
548 | 0 | pszFInfo[16] = nWidth % 256; |
549 | 0 | pszFInfo[17] = nWidth / 256; |
550 | 0 | } else { |
551 | 0 | pszFInfo[16] = nWidth; |
552 | 0 | pszFInfo[17] = nDecimals; |
553 | 0 | } |
554 | | |
555 | | /* -------------------------------------------------------------------- */ |
556 | | /* Make the current record buffer appropriately larger. */ |
557 | | /* -------------------------------------------------------------------- */ |
558 | 0 | psDBF->pszCurrentRecord = |
559 | 0 | (char *)SfRealloc(psDBF->pszCurrentRecord, psDBF->nRecordLength); |
560 | |
|
561 | 0 | return (psDBF->nFields - 1); |
562 | 0 | } |
563 | | |
564 | | /************************************************************************/ |
565 | | /* DBFIsValueNULL() */ |
566 | | /* */ |
567 | | /* Return TRUE if value is NULL (in DBF terms). */ |
568 | | /* */ |
569 | | /* Based on DBFIsAttributeNULL of shapelib */ |
570 | | /************************************************************************/ |
571 | | |
572 | | static int DBFIsValueNULL(const char *pszValue, char type) |
573 | | |
574 | 0 | { |
575 | 0 | switch (type) { |
576 | 0 | case 'N': |
577 | 0 | case 'F': |
578 | | /* NULL numeric fields have value "****************" */ |
579 | 0 | return pszValue[0] == '*'; |
580 | | |
581 | 0 | case 'D': |
582 | | /* NULL date fields have value "00000000" */ |
583 | 0 | return strncmp(pszValue, "00000000", 8) == 0; |
584 | | |
585 | 0 | case 'L': |
586 | | /* NULL boolean fields have value "?" */ |
587 | 0 | return pszValue[0] == '?'; |
588 | | |
589 | 0 | default: |
590 | | /* empty string fields are considered NULL */ |
591 | 0 | return strlen(pszValue) == 0; |
592 | 0 | } |
593 | 0 | } |
594 | | |
595 | | /************************************************************************/ |
596 | | /* msDBFReadAttribute() */ |
597 | | /* */ |
598 | | /* Read one of the attribute fields of a record. */ |
599 | | /************************************************************************/ |
600 | | static const char *msDBFReadAttribute(DBFHandle psDBF, int hEntity, int iField) |
601 | | |
602 | 0 | { |
603 | 0 | int i; |
604 | 0 | unsigned int nRecordOffset; |
605 | 0 | const uchar *pabyRec; |
606 | 0 | const char *pReturnField = NULL; |
607 | | |
608 | | /* -------------------------------------------------------------------- */ |
609 | | /* Is the request valid? */ |
610 | | /* -------------------------------------------------------------------- */ |
611 | 0 | if (iField < 0 || iField >= psDBF->nFields) { |
612 | 0 | msSetError(MS_DBFERR, "Invalid field index %d.", "msDBFReadAttribute()", |
613 | 0 | iField); |
614 | 0 | return (NULL); |
615 | 0 | } |
616 | | |
617 | 0 | if (hEntity < 0 || hEntity >= psDBF->nRecords) { |
618 | 0 | msSetError(MS_DBFERR, "Invalid record number %d.", "msDBFReadAttribute()", |
619 | 0 | hEntity); |
620 | 0 | return (NULL); |
621 | 0 | } |
622 | | |
623 | | /* -------------------------------------------------------------------- */ |
624 | | /* Have we read the record? */ |
625 | | /* -------------------------------------------------------------------- */ |
626 | 0 | if (psDBF->nCurrentRecord != hEntity) { |
627 | 0 | flushRecord(psDBF); |
628 | |
|
629 | 0 | nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength; |
630 | |
|
631 | 0 | VSIFSeekL(psDBF->fp, nRecordOffset, 0); |
632 | 0 | if (VSIFReadL(psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, |
633 | 0 | psDBF->fp) != 1) { |
634 | 0 | msSetError(MS_DBFERR, "Cannot read record %d.", "msDBFReadAttribute()", |
635 | 0 | hEntity); |
636 | 0 | return (NULL); |
637 | 0 | } |
638 | | |
639 | 0 | psDBF->nCurrentRecord = hEntity; |
640 | 0 | } |
641 | | |
642 | 0 | pabyRec = (const uchar *)psDBF->pszCurrentRecord; |
643 | | /* DEBUG */ |
644 | | /* printf("CurrentRecord(%c):%s\n", psDBF->pachFieldType[iField], pabyRec); */ |
645 | | |
646 | | /* -------------------------------------------------------------------- */ |
647 | | /* Ensure our field buffer is large enough to hold this buffer. */ |
648 | | /* -------------------------------------------------------------------- */ |
649 | 0 | if (psDBF->panFieldSize[iField] + 1 > psDBF->nStringFieldLen) { |
650 | 0 | psDBF->nStringFieldLen = psDBF->panFieldSize[iField] * 2 + 10; |
651 | 0 | psDBF->pszStringField = |
652 | 0 | (char *)SfRealloc(psDBF->pszStringField, psDBF->nStringFieldLen); |
653 | 0 | } |
654 | | |
655 | | /* -------------------------------------------------------------------- */ |
656 | | /* Extract the requested field. */ |
657 | | /* -------------------------------------------------------------------- */ |
658 | 0 | strncpy(psDBF->pszStringField, |
659 | 0 | (const char *)pabyRec + psDBF->panFieldOffset[iField], |
660 | 0 | psDBF->panFieldSize[iField]); |
661 | 0 | psDBF->pszStringField[psDBF->panFieldSize[iField]] = '\0'; |
662 | | |
663 | | /* |
664 | | ** Trim trailing blanks (SDL Modification) |
665 | | */ |
666 | 0 | for (i = strlen(psDBF->pszStringField) - 1; i >= 0; i--) { |
667 | 0 | if (psDBF->pszStringField[i] != ' ') { |
668 | 0 | psDBF->pszStringField[i + 1] = '\0'; |
669 | 0 | break; |
670 | 0 | } |
671 | 0 | } |
672 | |
|
673 | 0 | if (i == -1) |
674 | 0 | psDBF->pszStringField[0] = '\0'; /* whole string is blank (SDL fix) */ |
675 | | |
676 | | /* |
677 | | ** Trim/skip leading blanks (SDL/DM Modification - only on numeric types) |
678 | | */ |
679 | 0 | if (psDBF->pachFieldType[iField] == 'N' || |
680 | 0 | psDBF->pachFieldType[iField] == 'F' || |
681 | 0 | psDBF->pachFieldType[iField] == 'D') { |
682 | 0 | for (i = 0; psDBF->pszStringField[i] != '\0'; i++) { |
683 | 0 | if (psDBF->pszStringField[i] != ' ') |
684 | 0 | break; |
685 | 0 | } |
686 | 0 | pReturnField = psDBF->pszStringField + i; |
687 | 0 | } else |
688 | 0 | pReturnField = psDBF->pszStringField; |
689 | | |
690 | | /* detect null values */ |
691 | 0 | if (DBFIsValueNULL(pReturnField, psDBF->pachFieldType[iField])) { |
692 | 0 | if (psDBF->pachFieldType[iField] == 'N' || |
693 | 0 | psDBF->pachFieldType[iField] == 'F' || |
694 | 0 | psDBF->pachFieldType[iField] == 'D') |
695 | 0 | pReturnField = "0"; |
696 | 0 | } |
697 | 0 | return (pReturnField); |
698 | 0 | } |
699 | | |
700 | | /************************************************************************/ |
701 | | /* msDBFReadIntAttribute() */ |
702 | | /* */ |
703 | | /* Read an integer attribute. */ |
704 | | /************************************************************************/ |
705 | | int msDBFReadIntegerAttribute(DBFHandle psDBF, int iRecord, int iField) |
706 | | |
707 | 0 | { |
708 | 0 | return (atoi(msDBFReadAttribute(psDBF, iRecord, iField))); |
709 | 0 | } |
710 | | |
711 | | /************************************************************************/ |
712 | | /* msDBFReadDoubleAttribute() */ |
713 | | /* */ |
714 | | /* Read a double attribute. */ |
715 | | /************************************************************************/ |
716 | 0 | double msDBFReadDoubleAttribute(DBFHandle psDBF, int iRecord, int iField) { |
717 | 0 | return (atof(msDBFReadAttribute(psDBF, iRecord, iField))); |
718 | 0 | } |
719 | | |
720 | | /************************************************************************/ |
721 | | /* msDBFReadStringAttribute() */ |
722 | | /* */ |
723 | | /* Read a string attribute. */ |
724 | | /************************************************************************/ |
725 | 0 | const char *msDBFReadStringAttribute(DBFHandle psDBF, int iRecord, int iField) { |
726 | 0 | return (msDBFReadAttribute(psDBF, iRecord, iField)); |
727 | 0 | } |
728 | | |
729 | | /************************************************************************/ |
730 | | /* msDBFGetFieldCount() */ |
731 | | /* */ |
732 | | /* Return the number of fields in this table. */ |
733 | | /************************************************************************/ |
734 | 0 | int msDBFGetFieldCount(DBFHandle psDBF) { return (psDBF->nFields); } |
735 | | |
736 | | /************************************************************************/ |
737 | | /* msDBFGetRecordCount() */ |
738 | | /* */ |
739 | | /* Return the number of records in this table. */ |
740 | | /************************************************************************/ |
741 | 0 | int msDBFGetRecordCount(DBFHandle psDBF) { return (psDBF->nRecords); } |
742 | | |
743 | | /************************************************************************/ |
744 | | /* msDBFGetFieldInfo() */ |
745 | | /* */ |
746 | | /* Return any requested information about the field. */ |
747 | | /************************************************************************/ |
748 | | DBFFieldType msDBFGetFieldInfo(DBFHandle psDBF, int iField, char *pszFieldName, |
749 | 0 | int *pnWidth, int *pnDecimals) { |
750 | 0 | if (iField < 0 || iField >= psDBF->nFields) |
751 | 0 | return (FTInvalid); |
752 | | |
753 | 0 | if (pnWidth != NULL) |
754 | 0 | *pnWidth = psDBF->panFieldSize[iField]; |
755 | |
|
756 | 0 | if (pnDecimals != NULL) |
757 | 0 | *pnDecimals = psDBF->panFieldDecimals[iField]; |
758 | |
|
759 | 0 | if (pszFieldName != NULL) { |
760 | 0 | int i; |
761 | |
|
762 | 0 | strncpy(pszFieldName, (char *)psDBF->pszHeader + iField * 32, 11); |
763 | 0 | pszFieldName[11] = '\0'; |
764 | 0 | for (i = 10; i > 0 && pszFieldName[i] == ' '; i--) |
765 | 0 | pszFieldName[i] = '\0'; |
766 | 0 | } |
767 | |
|
768 | 0 | if (psDBF->pachFieldType[iField] == 'N' || |
769 | 0 | psDBF->pachFieldType[iField] == 'F' || |
770 | 0 | psDBF->pachFieldType[iField] == 'D') { |
771 | 0 | if (psDBF->panFieldDecimals[iField] > 0) |
772 | 0 | return (FTDouble); |
773 | 0 | else |
774 | 0 | return (FTInteger); |
775 | 0 | } else { |
776 | 0 | return (FTString); |
777 | 0 | } |
778 | 0 | } |
779 | | |
780 | | /************************************************************************/ |
781 | | /* msDBFWriteAttribute() */ |
782 | | /* */ |
783 | | /* Write an attribute record to the file. */ |
784 | | /************************************************************************/ |
785 | | static int msDBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, |
786 | 0 | void *pValue) { |
787 | 0 | unsigned int nRecordOffset; |
788 | 0 | int len; |
789 | 0 | uchar *pabyRec; |
790 | 0 | char szSField[40]; |
791 | | |
792 | | /* -------------------------------------------------------------------- */ |
793 | | /* Is this a valid record? */ |
794 | | /* -------------------------------------------------------------------- */ |
795 | 0 | if (hEntity < 0 || hEntity > psDBF->nRecords) |
796 | 0 | return (MS_FALSE); |
797 | | |
798 | 0 | if (psDBF->bNoHeader) |
799 | 0 | writeHeader(psDBF); |
800 | | |
801 | | /* -------------------------------------------------------------------- */ |
802 | | /* Is this a brand new record? */ |
803 | | /* -------------------------------------------------------------------- */ |
804 | 0 | if (hEntity == psDBF->nRecords) { |
805 | 0 | flushRecord(psDBF); |
806 | |
|
807 | 0 | psDBF->nRecords++; |
808 | 0 | for (unsigned i = 0; i < psDBF->nRecordLength; i++) |
809 | 0 | psDBF->pszCurrentRecord[i] = ' '; |
810 | |
|
811 | 0 | psDBF->nCurrentRecord = hEntity; |
812 | 0 | } |
813 | | |
814 | | /* -------------------------------------------------------------------- */ |
815 | | /* Is this an existing record, but different than the last one */ |
816 | | /* we accessed? */ |
817 | | /* -------------------------------------------------------------------- */ |
818 | 0 | if (psDBF->nCurrentRecord != hEntity) { |
819 | 0 | flushRecord(psDBF); |
820 | |
|
821 | 0 | nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength; |
822 | |
|
823 | 0 | VSIFSeekL(psDBF->fp, nRecordOffset, 0); |
824 | 0 | if (VSIFReadL(psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, |
825 | 0 | psDBF->fp) != 1) |
826 | 0 | return MS_FALSE; |
827 | | |
828 | 0 | psDBF->nCurrentRecord = hEntity; |
829 | 0 | } |
830 | | |
831 | 0 | pabyRec = (uchar *)psDBF->pszCurrentRecord; |
832 | | |
833 | | /* -------------------------------------------------------------------- */ |
834 | | /* Assign all the record fields. */ |
835 | | /* -------------------------------------------------------------------- */ |
836 | 0 | switch (psDBF->pachFieldType[iField]) { |
837 | 0 | case 'D': |
838 | 0 | case 'N': |
839 | 0 | case 'F': |
840 | 0 | snprintf(szSField, sizeof(szSField), "%*.*f", psDBF->panFieldSize[iField], |
841 | 0 | psDBF->panFieldDecimals[iField], *(double *)pValue); |
842 | 0 | len = strlen((char *)szSField); |
843 | 0 | memcpy(pabyRec + psDBF->panFieldOffset[iField], szSField, |
844 | 0 | MS_MIN(len, psDBF->panFieldSize[iField])); |
845 | 0 | break; |
846 | | |
847 | 0 | default: |
848 | 0 | len = strlen((char *)pValue); |
849 | 0 | memcpy(pabyRec + psDBF->panFieldOffset[iField], pValue, |
850 | 0 | MS_MIN(len, psDBF->panFieldSize[iField])); |
851 | 0 | break; |
852 | 0 | } |
853 | | |
854 | 0 | psDBF->bCurrentRecordModified = MS_TRUE; |
855 | 0 | psDBF->bUpdated = MS_TRUE; |
856 | |
|
857 | 0 | return (MS_TRUE); |
858 | 0 | } |
859 | | |
860 | | /************************************************************************/ |
861 | | /* msDBFWriteDoubleAttribute() */ |
862 | | /* */ |
863 | | /* Write a double attribute. */ |
864 | | /************************************************************************/ |
865 | | int msDBFWriteDoubleAttribute(DBFHandle psDBF, int iRecord, int iField, |
866 | 0 | double dValue) { |
867 | 0 | return (msDBFWriteAttribute(psDBF, iRecord, iField, (void *)&dValue)); |
868 | 0 | } |
869 | | |
870 | | /************************************************************************/ |
871 | | /* msDBFWriteIntegerAttribute() */ |
872 | | /* */ |
873 | | /* Write a integer attribute. */ |
874 | | /************************************************************************/ |
875 | | |
876 | | int msDBFWriteIntegerAttribute(DBFHandle psDBF, int iRecord, int iField, |
877 | 0 | int nValue) { |
878 | 0 | double dValue = nValue; |
879 | |
|
880 | 0 | return (msDBFWriteAttribute(psDBF, iRecord, iField, (void *)&dValue)); |
881 | 0 | } |
882 | | |
883 | | /************************************************************************/ |
884 | | /* msDBFWriteStringAttribute() */ |
885 | | /* */ |
886 | | /* Write a string attribute. */ |
887 | | /************************************************************************/ |
888 | | int msDBFWriteStringAttribute(DBFHandle psDBF, int iRecord, int iField, |
889 | 0 | const char *pszValue) { |
890 | 0 | return (msDBFWriteAttribute(psDBF, iRecord, iField, (void *)pszValue)); |
891 | 0 | } |
892 | | |
893 | | /* |
894 | | ** Which column number in the .DBF file does the item correspond to |
895 | | */ |
896 | 0 | int msDBFGetItemIndex(DBFHandle dbffile, char *name) { |
897 | 0 | int i; |
898 | 0 | int fWidth, fnDecimals; /* field width and number of decimals */ |
899 | 0 | char fName[32]; /* field name */ |
900 | |
|
901 | 0 | if (!name) { |
902 | 0 | msSetError(MS_MISCERR, "NULL item name passed.", "msGetItemIndex()"); |
903 | 0 | return (-1); |
904 | 0 | } |
905 | | |
906 | | /* does name exist as a field? */ |
907 | 0 | for (i = 0; i < msDBFGetFieldCount(dbffile); i++) { |
908 | 0 | msDBFGetFieldInfo(dbffile, i, fName, &fWidth, &fnDecimals); |
909 | 0 | if (strcasecmp(name, fName) == 0) /* found it */ |
910 | 0 | return (i); |
911 | 0 | } |
912 | | |
913 | 0 | msSetError(MS_DBFERR, "Item '%s' not found.", "msDBFGetItemIndex()", name); |
914 | 0 | return (-1); /* item not found */ |
915 | 0 | } |
916 | | |
917 | | /* |
918 | | ** Load item names into a character array |
919 | | */ |
920 | 0 | char **msDBFGetItems(DBFHandle dbffile) { |
921 | 0 | char **items; |
922 | 0 | int i, nFields; |
923 | 0 | char fName[32]; |
924 | |
|
925 | 0 | if ((nFields = msDBFGetFieldCount(dbffile)) == 0) { |
926 | 0 | msSetError(MS_DBFERR, "File contains no data.", "msGetDBFItems()"); |
927 | 0 | return (NULL); |
928 | 0 | } |
929 | | |
930 | 0 | items = (char **)malloc(sizeof(char *) * nFields); |
931 | 0 | MS_CHECK_ALLOC(items, sizeof(char *) * nFields, NULL); |
932 | |
|
933 | 0 | for (i = 0; i < nFields; i++) { |
934 | 0 | msDBFGetFieldInfo(dbffile, i, fName, NULL, NULL); |
935 | 0 | items[i] = msStrdup(fName); |
936 | 0 | } |
937 | |
|
938 | 0 | return (items); |
939 | 0 | } |
940 | | |
941 | | /* |
942 | | ** Load item values into a character array |
943 | | */ |
944 | 0 | char **msDBFGetValues(DBFHandle dbffile, int record) { |
945 | 0 | char **values; |
946 | 0 | int i, nFields; |
947 | |
|
948 | 0 | if ((nFields = msDBFGetFieldCount(dbffile)) == 0) { |
949 | 0 | msSetError(MS_DBFERR, "File contains no data.", "msGetDBFValues()"); |
950 | 0 | return (NULL); |
951 | 0 | } |
952 | | |
953 | 0 | values = (char **)malloc(sizeof(char *) * nFields); |
954 | 0 | MS_CHECK_ALLOC(values, sizeof(char *) * nFields, NULL); |
955 | |
|
956 | 0 | for (i = 0; i < nFields; i++) |
957 | 0 | values[i] = msStrdup(msDBFReadStringAttribute(dbffile, record, i)); |
958 | |
|
959 | 0 | return (values); |
960 | 0 | } |
961 | | |
962 | 0 | int *msDBFGetItemIndexes(DBFHandle dbffile, char **items, int numitems) { |
963 | 0 | int *itemindexes = NULL, i; |
964 | |
|
965 | 0 | if (numitems == 0) |
966 | 0 | return (NULL); |
967 | | |
968 | 0 | itemindexes = (int *)malloc(sizeof(int) * numitems); |
969 | 0 | MS_CHECK_ALLOC(itemindexes, sizeof(int) * numitems, NULL); |
970 | |
|
971 | 0 | for (i = 0; i < numitems; i++) { |
972 | 0 | itemindexes[i] = msDBFGetItemIndex(dbffile, items[i]); |
973 | 0 | if (itemindexes[i] == -1) { |
974 | 0 | free(itemindexes); |
975 | 0 | return (NULL); /* item not found */ |
976 | 0 | } |
977 | 0 | } |
978 | | |
979 | 0 | return (itemindexes); |
980 | 0 | } |
981 | | |
982 | | char **msDBFGetValueList(DBFHandle dbffile, int record, int *itemindexes, |
983 | 0 | int numitems) { |
984 | 0 | const char *value; |
985 | 0 | char **values = NULL; |
986 | 0 | int i; |
987 | |
|
988 | 0 | if (numitems == 0) |
989 | 0 | return (NULL); |
990 | | |
991 | 0 | values = (char **)malloc(sizeof(char *) * numitems); |
992 | 0 | MS_CHECK_ALLOC(values, sizeof(char *) * numitems, NULL); |
993 | |
|
994 | 0 | for (i = 0; i < numitems; i++) { |
995 | 0 | value = msDBFReadStringAttribute(dbffile, record, itemindexes[i]); |
996 | 0 | if (value == NULL) { |
997 | 0 | free(values); |
998 | 0 | return NULL; /* Error already reported by msDBFReadStringAttribute() */ |
999 | 0 | } |
1000 | 0 | values[i] = msStrdup(value); |
1001 | 0 | } |
1002 | | |
1003 | 0 | return (values); |
1004 | 0 | } |