mapxbase.c 32.9 KB
Newer Older
1
/******************************************************************************
2
 * $Id$
3 4
 *
 * Project:  MapServer
5
 * Purpose:  .dbf access API.  Derived from shapelib, and relicensed with
6 7 8 9 10 11 12 13 14 15 16 17 18
 *           permission of Frank Warmerdam (shapelib author).
 * Author:   Steve Lime and the MapServer team.
 *
 ******************************************************************************
 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
19
 * The above copyright notice and this permission notice shall be included in
20 21 22 23 24 25 26 27 28 29 30
 * all copies of this Software or works derived from this Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 ****************************************************************************/

31 32
#define _FILE_OFFSET_BITS 64

33 34 35 36
#include "mapserver.h"
#include <stdlib.h> /* for atof() and atoi() */
#include <math.h>

37

38 39 40 41 42 43 44 45 46

/* try to use a large file version of fseek for files up to 4GB (#3514) */
#if _MSC_VER > 1310
#  define safe_fseek _fseeki64
#elif defined(fseeko)
#  define safe_fseek fseeko
#else
#  define safe_fseek fseek
#endif
47

48 49
static inline void IGUR_sizet(size_t ignored) { (void)ignored; }  /* Ignore GCC Unused Result */

50 51 52 53 54 55 56 57 58
/************************************************************************/
/*                             SfRealloc()                              */
/*                                                                      */
/*      A realloc cover function that will access a NULL pointer as     */
/*      a valid input.                                                  */
/************************************************************************/
static void * SfRealloc( void * pMem, int nNewSize )

{
59
  return( (void *) realloc(pMem,nNewSize) );
60 61 62 63 64 65 66 67 68 69 70 71 72
}

/************************************************************************/
/*                           writeHeader()                              */
/*                                                                      */
/*      This is called to write out the file header, and field          */
/*      descriptions before writing any actual data records.  This      */
/*      also computes all the DBFDataSet field offset/size/decimals     */
/*      and so forth values.                                            */
/************************************************************************/
static void writeHeader(DBFHandle psDBF)

{
73 74
  uchar abyHeader[32];
  int   i;
75

76 77
  if( !psDBF->bNoHeader )
    return;
78

79
  psDBF->bNoHeader = MS_FALSE;
80

81 82 83 84 85
  /* -------------------------------------------------------------------- */
  /*  Initialize the file header information.               */
  /* -------------------------------------------------------------------- */
  for( i = 0; i < 32; i++ )
    abyHeader[i] = 0;
86

87
  abyHeader[0] = 0x03;    /* memo field? - just copying   */
88

89
  /* date updated on close, record count preset at zero */
90

91 92
  abyHeader[8] = psDBF->nHeaderLength % 256;
  abyHeader[9] = psDBF->nHeaderLength / 256;
93

94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
  abyHeader[10] = psDBF->nRecordLength % 256;
  abyHeader[11] = psDBF->nRecordLength / 256;

  /* -------------------------------------------------------------------- */
  /*      Write the initial 32 byte file header, and all the field        */
  /*      descriptions.                                             */
  /* -------------------------------------------------------------------- */
  fseek( psDBF->fp, 0, 0 );
  fwrite( abyHeader, 32, 1, psDBF->fp );
  fwrite( psDBF->pszHeader, 32, psDBF->nFields, psDBF->fp );

  /* -------------------------------------------------------------------- */
  /*      Write out the newline character if there is room for it.        */
  /* -------------------------------------------------------------------- */
  if( psDBF->nHeaderLength > 32*psDBF->nFields + 32 ) {
    char  cNewline;

    cNewline = 0x0d;
    fwrite( &cNewline, 1, 1, psDBF->fp );
  }
114 115 116 117 118 119 120 121 122 123
}

/************************************************************************/
/*                           flushRecord()                              */
/*                                                                      */
/*      Write out the current record if there is one.                   */
/************************************************************************/
static void flushRecord( DBFHandle psDBF )

{
124
  unsigned int nRecordOffset;
125

126 127
  if( psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1 ) {
    psDBF->bCurrentRecordModified = MS_FALSE;
128

129 130
    nRecordOffset = psDBF->nRecordLength * psDBF->nCurrentRecord
                    + psDBF->nHeaderLength;
131

132 133 134
    safe_fseek( psDBF->fp, nRecordOffset, 0 );
    fwrite( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp );
  }
135 136 137 138 139 140
}

/************************************************************************/
/*                              msDBFOpen()                             */
/*                                                                      */
/*      Open a .dbf file.                                               */
141
/************************************************************************/
142 143 144
DBFHandle msDBFOpen( const char * pszFilename, const char * pszAccess )

{
145 146 147 148
  DBFHandle   psDBF;
  uchar   *pabyBuf;
  int     nFields, nRecords, nHeadLen, nRecLen, iField;
  char          *pszDBFFilename;
149

150 151 152 153 154 155 156 157 158 159 160 161 162 163
  /* -------------------------------------------------------------------- */
  /*      We only allow the access strings "rb" and "r+".                 */
  /* -------------------------------------------------------------------- */
  if( strcmp(pszAccess,"r") != 0 && strcmp(pszAccess,"r+") != 0
      && strcmp(pszAccess,"rb") != 0 && strcmp(pszAccess,"r+b") != 0 )
    return( NULL );

  /* -------------------------------------------------------------------- */
  /*  Ensure the extension is converted to dbf or DBF if it is      */
  /*  currently .shp or .shx.               */
  /* -------------------------------------------------------------------- */
  pszDBFFilename = (char *) msSmallMalloc(strlen(pszFilename)+1);
  strcpy( pszDBFFilename, pszFilename );

164 165
  if( strcmp(pszFilename+strlen(pszFilename)-4,".shp") == 0
      || strcmp(pszFilename+strlen(pszFilename)-4,".shx") == 0 ) {
166
    strcpy( pszDBFFilename+strlen(pszDBFFilename)-4, ".dbf");
167 168
  } else if( strcmp(pszFilename+strlen(pszFilename)-4,".SHP") == 0
             || strcmp(pszFilename+strlen(pszFilename)-4,".SHX") == 0 ) {
169 170 171 172 173 174 175 176 177
    strcpy( pszDBFFilename+strlen(pszDBFFilename)-4, ".DBF");
  }

  /* -------------------------------------------------------------------- */
  /*      Open the file.                                                  */
  /* -------------------------------------------------------------------- */
  psDBF = (DBFHandle) calloc( 1, sizeof(DBFInfo) );
  MS_CHECK_ALLOC(psDBF, sizeof(DBFInfo), NULL);
  psDBF->fp = fopen( pszDBFFilename, pszAccess );
178 179 180 181 182 183 184
  if( psDBF->fp == NULL )
  {
    if( strcmp(pszDBFFilename+strlen(pszDBFFilename)-4,".dbf") == 0 ) {
      strcpy( pszDBFFilename+strlen(pszDBFFilename)-4, ".DBF");
      psDBF->fp = fopen( pszDBFFilename, pszAccess );
    }
  }
185 186 187
  if( psDBF->fp == NULL ) {
    msFree(pszDBFFilename);
    msFree(psDBF);
188
    return( NULL );
189
  }
190 191 192 193 194 195 196 197 198 199 200 201 202 203

  psDBF->bNoHeader = MS_FALSE;
  psDBF->nCurrentRecord = -1;
  psDBF->bCurrentRecordModified = MS_FALSE;

  psDBF->pszStringField = NULL;
  psDBF->nStringFieldLen = 0;

  free( pszDBFFilename );

  /* -------------------------------------------------------------------- */
  /*  Read Table Header info                                              */
  /* -------------------------------------------------------------------- */
  pabyBuf = (uchar *) msSmallMalloc(500);
204 205 206 207 208 209
  if( fread( pabyBuf, 32, 1, psDBF->fp ) != 1 )
  {
    msFree(psDBF);
    msFree(pabyBuf);
    return( NULL );
  }
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227

  psDBF->nRecords = nRecords =
                      pabyBuf[4] + pabyBuf[5]*256 + pabyBuf[6]*256*256 + pabyBuf[7]*256*256*256;

  psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9]*256;
  psDBF->nRecordLength = nRecLen = pabyBuf[10] + pabyBuf[11]*256;

  psDBF->nFields = nFields = (nHeadLen - 32) / 32;

  psDBF->pszCurrentRecord = (char *) msSmallMalloc(nRecLen);

  /* -------------------------------------------------------------------- */
  /*  Read in Field Definitions                                           */
  /* -------------------------------------------------------------------- */
  pabyBuf = (uchar *) SfRealloc(pabyBuf,nHeadLen);
  psDBF->pszHeader = (char *) pabyBuf;

  fseek( psDBF->fp, 32, 0 );
228 229 230 231 232 233 234
  if( fread( pabyBuf, nHeadLen - 32, 1, psDBF->fp ) != 1 )
  {
    msFree(psDBF->pszCurrentRecord);
    msFree(psDBF);
    msFree(pabyBuf);
    return( NULL );
  }
235 236 237 238 239

  psDBF->panFieldOffset = (int *) msSmallMalloc(sizeof(int) * nFields);
  psDBF->panFieldSize = (int *) msSmallMalloc(sizeof(int) * nFields);
  psDBF->panFieldDecimals = (int *) msSmallMalloc(sizeof(int) * nFields);
  psDBF->pachFieldType = (char *) msSmallMalloc(sizeof(char) * nFields);
240

241 242 243 244 245 246 247 248 249 250 251
  for( iField = 0; iField < nFields; iField++ ) {
    uchar   *pabyFInfo;

    pabyFInfo = pabyBuf+iField*32;

    if( pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F' ) {
      psDBF->panFieldSize[iField] = pabyFInfo[16];
      psDBF->panFieldDecimals[iField] = pabyFInfo[17];
    } else {
      psDBF->panFieldSize[iField] = pabyFInfo[16] + pabyFInfo[17]*256;
      psDBF->panFieldDecimals[iField] = 0;
252 253
    }

254 255 256 257 258 259 260 261 262
    psDBF->pachFieldType[iField] = (char) pabyFInfo[11];
    if( iField == 0 )
      psDBF->panFieldOffset[iField] = 1;
    else
      psDBF->panFieldOffset[iField] =
        psDBF->panFieldOffset[iField-1] + psDBF->panFieldSize[iField-1];
  }

  return( psDBF );
263 264 265 266 267 268 269 270 271 272 273
}

/************************************************************************/
/*                              msDBFClose()                            */
/************************************************************************/

void  msDBFClose(DBFHandle psDBF)
{
  /* -------------------------------------------------------------------- */
  /*      Write out header if not already written.                        */
  /* -------------------------------------------------------------------- */
274 275
  if( psDBF->bNoHeader )
    writeHeader( psDBF );
276

277
  flushRecord( psDBF );
278

279 280 281 282 283 284
  /* -------------------------------------------------------------------- */
  /*      Update last access date, and number of records if we have       */
  /*  write access.                             */
  /* -------------------------------------------------------------------- */
  if( psDBF->bUpdated ) {
    uchar   abyFileHeader[32];
285

286
    fseek( psDBF->fp, 0, 0 );
287
    IGUR_sizet(fread( abyFileHeader, 32, 1, psDBF->fp ));
288

289 290 291
    abyFileHeader[1] = 95;      /* YY */
    abyFileHeader[2] = 7;     /* MM */
    abyFileHeader[3] = 26;      /* DD */
292

293 294 295 296
    abyFileHeader[4] = psDBF->nRecords % 256;
    abyFileHeader[5] = (psDBF->nRecords/256) % 256;
    abyFileHeader[6] = (psDBF->nRecords/(256*256)) % 256;
    abyFileHeader[7] = (psDBF->nRecords/(256*256*256)) % 256;
297

298 299 300
    fseek( psDBF->fp, 0, 0 );
    fwrite( abyFileHeader, 32, 1, psDBF->fp );
  }
301

302 303 304 305 306 307 308 309 310 311 312
  /* -------------------------------------------------------------------- */
  /*      Close, and free resources.                                      */
  /* -------------------------------------------------------------------- */
  fclose( psDBF->fp );

  if( psDBF->panFieldOffset != NULL ) {
    free( psDBF->panFieldOffset );
    free( psDBF->panFieldSize );
    free( psDBF->panFieldDecimals );
    free( psDBF->pachFieldType );
  }
313

314 315
  free( psDBF->pszHeader );
  free( psDBF->pszCurrentRecord );
316

317
  free(psDBF->pszStringField);
318

319
  free( psDBF );
320 321 322 323 324 325 326 327 328 329
}

/************************************************************************/
/*                             msDBFCreate()                            */
/*                                                                      */
/*      Create a new .dbf file.                                         */
/************************************************************************/
DBFHandle msDBFCreate( const char * pszFilename )

{
330 331
  DBFHandle psDBF;
  FILE  *fp;
332

333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
  /* -------------------------------------------------------------------- */
  /*      Create the file.                                                */
  /* -------------------------------------------------------------------- */
  fp = fopen( pszFilename, "wb" );
  if( fp == NULL )
    return( NULL );

  fputc( 0, fp );
  fclose( fp );

  fp = fopen( pszFilename, "rb+" );
  if( fp == NULL )
    return( NULL );

  /* -------------------------------------------------------------------- */
  /*  Create the info structure.                */
  /* -------------------------------------------------------------------- */
  psDBF = (DBFHandle) malloc(sizeof(DBFInfo));
  if (psDBF == NULL) {
    msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n", "msDBFCreate()",
353
               __FILE__, __LINE__, (unsigned int)sizeof(DBFInfo));
354 355 356 357 358 359 360 361 362
    fclose(fp);
    return NULL;
  }

  psDBF->fp = fp;
  psDBF->nRecords = 0;
  psDBF->nFields = 0;
  psDBF->nRecordLength = 1;
  psDBF->nHeaderLength = 33;
363

364 365 366 367 368
  psDBF->panFieldOffset = NULL;
  psDBF->panFieldSize = NULL;
  psDBF->panFieldDecimals = NULL;
  psDBF->pachFieldType = NULL;
  psDBF->pszHeader = NULL;
369

370 371 372
  psDBF->nCurrentRecord = -1;
  psDBF->bCurrentRecordModified = MS_FALSE;
  psDBF->pszCurrentRecord = NULL;
373

374 375 376 377 378 379 380
  psDBF->pszStringField = NULL;
  psDBF->nStringFieldLen = 0;

  psDBF->bNoHeader = MS_TRUE;
  psDBF->bUpdated = MS_FALSE;

  return( psDBF );
381 382 383 384 385 386 387 388
}

/************************************************************************/
/*                            msDBFAddField()                           */
/*                                                                      */
/*      Add a field to a newly created .dbf file before any records     */
/*      are written.                                                    */
/************************************************************************/
389
int msDBFAddField(DBFHandle psDBF, const char * pszFieldName, DBFFieldType eType, int nWidth, int nDecimals )
390
{
391 392
  char  *pszFInfo;
  int   i;
393

394 395 396 397 398
  /* -------------------------------------------------------------------- */
  /*      Do some checking to ensure we can add records to this file.     */
  /* -------------------------------------------------------------------- */
  if( psDBF->nRecords > 0 )
    return( MS_FALSE );
399

400 401
  if( !psDBF->bNoHeader )
    return( MS_FALSE );
402

403 404
  if( eType != FTDouble && nDecimals != 0 )
    return( MS_FALSE );
405

406 407 408 409 410
  /* -------------------------------------------------------------------- */
  /*      SfRealloc all the arrays larger to hold the additional field    */
  /*      information.                                                    */
  /* -------------------------------------------------------------------- */
  psDBF->nFields++;
411

412 413
  psDBF->panFieldOffset = (int *)
                          SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields );
414

415 416
  psDBF->panFieldSize = (int *)
                        SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields );
417

418 419
  psDBF->panFieldDecimals = (int *)
                            SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields );
420

421 422
  psDBF->pachFieldType = (char *)
                         SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields );
423

424 425 426 427 428 429 430
  /* -------------------------------------------------------------------- */
  /*      Assign the new field information fields.                        */
  /* -------------------------------------------------------------------- */
  psDBF->panFieldOffset[psDBF->nFields-1] = psDBF->nRecordLength;
  psDBF->nRecordLength += nWidth;
  psDBF->panFieldSize[psDBF->nFields-1] = nWidth;
  psDBF->panFieldDecimals[psDBF->nFields-1] = nDecimals;
431

432 433 434 435
  if( eType == FTString )
    psDBF->pachFieldType[psDBF->nFields-1] = 'C';
  else
    psDBF->pachFieldType[psDBF->nFields-1] = 'N';
436

437 438 439 440 441
  /* -------------------------------------------------------------------- */
  /*      Extend the required header information.                         */
  /* -------------------------------------------------------------------- */
  psDBF->nHeaderLength += 32;
  psDBF->bUpdated = MS_FALSE;
442

443
  psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32);
444

445
  pszFInfo = psDBF->pszHeader + 32 * (psDBF->nFields-1);
446

447 448
  for( i = 0; i < 32; i++ )
    pszFInfo[i] = '\0';
449

450
  strncpy( pszFInfo, pszFieldName, 10);
451

452
  pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields-1];
453

454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
  if( eType == FTString ) {
    pszFInfo[16] = nWidth % 256;
    pszFInfo[17] = nWidth / 256;
  } else {
    pszFInfo[16] = nWidth;
    pszFInfo[17] = nDecimals;
  }

  /* -------------------------------------------------------------------- */
  /*      Make the current record buffer appropriately larger.            */
  /* -------------------------------------------------------------------- */
  psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord,
                            psDBF->nRecordLength);

  return( psDBF->nFields-1 );
469 470 471 472 473 474 475 476 477 478
}

/************************************************************************/
/*                         DBFIsValueNULL()                             */
/*                                                                      */
/*      Return TRUE if value is NULL (in DBF terms).                    */
/*                                                                      */
/*      Based on DBFIsAttributeNULL of shapelib                         */
/************************************************************************/

479
static int DBFIsValueNULL( const char* pszValue, char type )
480

481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
{
  switch(type) {
    case 'N':
    case 'F':
      /* NULL numeric fields have value "****************" */
      return pszValue[0] == '*';

    case 'D':
      /* NULL date fields have value "00000000" */
      return strncmp(pszValue,"00000000",8) == 0;

    case 'L':
      /* NULL boolean fields have value "?" */
      return pszValue[0] == '?';

    default:
      /* empty string fields are considered NULL */
      return strlen(pszValue) == 0;
  }
500 501 502 503 504 505 506
}

/************************************************************************/
/*                          msDBFReadAttribute()                        */
/*                                                                      */
/*      Read one of the attribute fields of a record.                   */
/************************************************************************/
507
static const char *msDBFReadAttribute(DBFHandle psDBF, int hEntity, int iField )
508 509

{
510 511
  int         i;
  unsigned int nRecordOffset;
512 513
  const uchar *pabyRec;
  const char  *pReturnField = NULL;
514

515 516 517 518 519 520 521
  /* -------------------------------------------------------------------- */
  /*  Is the request valid?                             */
  /* -------------------------------------------------------------------- */
  if( iField < 0 || iField >= psDBF->nFields ) {
    msSetError(MS_DBFERR, "Invalid field index %d.", "msDBFReadAttribute()",iField );
    return( NULL );
  }
522

523 524 525 526
  if( hEntity < 0 || hEntity >= psDBF->nRecords ) {
    msSetError(MS_DBFERR, "Invalid record number %d.", "msDBFReadAttribute()",hEntity );
    return( NULL );
  }
527

528 529 530 531 532
  /* -------------------------------------------------------------------- */
  /*  Have we read the record?              */
  /* -------------------------------------------------------------------- */
  if( psDBF->nCurrentRecord != hEntity ) {
    flushRecord( psDBF );
533

534
    nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;
535

536
    safe_fseek( psDBF->fp, nRecordOffset, 0 );
537 538 539 540 541
    if( fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ) != 1 )
    {
      msSetError(MS_DBFERR, "Cannot read record %d.", "msDBFReadAttribute()",hEntity );
      return( NULL );
    }
542

543 544
    psDBF->nCurrentRecord = hEntity;
  }
545

546
  pabyRec = (const uchar *) psDBF->pszCurrentRecord;
547 548 549 550 551 552 553 554 555 556 557 558 559 560
  /* DEBUG */
  /* printf("CurrentRecord(%c):%s\n", psDBF->pachFieldType[iField], pabyRec); */

  /* -------------------------------------------------------------------- */
  /*  Ensure our field buffer is large enough to hold this buffer.      */
  /* -------------------------------------------------------------------- */
  if( psDBF->panFieldSize[iField]+1 > psDBF->nStringFieldLen ) {
    psDBF->nStringFieldLen = psDBF->panFieldSize[iField]*2 + 10;
    psDBF->pszStringField = (char *) SfRealloc(psDBF->pszStringField,psDBF->nStringFieldLen);
  }

  /* -------------------------------------------------------------------- */
  /*  Extract the requested field.              */
  /* -------------------------------------------------------------------- */
561
  strncpy( psDBF->pszStringField,(const char *) pabyRec+psDBF->panFieldOffset[iField], psDBF->panFieldSize[iField] );
562 563 564 565 566 567 568 569 570
  psDBF->pszStringField[psDBF->panFieldSize[iField]] = '\0';

  /*
  ** Trim trailing blanks (SDL Modification)
  */
  for(i=strlen(psDBF->pszStringField)-1; i>=0; i--) {
    if(psDBF->pszStringField[i] != ' ') {
      psDBF->pszStringField[i+1] = '\0';
      break;
571
    }
572 573 574
  }

  if(i == -1) psDBF->pszStringField[0] = '\0'; /* whole string is blank (SDL fix)       */
575

576 577 578 579 580 581 582
  /*
  ** Trim/skip leading blanks (SDL/DM Modification - only on numeric types)
  */
  if( psDBF->pachFieldType[iField] == 'N' || psDBF->pachFieldType[iField] == 'F' || psDBF->pachFieldType[iField] == 'D' ) {
    for(i=0; psDBF->pszStringField[i] != '\0' ; i++) {
      if(psDBF->pszStringField[i] != ' ')
        break;
583
    }
584 585 586 587 588 589 590 591 592 593
    pReturnField = psDBF->pszStringField+i;
  } else
    pReturnField = psDBF->pszStringField;

  /*  detect null values */
  if ( DBFIsValueNULL( pReturnField, psDBF->pachFieldType[iField] )  ) {
    if (psDBF->pachFieldType[iField] == 'N' || psDBF->pachFieldType[iField] == 'F' || psDBF->pachFieldType[iField] == 'D')
      pReturnField="0";
  }
  return( pReturnField );
594 595 596 597 598 599 600
}

/************************************************************************/
/*                        msDBFReadIntAttribute()                       */
/*                                                                      */
/*      Read an integer attribute.                                      */
/************************************************************************/
601
int msDBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField )
602 603 604 605 606 607 608 609 610 611

{
  return(atoi(msDBFReadAttribute( psDBF, iRecord, iField )));
}

/************************************************************************/
/*                        msDBFReadDoubleAttribute()                    */
/*                                                                      */
/*      Read a double attribute.                                        */
/************************************************************************/
612
double  msDBFReadDoubleAttribute( DBFHandle psDBF, int iRecord, int iField )
613 614 615 616 617 618 619 620 621 622 623
{
  return(atof(msDBFReadAttribute( psDBF, iRecord, iField )));
}

/************************************************************************/
/*                        msDBFReadStringAttribute()                      */
/*                                                                      */
/*      Read a string attribute.                                        */
/************************************************************************/
const char *msDBFReadStringAttribute( DBFHandle psDBF, int iRecord, int iField )
{
624
  return( msDBFReadAttribute( psDBF, iRecord, iField ) );
625 626 627 628 629 630 631
}

/************************************************************************/
/*                          msDBFGetFieldCount()                        */
/*                                                                      */
/*      Return the number of fields in this table.                      */
/************************************************************************/
632
int msDBFGetFieldCount( DBFHandle psDBF )
633 634 635 636 637 638 639 640 641
{
  return( psDBF->nFields );
}

/************************************************************************/
/*                         msDBFGetRecordCount()                        */
/*                                                                      */
/*      Return the number of records in this table.                     */
/************************************************************************/
642
int msDBFGetRecordCount( DBFHandle psDBF )
643 644 645 646 647 648 649 650 651 652 653 654 655
{
  return( psDBF->nRecords );
}

/************************************************************************/
/*                          msDBFGetFieldInfo()                         */
/*                                                                      */
/*      Return any requested information about the field.               */
/************************************************************************/
DBFFieldType msDBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName, int * pnWidth, int * pnDecimals )
{
  if( iField < 0 || iField >= psDBF->nFields )
    return( FTInvalid );
656

657 658
  if( pnWidth != NULL )
    *pnWidth = psDBF->panFieldSize[iField];
659

660 661
  if( pnDecimals != NULL )
    *pnDecimals = psDBF->panFieldDecimals[iField];
662 663 664 665 666 667 668 669 670 671 672

  if( pszFieldName != NULL ) {
    int i;

    strncpy( pszFieldName, (char *) psDBF->pszHeader+iField*32, 11 );
    pszFieldName[11] = '\0';
    for( i = 10; i > 0 && pszFieldName[i] == ' '; i-- )
      pszFieldName[i] = '\0';
  }

  if( psDBF->pachFieldType[iField] == 'N'
673
      || psDBF->pachFieldType[iField] == 'F'
674 675 676 677 678 679 680 681
      || psDBF->pachFieldType[iField] == 'D' ) {
    if( psDBF->panFieldDecimals[iField] > 0 )
      return( FTDouble );
    else
      return( FTInteger );
  } else {
    return( FTString );
  }
682 683 684 685
}

/************************************************************************/
/*                         msDBFWriteAttribute()                        */
686 687
/*                  */
/*  Write an attribute record to the file.        */
688 689 690
/************************************************************************/
static int msDBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, void * pValue )
{
691
  unsigned int          nRecordOffset;
692
  int  i, len;
693
  uchar *pabyRec;
694
  char  szSField[40];
695

696
  /* -------------------------------------------------------------------- */
697
  /*  Is this a valid record?             */
698 699 700
  /* -------------------------------------------------------------------- */
  if( hEntity < 0 || hEntity > psDBF->nRecords )
    return( MS_FALSE );
701

702 703
  if( psDBF->bNoHeader )
    writeHeader(psDBF);
704

705 706 707
  /* -------------------------------------------------------------------- */
  /*      Is this a brand new record?                                     */
  /* -------------------------------------------------------------------- */
708 709 710 711 712 713 714 715 716 717
  if( hEntity == psDBF->nRecords ) {
    flushRecord( psDBF );

    psDBF->nRecords++;
    for( i = 0; i < psDBF->nRecordLength; i++ )
      psDBF->pszCurrentRecord[i] = ' ';

    psDBF->nCurrentRecord = hEntity;
  }

718 719 720 721
  /* -------------------------------------------------------------------- */
  /*      Is this an existing record, but different than the last one     */
  /*      we accessed?                                                    */
  /* -------------------------------------------------------------------- */
722 723 724 725 726 727
  if( psDBF->nCurrentRecord != hEntity ) {
    flushRecord( psDBF );

    nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength;

    safe_fseek( psDBF->fp, nRecordOffset, 0 );
728 729
    if( fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ) != 1 )
      return MS_FALSE;
730 731 732

    psDBF->nCurrentRecord = hEntity;
  }
733 734

  pabyRec = (uchar *) psDBF->pszCurrentRecord;
735

736 737 738 739
  /* -------------------------------------------------------------------- */
  /*      Assign all the record fields.                                   */
  /* -------------------------------------------------------------------- */
  switch( psDBF->pachFieldType[iField] ) {
740 741 742
    case 'D':
    case 'N':
    case 'F':
743 744 745
      snprintf(szSField, sizeof(szSField), "%*.*f", psDBF->panFieldSize[iField], psDBF->panFieldDecimals[iField], *(double*) pValue); 
      len = strlen((char *) szSField);
      memcpy(pabyRec+psDBF->panFieldOffset[iField], szSField, MS_MIN(len, psDBF->panFieldSize[iField]));
746 747 748
      break;

    default:
749 750
      len = strlen((char *) pValue);
      memcpy(pabyRec+psDBF->panFieldOffset[iField], pValue, MS_MIN(len, psDBF->panFieldSize[iField]));
751
      break;
752
  }
753

754 755
  psDBF->bCurrentRecordModified = MS_TRUE;
  psDBF->bUpdated = MS_TRUE;
756

757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
  return( MS_TRUE );
}

/************************************************************************/
/*                      msDBFWriteDoubleAttribute()                     */
/*                                                                      */
/*      Write a double attribute.                                       */
/************************************************************************/
int msDBFWriteDoubleAttribute( DBFHandle psDBF, int iRecord, int iField, double dValue )
{
  return( msDBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) );
}

/************************************************************************/
/*                      msDBFWriteIntegerAttribute()                    */
/*                                                                      */
/*      Write a integer attribute.                                      */
/************************************************************************/

776
int msDBFWriteIntegerAttribute( DBFHandle psDBF, int iRecord, int iField, int nValue )
777
{
778 779
  double  dValue = nValue;

780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
  return( msDBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) );
}

/************************************************************************/
/*                      msDBFWriteStringAttribute()                     */
/*                                                                      */
/*      Write a string attribute.                                       */
/************************************************************************/
int msDBFWriteStringAttribute( DBFHandle psDBF, int iRecord, int iField, const char * pszValue )
{
  return( msDBFWriteAttribute( psDBF, iRecord, iField, (void *) pszValue ) );
}

/*
** Which column number in the .DBF file does the item correspond to
*/
int msDBFGetItemIndex(DBFHandle dbffile, char *name)
{
  int i;
799
  int fWidth,fnDecimals; /* field width and number of decimals */
800 801 802
  char fName[32]; /* field name */

  if(!name) {
803
    msSetError(MS_MISCERR, "NULL item name passed.", "msGetItemIndex()");
804 805 806 807
    return(-1);
  }

  /* does name exist as a field? */
808 809
  for(i=0; i<msDBFGetFieldCount(dbffile); i++) {
    msDBFGetFieldInfo(dbffile,i,fName,&fWidth,&fnDecimals);
810 811 812 813
    if(strcasecmp(name,fName) == 0) /* found it */
      return(i);
  }

814
  msSetError(MS_DBFERR, "Item '%s' not found.", "msDBFGetItemIndex()",name);
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831
  return(-1); /* item not found */
}

/*
** Load item names into a character array
*/
char **msDBFGetItems(DBFHandle dbffile)
{
  char **items;
  int i, nFields;
  char fName[32];

  if((nFields = msDBFGetFieldCount(dbffile)) == 0) {
    msSetError(MS_DBFERR, "File contains no data.", "msGetDBFItems()");
    return(NULL);
  }

832 833
  items = (char **)malloc(sizeof(char *)*nFields);
  MS_CHECK_ALLOC(items, sizeof(char *)*nFields, NULL);
834

835
  for(i=0; i<nFields; i++) {
836
    msDBFGetFieldInfo(dbffile, i, fName, NULL, NULL);
837
    items[i] = msStrdup(fName);
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855
  }

  return(items);
}

/*
** Load item values into a character array
*/
char **msDBFGetValues(DBFHandle dbffile, int record)
{
  char **values;
  int i, nFields;

  if((nFields = msDBFGetFieldCount(dbffile)) == 0) {
    msSetError(MS_DBFERR, "File contains no data.", "msGetDBFValues()");
    return(NULL);
  }

856 857
  values = (char **)malloc(sizeof(char *)*nFields);
  MS_CHECK_ALLOC(values, sizeof(char *)*nFields, NULL);
858

859
  for(i=0; i<nFields; i++)
860
    values[i] = msStrdup(msDBFReadStringAttribute(dbffile, record, i));
861 862 863 864 865 866 867 868 869

  return(values);
}

int *msDBFGetItemIndexes(DBFHandle dbffile, char **items, int numitems)
{
  int *itemindexes=NULL, i;

  if(numitems == 0) return(NULL);
870

871
  itemindexes = (int *)malloc(sizeof(int)*numitems);
872
  MS_CHECK_ALLOC(itemindexes, sizeof(int)*numitems, NULL);
873 874

  for(i=0; i<numitems; i++) {
875
    itemindexes[i] = msDBFGetItemIndex(dbffile, items[i]);
876
    if(itemindexes[i] == -1) {
877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
      free(itemindexes);
      return(NULL); /* item not found */
    }
  }

  return(itemindexes);
}

char **msDBFGetValueList(DBFHandle dbffile, int record, int *itemindexes, int numitems)
{
  const char *value;
  char **values=NULL;
  int i;

  if(numitems == 0) return(NULL);

893 894
  values = (char **)malloc(sizeof(char *)*numitems);
  MS_CHECK_ALLOC(values, sizeof(char *)*numitems, NULL);
895

896
  for(i=0; i<numitems; i++) {
897
    value = msDBFReadStringAttribute(dbffile, record, itemindexes[i]);
898 899
    if (value == NULL) {
      free(values);
900
      return NULL; /* Error already reported by msDBFReadStringAttribute() */
901
    }
902
    values[i] = msStrdup(value);
903 904 905 906
  }

  return(values);
}