/* d4open.c   (c)Copyright Sequiter Software Inc., 1988-1996.  All rights reserved. */

#include "d4all.h"
#ifdef __TURBOC__
   #pragma hdrstop
#endif

#ifdef S4OFF_MEMO
   extern char f4memoNullChar ;
#endif

/* */
   #ifdef S4UNIX
      #include <sys/stat.h>
   #else
      #include "sys\stat.h"
   #endif
/* */

static DATA4FILE *data4reopen( DATA4FILE *, char ** ) ;

static DATA4 *d4openInit( CODE4 *c4 )
{
   DATA4 *d4 ;
   #ifdef S4STAND_ALONE
      #ifndef S4OFF_TRAN
         int rc;
      #endif
   #endif

   #ifdef S4VBASIC
      if ( c4parm_check( c4, 1, E94301 ) )
         return 0 ;
   #endif

   if ( error4code( c4 ) < 0 )
      return 0 ;

   #ifdef E4ANALYZE
      if ( c4->debugInt != 0x5281 )
      {
         error4( 0, e4result, E81301 ) ;
         return 0 ;
      }
   #endif

   #ifdef S4STAND_ALONE
   #ifndef S4OFF_TRAN
      if ( c4->logOpen )
      {
         rc = code4logOpen( c4, 0, 0 ) ;
         if ( rc < 0 )
            return 0 ;
         else
            error4set( c4, 0 ) ;   /* remove r4open if it already existed */
      }
   #endif
   #endif

   #ifndef S4CLIENT
      #ifdef E4ANALYZE
         #ifndef S4STAND_ALONE
            if ( c4->currentClient == 0 )
            {
               error4( c4, e4struct, E94301 ) ;
               return 0 ;
            }
            if ( c4->currentClient->trans.c4trans == 0 )
            {
               error4( c4, e4struct, E94301 ) ;
               return 0 ;
            }
         #endif
      #endif
   #endif

   if ( c4->dataMemory == 0 )
   {
      c4->dataMemory = mem4create( c4, c4->memStartData, sizeof(DATA4), c4->memExpandData, 0 ) ;
      if ( c4->dataMemory == 0 )
      {
         #ifdef E4STACK
            error4stack( c4, e4memory, E94301 ) ;
         #endif
         return 0 ;
      }
   }
   d4 = (DATA4 *)mem4alloc( c4->dataMemory ) ;
   if ( d4 == 0 )
   {
      #ifdef E4STACK
         error4stack( c4, e4memory, E94301 ) ;
      #endif
      return 0 ;
   }

   #ifdef S4VBASIC
      d4->debugInt = E4DEBUG_INT ;
   #endif
   d4->codeBase = c4 ;

   #ifdef S4SERVER
      d4->clientId = 1L ;  /* should get overridden at a later point, unless this is a server-only data file */
      d4->serverId = c4->server->serverDataCount ;
      c4->server->serverDataCount++ ;
      d4->trans = &c4->currentClient->trans ;
      l4add( tran4dataList( d4->trans ), d4 ) ;
   #else
      d4->trans = &c4->c4trans.trans ;
      l4add( tran4dataList( (&(c4->c4trans.trans)) ), d4 ) ;
   #endif

   return d4 ;
}

static int d4openConclude( DATA4 *d4, const char *name, char *info )
{
   CODE4 *c4 ;
   int iFields, fieldType ;
   unsigned int recOffset ;
   long recWidth ;
   #ifdef S4CLIENT_OR_FOX
      int nullCount ;
   #endif
   char fieldBuf[2] ;
   FIELD4IMAGE *image ;
   #ifndef S4CLIENT
      #ifdef S4CLIPPER
         #ifndef S4OFF_INDEX
            char nameBuf[258] ;
         #else
            #ifndef S4OFF_TRAN
               #ifndef S4OFF_WRITE
                  char nameBuf[258] ;
               #endif
            #endif
         #endif
      #else
         #ifndef S4OFF_TRAN
            #ifndef S4OFF_WRITE
               char nameBuf[258] ;
            #endif
         #endif
      #endif
      #ifndef S4OFF_WRITE
         #ifndef S4OFF_TRAN
            TRAN4  *trans ;
            int tranCode ;
/* */
               struct stat bufStat ;
/* */
/* */
/* */
/* */
            long connectionId, rcl ;
            short rc ;
         #endif
      #endif
      #ifndef S4OFF_INDEX
         INDEX4 *i4 ;
         #ifndef S4SERVER
            #ifndef S4CLIPPER
               int oldSingleOpen ;
            #endif
         #endif
      #endif
   #else
      #ifndef S4OFF_INDEX
         char nameBuf[258] ;
      #endif
   #endif
   #ifndef S4OFF_MEMO
      int i_memo ;
   #endif

   c4 = d4->codeBase ;

   #ifdef E4ANALYZE
      if ( d4->dataFile->nFields == 0 )
         return error4describe( c4, e4struct, E94301, name, 0, 0 ) ;
   #endif

   u4namePiece( d4->alias, sizeof( d4->alias ), name, 0, 0 ) ;
   d4->alias[ sizeof( d4->alias ) - 1 ] = 0 ;

   recWidth = dfile4recWidth( d4->dataFile ) ;
   if ( ( ( recWidth + 50L + 1L ) * 2L + (long)sizeof( FIELD4 ) * (long)d4->dataFile->nFields ) > (long)UINT_MAX )  /* try allocating records and fields together, 50 bytes for overhead */
   {
      d4->groupRecordAlloc = (char *)u4allocFree( c4, (recWidth + 1L) * 2L + (long)sizeof( FIELD4 ) * (long)d4->dataFile->nFields ) ;
      if ( d4->groupRecordAlloc != 0 )
      {
         d4->record = d4->groupRecordAlloc ;
         d4->recordOld = d4->groupRecordAlloc + recWidth + 1 ;
         d4->fields = (FIELD4 *)(d4->groupRecordAlloc + 2 * ( recWidth + 1 ) ) ;
      }
   }

   if ( d4->groupRecordAlloc == 0 )
   {
      d4->record = (char *)u4allocFree( c4, recWidth + 1 ) ;
      d4->recordOld = (char *)u4allocFree( c4, recWidth + 1 ) ;
      d4->fields = (FIELD4 *)u4allocFree( c4, sizeof( FIELD4 ) * (long)d4->dataFile->nFields ) ;
   }
   if ( d4->record == 0 || d4->recordOld == 0 || d4->fields == 0 )
      return error4stack( c4, e4memory, E94301 ) ;

   recOffset = 1 ;

   #ifdef S4CLIENT_OR_FOX
      nullCount = 0 ;
   #endif

   if ( !( error4code( c4 ) < 0 ) )
      for ( iFields = 0 ; iFields < d4->dataFile->nFields ; iFields++ )
      {
         image = (FIELD4IMAGE *)( info + iFields * 32 ) ;
         u4ncpy( d4->fields[iFields].name, image->name, sizeof( d4->fields->name ) ) ;

         u4ncpy( fieldBuf, &image->type, 2 ) ;
         c4upper( fieldBuf ) ;
         d4->fields[iFields].type = *fieldBuf ;
         fieldType = d4->fields[iFields].type ;
         #ifdef S4CLIENT_OR_FOX
            if ( d4version( d4 ) == 0x30 )  /* FOX 3.0 */
            {
               d4->fields[iFields].null = ( image->nullBinary & 0x02 ) ? 1 : 0 ;
               if ( d4->fields[iFields].null == 1 )
               {
                  d4->fields[iFields].nullBit = nullCount ;
                  nullCount++ ;
               }
               if ( image->nullBinary & 0x04 )
                  d4->fields[iFields].binary = 1 ;
               else
               {
                  if ( fieldType == r4memo || fieldType == r4gen )  /* memo fields are also stored binary */
                     d4->fields[iFields].binary = 2 ;
                  else
                     d4->fields[iFields].binary = 0 ;
               }
            }
         #endif

         switch( fieldType )
         {
            #ifdef S4CLIENT_OR_FOX
               case r4int:
            #endif
            case r4log:
            case r4date:
               d4->fields[iFields].len = image->len ;
               break ;
            case r4double:  /* same as r4bin */
               if ( d4version( d4 ) == 0x30 )  /* double */
               {
                  d4->fields[iFields].len = image->len ;
                  d4->fields[iFields].dec = image->dec ;
               }
               else  /* binary */
                  d4->fields[iFields].len = image->len ;
               break ;
            case r4num:
            case r4float:
            #ifdef S4CLIENT_OR_FOX
               case r4currency:
               case r4dateTime:
            #endif
               d4->fields[iFields].len = image->len ;
               d4->fields[iFields].dec = image->dec ;
               break ;
            case r4memo:
            case r4gen:
/*               #ifdef S4OFF_MEMO */
/*                  d4->fields[iFields].memo = (F4MEMO *)&f4memoNullChar ; */
/*               #endif */
               d4->fields[iFields].len = image->len ;
               break ;
            default:
               d4->fields[iFields].len = image->len + ( image->dec << 8 ) ;
               break ;
         }

         #ifdef S4VBASIC
            d4->fields[iFields].debugInt = E4DEBUG_INT ;
         #endif
         d4->fields[iFields].offset = recOffset ;
         recOffset += d4->fields[iFields].len ;
         d4->fields[iFields].data = d4 ;
      }

   #ifndef S4OFF_MEMO
      if ( d4->dataFile->nFieldsMemo > 0 && !( error4code( c4 ) < 0 ) )
      {
         i_memo = 0 ;

         d4->fieldsMemo = (F4MEMO *)u4allocFree( c4, (long)sizeof(F4MEMO) * d4->dataFile->nFieldsMemo ) ;
         #ifdef E4STACK
            if ( d4->fieldsMemo == 0 )
               error4stack( c4, e4memory, E94301 ) ;
         #endif
         if ( d4->fieldsMemo != 0 )
            for ( iFields = 0 ; iFields < d4->dataFile->nFields ; iFields++ )
            {
               fieldType = d4->fields[iFields].type ;
               if ( fieldType == r4memo || fieldType == r4gen || ( fieldType == r4bin && d4version( d4 ) != 0x30 ) )
               {
                  d4->fields[iFields].memo = d4->fieldsMemo+i_memo ;
                  d4->fieldsMemo[i_memo].status = 1 ;
                  d4->fieldsMemo[i_memo].field = d4->fields+iFields ;
                  i_memo++ ;
               }
            }
      }
   #endif

   if ( error4code( c4 ) < 0 )
      return -1 ;

   d4->recNum = d4->recNumOld = -1 ;

   d4blank( d4 ) ;
   memcpy( d4->recordOld, d4->record, (unsigned)recWidth ) ;
   d4->recordChanged = 0 ;

   d4->record[recWidth] = 0 ;
   d4->recordOld[recWidth] = 0 ;

   #ifndef S4OFF_INDEX
      #ifdef S4CLIENT
         /* client will get all the index tags if autoOpen set to 1, else
            will get none -- this is an undocumented side-effect */
         if ( d4->dataFile->indexes.nLink > 0 && c4->autoOpen == 1 )
         {
            u4namePiece( nameBuf, sizeof( nameBuf ), name, 0, 0 ) ;
            if ( i4setup( c4, d4, nameBuf, 1, 0 ) < 0 )
               return -1 ;
         }
      #else
         #ifdef S4CLIPPER
            if ( c4->autoOpen )
            {
               if ( d4->dataFile->userCount > 1 )  /* already open, just set up tags */
               {
                  u4namePiece( nameBuf, sizeof( nameBuf ), name, 0, 0 ) ;
                  if ( i4setup( c4, d4, nameBuf, 1 ) < 0 )
                     return -1 ;
               }
               else
               {
                  #ifdef S4SERVER
                     /* if a temp file, tags already available... */
                     if ( d4->dataFile->file.isTemp == 1 )
                     {
                        if ( i4setup( c4, d4, nameBuf, 1 ) < 0 )
                           return -1 ;
                     }
                  #endif
                  i4 = i4open( d4, 0 ) ;
                  #ifdef S4SERVER
                     if ( i4 == 0 )  /* server version, if no .cgp file then don't open index */
                        error4set( c4, 0 ) ;
                  #else
                     if ( i4 == 0 )
                        return -1 ;
                  #endif
               }
            }
         #else
            #ifdef S4STAND_ALONE
               d4->dataFile->openMdx = 0 ;
            #endif
            if ( ( d4->dataFile->hasMdxMemo & 0x01 ) && c4->autoOpen )
            {
               #ifndef S4SERVER
                  oldSingleOpen = c4->singleOpen ;
                  c4->singleOpen = OPEN4SPECIAL ;
               #endif
               i4 = i4open( d4, 0 ) ;
               #ifndef S4SERVER
                  c4->singleOpen = oldSingleOpen ;
               #endif
               if ( i4 == 0 )
                  return -1 ;
               #ifdef S4MDX
                  if ( !i4->indexFile->header.isProduction )
                     i4close( i4 ) ;
               #endif
               #ifdef S4STAND_ALONE
                  d4->dataFile->openMdx = 1 ;
               #endif
            }
         #endif
      #endif  /* S4CLIENT */
   #endif  /* S4OFF_INDEX */

   #ifndef S4SERVER
      c4->clientDataCount++ ;
      d4->clientId = c4->clientDataCount ;
   #endif
   #ifndef S4OFF_WRITE
      #ifndef S4OFF_TRAN
         #ifndef S4CLIENT
            if ( code4transEnabled( c4 ) )
            {
               trans = code4trans( c4 ) ;
               #ifdef S4STAND_ALONE
                  connectionId = 0L ;
               #else
                  connectionId = connection4id( c4->currentClient->connection ) ;
               #endif
               rc = u4nameCurrent( nameBuf, sizeof( nameBuf ), dfile4name( d4->dataFile ) ) ;
               if ( rc < 0 )
                  return error4stack( c4, rc, E94301 ) ;
               #ifdef S4CASE_SEN
                  rc = u4nameExt( nameBuf, sizeof( nameBuf ), "dbf", 0 ) ;
               #else
                  rc = u4nameExt( nameBuf, sizeof( nameBuf ), "DBF", 0 ) ;
               #endif
               if ( rc < 0 )
                  return error4stack( c4, rc, E94301 ) ;
               rc = strlen(nameBuf) ;
               if (c4->createTemp == 1)
                  tranCode = TRAN4OPEN_TEMP ;
               else
                  tranCode = TRAN4OPEN ;
               if ( tran4set( trans, trans->currentTranStatus, -1L, connectionId, tranCode,
                    (unsigned)rc + 19, data4clientId( d4 ), data4serverId( d4 ) ) == 0 )
                  {
                     tran4putData( trans, &rc, 2 ) ;
                     tran4putData( trans, nameBuf, (unsigned)rc ) ;
                     rcl = recWidth ;
                     tran4putData( trans, &rcl, 4 ) ;
                     rc = d4numFields( d4 ) ;
                     tran4putData( trans, &rc, 2 ) ;
                     rcl = d4recCount( d4 ) ;
                     tran4putData( trans, &rcl, 4 ) ;
/* */
                        if ( stat( nameBuf, &bufStat ) != 0 )
                           return -1 ;
                        tran4putData( trans, &bufStat.st_atime, 4 ) ;
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
                     tran4putData( trans, &d4->dataFile->yy, 3 ) ;
                     tran4lowAppend( trans, 0 ) ;
                  }
               else
                  return -1 ;
            }
         #endif /* S4CLIENT */
      #endif /* S4OFF_TRAN */
   #endif /* S4OFF_WRITE */

   #ifdef S4SERVER
      d4->accessMode = c4->singleClient ;
      if ( d4->accessMode == OPEN4DENY_RW )
         d4->dataFile->exclusiveOpen = d4 ;
   #endif

   #ifndef S4CLIENT
      d4->readOnly = c4->readOnly ;
   #endif

   #ifndef S4OFF_TRAN
      #ifndef S4CLIENT
         if ( code4transEnabled( d4->codeBase ) == 1 )
            d4->logVal = c4->log ;
      #endif
   #endif

   return 0 ;
}

DATA4 *S4FUNCTION d4open( CODE4 *c4, const char *name )
{
   int rc ;
   char *info ;
   DATA4 *d4 ;

   #ifdef E4PARM_HIGH
      if ( c4 == 0 || name == 0 )
      {
         error4( 0, e4parm_null, E94301 ) ;
         return 0 ;
      }
   #endif

   d4 = d4openInit( c4 ) ;
   if ( d4 != 0 )
   {
      d4->dataFile = dfile4open( c4, d4, name, &info ) ;
      if ( d4->dataFile == 0 )
      {
         d4close( d4 ) ;
         return 0 ;
      }

      rc = d4openConclude( d4, name, info ) ;
      if ( rc < 0 )
      {
         d4close( d4 ) ;
         return 0 ;
      }
   }

   return d4 ;
}

DATA4 *S4FUNCTION d4openClone( DATA4 *dataOld )
{
   DATA4 *d4 ;
   int rc ;
   char *info ;
   #ifndef S4SERVER
      int oldSingleOpen ;
   #endif

   #ifdef E4PARM_HIGH
      if ( dataOld == 0 )
      {
         error4( 0, e4parm_null, E94301 ) ;
         return 0 ;
      }
   #endif

   d4 = d4openInit( dataOld->codeBase ) ;
   if ( d4 == 0 )
      return 0 ;
   #ifndef S4SERVER
      oldSingleOpen = dataOld->codeBase->singleOpen ;
      dataOld->codeBase->singleOpen = OPEN4DENY_NONE ;
   #endif
   d4->dataFile = data4reopen( dataOld->dataFile, &info ) ;
   if ( d4->dataFile == 0 )
   {
      #ifndef S4SERVER
         dataOld->codeBase->singleOpen = oldSingleOpen ;
      #endif
      d4close( d4 ) ;
      return 0 ;
   }
   rc = d4openConclude( d4, dfile4name( d4->dataFile ), info ) ;
   #ifndef S4SERVER
      dataOld->codeBase->singleOpen = oldSingleOpen ;
   #endif
   if ( rc < 0 )
   {
      d4close( d4 ) ;
      return 0 ;
   }

   return d4 ;
}

#ifndef S4OFF_INDEX
#ifdef S4CLIENT
int client4indexSetup( CODE4 *c4, DATA4 *d4, DATA4FILE *data, unsigned int numTags, const char *info, unsigned int iLen, const char *indexAlias, INDEX4 *i4ndx )
{
   unsigned int i ;
   TAG4FILE *tag, *first ;
   INDEX4FILE *i4file ;
   long infoLen ;
   DATA4FILE *oldDataFile ;
   int doTags ;

   #ifdef E4PARM_LOW
      if ( c4 == 0 || d4 == 0 || data == 0 || info == 0 )
         return error4( c4, e4parm_null, E94302 ) ;
   #endif

   infoLen = iLen ;

   if ( numTags == 0 )
      return 0 ;

   if ( c4->index4fileMemory == 0 )
   {
      c4->index4fileMemory = mem4create( c4, c4->memStartIndexFile, sizeof(INDEX4FILE), c4->memExpandIndexFile, 0 ) ;
      if ( c4->index4fileMemory == 0 )
         return e4memory ;
   }

   if ( c4->tagFileMemory == 0 )
   {
      c4->tagFileMemory = mem4create( c4, c4->memStartTagFile, sizeof(TAG4FILE), c4->memExpandTagFile, 0 ) ;
      if ( c4->tagFileMemory == 0 )
         return e4memory ;
   }

   oldDataFile = d4->dataFile ; ;
   d4->dataFile = data ;
   /* passing non-null into index4open will ensure that an actual open
      does not occur, but simply a check will occur ... */
   if ( i4ndx != 0 )
      i4file = i4ndx->indexFile ;
   else
      i4file = index4open( d4, indexAlias, (INDEX4 *)1 ) ;
   if ( i4file == 0 )
   {
      i4file = (INDEX4FILE *)mem4alloc( c4->index4fileMemory ) ;
      if ( i4file == 0 )
      {
         d4->dataFile = oldDataFile ;
         return error4stack( c4, e4memory, E94302 ) ;
      }

      i4file->codeBase = c4 ;
      i4file->autoOpened = 1 ;
      i4file->dataFile = data ;
      i4file->clientId = data4clientId( d4 ) ;
      i4file->serverId = data4serverId( d4 ) ;
      d4->dataFile = oldDataFile ;

      #ifdef E4MISC
         if ( strlen( indexAlias ) > sizeof( i4file->accessName ) )
            return error4describe( c4, e4name, E91102, indexAlias, 0, 0 ) ;
      #endif
      strcpy( i4file->accessName, indexAlias ) ;
      c4upper( i4file->accessName ) ;

      l4add( &data->indexes, i4file ) ;
      doTags = 1 ;
   }
   else
      doTags = 0 ;

   /* only execute next if i4file was not blank and i4ndx was blank */
   if ( i4ndx != 0 || doTags == 1 )   /* new index file, or add to existing */
   {
      for ( i = 0 ; i < numTags ; i++ )
      {
         tag = (TAG4FILE *)mem4alloc( c4->tagFileMemory ) ;
         if ( tag == 0 )
            return e4memory ;
         infoLen -= LEN4TAG_ALIAS ;
         if ( infoLen < 0 )
            return e4connection ;
         memcpy( tag->alias, info, LEN4TAG_ALIAS ) ;
         tag->indexFile = i4file ;
         info += LEN4TAG_ALIAS ;
         tag->errUniqueHold = *(int *)info ;
         info += sizeof( short int ) ;
         first = (TAG4FILE *)l4first( &i4file->tags ) ;
         if ( first == 0 )
            l4add( &i4file->tags, tag ) ;
         else
            l4addBefore( &i4file->tags, first, tag ) ;
      }
   }
   else
      d4->dataFile = oldDataFile ;
   return 0 ;
}
#endif  /* S4CLIENT */
#endif  /* not S4OFF_INDEX */

#ifdef S4SERVER
/* what is the maximal read and access setting on the data file */
/* does not report results for current client */
static void dfile4accesses( DATA4FILE *d4, int *readMode, int *accessMode, int *otherUsers )
{
   SERVER4CLIENT *client ;
   DATA4 *data ;

   *readMode = 1 ;
   *accessMode = OPEN4DENY_NONE ;
   *otherUsers = 0 ;

   for ( client = 0 ;; )
   {
      client = (SERVER4CLIENT *)l4next( &d4->c4->server->clients, client ) ;
      if ( client == 0 )
         break ;
      if ( client == d4->c4->currentClient )
         continue ;
      for ( data = 0 ;; )
      {
         data = (DATA4 *)l4next( tran4dataList( &client->trans ), data ) ;
         if ( data == 0 )
            break ;
         if ( data->dataFile == d4 )
         {
            *otherUsers = 1 ;
            if ( data->readOnly == 0 )
               *readMode = 0 ;
            if ( *accessMode != OPEN4DENY_RW)
               if ( data->accessMode != *accessMode )
                  if ( data->accessMode != OPEN4DENY_NONE )
                     *accessMode = data->accessMode ;
            if ( *readMode == 0 && *accessMode == OPEN4DENY_RW )  /* maximal already */
               return ;
         }
      }
   }

   return ;
}

/* returns 1 if file can be accessed in desired mode */
static int dfile4checkAccess( DATA4FILE *d4, int accessRequested, int readOnly )
{
   int maxAccess, maxRead, otherUsers ;

   if ( d4->userCount == 0 )
      return 1 ;

   /* first get maximal access and read modes of other users */
   dfile4accesses( d4, &maxRead, &maxAccess, &otherUsers ) ;

   if ( otherUsers == 0 )   /* no other users, so any requests are ok */
      return 1 ;

   if ( accessRequested == OPEN4DENY_RW )   /* others users accessing, so no */
      return 0 ;

   if ( maxAccess == OPEN4DENY_RW )   /* other user is disallowing our access */
      return 0 ;

   if ( readOnly == 0 )   /* need write access */
   {
      if ( maxAccess != OPEN4DENY_NONE )
         return 0 ;
      /* maxAccess is DENY_NONE, so continue */
      switch( accessRequested )
      {
         case OPEN4DENY_NONE:
            return 1 ;
         case OPEN4DENY_WRITE:
            if ( maxRead == 1 )   /* others only reading, so ok */
               return 1 ;
         /* fall through, and any other case, access denied */
         default:
            return 0 ;
      }
   }

   /* is readOnly, so deny_write allowable by others */
   switch( maxAccess )
   {
      case OPEN4DENY_RW:
         return 0 ;
      case OPEN4DENY_WRITE:
         switch ( accessRequested )
         {
            case OPEN4DENY_NONE:
               return 1 ;
            case OPEN4DENY_WRITE:
               if ( maxRead == 1 )
                  return 1 ;
            /* fall through or default, no access */
            default:
               return 0 ;
         }
      default:
         break ;
   }

   return 1 ;
}
#endif  /* S4SERVER */

static DATA4FILE *data4reopen( DATA4FILE *d4, char **info )
{
   #ifndef S4CLIENT
      #ifndef S4OFF_MULTI
         int rc ;
         #ifndef S4OFF_INDEX
            #ifdef S4CLIPPER
               TAG4FILE *t4file ;
            #else
               INDEX4FILE *i4file ;
               #ifdef E4ANALYZE
                  #ifndef S4CLIPPER
                     unsigned short int nCheck ;
                  #endif
               #endif
            #endif
         #endif
      #endif
   #endif
   #ifndef S4SERVER
      #ifndef S4OFF_TRAN
         DATA4 *data4 ;
         LIST4 *list ;
      #endif
   #endif
   CODE4 *c4 ;

   if ( d4 == 0 )
      return 0 ;

   c4 = d4->c4 ;
   #ifndef S4CLIENT
      if ( d4->userCount == 0 )
      {
         #ifndef S4OFF_MULTI
            if ( d4->file.lowAccessMode != c4->accessMode )  /* need to open in updated mode */
            {
               rc = dfile4closeLow( d4 ) ;
               if ( rc != 0 )
                  return 0 ;
               #ifndef S4OFF_INDEX
                  #ifdef S4CLIPPER
                     for ( t4file = 0 ;; )
                     {
                        t4file = (TAG4FILE *)l4next( &d4->tagfiles, t4file ) ;
                        if ( t4file == 0 )
                           break ;
                        rc = tfile4close( t4file, d4 ) ;
                        if ( rc < 0 )
                           return 0 ;
                     }
                  #else
                     if ( d4->indexes.nLink != ((unsigned int)d4->hasMdxMemo & 0x01 ) )
                     {
                        for ( i4file = 0 ;; )
                        {
                           i4file = (INDEX4FILE *)l4next( &d4->indexes, i4file ) ;
                           if ( i4file == 0 )
                              break ;
                           if ( index4isProduction( i4file ) == 1 )
                              continue ;
                           #ifdef E4ANALYZE
                              nCheck = d4->indexes.nLink ;
                           #endif
                           rc = index4close( i4file ) ;
                           #ifdef E4ANALYZE
                              if ( nCheck != d4->indexes.nLink + 1 )
                              {
                                 error4describe( c4, e4result, E91102, dfile4name( d4 ), 0, 0 ) ;
                                 return 0 ;
                              }
                           #endif
                           if ( rc < 0 )
                              return 0 ;
                        }
                     }
                  #endif /* S4CLIPPER */
               #endif /* S4OFF_INDEX */
               d4 = 0 ;
            }
         #endif /* S4OFF_MULTI */
      }
      else
      {
   #endif /* S4CLIENT */
      #ifndef S4SERVER
         if ( c4->singleOpen != OPEN4DENY_NONE )   /* only one instance allowed... */
         {
            #ifndef S4SERVER
               #ifndef S4OFF_TRAN
                  /* verify that data4 not on the closed data list if within a
                     transaction (which is allowed) */
                  if ( code4tranStatus( c4 ) == r4active )
                  {
                     list = tran4dataList( code4trans( c4 ) ) ;
                     for ( data4 = 0 ;; )
                     {
                        data4 = (DATA4 *)l4next( list, data4 ) ;
                        if ( data4 == 0 )
                           break ;
                        if ( data4->dataFile == d4 )
                        {
                           error4describe( c4, e4instance, E91102, dfile4name( d4 ), 0, 0 ) ;
                           return 0 ;
                        }
                     }
                     #ifdef E4ANALYZE
                        /* ensure that the datafile exists somewhere! */
                        list = &( code4trans( c4 )->closedDataFiles ) ;
                        for ( data4 = 0 ;; )
                        {
                           data4 = (DATA4 *)l4next( list, data4 ) ;
                           if ( data4 == 0 )
                           {
                              error4describe( c4, e4struct, E91102, dfile4name( d4 ), 0, 0 ) ;
                              return 0 ;
                           }
                           if ( data4->dataFile == d4 )
                              break ;
                        }
                     #endif
                  }
                  else
               #endif /* S4OFF_TRAN */
            #endif /* S4STAND_ALONE */
            {
               error4describe( c4, e4instance, E91102, dfile4name( d4 ), 0, 0 ) ;
               return 0 ;
            }
         }
      #endif
      #ifdef E4ANALYZE
         if ( d4->info == 0 )
         {
            error4describe( c4, e4struct, E91102, dfile4name( d4 ), 0, 0 ) ;
            return 0 ;
         }
      #endif
      /* verify that the desired access level is available in terms of the actual physical open */
      #ifndef S4OFF_MULTI
         switch( c4->accessMode )
         {
            case OPEN4DENY_NONE:
               break ;
            case OPEN4DENY_RW:
               #ifdef S4CLIENT
                  if ( d4->accessMode != OPEN4DENY_RW )
               #else
                  if ( d4->file.lowAccessMode != OPEN4DENY_RW )
               #endif
                  {
                     error4describe( c4, e4access, E84307, dfile4name( d4 ), 0, 0 ) ;
                     return 0 ;
                  }
               break ;
            case OPEN4DENY_WRITE:
               #ifdef S4CLIENT
                  if ( d4->accessMode == OPEN4DENY_NONE )
               #else
                  if ( d4->file.lowAccessMode == OPEN4DENY_NONE )
               #endif
                  {
                     error4describe( c4, e4access, E84307, dfile4name( d4 ), 0, 0 ) ;
                     return 0 ;
                  }
               break ;
            default:
               {
                  error4describe( c4, e4access, E82502, dfile4name( d4 ), 0, 0 ) ;
                  return 0 ;
               }
         }
      #endif /* S4OFF_MULTI */

      #ifdef S4SERVER
         /* singleClient is the client's requested access mode */
         if ( d4 != 0 )
            if ( dfile4checkAccess( d4, c4->singleClient, c4->readOnly ) == 0 )  /* access denied */
            {
               error4describe( c4, e4access, E91102, dfile4name( d4 ), 0, 0 ) ;
               return 0 ;
            }
      #endif
   #ifndef S4CLIENT
      }
   #endif

   if ( d4 != 0 )
   {
      d4->userCount++ ;
      *info = d4->info ;
      #ifdef E4ANALYZE
         if ( d4->nFields == 0 )
         {
            error4describe( c4, e4struct, E91102, dfile4name( d4 ), 0, 0 ) ;
            return 0 ;
         }
      #endif
      return d4 ;
   }

   return 0 ;
}

#ifdef P4ARGS_USED
   #pragma argsused
#endif
DATA4FILE *dfile4open( CODE4 *c4, DATA4 *data, const char *name, char **info )
{
   int rc ;
   DATA4FILE *d4 ;
   unsigned int count ;
   int iFields ;
   FIELD4IMAGE *image ;
   #ifdef S4CLIENT
      SOCKET4 *socket ;
      CONNECTION4 *connection ;
      int len2, len3 ;
      CONNECTION4OPEN_INFO_IN dataIn ;
      CONNECTION4OPEN_INFO_OUT *dataInfo ;
      #ifndef S4OFF_INDEX
         char indexName[258] ;
      #endif
   #else
      char nameBuf[258] ;
      DATA4HEADER_FULL fullHeader ;
      unsigned fieldDataLen ;
      #ifndef S4OFF_MEMO
         int hasMemo ;
      #endif
      #ifndef S4OFF_CATALOG
         int i ;
      #endif
   #endif

   #ifdef S4VBASIC
      if ( c4parm_check( c4, 1, E91102 ) )
         return 0 ;
   #endif

   #ifdef E4PARM_LOW
      if ( c4 == 0 || name == 0 )
      {
         error4( c4, e4parm_null, E91102 ) ;
         return 0 ;
      }
      #ifdef S4CLIENT
         if ( data == 0 )
         {
            error4( c4, e4parm_null, E91102 ) ;
            return 0 ;
         }
      #endif
   #endif

   if ( error4code( c4 ) < 0 )
      return 0 ;

   #ifdef E4ANALYZE
      if ( c4->debugInt != 0x5281 )
      {
         error4( 0, e4result, E81301 ) ;
         return 0 ;
      }
   #endif

   #ifdef S4CLIENT
      d4 = dfile4data( c4, name ) ;
   #else
      u4nameCurrent( nameBuf, sizeof( nameBuf ), name ) ;
      u4nameExt( nameBuf, sizeof(nameBuf), "dbf", 0 ) ;
      #ifndef S4CASE_SEN                     /* preserve the case sensitivity for unix */
         c4upper( nameBuf ) ;
      #endif
      d4 = dfile4data( c4, nameBuf ) ;
   #endif

   if ( d4 != 0 )
   {
      d4 = data4reopen( d4, info ) ;
      if ( error4code( c4 ) < 0 )
         return 0 ;
      if ( d4 != 0 )
         return d4 ;
   }

   #ifdef S4CLIENT
      #ifdef E4MISC
         if ( strlen( name ) > LEN4PATH )
         {
            error4describe( c4, e4name, E84301, name, 0, 0 ) ;
            return 0 ;
         }
      #endif
      if ( c4->defaultServer == 0 )
      {
         rc = code4connect( c4, 0, DEF4PROCESS_ID, 0, 0, 0 ) ;
         if ( rc == 0 )
         {
            if ( c4->defaultServer == 0 )
            {
               error4describe( c4, e4connection, E84302, DEF4SERVER_ID, DEF4PROCESS_ID, 0 ) ;
               return 0 ;
            }
            rc = code4dateFormatSet( c4, code4dateFormat( c4 ) ) ;
         }
         if ( rc != 0 )
         {
            if ( c4->defaultServer != 0 )
            {
               socket4initUndo( c4->defaultServer ) ;
               socket4free( c4->defaultServer ) ;
               c4->defaultServer = 0 ;
            }
            error4describe( c4, e4connection, E81001, DEF4SERVER_ID, DEF4PROCESS_ID, 0 ) ;
            return 0 ;
         }
      }
      socket = c4->defaultServer ;
      memset( &dataIn, 0, sizeof( CONNECTION4OPEN_INFO_IN ) ) ;
      if ( socket == 0 )
      {
         error4( c4, e4connection, E84303 ) ;
         return 0 ;
      }
      connection = socket->connect ;
      if ( connection == 0 )
      {
         error4( c4, e4connection, E84303 ) ;
         return 0 ;
      }
      connection4assign( connection, CON4OPEN, data->trans->dataIdCount,0 ) ;
      data->trans->dataIdCount++ ;

      len3 = strlen( name ) + 1 ;
      if ( len3 > LEN4PATH )
         len3 = LEN4PATH ;

      memcpy( dataIn.name, name, len3 ) ;
      dataIn.name[LEN4PATH] = 0 ;

      #ifdef S4OFF_MULTI
         dataIn.exclusiveClient = 1 ;
      #else
         dataIn.accessMode = c4->accessMode ;
      #endif

      dataIn.readOnly = c4->readOnly ;
      dataIn.safety = c4->safety ;  /* for catalog */
      dataIn.errDefaultUnique = c4->errDefaultUnique ;
      dataIn.openForCreate = c4->openForCreate ;
      dataIn.singleOpen = c4->singleOpen ;
      dataIn.log = c4->log ;

      connection4addData( connection, &dataIn, sizeof( CONNECTION4OPEN_INFO_IN ), 0 ) ;
      connection4send( connection ) ;
      rc = connection4receive( connection ) ;
      if ( rc < 0 )
      {
         error4( c4, rc, E91102 ) ;
         return 0 ;
      }
      if ( connection4type( connection ) != CON4OPEN )
      {
         error4( c4, e4connection, E84304 ) ;
         return 0 ;
      }

      rc = connection4status( connection ) ;
      if ( rc < 0 )
      {
         if ( c4->errOpen == 0 )
            error4set( c4, r4noOpen ) ;
         else
            connection4errorDescribe( connection, c4, rc, E91102, name, 0, 0 ) ;
         return 0 ;
      }

      if ( connection4len( connection ) < sizeof( CONNECTION4OPEN_INFO_OUT ) )
      {
         error4( c4, e4connection, E84305 ) ;
         return 0 ;
      }

      dataInfo = (CONNECTION4OPEN_INFO_OUT *)connection4data( connection ) ;
   #endif  /* S4CLIENT */

   if ( c4->data4fileMemory == 0 )
   {
      c4->data4fileMemory = mem4create( c4, c4->memStartDataFile, sizeof(DATA4FILE), c4->memExpandDataFile, 0 ) ;
      if ( c4->data4fileMemory == 0 )
      {
         error4( c4, e4memory, E91102 ) ;
         return 0 ;
      }
   }
   d4 = (DATA4FILE *)mem4alloc( c4->data4fileMemory ) ;
   if ( d4 == 0 )
   {
      error4( c4, e4memory, E91102 ) ;
      return 0 ;
   }

   d4->c4 = c4 ;
   d4->userCount = 1 ;

   #ifndef S4CLIENT
      #ifndef S4OFF_MEMO
         d4->memoFile.file.hand = -1 ;
      #endif

      #ifdef S4SERVER
         #ifndef S4OFF_CATALOG
            if ( cat4avail( c4->catalog ) )
            {
               u4ncpy( nameBuf, cat4pathName( c4->catalog ), (unsigned int)cat4pathNameLen( c4->catalog ) ) ;
               for ( i = 0 ; i < cat4pathNameLen( c4->catalog ) ; i++ )
                  if ( nameBuf[i] == ' ' )
                  {
                     nameBuf[i] = 0 ;
                     break ;
                  }

            }
         #endif  /* S4OFF_CATALOG */
      #endif /* S4SERVER */
      rc = file4open( &d4->file, c4, nameBuf, 1 ) ;

      if ( rc )
      {
         dfile4close( d4 ) ;
         return 0 ;
      }
   #endif

   l4add( &c4->dataFileList, &d4->link ) ;

   #ifdef S4CLIENT
      #ifdef E4MISC
         if ( strlen( name ) > sizeof( d4->accessName ) )
         {
            error4describe( c4, e4name, E91102, name, 0, 0 ) ;
            dfile4close( d4 ) ;
            return 0 ;
         }
      #endif
      strcpy( d4->accessName, name ) ;
      c4upper( d4->accessName ) ;
      d4->connection = connection ;
      d4->recWidth = dataInfo->recWidth ;
      d4->headerLen = dataInfo->headerLen ;
      d4->version = dataInfo->version ;
      d4->serverId = dataInfo->serverId ;
      data->readOnly = dataInfo->readOnly ;

      d4->info = (char *)u4allocFree( c4, dataInfo->infoLen ) ;
      if ( d4->info == 0 )
      {
         dfile4close( d4 ) ;
         return 0 ;
      }
      len2 = sizeof( CONNECTION4OPEN_INFO_OUT ) ;
      memcpy( d4->info, connection4data( connection ) + len2, dataInfo->infoLen ) ;
      d4->infoLen = dataInfo->infoLen ;
      len2 += dataInfo->infoLen ;

      #ifndef S4OFF_INDEX
         /* index file information... */
         if ( c4->autoOpen == 1 && dataInfo->numTags > 0 )
         {
            u4namePiece( indexName, sizeof(indexName), name, 1, 0 ) ;
            rc = client4indexSetup( c4, data, d4, dataInfo->numTags, connection4data( connection ) + len2, (unsigned int)connection4len( connection ) - len2, indexName, 0 ) ;
            if ( rc < 0 )
            {
               dfile4close( d4 ) ;
               return 0 ;
            }
         }
      #endif
   #else
      if ( file4readAll( &d4->file, 0L, &fullHeader, sizeof( fullHeader ) ) < 0 )
      {
         dfile4close( d4 ) ;
         return 0 ;
      }

      #ifdef S4BYTE_SWAP
         fullHeader.numRecs = x4reverseLong( (void *)&fullHeader.numRecs ) ;
         fullHeader.headerLen = x4reverseShort( (void *)&fullHeader.headerLen ) ;
         fullHeader.recordLen = x4reverseShort( (void *)&fullHeader.recordLen ) ;
      #endif

      #ifdef S4DEMO
         if ( fullHeader.numRecs > 200L)
         {
            error4( c4, e4demo, 0 ) ;
            dfile4close( d4 ) ;
            return 0 ;
         }
      #endif

      if ( fullHeader.recordLen == 0 )  /* divide by zero */
      {
         error4describe( c4, e4data, E83805, nameBuf, dfile4name( d4 ), (char *)0 ) ;
         dfile4close( d4 ) ;
         return 0 ;
      }

      #ifndef S4CLIENT
         /* if the file is opened deny write/exclusively, and this was the first open, then
            verify that the record count matches the file length (i.e. to avoid
            data file corruption) */
         if ( c4->accessMode == OPEN4DENY_WRITE || c4->accessMode == OPEN4DENY_RW )
            if ( fullHeader.numRecs != ( file4len( &d4->file ) - fullHeader.headerLen ) / fullHeader.recordLen )
            {
               error4describe( c4, e4data, E83805, nameBuf, dfile4name( d4 ), (char *)0 ) ;
               dfile4close( d4 ) ;
               return 0 ;
            }
      #endif

      if ( fullHeader.numRecs < 0L || fullHeader.numRecs > ( 1 +  ( file4len ( &d4->file )  - fullHeader.headerLen ) / fullHeader.recordLen ) )
      {
         error4describe( c4, e4data, E83805, nameBuf, dfile4name( d4 ), (char *)0 ) ;
         dfile4close( d4 ) ;
         return 0 ;
      }

      memcpy( (void *)&d4->version, (void *)&fullHeader.version, (4+(sizeof(S4LONG))+(sizeof(short))) ) ;

      #ifdef S4FOX
         data->codePage = fullHeader.codePage ;
      #endif

      d4->hasMdxMemo = fullHeader.hasMdxMemo ;

      fieldDataLen = fullHeader.headerLen-sizeof(fullHeader) ;
      if ( fullHeader.headerLen <= sizeof(fullHeader) )
      {
         error4describe( c4, e4data, E83805, nameBuf, dfile4name( d4 ), (char *)0 ) ;
         dfile4close( d4 ) ;
         return 0 ;
      }

      d4->info = (char *)u4allocFree( c4, (long)fieldDataLen ) ;
      d4->infoLen = fieldDataLen ;
      d4->headerLen = fullHeader.headerLen ;
      if ( d4->info == 0 )
      {
         #ifdef E4STACK
            error4stack( c4, e4memory, E91102 ) ;
         #endif
         dfile4close( d4 ) ;
         return 0 ;
      }

      /* 06/18/96 AS --> fullHeader not read in prior place, must be after previous read */
      #ifndef S4OFF_OPTIMIZE
         file4optimizeLow( &d4->file, c4->optimize, OPT4DBF, fullHeader.recordLen, d4 ) ;
      #endif

      if ( file4readAll( &d4->file, (long)sizeof(fullHeader), d4->info, fieldDataLen ) < 0 )
      {
         error4describe( c4, e4data, E84306, name, 0, 0 ) ;
         dfile4close( d4 ) ;
         return 0 ;
      }

      if ( error4code( c4 ) < 0 )
      {
         dfile4close( d4 ) ;
         return 0 ;
      }

      #ifndef S4OFF_MEMO
         if ( d4->version == 0x30 )  /* visual FP 3.0 */
            hasMemo = fullHeader.hasMdxMemo & 0x02 ;
         else
            hasMemo = d4->version & 0x80 ;
         if ( hasMemo )
         {
            #ifdef S4MFOX
               #ifdef S4CASE_SEN
                  u4nameExt( nameBuf, sizeof(nameBuf), "fpt", 1 ) ;
               #else
                  u4nameExt( nameBuf, sizeof(nameBuf), "FPT", 1 ) ;
               #endif
            #else
               #ifdef S4CASE_SEN
                  u4nameExt( nameBuf, sizeof(nameBuf), "dbt", 1 ) ;
               #else
                  u4nameExt( nameBuf, sizeof(nameBuf), "DBT", 1 ) ;
               #endif
            #endif
            if ( memo4fileOpen( &d4->memoFile, d4, nameBuf ) < 0 )
            {
               dfile4close( d4 ) ;
               return 0 ;
            }
         }
      #endif
   #endif

   d4->numRecs = -1L ;
   *info = d4->info ;

   /* count the number of fields */
   for ( count = 0 ; d4->info[count] != 0xD ; count += 32 ) ;
   d4->nFields = (int)( count / 32 ) ;

   #ifdef E4ANALYZE
      if ( d4->nFields == 0 )
      {
         error4describe( c4, e4data, E84309, name, dfile4name( d4 ), 0 ) ;
         return 0 ;
      }
   #endif

   d4->recWidth = 1 ;
   d4->nFieldsMemo = 0 ;

   for ( iFields = 0; iFields < d4->nFields; iFields++ )
   {
      image = (FIELD4IMAGE *)(((char *)*info) + iFields * 32 ) ;

      switch( image->type )
      {
         case r4memo:
         case r4gen:
            d4->nFieldsMemo++ ;
            d4->recWidth += image->len ;
            break ;
         case r4num:
         case r4float:
         case r4log:
         case r4date:
            d4->recWidth += image->len ;
            break ;
         case r4str:
            d4->recWidth += ( image->len + (image->dec << 8) ) ;
            break ;
         case r4double:   /* r4bin and r4double the same */
            if ( d4->version == 0x30 )
            {
               d4->recWidth += image->len ;
               if ( d4->version != 0x30 )  /* 2.5 data files disallowed these fields */
               {
                  dfile4close( d4 ) ;
                  #ifdef S4SERVER
                     error4describe( c4, e4data, E80501, nameBuf, dfile4name( d4 ), 0 ) ;
                  #else
                     error4describe( c4, e4data, E80501, name, dfile4name( d4 ), 0 ) ;
                  #endif
                  return 0 ;
               }
            }
            else
            {
               d4->nFieldsMemo++ ;
               d4->recWidth += image->len ;
            }
            break ;
         #ifdef S4CLIENT_OR_FOX
            case r4currency:
            case r4int:
            case r4dateTime:
               d4->recWidth += image->len ;
               if ( d4->version != 0x30 )  /* 2.5 data files disallowed these fields */
               {
                  dfile4close( d4 ) ;
                  #ifdef S4SERVER
                     error4describe( c4, e4data, E80501, nameBuf, dfile4name( d4 ), 0 ) ;
                  #else
                     error4describe( c4, e4data, E80501, name, dfile4name( d4 ), 0 ) ;
                  #endif
                  return 0 ;
               }
               break ;
            case r4system:  /* null-fields/system field */
               if ( ( d4->version != 0x30 ) || ( memcmp( image->name, "_NullFlags", 10 ) != 0 ) )  /* not visual FP 3.0 */
               {
                  dfile4close( d4 ) ;
                  #ifdef S4SERVER
                     error4describe( c4, e4data, E80501, nameBuf, dfile4name( d4 ), 0 ) ;
                  #else
                     error4describe( c4, e4data, E80501, name, dfile4name( d4 ), 0 ) ;
                  #endif
               }
               d4->recWidth += image->len ;
               break ;
         #endif
         default:
            dfile4close( d4 ) ;
            #ifdef S4SERVER
               error4describe( c4, e4data, E80501, nameBuf, dfile4name( d4 ), 0 ) ;
            #else
               error4describe( c4, e4data, E80501, name, dfile4name( d4 ), 0 ) ;
            #endif
            return 0 ;
      }
   }

   #ifdef S4SERVER
      if ( d4->recWidth != fullHeader.recordLen )
      {
         dfile4close( d4 ) ;
         error4describe( c4, e4data, E91102, nameBuf, dfile4name( d4 ), 0 ) ;
         return 0 ;
      }
   #endif

   #ifdef S4CLIENT
      d4->accessMode = c4->accessMode ;
   #else
      d4->valid = 1 ;   /* valid, so low closes will leave open. */
   #endif

   return d4 ;
}

#ifdef S4VB_DOS

DATA4 * d4open_v( CODE4 *c4, char *name )
{
   return d4open( c4, c4str(name) ) ;
}

#endif