1 : /******************************************************************************
2 : * $Id: ddfrecord.cpp 16282 2009-02-09 20:41:42Z warmerdam $
3 : *
4 : * Project: ISO 8211 Access
5 : * Purpose: Implements the DDFRecord class.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 1999, Frank Warmerdam
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "iso8211.h"
31 : #include "cpl_conv.h"
32 :
33 : CPL_CVSID("$Id: ddfrecord.cpp 16282 2009-02-09 20:41:42Z warmerdam $");
34 :
35 : static const size_t nLeaderSize = 24;
36 :
37 : /************************************************************************/
38 : /* DDFRecord() */
39 : /************************************************************************/
40 :
41 548 : DDFRecord::DDFRecord( DDFModule * poModuleIn )
42 :
43 : {
44 548 : poModule = poModuleIn;
45 :
46 548 : nReuseHeader = FALSE;
47 :
48 548 : nFieldOffset = 0;
49 :
50 548 : nDataSize = 0;
51 548 : pachData = NULL;
52 :
53 548 : nFieldCount = 0;
54 548 : paoFields = NULL;
55 :
56 548 : bIsClone = FALSE;
57 :
58 548 : _sizeFieldTag = 4;
59 548 : _sizeFieldPos = 0;
60 548 : _sizeFieldLength = 0;
61 548 : }
62 :
63 : /************************************************************************/
64 : /* ~DDFRecord() */
65 : /************************************************************************/
66 :
67 548 : DDFRecord::~DDFRecord()
68 :
69 : {
70 548 : Clear();
71 :
72 548 : if( bIsClone )
73 499 : poModule->RemoveCloneRecord( this );
74 548 : }
75 :
76 : /************************************************************************/
77 : /* Dump() */
78 : /************************************************************************/
79 :
80 : /**
81 : * Write out record contents to debugging file.
82 : *
83 : * A variety of information about this record, and all it's fields and
84 : * subfields is written to the given debugging file handle. Note that
85 : * field definition information (ala DDFFieldDefn) isn't written.
86 : *
87 : * @param fp The standard io file handle to write to. ie. stderr
88 : */
89 :
90 0 : void DDFRecord::Dump( FILE * fp )
91 :
92 : {
93 0 : fprintf( fp, "DDFRecord:\n" );
94 0 : fprintf( fp, " nReuseHeader = %d\n", nReuseHeader );
95 0 : fprintf( fp, " nDataSize = %d\n", nDataSize );
96 : fprintf( fp,
97 : " _sizeFieldLength=%d, _sizeFieldPos=%d, _sizeFieldTag=%d\n",
98 0 : _sizeFieldLength, _sizeFieldPos, _sizeFieldTag );
99 :
100 0 : for( int i = 0; i < nFieldCount; i++ )
101 : {
102 0 : paoFields[i].Dump( fp );
103 : }
104 0 : }
105 :
106 : /************************************************************************/
107 : /* Read() */
108 : /* */
109 : /* Read a record of data from the file, and parse the header to */
110 : /* build a field list for the record (or reuse the existing one */
111 : /* if reusing headers). It is expected that the file pointer */
112 : /* will be positioned at the beginning of a data record. It is */
113 : /* the DDFModule's responsibility to do so. */
114 : /* */
115 : /* This method should only be called by the DDFModule class. */
116 : /************************************************************************/
117 :
118 1063 : int DDFRecord::Read()
119 :
120 : {
121 : /* -------------------------------------------------------------------- */
122 : /* Redefine the record on the basis of the header if needed. */
123 : /* As a side effect this will read the data for the record as well.*/
124 : /* -------------------------------------------------------------------- */
125 1063 : if( !nReuseHeader )
126 : {
127 627 : return( ReadHeader() );
128 : }
129 :
130 : /* -------------------------------------------------------------------- */
131 : /* Otherwise we read just the data and carefully overlay it on */
132 : /* the previous records data without disturbing the rest of the */
133 : /* record. */
134 : /* -------------------------------------------------------------------- */
135 : size_t nReadBytes;
136 :
137 : nReadBytes = VSIFReadL( pachData + nFieldOffset, 1,
138 : nDataSize - nFieldOffset,
139 436 : poModule->GetFP() );
140 436 : if( nReadBytes != (size_t) (nDataSize - nFieldOffset)
141 : && nReadBytes == 0
142 : && VSIFEofL( poModule->GetFP() ) )
143 : {
144 7 : return FALSE;
145 : }
146 429 : else if( nReadBytes != (size_t) (nDataSize - nFieldOffset) )
147 : {
148 : CPLError( CE_Failure, CPLE_FileIO,
149 0 : "Data record is short on DDF file.\n" );
150 :
151 0 : return FALSE;
152 : }
153 :
154 : // notdef: eventually we may have to do something at this point to
155 : // notify the DDFField's that their data values have changed.
156 :
157 429 : return TRUE;
158 : }
159 :
160 : /************************************************************************/
161 : /* Write() */
162 : /************************************************************************/
163 :
164 : /**
165 : * Write record out to module.
166 : *
167 : * This method writes the current record to the module to which it is
168 : * attached. Normally this would be at the end of the file, and only used
169 : * for modules newly created with DDFModule::Create(). Rewriting existing
170 : * records is not supported at this time. Calling Write() multiple times
171 : * on a DDFRecord will result it multiple copies being written at the end of
172 : * the module.
173 : *
174 : * @return TRUE on success or FALSE on failure.
175 : */
176 :
177 0 : int DDFRecord::Write()
178 :
179 : {
180 0 : if( !ResetDirectory() )
181 0 : return FALSE;
182 :
183 : /* -------------------------------------------------------------------- */
184 : /* Prepare leader. */
185 : /* -------------------------------------------------------------------- */
186 : char szLeader[nLeaderSize+1];
187 :
188 0 : memset( szLeader, ' ', nLeaderSize );
189 :
190 0 : sprintf( szLeader+0, "%05d", (int) (nDataSize + nLeaderSize) );
191 0 : szLeader[5] = ' ';
192 0 : szLeader[6] = 'D';
193 :
194 0 : sprintf( szLeader + 12, "%05d", (int) (nFieldOffset + nLeaderSize) );
195 0 : szLeader[17] = ' ';
196 :
197 0 : szLeader[20] = (char) ('0' + _sizeFieldLength);
198 0 : szLeader[21] = (char) ('0' + _sizeFieldPos);
199 0 : szLeader[22] = '0';
200 0 : szLeader[23] = (char) ('0' + _sizeFieldTag);
201 :
202 : /* notdef: lots of stuff missing */
203 :
204 : /* -------------------------------------------------------------------- */
205 : /* Write the leader. */
206 : /* -------------------------------------------------------------------- */
207 0 : VSIFWriteL( szLeader, nLeaderSize, 1, poModule->GetFP() );
208 :
209 : /* -------------------------------------------------------------------- */
210 : /* Write the remainder of the record. */
211 : /* -------------------------------------------------------------------- */
212 0 : VSIFWriteL( pachData, nDataSize, 1, poModule->GetFP() );
213 :
214 0 : return TRUE;
215 : }
216 :
217 : /************************************************************************/
218 : /* Clear() */
219 : /* */
220 : /* Clear any information associated with the last header in */
221 : /* preparation for reading a new header. */
222 : /************************************************************************/
223 :
224 1191 : void DDFRecord::Clear()
225 :
226 : {
227 1191 : if( paoFields != NULL )
228 1104 : delete[] paoFields;
229 :
230 1191 : paoFields = NULL;
231 1191 : nFieldCount = 0;
232 :
233 1191 : if( pachData != NULL )
234 1104 : CPLFree( pachData );
235 :
236 1191 : pachData = NULL;
237 1191 : nDataSize = 0;
238 1191 : nReuseHeader = FALSE;
239 1191 : }
240 :
241 : /************************************************************************/
242 : /* ReadHeader() */
243 : /* */
244 : /* This perform the header reading and parsing job for the */
245 : /* Read() method. It reads the header, and builds a field */
246 : /* list. */
247 : /************************************************************************/
248 :
249 627 : int DDFRecord::ReadHeader()
250 :
251 : {
252 : /* -------------------------------------------------------------------- */
253 : /* Clear any existing information. */
254 : /* -------------------------------------------------------------------- */
255 627 : Clear();
256 :
257 : /* -------------------------------------------------------------------- */
258 : /* Read the 24 byte leader. */
259 : /* -------------------------------------------------------------------- */
260 : char achLeader[nLeaderSize];
261 : int nReadBytes;
262 :
263 627 : nReadBytes = VSIFReadL(achLeader,1,nLeaderSize,poModule->GetFP());
264 627 : if( nReadBytes == 0 && VSIFEofL( poModule->GetFP() ) )
265 : {
266 22 : return FALSE;
267 : }
268 605 : else if( nReadBytes != (int) nLeaderSize )
269 : {
270 : CPLError( CE_Failure, CPLE_FileIO,
271 0 : "Leader is short on DDF file." );
272 :
273 0 : return FALSE;
274 : }
275 :
276 : /* -------------------------------------------------------------------- */
277 : /* Extract information from leader. */
278 : /* -------------------------------------------------------------------- */
279 : int _recLength, _fieldAreaStart;
280 : char _leaderIden;
281 :
282 605 : _recLength = DDFScanInt( achLeader+0, 5 );
283 605 : _leaderIden = achLeader[6];
284 605 : _fieldAreaStart = DDFScanInt(achLeader+12,5);
285 :
286 605 : _sizeFieldLength = achLeader[20] - '0';
287 605 : _sizeFieldPos = achLeader[21] - '0';
288 605 : _sizeFieldTag = achLeader[23] - '0';
289 :
290 605 : if( _sizeFieldLength < 0 || _sizeFieldLength > 9
291 : || _sizeFieldPos < 0 || _sizeFieldPos > 9
292 : || _sizeFieldTag < 0 || _sizeFieldTag > 9 )
293 : {
294 : CPLError( CE_Failure, CPLE_AppDefined,
295 0 : "ISO8211 record leader appears to be corrupt." );
296 0 : return FALSE;
297 : }
298 :
299 605 : if( _leaderIden == 'R' )
300 13 : nReuseHeader = TRUE;
301 :
302 605 : nFieldOffset = _fieldAreaStart - nLeaderSize;
303 :
304 : /* -------------------------------------------------------------------- */
305 : /* Is there anything seemly screwy about this record? */
306 : /* -------------------------------------------------------------------- */
307 605 : if(( _recLength < 24 || _recLength > 100000000
308 : || _fieldAreaStart < 24 || _fieldAreaStart > 100000 )
309 : && (_recLength != 0))
310 : {
311 : CPLError( CE_Failure, CPLE_FileIO,
312 : "Data record appears to be corrupt on DDF file.\n"
313 : " -- ensure that the files were uncompressed without modifying\n"
314 0 : "carriage return/linefeeds (by default WINZIP does this)." );
315 :
316 0 : return FALSE;
317 : }
318 :
319 : /* ==================================================================== */
320 : /* Handle the normal case with the record length available. */
321 : /* ==================================================================== */
322 605 : if(_recLength != 0) {
323 : /* -------------------------------------------------------------------- */
324 : /* Read the remainder of the record. */
325 : /* -------------------------------------------------------------------- */
326 605 : nDataSize = _recLength - nLeaderSize;
327 605 : pachData = (char *) CPLMalloc(nDataSize);
328 :
329 605 : if( VSIFReadL( pachData, 1, nDataSize, poModule->GetFP()) !=
330 : (size_t) nDataSize )
331 : {
332 : CPLError( CE_Failure, CPLE_FileIO,
333 0 : "Data record is short on DDF file." );
334 :
335 0 : return FALSE;
336 : }
337 :
338 : /* -------------------------------------------------------------------- */
339 : /* If we don't find a field terminator at the end of the record */
340 : /* we will read extra bytes till we get to it. */
341 : /* -------------------------------------------------------------------- */
342 1211 : while( pachData[nDataSize-1] != DDF_FIELD_TERMINATOR
343 1 : && (nDataSize == 0 || pachData[nDataSize-2] != DDF_FIELD_TERMINATOR) )
344 : {
345 0 : nDataSize++;
346 0 : pachData = (char *) CPLRealloc(pachData,nDataSize);
347 :
348 0 : if( VSIFReadL( pachData + nDataSize - 1, 1, 1, poModule->GetFP() )
349 : != 1 )
350 : {
351 : CPLError( CE_Failure, CPLE_FileIO,
352 0 : "Data record is short on DDF file." );
353 :
354 0 : return FALSE;
355 : }
356 : CPLDebug( "ISO8211",
357 0 : "Didn't find field terminator, read one more byte." );
358 : }
359 :
360 : /* -------------------------------------------------------------------- */
361 : /* Loop over the directory entries, making a pass counting them. */
362 : /* -------------------------------------------------------------------- */
363 : int i;
364 : int nFieldEntryWidth;
365 :
366 605 : nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag;
367 605 : nFieldCount = 0;
368 2885 : for( i = 0; i < nDataSize; i += nFieldEntryWidth )
369 : {
370 2885 : if( pachData[i] == DDF_FIELD_TERMINATOR )
371 605 : break;
372 :
373 2280 : nFieldCount++;
374 : }
375 :
376 : /* -------------------------------------------------------------------- */
377 : /* Allocate, and read field definitions. */
378 : /* -------------------------------------------------------------------- */
379 605 : paoFields = new DDFField[nFieldCount];
380 :
381 2885 : for( i = 0; i < nFieldCount; i++ )
382 : {
383 : char szTag[128];
384 2280 : int nEntryOffset = i*nFieldEntryWidth;
385 : int nFieldLength, nFieldPos;
386 :
387 : /* -------------------------------------------------------------------- */
388 : /* Read the position information and tag. */
389 : /* -------------------------------------------------------------------- */
390 2280 : strncpy( szTag, pachData+nEntryOffset, _sizeFieldTag );
391 2280 : szTag[_sizeFieldTag] = '\0';
392 :
393 2280 : nEntryOffset += _sizeFieldTag;
394 2280 : nFieldLength = DDFScanInt( pachData+nEntryOffset, _sizeFieldLength );
395 :
396 2280 : nEntryOffset += _sizeFieldLength;
397 2280 : nFieldPos = DDFScanInt( pachData+nEntryOffset, _sizeFieldPos );
398 :
399 : /* -------------------------------------------------------------------- */
400 : /* Find the corresponding field in the module directory. */
401 : /* -------------------------------------------------------------------- */
402 2280 : DDFFieldDefn *poFieldDefn = poModule->FindFieldDefn( szTag );
403 :
404 2280 : if( poFieldDefn == NULL )
405 : {
406 : CPLError( CE_Failure, CPLE_AppDefined,
407 : "Undefined field `%s' encountered in data record.",
408 0 : szTag );
409 0 : return FALSE;
410 : }
411 :
412 : /* -------------------------------------------------------------------- */
413 : /* Assign info the DDFField. */
414 : /* -------------------------------------------------------------------- */
415 : paoFields[i].Initialize( poFieldDefn,
416 : pachData + _fieldAreaStart + nFieldPos - nLeaderSize,
417 2280 : nFieldLength );
418 : }
419 :
420 605 : return TRUE;
421 : }
422 : /* ==================================================================== */
423 : /* Handle the exceptional case where the record length is */
424 : /* zero. In this case we have to read all the data based on */
425 : /* the size of data items as per ISO8211 spec Annex C, 1.5.1. */
426 : /* */
427 : /* See Bugzilla bug 181 and test with file US4CN21M.000. */
428 : /* ==================================================================== */
429 : else {
430 : CPLDebug( "ISO8211",
431 0 : "Record with zero length, use variant (C.1.5.1) logic." );
432 :
433 : /* ----------------------------------------------------------------- */
434 : /* _recLength == 0, handle the large record. */
435 : /* */
436 : /* Read the remainder of the record. */
437 : /* ----------------------------------------------------------------- */
438 0 : nDataSize = 0;
439 0 : pachData = NULL;
440 :
441 : /* ----------------------------------------------------------------- */
442 : /* Loop over the directory entries, making a pass counting them. */
443 : /* ----------------------------------------------------------------- */
444 0 : int nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag;
445 0 : nFieldCount = 0;
446 0 : int i=0;
447 0 : char *tmpBuf = (char*)VSIMalloc(nFieldEntryWidth);
448 :
449 0 : if( tmpBuf == NULL )
450 : {
451 : CPLError( CE_Failure, CPLE_OutOfMemory,
452 : "Attempt to allocate %d byte ISO8211 record buffer failed.",
453 0 : nFieldEntryWidth );
454 0 : return FALSE;
455 : }
456 :
457 : // while we're not at the end, store this entry,
458 : // and keep on reading...
459 0 : do {
460 : // read an Entry:
461 0 : if(nFieldEntryWidth !=
462 : (int) VSIFReadL(tmpBuf, 1, nFieldEntryWidth, poModule->GetFP())) {
463 : CPLError(CE_Failure, CPLE_FileIO,
464 0 : "Data record is short on DDF file.");
465 0 : return FALSE;
466 : }
467 :
468 : // move this temp buffer into more permanent storage:
469 0 : char *newBuf = (char*)CPLMalloc(nDataSize+nFieldEntryWidth);
470 0 : if(pachData!=NULL) {
471 0 : memcpy(newBuf, pachData, nDataSize);
472 0 : CPLFree(pachData);
473 : }
474 0 : memcpy(&newBuf[nDataSize], tmpBuf, nFieldEntryWidth);
475 0 : pachData = newBuf;
476 0 : nDataSize += nFieldEntryWidth;
477 :
478 0 : if(DDF_FIELD_TERMINATOR != tmpBuf[0]) {
479 0 : nFieldCount++;
480 : }
481 : }
482 0 : while(DDF_FIELD_TERMINATOR != tmpBuf[0]);
483 :
484 : // --------------------------------------------------------------------
485 : // Now, rewind a little. Only the TERMINATOR should have been read
486 : // --------------------------------------------------------------------
487 0 : int rewindSize = nFieldEntryWidth - 1;
488 0 : FILE *fp = poModule->GetFP();
489 0 : vsi_l_offset pos = VSIFTellL(fp) - rewindSize;
490 0 : VSIFSeekL(fp, pos, SEEK_SET);
491 0 : nDataSize -= rewindSize;
492 :
493 : // --------------------------------------------------------------------
494 : // Okay, now let's populate the heck out of pachData...
495 : // --------------------------------------------------------------------
496 0 : for(i=0; i<nFieldCount; i++) {
497 0 : int nEntryOffset = (i*nFieldEntryWidth) + _sizeFieldTag;
498 : int nFieldLength = DDFScanInt(pachData + nEntryOffset,
499 0 : _sizeFieldLength);
500 0 : char *tmpBuf = (char*)CPLMalloc(nFieldLength);
501 :
502 : // read an Entry:
503 0 : if(nFieldLength !=
504 : (int) VSIFReadL(tmpBuf, 1, nFieldLength, poModule->GetFP())) {
505 : CPLError(CE_Failure, CPLE_FileIO,
506 0 : "Data record is short on DDF file.");
507 0 : return FALSE;
508 : }
509 :
510 : // move this temp buffer into more permanent storage:
511 0 : char *newBuf = (char*)CPLMalloc(nDataSize+nFieldLength);
512 0 : memcpy(newBuf, pachData, nDataSize);
513 0 : CPLFree(pachData);
514 0 : memcpy(&newBuf[nDataSize], tmpBuf, nFieldLength);
515 0 : CPLFree(tmpBuf);
516 0 : pachData = newBuf;
517 0 : nDataSize += nFieldLength;
518 : }
519 :
520 : /* ----------------------------------------------------------------- */
521 : /* Allocate, and read field definitions. */
522 : /* ----------------------------------------------------------------- */
523 0 : paoFields = new DDFField[nFieldCount];
524 :
525 0 : for( i = 0; i < nFieldCount; i++ )
526 : {
527 : char szTag[128];
528 0 : int nEntryOffset = i*nFieldEntryWidth;
529 : int nFieldLength, nFieldPos;
530 :
531 : /* ------------------------------------------------------------- */
532 : /* Read the position information and tag. */
533 : /* ------------------------------------------------------------- */
534 0 : strncpy( szTag, pachData+nEntryOffset, _sizeFieldTag );
535 0 : szTag[_sizeFieldTag] = '\0';
536 :
537 0 : nEntryOffset += _sizeFieldTag;
538 0 : nFieldLength = DDFScanInt( pachData+nEntryOffset, _sizeFieldLength );
539 :
540 0 : nEntryOffset += _sizeFieldLength;
541 0 : nFieldPos = DDFScanInt( pachData+nEntryOffset, _sizeFieldPos );
542 :
543 : /* ------------------------------------------------------------- */
544 : /* Find the corresponding field in the module directory. */
545 : /* ------------------------------------------------------------- */
546 0 : DDFFieldDefn *poFieldDefn = poModule->FindFieldDefn( szTag );
547 :
548 0 : if( poFieldDefn == NULL )
549 : {
550 : CPLError( CE_Failure, CPLE_AppDefined,
551 : "Undefined field `%s' encountered in data record.",
552 0 : szTag );
553 0 : return FALSE;
554 : }
555 :
556 : /* ------------------------------------------------------------- */
557 : /* Assign info the DDFField. */
558 : /* ------------------------------------------------------------- */
559 :
560 : paoFields[i].Initialize( poFieldDefn,
561 : pachData + _fieldAreaStart
562 : + nFieldPos - nLeaderSize,
563 0 : nFieldLength );
564 : }
565 :
566 0 : return TRUE;
567 : }
568 : }
569 :
570 : /************************************************************************/
571 : /* FindField() */
572 : /************************************************************************/
573 :
574 : /**
575 : * Find the named field within this record.
576 : *
577 : * @param pszName The name of the field to fetch. The comparison is
578 : * case insensitive.
579 : * @param iFieldIndex The instance of this field to fetch. Use zero (the
580 : * default) for the first instance.
581 : *
582 : * @return Pointer to the requested DDFField. This pointer is to an
583 : * internal object, and should not be freed. It remains valid until
584 : * the next record read.
585 : */
586 :
587 1776 : DDFField * DDFRecord::FindField( const char * pszName, int iFieldIndex )
588 :
589 : {
590 4303 : for( int i = 0; i < nFieldCount; i++ )
591 : {
592 4269 : if( EQUAL(paoFields[i].GetFieldDefn()->GetName(),pszName) )
593 : {
594 1743 : if( iFieldIndex == 0 )
595 1742 : return paoFields + i;
596 : else
597 1 : iFieldIndex--;
598 : }
599 : }
600 :
601 34 : return NULL;
602 : }
603 :
604 : /************************************************************************/
605 : /* GetField() */
606 : /************************************************************************/
607 :
608 : /**
609 : * Fetch field object based on index.
610 : *
611 : * @param i The index of the field to fetch. Between 0 and GetFieldCount()-1.
612 : *
613 : * @return A DDFField pointer, or NULL if the index is out of range.
614 : */
615 :
616 1658 : DDFField *DDFRecord::GetField( int i )
617 :
618 : {
619 1658 : if( i < 0 || i >= nFieldCount )
620 0 : return NULL;
621 : else
622 1658 : return paoFields + i;
623 : }
624 :
625 : /************************************************************************/
626 : /* GetIntSubfield() */
627 : /************************************************************************/
628 :
629 : /**
630 : * Fetch value of a subfield as an integer. This is a convenience
631 : * function for fetching a subfield of a field within this record.
632 : *
633 : * @param pszField The name of the field containing the subfield.
634 : * @param iFieldIndex The instance of this field within the record. Use
635 : * zero for the first instance of this field.
636 : * @param pszSubfield The name of the subfield within the selected field.
637 : * @param iSubfieldIndex The instance of this subfield within the record.
638 : * Use zero for the first instance.
639 : * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
640 : * succeeds, or FALSE if it fails. Use NULL if you don't want to check
641 : * success.
642 : * @return The value of the subfield, or zero if it failed for some reason.
643 : */
644 :
645 546 : int DDFRecord::GetIntSubfield( const char * pszField, int iFieldIndex,
646 : const char * pszSubfield, int iSubfieldIndex,
647 : int * pnSuccess )
648 :
649 : {
650 : DDFField *poField;
651 : int nDummyErr;
652 :
653 546 : if( pnSuccess == NULL )
654 546 : pnSuccess = &nDummyErr;
655 :
656 546 : *pnSuccess = FALSE;
657 :
658 : /* -------------------------------------------------------------------- */
659 : /* Fetch the field. If this fails, return zero. */
660 : /* -------------------------------------------------------------------- */
661 546 : poField = FindField( pszField, iFieldIndex );
662 546 : if( poField == NULL )
663 0 : return 0;
664 :
665 : /* -------------------------------------------------------------------- */
666 : /* Get the subfield definition */
667 : /* -------------------------------------------------------------------- */
668 : DDFSubfieldDefn *poSFDefn;
669 :
670 546 : poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield );
671 546 : if( poSFDefn == NULL )
672 0 : return 0;
673 :
674 : /* -------------------------------------------------------------------- */
675 : /* Get a pointer to the data. */
676 : /* -------------------------------------------------------------------- */
677 : int nBytesRemaining;
678 :
679 : const char *pachData = poField->GetSubfieldData(poSFDefn,
680 : &nBytesRemaining,
681 546 : iSubfieldIndex);
682 :
683 : /* -------------------------------------------------------------------- */
684 : /* Return the extracted value. */
685 : /* */
686 : /* Assume an error has occured if no bytes are consumed. */
687 : /* -------------------------------------------------------------------- */
688 546 : int nConsumedBytes = 0;
689 : int nResult = poSFDefn->ExtractIntData( pachData, nBytesRemaining,
690 546 : &nConsumedBytes );
691 :
692 546 : if( nConsumedBytes > 0 )
693 546 : *pnSuccess = TRUE;
694 :
695 546 : return nResult;
696 : }
697 :
698 : /************************************************************************/
699 : /* GetFloatSubfield() */
700 : /************************************************************************/
701 :
702 : /**
703 : * Fetch value of a subfield as a float (double). This is a convenience
704 : * function for fetching a subfield of a field within this record.
705 : *
706 : * @param pszField The name of the field containing the subfield.
707 : * @param iFieldIndex The instance of this field within the record. Use
708 : * zero for the first instance of this field.
709 : * @param pszSubfield The name of the subfield within the selected field.
710 : * @param iSubfieldIndex The instance of this subfield within the record.
711 : * Use zero for the first instance.
712 : * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
713 : * succeeds, or FALSE if it fails. Use NULL if you don't want to check
714 : * success.
715 : * @return The value of the subfield, or zero if it failed for some reason.
716 : */
717 :
718 19 : double DDFRecord::GetFloatSubfield( const char * pszField, int iFieldIndex,
719 : const char * pszSubfield, int iSubfieldIndex,
720 : int * pnSuccess )
721 :
722 : {
723 : DDFField *poField;
724 : int nDummyErr;
725 :
726 19 : if( pnSuccess == NULL )
727 19 : pnSuccess = &nDummyErr;
728 :
729 19 : *pnSuccess = FALSE;
730 :
731 : /* -------------------------------------------------------------------- */
732 : /* Fetch the field. If this fails, return zero. */
733 : /* -------------------------------------------------------------------- */
734 19 : poField = FindField( pszField, iFieldIndex );
735 19 : if( poField == NULL )
736 0 : return 0;
737 :
738 : /* -------------------------------------------------------------------- */
739 : /* Get the subfield definition */
740 : /* -------------------------------------------------------------------- */
741 : DDFSubfieldDefn *poSFDefn;
742 :
743 19 : poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield );
744 19 : if( poSFDefn == NULL )
745 0 : return 0;
746 :
747 : /* -------------------------------------------------------------------- */
748 : /* Get a pointer to the data. */
749 : /* -------------------------------------------------------------------- */
750 : int nBytesRemaining;
751 :
752 : const char *pachData = poField->GetSubfieldData(poSFDefn,
753 : &nBytesRemaining,
754 19 : iSubfieldIndex);
755 :
756 : /* -------------------------------------------------------------------- */
757 : /* Return the extracted value. */
758 : /* -------------------------------------------------------------------- */
759 19 : int nConsumedBytes = 0;
760 : double dfResult = poSFDefn->ExtractFloatData( pachData, nBytesRemaining,
761 19 : &nConsumedBytes );
762 :
763 19 : if( nConsumedBytes > 0 )
764 19 : *pnSuccess = TRUE;
765 :
766 19 : return dfResult;
767 : }
768 :
769 : /************************************************************************/
770 : /* GetStringSubfield() */
771 : /************************************************************************/
772 :
773 : /**
774 : * Fetch value of a subfield as a string. This is a convenience
775 : * function for fetching a subfield of a field within this record.
776 : *
777 : * @param pszField The name of the field containing the subfield.
778 : * @param iFieldIndex The instance of this field within the record. Use
779 : * zero for the first instance of this field.
780 : * @param pszSubfield The name of the subfield within the selected field.
781 : * @param iSubfieldIndex The instance of this subfield within the record.
782 : * Use zero for the first instance.
783 : * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
784 : * succeeds, or FALSE if it fails. Use NULL if you don't want to check
785 : * success.
786 : * @return The value of the subfield, or NULL if it failed for some reason.
787 : * The returned pointer is to internal data and should not be modified or
788 : * freed by the application.
789 : */
790 :
791 : const char *
792 370 : DDFRecord::GetStringSubfield( const char * pszField, int iFieldIndex,
793 : const char * pszSubfield, int iSubfieldIndex,
794 : int * pnSuccess )
795 :
796 : {
797 : DDFField *poField;
798 : int nDummyErr;
799 :
800 370 : if( pnSuccess == NULL )
801 370 : pnSuccess = &nDummyErr;
802 :
803 370 : *pnSuccess = FALSE;
804 :
805 : /* -------------------------------------------------------------------- */
806 : /* Fetch the field. If this fails, return zero. */
807 : /* -------------------------------------------------------------------- */
808 370 : poField = FindField( pszField, iFieldIndex );
809 370 : if( poField == NULL )
810 0 : return NULL;
811 :
812 : /* -------------------------------------------------------------------- */
813 : /* Get the subfield definition */
814 : /* -------------------------------------------------------------------- */
815 : DDFSubfieldDefn *poSFDefn;
816 :
817 370 : poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield );
818 370 : if( poSFDefn == NULL )
819 0 : return NULL;
820 :
821 : /* -------------------------------------------------------------------- */
822 : /* Get a pointer to the data. */
823 : /* -------------------------------------------------------------------- */
824 : int nBytesRemaining;
825 :
826 : const char *pachData = poField->GetSubfieldData(poSFDefn,
827 : &nBytesRemaining,
828 370 : iSubfieldIndex);
829 :
830 : /* -------------------------------------------------------------------- */
831 : /* Return the extracted value. */
832 : /* -------------------------------------------------------------------- */
833 370 : *pnSuccess = TRUE;
834 :
835 370 : return( poSFDefn->ExtractStringData( pachData, nBytesRemaining, NULL ) );
836 : }
837 :
838 : /************************************************************************/
839 : /* Clone() */
840 : /************************************************************************/
841 :
842 : /**
843 : * Make a copy of a record.
844 : *
845 : * This method is used to make a copy of a record that will become (mostly)
846 : * the properly of application. However, it is automatically destroyed if
847 : * the DDFModule it was created relative to is destroyed, as it's field
848 : * and subfield definitions relate to that DDFModule. However, it does
849 : * persist even when the record returned by DDFModule::ReadRecord() is
850 : * invalidated, such as when reading a new record. This allows an application
851 : * to cache whole DDFRecords.
852 : *
853 : * @return A new copy of the DDFRecord. This can be delete'd by the
854 : * application when no longer needed, otherwise it will be cleaned up when
855 : * the DDFModule it relates to is destroyed or closed.
856 : */
857 :
858 499 : DDFRecord * DDFRecord::Clone()
859 :
860 : {
861 : DDFRecord *poNR;
862 :
863 499 : poNR = new DDFRecord( poModule );
864 :
865 499 : poNR->nReuseHeader = FALSE;
866 499 : poNR->nFieldOffset = nFieldOffset;
867 :
868 499 : poNR->nDataSize = nDataSize;
869 499 : poNR->pachData = (char *) CPLMalloc(nDataSize);
870 499 : memcpy( poNR->pachData, pachData, nDataSize );
871 :
872 499 : poNR->nFieldCount = nFieldCount;
873 499 : poNR->paoFields = new DDFField[nFieldCount];
874 2114 : for( int i = 0; i < nFieldCount; i++ )
875 : {
876 : int nOffset;
877 :
878 1615 : nOffset = (paoFields[i].GetData() - pachData);
879 : poNR->paoFields[i].Initialize( paoFields[i].GetFieldDefn(),
880 : poNR->pachData + nOffset,
881 1615 : paoFields[i].GetDataSize() );
882 : }
883 :
884 499 : poNR->bIsClone = TRUE;
885 499 : poModule->AddCloneRecord( poNR );
886 :
887 499 : return poNR;
888 : }
889 :
890 : /************************************************************************/
891 : /* CloneOn() */
892 : /************************************************************************/
893 :
894 : /**
895 : * Recreate a record referencing another module.
896 : *
897 : * Works similarly to the DDFRecord::Clone() method, but creates the
898 : * new record with reference to a different DDFModule. All DDFFieldDefn
899 : * references are transcribed onto the new module based on field names.
900 : * If any fields don't have a similarly named field on the target module
901 : * the operation will fail. No validation of field types and properties
902 : * is done, but this operation is intended only to be used between
903 : * modules with matching definitions of all affected fields.
904 : *
905 : * The new record will be managed as a clone by the target module in
906 : * a manner similar to regular clones.
907 : *
908 : * @param poTargetModule the module on which the record copy should be
909 : * created.
910 : *
911 : * @return NULL on failure or a pointer to the cloned record.
912 : */
913 :
914 0 : DDFRecord *DDFRecord::CloneOn( DDFModule *poTargetModule )
915 :
916 : {
917 : /* -------------------------------------------------------------------- */
918 : /* Verify that all fields have a corresponding field definition */
919 : /* on the target module. */
920 : /* -------------------------------------------------------------------- */
921 : int i;
922 :
923 0 : for( i = 0; i < nFieldCount; i++ )
924 : {
925 0 : DDFFieldDefn *poDefn = paoFields[i].GetFieldDefn();
926 :
927 0 : if( poTargetModule->FindFieldDefn( poDefn->GetName() ) == NULL )
928 0 : return NULL;
929 : }
930 :
931 : /* -------------------------------------------------------------------- */
932 : /* Create a clone. */
933 : /* -------------------------------------------------------------------- */
934 : DDFRecord *poClone;
935 :
936 0 : poClone = Clone();
937 :
938 : /* -------------------------------------------------------------------- */
939 : /* Update all internal information to reference other module. */
940 : /* -------------------------------------------------------------------- */
941 0 : for( i = 0; i < nFieldCount; i++ )
942 : {
943 0 : DDFField *poField = poClone->paoFields+i;
944 : DDFFieldDefn *poDefn;
945 :
946 : poDefn = poTargetModule->FindFieldDefn(
947 0 : poField->GetFieldDefn()->GetName() );
948 :
949 : poField->Initialize( poDefn, poField->GetData(),
950 0 : poField->GetDataSize() );
951 : }
952 :
953 0 : poModule->RemoveCloneRecord( poClone );
954 0 : poClone->poModule = poTargetModule;
955 0 : poTargetModule->AddCloneRecord( poClone );
956 :
957 0 : return poClone;
958 : }
959 :
960 :
961 : /************************************************************************/
962 : /* DeleteField() */
963 : /************************************************************************/
964 :
965 : /**
966 : * Delete a field instance from a record.
967 : *
968 : * Remove a field from this record, cleaning up the data
969 : * portion and repacking the fields list. We don't try to
970 : * reallocate the data area of the record to be smaller.
971 : *
972 : * NOTE: This method doesn't actually remove the header
973 : * information for this field from the record tag list yet.
974 : * This should be added if the resulting record is even to be
975 : * written back to disk!
976 : *
977 : * @param poTarget the field instance on this record to delete.
978 : *
979 : * @return TRUE on success, or FALSE on failure. Failure can occur if
980 : * poTarget isn't really a field on this record.
981 : */
982 :
983 0 : int DDFRecord::DeleteField( DDFField *poTarget )
984 :
985 : {
986 : int iTarget, i;
987 :
988 : /* -------------------------------------------------------------------- */
989 : /* Find which field we are to delete. */
990 : /* -------------------------------------------------------------------- */
991 0 : for( iTarget = 0; iTarget < nFieldCount; iTarget++ )
992 : {
993 0 : if( paoFields + iTarget == poTarget )
994 0 : break;
995 : }
996 :
997 0 : if( iTarget == nFieldCount )
998 0 : return FALSE;
999 :
1000 : /* -------------------------------------------------------------------- */
1001 : /* Change the target fields data size to zero. This takes care */
1002 : /* of repacking the data array, and updating all the following */
1003 : /* field data pointers. */
1004 : /* -------------------------------------------------------------------- */
1005 0 : ResizeField( poTarget, 0 );
1006 :
1007 : /* -------------------------------------------------------------------- */
1008 : /* remove the target field, moving down all the other fields */
1009 : /* one step in the field list. */
1010 : /* -------------------------------------------------------------------- */
1011 0 : for( i = iTarget; i < nFieldCount-1; i++ )
1012 : {
1013 0 : paoFields[i] = paoFields[i+1];
1014 : }
1015 :
1016 0 : nFieldCount--;
1017 :
1018 0 : return TRUE;
1019 : }
1020 :
1021 : /************************************************************************/
1022 : /* ResizeField() */
1023 : /************************************************************************/
1024 :
1025 : /**
1026 : * Alter field data size within record.
1027 : *
1028 : * This method will rearrange a DDFRecord altering the amount of space
1029 : * reserved for one of the existing fields. All following fields will
1030 : * be shifted accordingly. This includes updating the DDFField infos,
1031 : * and actually moving stuff within the data array after reallocating
1032 : * to the desired size.
1033 : *
1034 : * @param poField the field to alter.
1035 : * @param nNewDataSize the number of data bytes to be reserved for the field.
1036 : *
1037 : * @return TRUE on success or FALSE on failure.
1038 : */
1039 :
1040 0 : int DDFRecord::ResizeField( DDFField *poField, int nNewDataSize )
1041 :
1042 : {
1043 : int iTarget, i;
1044 : int nBytesToMove;
1045 :
1046 : /* -------------------------------------------------------------------- */
1047 : /* Find which field we are to resize. */
1048 : /* -------------------------------------------------------------------- */
1049 0 : for( iTarget = 0; iTarget < nFieldCount; iTarget++ )
1050 : {
1051 0 : if( paoFields + iTarget == poField )
1052 0 : break;
1053 : }
1054 :
1055 0 : if( iTarget == nFieldCount )
1056 : {
1057 : CPLAssert( FALSE );
1058 0 : return FALSE;
1059 : }
1060 :
1061 : /* -------------------------------------------------------------------- */
1062 : /* Reallocate the data buffer accordingly. */
1063 : /* -------------------------------------------------------------------- */
1064 0 : int nBytesToAdd = nNewDataSize - poField->GetDataSize();
1065 0 : const char *pachOldData = pachData;
1066 :
1067 : // Don't realloc things smaller ... we will cut off some data.
1068 0 : if( nBytesToAdd > 0 )
1069 0 : pachData = (char *) CPLRealloc(pachData, nDataSize + nBytesToAdd );
1070 :
1071 0 : nDataSize += nBytesToAdd;
1072 :
1073 : /* -------------------------------------------------------------------- */
1074 : /* How much data needs to be shifted up or down after this field? */
1075 : /* -------------------------------------------------------------------- */
1076 : nBytesToMove = nDataSize
1077 0 : - (poField->GetData()+poField->GetDataSize()-pachOldData+nBytesToAdd);
1078 :
1079 : /* -------------------------------------------------------------------- */
1080 : /* Update fields to point into newly allocated buffer. */
1081 : /* -------------------------------------------------------------------- */
1082 0 : for( i = 0; i < nFieldCount; i++ )
1083 : {
1084 : int nOffset;
1085 :
1086 0 : nOffset = paoFields[i].GetData() - pachOldData;
1087 : paoFields[i].Initialize( paoFields[i].GetFieldDefn(),
1088 : pachData + nOffset,
1089 0 : paoFields[i].GetDataSize() );
1090 : }
1091 :
1092 : /* -------------------------------------------------------------------- */
1093 : /* Shift the data beyond this field up or down as needed. */
1094 : /* -------------------------------------------------------------------- */
1095 0 : if( nBytesToMove > 0 )
1096 : memmove( (char *)poField->GetData()+poField->GetDataSize()+nBytesToAdd,
1097 : (char *)poField->GetData()+poField->GetDataSize(),
1098 0 : nBytesToMove );
1099 :
1100 : /* -------------------------------------------------------------------- */
1101 : /* Update the target fields info. */
1102 : /* -------------------------------------------------------------------- */
1103 : poField->Initialize( poField->GetFieldDefn(),
1104 : poField->GetData(),
1105 0 : poField->GetDataSize() + nBytesToAdd );
1106 :
1107 : /* -------------------------------------------------------------------- */
1108 : /* Shift all following fields down, and update their data */
1109 : /* locations. */
1110 : /* -------------------------------------------------------------------- */
1111 0 : if( nBytesToAdd < 0 )
1112 : {
1113 0 : for( i = iTarget+1; i < nFieldCount; i++ )
1114 : {
1115 : char *pszOldDataLocation;
1116 :
1117 0 : pszOldDataLocation = (char *) paoFields[i].GetData();
1118 :
1119 : paoFields[i].Initialize( paoFields[i].GetFieldDefn(),
1120 : pszOldDataLocation + nBytesToAdd,
1121 0 : paoFields[i].GetDataSize() );
1122 : }
1123 : }
1124 : else
1125 : {
1126 0 : for( i = nFieldCount-1; i > iTarget; i-- )
1127 : {
1128 : char *pszOldDataLocation;
1129 :
1130 0 : pszOldDataLocation = (char *) paoFields[i].GetData();
1131 :
1132 : paoFields[i].Initialize( paoFields[i].GetFieldDefn(),
1133 : pszOldDataLocation + nBytesToAdd,
1134 0 : paoFields[i].GetDataSize() );
1135 : }
1136 : }
1137 :
1138 0 : return TRUE;
1139 : }
1140 :
1141 : /************************************************************************/
1142 : /* AddField() */
1143 : /************************************************************************/
1144 :
1145 : /**
1146 : * Add a new field to record.
1147 : *
1148 : * Add a new zero sized field to the record. The new field is always
1149 : * added at the end of the record.
1150 : *
1151 : * NOTE: This method doesn't currently update the header information for
1152 : * the record to include the field information for this field, so the
1153 : * resulting record image isn't suitable for writing to disk. However,
1154 : * everything else about the record state should be updated properly to
1155 : * reflect the new field.
1156 : *
1157 : * @param poDefn the definition of the field to be added.
1158 : *
1159 : * @return the field object on success, or NULL on failure.
1160 : */
1161 :
1162 0 : DDFField *DDFRecord::AddField( DDFFieldDefn *poDefn )
1163 :
1164 : {
1165 : /* -------------------------------------------------------------------- */
1166 : /* Reallocate the fields array larger by one, and initialize */
1167 : /* the new field. */
1168 : /* -------------------------------------------------------------------- */
1169 : DDFField *paoNewFields;
1170 :
1171 0 : paoNewFields = new DDFField[nFieldCount+1];
1172 0 : if( nFieldCount > 0 )
1173 : {
1174 0 : memcpy( paoNewFields, paoFields, sizeof(DDFField) * nFieldCount );
1175 0 : delete[] paoFields;
1176 : }
1177 0 : paoFields = paoNewFields;
1178 0 : nFieldCount++;
1179 :
1180 : /* -------------------------------------------------------------------- */
1181 : /* Initialize the new field properly. */
1182 : /* -------------------------------------------------------------------- */
1183 0 : if( nFieldCount == 1 )
1184 : {
1185 0 : paoFields[0].Initialize( poDefn, GetData(), 0 );
1186 : }
1187 : else
1188 : {
1189 : paoFields[nFieldCount-1].Initialize(
1190 : poDefn,
1191 : paoFields[nFieldCount-2].GetData()
1192 : + paoFields[nFieldCount-2].GetDataSize(),
1193 0 : 0 );
1194 : }
1195 :
1196 : /* -------------------------------------------------------------------- */
1197 : /* Initialize field. */
1198 : /* -------------------------------------------------------------------- */
1199 0 : CreateDefaultFieldInstance( paoFields + nFieldCount-1, 0 );
1200 :
1201 0 : return paoFields + (nFieldCount - 1);
1202 : }
1203 :
1204 : /************************************************************************/
1205 : /* SetFieldRaw() */
1206 : /************************************************************************/
1207 :
1208 : /**
1209 : * Set the raw contents of a field instance.
1210 : *
1211 : * @param poField the field to set data within.
1212 : * @param iIndexWithinField The instance of this field to replace. Must
1213 : * be a value between 0 and GetRepeatCount(). If GetRepeatCount() is used, a
1214 : * new instance of the field is appeneded.
1215 : * @param pachRawData the raw data to replace this field instance with.
1216 : * @param nRawDataSize the number of bytes pointed to by pachRawData.
1217 : *
1218 : * @return TRUE on success or FALSE on failure.
1219 : */
1220 :
1221 : int
1222 0 : DDFRecord::SetFieldRaw( DDFField *poField, int iIndexWithinField,
1223 : const char *pachRawData, int nRawDataSize )
1224 :
1225 : {
1226 : int iTarget, nRepeatCount;
1227 :
1228 : /* -------------------------------------------------------------------- */
1229 : /* Find which field we are to update. */
1230 : /* -------------------------------------------------------------------- */
1231 0 : for( iTarget = 0; iTarget < nFieldCount; iTarget++ )
1232 : {
1233 0 : if( paoFields + iTarget == poField )
1234 0 : break;
1235 : }
1236 :
1237 0 : if( iTarget == nFieldCount )
1238 0 : return FALSE;
1239 :
1240 0 : nRepeatCount = poField->GetRepeatCount();
1241 :
1242 0 : if( iIndexWithinField < 0 || iIndexWithinField > nRepeatCount )
1243 0 : return FALSE;
1244 :
1245 : /* -------------------------------------------------------------------- */
1246 : /* Are we adding an instance? This is easier and different */
1247 : /* than replacing an existing instance. */
1248 : /* -------------------------------------------------------------------- */
1249 0 : if( iIndexWithinField == nRepeatCount
1250 : || !poField->GetFieldDefn()->IsRepeating() )
1251 : {
1252 : char *pachFieldData;
1253 : int nOldSize;
1254 :
1255 0 : if( !poField->GetFieldDefn()->IsRepeating() && iIndexWithinField != 0 )
1256 0 : return FALSE;
1257 :
1258 0 : nOldSize = poField->GetDataSize();
1259 0 : if( nOldSize == 0 )
1260 0 : nOldSize++; // for added DDF_FIELD_TERMINATOR.
1261 :
1262 0 : if( !ResizeField( poField, nOldSize + nRawDataSize ) )
1263 0 : return FALSE;
1264 :
1265 0 : pachFieldData = (char *) poField->GetData();
1266 : memcpy( pachFieldData + nOldSize - 1,
1267 0 : pachRawData, nRawDataSize );
1268 0 : pachFieldData[nOldSize+nRawDataSize-1] = DDF_FIELD_TERMINATOR;
1269 :
1270 0 : return TRUE;
1271 : }
1272 :
1273 : /* -------------------------------------------------------------------- */
1274 : /* Get a pointer to the start of the existing data for this */
1275 : /* iteration of the field. */
1276 : /* -------------------------------------------------------------------- */
1277 : const char *pachWrkData;
1278 : int nInstanceSize;
1279 :
1280 : // We special case this to avoid alot of warnings when initializing
1281 : // the field the first time.
1282 0 : if( poField->GetDataSize() == 0 )
1283 : {
1284 0 : pachWrkData = poField->GetData();
1285 0 : nInstanceSize = 0;
1286 : }
1287 : else
1288 : {
1289 : pachWrkData = poField->GetInstanceData( iIndexWithinField,
1290 0 : &nInstanceSize );
1291 : }
1292 :
1293 : /* -------------------------------------------------------------------- */
1294 : /* Create new image of this whole field. */
1295 : /* -------------------------------------------------------------------- */
1296 : char *pachNewImage;
1297 : int nPreBytes, nPostBytes, nNewFieldSize;
1298 :
1299 0 : nNewFieldSize = poField->GetDataSize() - nInstanceSize + nRawDataSize;
1300 :
1301 0 : pachNewImage = (char *) CPLMalloc(nNewFieldSize);
1302 :
1303 0 : nPreBytes = pachWrkData - poField->GetData();
1304 0 : nPostBytes = poField->GetDataSize() - nPreBytes - nInstanceSize;
1305 :
1306 0 : memcpy( pachNewImage, poField->GetData(), nPreBytes );
1307 : memcpy( pachNewImage + nPreBytes + nRawDataSize,
1308 : poField->GetData() + nPreBytes + nInstanceSize,
1309 0 : nPostBytes );
1310 0 : memcpy( pachNewImage + nPreBytes, pachRawData, nRawDataSize );
1311 :
1312 : /* -------------------------------------------------------------------- */
1313 : /* Resize the field to the desired new size. */
1314 : /* -------------------------------------------------------------------- */
1315 0 : ResizeField( poField, nNewFieldSize );
1316 :
1317 0 : memcpy( (void *) poField->GetData(), pachNewImage, nNewFieldSize );
1318 0 : CPLFree( pachNewImage );
1319 :
1320 0 : return TRUE;
1321 : }
1322 :
1323 : /************************************************************************/
1324 : /* UpdateFieldRaw() */
1325 : /************************************************************************/
1326 :
1327 : int
1328 0 : DDFRecord::UpdateFieldRaw( DDFField *poField, int iIndexWithinField,
1329 : int nStartOffset, int nOldSize,
1330 : const char *pachRawData, int nRawDataSize )
1331 :
1332 : {
1333 : int iTarget, nRepeatCount;
1334 :
1335 : /* -------------------------------------------------------------------- */
1336 : /* Find which field we are to update. */
1337 : /* -------------------------------------------------------------------- */
1338 0 : for( iTarget = 0; iTarget < nFieldCount; iTarget++ )
1339 : {
1340 0 : if( paoFields + iTarget == poField )
1341 0 : break;
1342 : }
1343 :
1344 0 : if( iTarget == nFieldCount )
1345 0 : return FALSE;
1346 :
1347 0 : nRepeatCount = poField->GetRepeatCount();
1348 :
1349 0 : if( iIndexWithinField < 0 || iIndexWithinField >= nRepeatCount )
1350 0 : return FALSE;
1351 :
1352 : /* -------------------------------------------------------------------- */
1353 : /* Figure out how much pre and post data there is. */
1354 : /* -------------------------------------------------------------------- */
1355 : char *pachWrkData;
1356 : int nInstanceSize, nPostBytes, nPreBytes;
1357 :
1358 : pachWrkData = (char *) poField->GetInstanceData( iIndexWithinField,
1359 0 : &nInstanceSize );
1360 0 : nPreBytes = pachWrkData - poField->GetData() + nStartOffset;
1361 0 : nPostBytes = poField->GetDataSize() - nPreBytes - nOldSize;
1362 :
1363 : /* -------------------------------------------------------------------- */
1364 : /* If we aren't changing the size, just copy over the existing */
1365 : /* data. */
1366 : /* -------------------------------------------------------------------- */
1367 0 : if( nOldSize == nRawDataSize )
1368 : {
1369 0 : memcpy( pachWrkData + nStartOffset, pachRawData, nRawDataSize );
1370 0 : return TRUE;
1371 : }
1372 :
1373 : /* -------------------------------------------------------------------- */
1374 : /* If we are shrinking, move in the new data, and shuffle down */
1375 : /* the old before resizing. */
1376 : /* -------------------------------------------------------------------- */
1377 0 : if( nRawDataSize < nOldSize )
1378 : {
1379 : memcpy( ((char*) poField->GetData()) + nPreBytes,
1380 0 : pachRawData, nRawDataSize );
1381 : memmove( ((char *) poField->GetData()) + nPreBytes + nRawDataSize,
1382 : ((char *) poField->GetData()) + nPreBytes + nOldSize,
1383 0 : nPostBytes );
1384 : }
1385 :
1386 : /* -------------------------------------------------------------------- */
1387 : /* Resize the whole buffer. */
1388 : /* -------------------------------------------------------------------- */
1389 0 : if( !ResizeField( poField,
1390 : poField->GetDataSize() - nOldSize + nRawDataSize ) )
1391 0 : return FALSE;
1392 :
1393 : /* -------------------------------------------------------------------- */
1394 : /* If we growing the buffer, shuffle up the post data, and */
1395 : /* move in our new values. */
1396 : /* -------------------------------------------------------------------- */
1397 0 : if( nRawDataSize >= nOldSize )
1398 : {
1399 : memmove( ((char *) poField->GetData()) + nPreBytes + nRawDataSize,
1400 : ((char *) poField->GetData()) + nPreBytes + nOldSize,
1401 0 : nPostBytes );
1402 : memcpy( ((char*) poField->GetData()) + nPreBytes,
1403 0 : pachRawData, nRawDataSize );
1404 : }
1405 :
1406 0 : return TRUE;
1407 : }
1408 :
1409 : /************************************************************************/
1410 : /* ResetDirectory() */
1411 : /* */
1412 : /* Re-prepares the directory information for the record. */
1413 : /************************************************************************/
1414 :
1415 0 : int DDFRecord::ResetDirectory()
1416 :
1417 : {
1418 : int iField;
1419 :
1420 : /* -------------------------------------------------------------------- */
1421 : /* Eventually we should try to optimize the size of offset and */
1422 : /* field length. For now we will use 5 for each which is */
1423 : /* pretty big. */
1424 : /* -------------------------------------------------------------------- */
1425 0 : _sizeFieldPos = 5;
1426 0 : _sizeFieldLength = 5;
1427 :
1428 : /* -------------------------------------------------------------------- */
1429 : /* Compute how large the directory needs to be. */
1430 : /* -------------------------------------------------------------------- */
1431 : int nEntrySize, nDirSize;
1432 :
1433 0 : nEntrySize = _sizeFieldPos + _sizeFieldLength + _sizeFieldTag;
1434 0 : nDirSize = nEntrySize * nFieldCount + 1;
1435 :
1436 : /* -------------------------------------------------------------------- */
1437 : /* If the directory size is different than what is currently */
1438 : /* reserved for it, we must resize. */
1439 : /* -------------------------------------------------------------------- */
1440 0 : if( nDirSize != nFieldOffset )
1441 : {
1442 : char *pachNewData;
1443 : int nNewDataSize;
1444 :
1445 0 : nNewDataSize = nDataSize - nFieldOffset + nDirSize;
1446 0 : pachNewData = (char *) CPLMalloc(nNewDataSize);
1447 : memcpy( pachNewData + nDirSize,
1448 : pachData + nFieldOffset,
1449 0 : nNewDataSize - nDirSize );
1450 :
1451 0 : for( iField = 0; iField < nFieldCount; iField++ )
1452 : {
1453 : int nOffset;
1454 0 : DDFField *poField = GetField( iField );
1455 :
1456 0 : nOffset = poField->GetData() - pachData - nFieldOffset + nDirSize;
1457 : poField->Initialize( poField->GetFieldDefn(),
1458 : pachNewData + nOffset,
1459 0 : poField->GetDataSize() );
1460 : }
1461 :
1462 0 : CPLFree( pachData );
1463 0 : pachData = pachNewData;
1464 0 : nDataSize = nNewDataSize;
1465 0 : nFieldOffset = nDirSize;
1466 : }
1467 :
1468 : /* -------------------------------------------------------------------- */
1469 : /* Now set each directory entry. */
1470 : /* -------------------------------------------------------------------- */
1471 0 : for( iField = 0; iField < nFieldCount; iField++ )
1472 : {
1473 0 : DDFField *poField = GetField( iField );
1474 0 : DDFFieldDefn *poDefn = poField->GetFieldDefn();
1475 : char szFormat[128];
1476 :
1477 : sprintf( szFormat, "%%%ds%%0%dd%%0%dd",
1478 0 : _sizeFieldTag, _sizeFieldLength, _sizeFieldPos );
1479 :
1480 : sprintf( pachData + nEntrySize * iField, szFormat,
1481 : poDefn->GetName(), poField->GetDataSize(),
1482 0 : poField->GetData() - pachData - nFieldOffset );
1483 : }
1484 :
1485 0 : pachData[nEntrySize * nFieldCount] = DDF_FIELD_TERMINATOR;
1486 :
1487 0 : return TRUE;
1488 : }
1489 :
1490 : /************************************************************************/
1491 : /* CreateDefaultFieldInstance() */
1492 : /************************************************************************/
1493 :
1494 : /**
1495 : * Initialize default instance.
1496 : *
1497 : * This method is normally only used internally by the AddField() method
1498 : * to initialize the new field instance with default subfield values. It
1499 : * installs default data for one instance of the field in the record
1500 : * using the DDFFieldDefn::GetDefaultValue() method and
1501 : * DDFRecord::SetFieldRaw().
1502 : *
1503 : * @param poField the field within the record to be assign a default
1504 : * instance.
1505 : * @param iIndexWithinField the instance to set (may not have been tested with
1506 : * values other than 0).
1507 : *
1508 : * @return TRUE on success or FALSE on failure.
1509 : */
1510 :
1511 0 : int DDFRecord::CreateDefaultFieldInstance( DDFField *poField,
1512 : int iIndexWithinField )
1513 :
1514 : {
1515 : int nRawSize, nSuccess;
1516 : char *pachRawData;
1517 :
1518 0 : pachRawData = poField->GetFieldDefn()->GetDefaultValue( &nRawSize );
1519 0 : if( pachRawData == NULL )
1520 0 : return FALSE;
1521 :
1522 0 : nSuccess = SetFieldRaw( poField, iIndexWithinField, pachRawData, nRawSize);
1523 :
1524 0 : CPLFree( pachRawData );
1525 :
1526 0 : return nSuccess;
1527 : }
1528 :
1529 : /************************************************************************/
1530 : /* SetStringSubfield() */
1531 : /************************************************************************/
1532 :
1533 : /**
1534 : * Set a string subfield in record.
1535 : *
1536 : * The value of a given subfield is replaced with a new string value
1537 : * formatted appropriately.
1538 : *
1539 : * @param pszField the field name to operate on.
1540 : * @param iFieldIndex the field index to operate on (zero based).
1541 : * @param pszSubfield the subfield name to operate on.
1542 : * @param iSubfieldIndex the subfield index to operate on (zero based).
1543 : * @param pszValue the new string to place in the subfield. This may be
1544 : * arbitrary binary bytes if nValueLength is specified.
1545 : * @param nValueLength the number of valid bytes in pszValue, may be -1 to
1546 : * internally fetch with strlen().
1547 : *
1548 : * @return TRUE if successful, and FALSE if not.
1549 : */
1550 :
1551 0 : int DDFRecord::SetStringSubfield( const char *pszField, int iFieldIndex,
1552 : const char *pszSubfield, int iSubfieldIndex,
1553 : const char *pszValue, int nValueLength )
1554 :
1555 : {
1556 : /* -------------------------------------------------------------------- */
1557 : /* Fetch the field. If this fails, return zero. */
1558 : /* -------------------------------------------------------------------- */
1559 : DDFField *poField;
1560 :
1561 0 : poField = FindField( pszField, iFieldIndex );
1562 0 : if( poField == NULL )
1563 0 : return FALSE;
1564 :
1565 : /* -------------------------------------------------------------------- */
1566 : /* Get the subfield definition */
1567 : /* -------------------------------------------------------------------- */
1568 : DDFSubfieldDefn *poSFDefn;
1569 :
1570 0 : poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield );
1571 0 : if( poSFDefn == NULL )
1572 0 : return FALSE;
1573 :
1574 : /* -------------------------------------------------------------------- */
1575 : /* How long will the formatted value be? */
1576 : /* -------------------------------------------------------------------- */
1577 : int nFormattedLen;
1578 :
1579 0 : if( !poSFDefn->FormatStringValue( NULL, 0, &nFormattedLen, pszValue,
1580 : nValueLength ) )
1581 0 : return FALSE;
1582 :
1583 : /* -------------------------------------------------------------------- */
1584 : /* Get a pointer to the data. */
1585 : /* -------------------------------------------------------------------- */
1586 : int nMaxBytes;
1587 : char *pachSubfieldData = (char *)
1588 : poField->GetSubfieldData(poSFDefn, &nMaxBytes,
1589 0 : iSubfieldIndex);
1590 :
1591 : /* -------------------------------------------------------------------- */
1592 : /* Add new instance if we have run out of data. */
1593 : /* -------------------------------------------------------------------- */
1594 0 : if( nMaxBytes == 0
1595 0 : || (nMaxBytes == 1 && pachSubfieldData[0] == DDF_FIELD_TERMINATOR) )
1596 : {
1597 0 : CreateDefaultFieldInstance( poField, iSubfieldIndex );
1598 :
1599 : // Refetch.
1600 : pachSubfieldData = (char *)
1601 : poField->GetSubfieldData(poSFDefn, &nMaxBytes,
1602 0 : iSubfieldIndex);
1603 : }
1604 :
1605 : /* -------------------------------------------------------------------- */
1606 : /* If the new length matches the existing length, just overlay */
1607 : /* and return. */
1608 : /* -------------------------------------------------------------------- */
1609 : int nExistingLength;
1610 :
1611 0 : poSFDefn->GetDataLength( pachSubfieldData, nMaxBytes, &nExistingLength );
1612 :
1613 0 : if( nExistingLength == nFormattedLen )
1614 : {
1615 : return poSFDefn->FormatStringValue( pachSubfieldData, nFormattedLen,
1616 0 : NULL, pszValue, nValueLength );
1617 : }
1618 :
1619 : /* -------------------------------------------------------------------- */
1620 : /* We will need to resize the raw data. */
1621 : /* -------------------------------------------------------------------- */
1622 : const char *pachFieldInstData;
1623 : int nInstanceSize, nStartOffset, nSuccess;
1624 : char *pachNewData;
1625 :
1626 : pachFieldInstData = poField->GetInstanceData( iFieldIndex,
1627 0 : &nInstanceSize );
1628 :
1629 0 : nStartOffset = pachSubfieldData - pachFieldInstData;
1630 :
1631 0 : pachNewData = (char *) CPLMalloc(nFormattedLen);
1632 : poSFDefn->FormatStringValue( pachNewData, nFormattedLen, NULL,
1633 0 : pszValue, nValueLength );
1634 :
1635 : nSuccess = UpdateFieldRaw( poField, iFieldIndex,
1636 : nStartOffset, nExistingLength,
1637 0 : pachNewData, nFormattedLen );
1638 :
1639 0 : CPLFree( pachNewData );
1640 :
1641 0 : return nSuccess;
1642 : }
1643 :
1644 : /************************************************************************/
1645 : /* SetIntSubfield() */
1646 : /************************************************************************/
1647 :
1648 : /**
1649 : * Set an integer subfield in record.
1650 : *
1651 : * The value of a given subfield is replaced with a new integer value
1652 : * formatted appropriately.
1653 : *
1654 : * @param pszField the field name to operate on.
1655 : * @param iFieldIndex the field index to operate on (zero based).
1656 : * @param pszSubfield the subfield name to operate on.
1657 : * @param iSubfieldIndex the subfield index to operate on (zero based).
1658 : * @param nNewValue the new value to place in the subfield.
1659 : *
1660 : * @return TRUE if successful, and FALSE if not.
1661 : */
1662 :
1663 0 : int DDFRecord::SetIntSubfield( const char *pszField, int iFieldIndex,
1664 : const char *pszSubfield, int iSubfieldIndex,
1665 : int nNewValue )
1666 :
1667 : {
1668 : /* -------------------------------------------------------------------- */
1669 : /* Fetch the field. If this fails, return zero. */
1670 : /* -------------------------------------------------------------------- */
1671 : DDFField *poField;
1672 :
1673 0 : poField = FindField( pszField, iFieldIndex );
1674 0 : if( poField == NULL )
1675 0 : return FALSE;
1676 :
1677 : /* -------------------------------------------------------------------- */
1678 : /* Get the subfield definition */
1679 : /* -------------------------------------------------------------------- */
1680 : DDFSubfieldDefn *poSFDefn;
1681 :
1682 0 : poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield );
1683 0 : if( poSFDefn == NULL )
1684 0 : return FALSE;
1685 :
1686 : /* -------------------------------------------------------------------- */
1687 : /* How long will the formatted value be? */
1688 : /* -------------------------------------------------------------------- */
1689 : int nFormattedLen;
1690 :
1691 0 : if( !poSFDefn->FormatIntValue( NULL, 0, &nFormattedLen, nNewValue ) )
1692 0 : return FALSE;
1693 :
1694 : /* -------------------------------------------------------------------- */
1695 : /* Get a pointer to the data. */
1696 : /* -------------------------------------------------------------------- */
1697 : int nMaxBytes;
1698 : char *pachSubfieldData = (char *)
1699 : poField->GetSubfieldData(poSFDefn, &nMaxBytes,
1700 0 : iSubfieldIndex);
1701 :
1702 : /* -------------------------------------------------------------------- */
1703 : /* Add new instance if we have run out of data. */
1704 : /* -------------------------------------------------------------------- */
1705 0 : if( nMaxBytes == 0
1706 0 : || (nMaxBytes == 1 && pachSubfieldData[0] == DDF_FIELD_TERMINATOR) )
1707 : {
1708 0 : CreateDefaultFieldInstance( poField, iSubfieldIndex );
1709 :
1710 : // Refetch.
1711 : pachSubfieldData = (char *)
1712 : poField->GetSubfieldData(poSFDefn, &nMaxBytes,
1713 0 : iSubfieldIndex);
1714 : }
1715 :
1716 : /* -------------------------------------------------------------------- */
1717 : /* If the new length matches the existing length, just overlay */
1718 : /* and return. */
1719 : /* -------------------------------------------------------------------- */
1720 : int nExistingLength;
1721 :
1722 0 : poSFDefn->GetDataLength( pachSubfieldData, nMaxBytes, &nExistingLength );
1723 :
1724 0 : if( nExistingLength == nFormattedLen )
1725 : {
1726 : return poSFDefn->FormatIntValue( pachSubfieldData, nFormattedLen,
1727 0 : NULL, nNewValue );
1728 : }
1729 :
1730 : /* -------------------------------------------------------------------- */
1731 : /* We will need to resize the raw data. */
1732 : /* -------------------------------------------------------------------- */
1733 : const char *pachFieldInstData;
1734 : int nInstanceSize, nStartOffset, nSuccess;
1735 : char *pachNewData;
1736 :
1737 : pachFieldInstData = poField->GetInstanceData( iFieldIndex,
1738 0 : &nInstanceSize );
1739 :
1740 0 : nStartOffset = pachSubfieldData - pachFieldInstData;
1741 :
1742 0 : pachNewData = (char *) CPLMalloc(nFormattedLen);
1743 : poSFDefn->FormatIntValue( pachNewData, nFormattedLen, NULL,
1744 0 : nNewValue );
1745 :
1746 : nSuccess = UpdateFieldRaw( poField, iFieldIndex,
1747 : nStartOffset, nExistingLength,
1748 0 : pachNewData, nFormattedLen );
1749 :
1750 0 : CPLFree( pachNewData );
1751 :
1752 0 : return nSuccess;
1753 : }
1754 :
1755 : /************************************************************************/
1756 : /* SetFloatSubfield() */
1757 : /************************************************************************/
1758 :
1759 : /**
1760 : * Set a float subfield in record.
1761 : *
1762 : * The value of a given subfield is replaced with a new float value
1763 : * formatted appropriately.
1764 : *
1765 : * @param pszField the field name to operate on.
1766 : * @param iFieldIndex the field index to operate on (zero based).
1767 : * @param pszSubfield the subfield name to operate on.
1768 : * @param iSubfieldIndex the subfield index to operate on (zero based).
1769 : * @param dfNewValue the new value to place in the subfield.
1770 : *
1771 : * @return TRUE if successful, and FALSE if not.
1772 : */
1773 :
1774 0 : int DDFRecord::SetFloatSubfield( const char *pszField, int iFieldIndex,
1775 : const char *pszSubfield, int iSubfieldIndex,
1776 : double dfNewValue )
1777 :
1778 : {
1779 : /* -------------------------------------------------------------------- */
1780 : /* Fetch the field. If this fails, return zero. */
1781 : /* -------------------------------------------------------------------- */
1782 : DDFField *poField;
1783 :
1784 0 : poField = FindField( pszField, iFieldIndex );
1785 0 : if( poField == NULL )
1786 0 : return FALSE;
1787 :
1788 : /* -------------------------------------------------------------------- */
1789 : /* Get the subfield definition */
1790 : /* -------------------------------------------------------------------- */
1791 : DDFSubfieldDefn *poSFDefn;
1792 :
1793 0 : poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield );
1794 0 : if( poSFDefn == NULL )
1795 0 : return FALSE;
1796 :
1797 : /* -------------------------------------------------------------------- */
1798 : /* How long will the formatted value be? */
1799 : /* -------------------------------------------------------------------- */
1800 : int nFormattedLen;
1801 :
1802 0 : if( !poSFDefn->FormatFloatValue( NULL, 0, &nFormattedLen, dfNewValue ) )
1803 0 : return FALSE;
1804 :
1805 : /* -------------------------------------------------------------------- */
1806 : /* Get a pointer to the data. */
1807 : /* -------------------------------------------------------------------- */
1808 : int nMaxBytes;
1809 : char *pachSubfieldData = (char *)
1810 : poField->GetSubfieldData(poSFDefn, &nMaxBytes,
1811 0 : iSubfieldIndex);
1812 :
1813 : /* -------------------------------------------------------------------- */
1814 : /* Add new instance if we have run out of data. */
1815 : /* -------------------------------------------------------------------- */
1816 0 : if( nMaxBytes == 0
1817 0 : || (nMaxBytes == 1 && pachSubfieldData[0] == DDF_FIELD_TERMINATOR) )
1818 : {
1819 0 : CreateDefaultFieldInstance( poField, iSubfieldIndex );
1820 :
1821 : // Refetch.
1822 : pachSubfieldData = (char *)
1823 : poField->GetSubfieldData(poSFDefn, &nMaxBytes,
1824 0 : iSubfieldIndex);
1825 : }
1826 :
1827 : /* -------------------------------------------------------------------- */
1828 : /* If the new length matches the existing length, just overlay */
1829 : /* and return. */
1830 : /* -------------------------------------------------------------------- */
1831 : int nExistingLength;
1832 :
1833 0 : poSFDefn->GetDataLength( pachSubfieldData, nMaxBytes, &nExistingLength );
1834 :
1835 0 : if( nExistingLength == nFormattedLen )
1836 : {
1837 : return poSFDefn->FormatFloatValue( pachSubfieldData, nFormattedLen,
1838 0 : NULL, dfNewValue );
1839 : }
1840 :
1841 : /* -------------------------------------------------------------------- */
1842 : /* We will need to resize the raw data. */
1843 : /* -------------------------------------------------------------------- */
1844 : const char *pachFieldInstData;
1845 : int nInstanceSize, nStartOffset, nSuccess;
1846 : char *pachNewData;
1847 :
1848 : pachFieldInstData = poField->GetInstanceData( iFieldIndex,
1849 0 : &nInstanceSize );
1850 :
1851 0 : nStartOffset = (int) (pachSubfieldData - pachFieldInstData);
1852 :
1853 0 : pachNewData = (char *) CPLMalloc(nFormattedLen);
1854 : poSFDefn->FormatFloatValue( pachNewData, nFormattedLen, NULL,
1855 0 : dfNewValue );
1856 :
1857 : nSuccess = UpdateFieldRaw( poField, iFieldIndex,
1858 : nStartOffset, nExistingLength,
1859 0 : pachNewData, nFormattedLen );
1860 :
1861 0 : CPLFree( pachNewData );
1862 :
1863 0 : return nSuccess;
1864 : }
|