3926 lines
		
	
	
		
			108 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			3926 lines
		
	
	
		
			108 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /* r4relate.c   (c)Copyright Sequiter Software Inc., 1988-1996.  All rights reserved. */
 | |
| 
 | |
| #include "d4all.h"
 | |
| #ifdef __TURBOC__
 | |
|    #pragma hdrstop
 | |
| #endif
 | |
| 
 | |
| #ifdef S4CLIENT
 | |
|    static int relate4clientInit( RELATE4 * ) ;
 | |
| #else
 | |
|    static int relate4currentIsChild( RELATE4 * ) ;
 | |
|    static int relate4parent( RELATE4 *, RELATE4 * ) ;
 | |
|    static int relate4nextRelationList( RELATION4 *, int ) ;
 | |
|    static int relate4prevRecordInScan( RELATE4 * ) ;
 | |
|    static int relate4prevRelationList( RELATION4 *, int ) ;
 | |
| #endif
 | |
| 
 | |
| static int relate4initRelate( RELATE4 *, RELATION4 *, DATA4 *, CODE4 *, int ) ;
 | |
| static int relate4lookup( RELATE4 *, const char ) ;
 | |
| static int relate4readRest( RELATE4 *, char ) ;
 | |
| static void relate4setNotRead( RELATE4 * ) ;
 | |
| static void relate4sortFree( RELATION4 *, const int ) ;
 | |
| static int relate4sortGetRecord( RELATION4 *, const long ) ;
 | |
| static int relate4sortNextRecord( RELATION4 * ) ;
 | |
| static int relate4sortPrevRecord( RELATION4 * ) ;
 | |
| #ifndef S4CLIENT
 | |
| 
 | |
| static int relate4topInit( RELATE4 * ) ;
 | |
| 
 | |
| static int f4flagIsSetFlip( F4FLAG *flagPtr, const unsigned long r )
 | |
| {
 | |
|    if ( flagPtr->flags == 0 )
 | |
|       return 1 ;
 | |
| 
 | |
|    if ( flagPtr->isFlip )
 | |
|       return ! f4flagIsSet( flagPtr, r ) ;
 | |
|    else
 | |
|       return f4flagIsSet( flagPtr, r ) ;
 | |
| }
 | |
| 
 | |
| /* returns the position of the next flipped flag in the flag set - start at r */
 | |
| static unsigned long f4flagGetNextFlip( F4FLAG *f4, const unsigned long r, const char direction )
 | |
| {
 | |
|    unsigned char cFlag ;
 | |
|    unsigned long lowVal, onVal, highVal ;
 | |
|    char i ;
 | |
| 
 | |
|    #ifdef E4PARM_LOW
 | |
|       if ( direction != -1 && direction != 1 )
 | |
|          return error4( 0, e4parm, E90812 ) ;
 | |
|    #endif
 | |
| 
 | |
|    onVal = r ;
 | |
|    if ( f4->flags == 0 || r > f4->numFlags )
 | |
|       return 0 ;
 | |
| 
 | |
|    lowVal = (unsigned long)( r & 0x7 ) ;
 | |
|    highVal = (unsigned long)( r >> 3 ) ;
 | |
| 
 | |
|    if ( (int)direction == -1 )
 | |
|    {
 | |
|       cFlag = (unsigned char)( f4->flags[highVal] ) ;
 | |
|       if ( f4->isFlip )
 | |
|          cFlag = (unsigned char) ~cFlag ;
 | |
| 
 | |
|       cFlag = (unsigned char)( (unsigned char)( cFlag << ( 7 - lowVal ) ) >> ( 7 - lowVal )) ;
 | |
| 
 | |
|       onVal += ( 7 - lowVal ) ;
 | |
| 
 | |
|       if ( cFlag == 0 )
 | |
|          for( ; cFlag == 0 ; onVal -= 8 )
 | |
|          {
 | |
|             if ( highVal-- <= 1 )  /* if was zero, or is now zero */
 | |
|             {
 | |
|                if ( f4->flags[0] == 0 )
 | |
|                   return r ;
 | |
|                cFlag = f4->flags[0] ;
 | |
|                if ( f4->isFlip )
 | |
|                   cFlag = (unsigned char) ~cFlag ;
 | |
|                onVal -= 8 ;  /* for sure if highVal == 0, else?? */
 | |
|                break ;
 | |
|             }
 | |
|             cFlag = f4->flags[highVal] ;
 | |
|             if ( f4->isFlip )
 | |
|                cFlag = (unsigned char) ~cFlag ;
 | |
|          }
 | |
| 
 | |
|       for( i = 7 ; (int)i >= 0 ; i--, onVal-- )
 | |
|          if ( cFlag & ( 0x01 << i ) )
 | |
|             break ;
 | |
| 
 | |
|       return (r - onVal) ;
 | |
|    }
 | |
|    else
 | |
|    {
 | |
|       cFlag = (unsigned char)f4->flags[highVal] ;
 | |
|       if ( f4->isFlip )
 | |
|          cFlag = (unsigned char) ~cFlag ;
 | |
|       cFlag = (unsigned char) (cFlag >> lowVal) ;
 | |
|       if ( cFlag == 0 )
 | |
|       {
 | |
|          onVal -= lowVal ;
 | |
|          for( ; cFlag == 0 ; onVal += 8 )
 | |
|          {
 | |
|             if ( onVal >= f4->numFlags )
 | |
|                return (f4->numFlags - r + 1) ;
 | |
|             cFlag = f4->flags[++highVal] ;
 | |
|             if ( f4->isFlip )
 | |
|                cFlag = (unsigned char) ~cFlag ;
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       for( i = 0 ; i <= 7 ; i++, onVal++ )
 | |
|          if ( cFlag & ( 0x01 << i ) )
 | |
|             break ;
 | |
| 
 | |
|       return (onVal - r) ;
 | |
|    }
 | |
| }
 | |
| 
 | |
| int r4dataListAdd( LIST4 *l4, DATA4 *data, RELATE4 *relate )
 | |
| {
 | |
|    R4DATA_LIST *r4 ;
 | |
|    CODE4 *c4 ;
 | |
| 
 | |
|    c4 = relate->codeBase ;
 | |
| 
 | |
|    if ( error4code( c4 ) < 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    if ( c4->relateDataListMemory == 0 )
 | |
|    {
 | |
|       c4->relateDataListMemory = mem4create( c4, 10, sizeof( R4DATA_LIST ), 10, 0 ) ;
 | |
|       if ( c4->relateDataListMemory == 0 )
 | |
|          return 0 ;
 | |
|    }
 | |
| 
 | |
|    r4 = (R4DATA_LIST *)mem4alloc( c4->relateDataListMemory ) ;
 | |
|    if ( r4 == 0 )
 | |
|       return -1 ;
 | |
|    r4->data = data ;
 | |
|    r4->relate = relate ;
 | |
|    l4add( l4, r4 ) ;
 | |
|    return 0 ;
 | |
| }
 | |
| 
 | |
| int r4dataListFind( LIST4 *l4, RELATE4 *r4 )
 | |
| {
 | |
|    R4DATA_LIST *link ;
 | |
| 
 | |
|    for ( link = 0 ;; )
 | |
|    {
 | |
|       link = (R4DATA_LIST *)l4next( l4, link ) ;
 | |
|       if ( link == 0 )
 | |
|          return 0 ;
 | |
|       if ( link->relate == r4 )
 | |
|          return 1 ;
 | |
|    }
 | |
| }
 | |
| 
 | |
| void r4dataListFree( LIST4 *l4 )
 | |
| {
 | |
|    R4DATA_LIST *r4data, *r4data2 ;
 | |
| 
 | |
|    for ( r4data = (R4DATA_LIST *)l4first( l4 ) ; r4data ; )
 | |
|    {
 | |
|       r4data->relate->sortType = 0 ;
 | |
|       r4data2 = (R4DATA_LIST *)l4next( l4, r4data ) ;
 | |
|       l4remove( l4, r4data ) ;
 | |
|       mem4free( r4data->relate->codeBase->relateDataListMemory, r4data ) ;
 | |
|       r4data = r4data2 ;
 | |
|    }
 | |
| }
 | |
| 
 | |
| /* this function takes a completed sort list, and adds data members in the
 | |
|    following case:
 | |
| 
 | |
|    If (r)elate must be added, r's siblings must also be added
 | |
| 
 | |
|    Or, interpreted differently, if r is a relation, and any of it's children
 | |
|    must be added, then all of its children must be added.
 | |
| */
 | |
| static int r4dataListMassage( LIST4 *l4 )
 | |
| {
 | |
|    RELATE4 *relateChild ;
 | |
|    R4DATA_LIST *r4data ;
 | |
|    int addChildren, relateAdded ;
 | |
| 
 | |
|    if ( l4->nLink == 0 )  /* no work required */
 | |
|       return 0 ;
 | |
| 
 | |
|    r4data = 0 ;
 | |
| 
 | |
|    for( ;; )
 | |
|    {
 | |
|       r4data = (R4DATA_LIST *)l4next( l4, r4data ) ;
 | |
|       if ( r4data == 0 )
 | |
|          break ;
 | |
| 
 | |
|       relateChild = 0 ;
 | |
|       addChildren = 0 ;
 | |
|       for( ;; )
 | |
|       {
 | |
|          relateChild = (RELATE4 *)l4next( &r4data->relate->slaves, relateChild ) ;
 | |
|          if ( relateChild == 0 )
 | |
|             break ;
 | |
|          if ( r4dataListFind( l4, relateChild ) )
 | |
|          {
 | |
|             addChildren = 1 ;
 | |
|             break ;
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       if ( addChildren == 1 )
 | |
|       {
 | |
|          relateAdded = 0 ;
 | |
|          relateChild = 0 ;
 | |
|          for( ;; )
 | |
|          {
 | |
|             relateChild = (RELATE4 *)l4next( &r4data->relate->slaves, relateChild ) ;
 | |
|             if ( relateChild == 0 )
 | |
|                break ;
 | |
|             if ( r4dataListFind( l4, relateChild ) == 0 )
 | |
|             {
 | |
|                r4dataListAdd( l4, relateChild->data, relateChild ) ;
 | |
|                relateChild->sortType = relate4exact ;
 | |
|                relateAdded = 1 ;
 | |
|             }
 | |
|          }
 | |
|          if ( relateAdded == 1 )
 | |
|             r4data = 0 ;   /* start at list top again to be sure none missed */
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    return 0 ;
 | |
| }
 | |
| 
 | |
| /* 1 - database added, 0 - database not added, -1 - error */
 | |
| /* checkType gives the caller's status in terms of whether we should be included */
 | |
| int r4dataListBuild( LIST4 *l4, RELATE4 *relate, EXPR4 *expr, int checkType )
 | |
| {
 | |
|    int i ;
 | |
|    char mustAdd ;
 | |
|    E4INFO *info ;
 | |
|    RELATE4 *slaveOn ;
 | |
| 
 | |
|    if ( error4code( relate->codeBase ) < 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    mustAdd = 0 ;
 | |
| 
 | |
|    /* 1st check if we must belong */
 | |
|    for( i = 0 ; i < expr->infoN ; i++ )
 | |
|    {
 | |
|       info = expr->info + i ;
 | |
|       if ( info->fieldPtr )
 | |
|       {
 | |
|          if ( info->fieldPtr->data == relate->data )
 | |
|          {
 | |
|             mustAdd = 1 ;
 | |
|             break ;
 | |
|          }
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    relate->sortType = relate4exact ;
 | |
| 
 | |
|    if ( mustAdd )
 | |
|       checkType = relate4exact ;
 | |
|    else
 | |
|    {
 | |
|       if ( relate->relationType == relate4scan )
 | |
|          checkType = relate4scan ;
 | |
|       else
 | |
|          if ( checkType != relate4scan )   /* non-scan parent must be added, so we add ourselves too, in order to save work later */
 | |
|             mustAdd = 1 ;
 | |
|    }
 | |
| 
 | |
|    /* if a child must be added, we must be too: */
 | |
|    for ( slaveOn = 0 ;; )
 | |
|    {
 | |
|       slaveOn = (RELATE4 *)l4next( &relate->slaves, slaveOn ) ;
 | |
|       if ( slaveOn == 0 )
 | |
|          break ;
 | |
|       if ( r4dataListBuild( l4, slaveOn, expr, checkType ) == 1 )
 | |
|          mustAdd = 1 ;
 | |
|    }
 | |
| 
 | |
|    if ( mustAdd )
 | |
|       r4dataListAdd( l4, relate->data, relate ) ;
 | |
|    else
 | |
|       if ( relate->relationType == relate4scan )
 | |
|          relate->sortType = relate4sortSkip ;
 | |
| 
 | |
|    return mustAdd ;
 | |
| }
 | |
| 
 | |
| /* direction : -1 = look backwards, 0 = lookup only, 1 = look forwards */
 | |
| static int relate4blankSet( RELATE4 *relate, const char direction )
 | |
| {
 | |
|    RELATE4 *slave ;
 | |
|    CODE4 *c4 ;
 | |
|    int rc ;
 | |
| 
 | |
|    #ifdef E4PARM_LOW
 | |
|       if ( direction < -1 || direction > 1 )
 | |
|          return error4( 0, e4parm, E94417 ) ;
 | |
|    #endif
 | |
| 
 | |
|    c4 = relate->codeBase ;
 | |
| 
 | |
|    if ( error4code( c4 ) < 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    relate->isRead = 1 ;
 | |
|    if ( direction >= 0 )
 | |
|    {
 | |
|       if ( d4goEof( relate->data ) < 0 )
 | |
|          return -1 ;
 | |
|    }
 | |
|    else
 | |
|    {
 | |
|       rc = d4top( relate->data ) ;
 | |
|       if ( rc )
 | |
|          return rc ;
 | |
|       rc = d4skip( relate->data, -1L ) ;
 | |
|       relate->data->recNum = -1 ;
 | |
|       d4blank( relate->data ) ;
 | |
|       relate->data->recordChanged = 0 ;
 | |
|       if ( error4code( c4 ) < 0 )
 | |
|          return -1 ;
 | |
|       #ifdef S4SINGLE
 | |
|          if ( rc < 0 )
 | |
|       #else
 | |
|          if ( rc == r4locked || rc < 0 )
 | |
|       #endif
 | |
|             return rc ;
 | |
|    }
 | |
| 
 | |
|    for( slave = 0 ;; )
 | |
|    {
 | |
|       slave = (RELATE4 *)l4next( &relate->slaves, slave ) ;
 | |
|       if ( slave == 0 )
 | |
|          return 0 ;
 | |
|       rc = relate4blankSet( slave, direction ) ;
 | |
|       if ( rc < 0 )
 | |
|          return rc ;
 | |
|    }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef S4CLIENT
 | |
| int relate4unpack( RELATION4 *relation, CONNECTION4 *connection )
 | |
| {
 | |
|    CONNECTION4RELATION_DATA_OUT *info ;
 | |
|    RELATE4 *relate ;
 | |
|    unsigned int pos ;
 | |
|    long recCount, len ;
 | |
|    const char *data ;
 | |
|    #ifndef S4MEMO_OFF
 | |
|       int i ;
 | |
|    #endif
 | |
| 
 | |
|    len = connection4len( connection ) ;
 | |
|    data = connection4data( connection ) ;
 | |
|    if ( len < sizeof( CONNECTION4RELATION_DATA_OUT ) )
 | |
|       return error4( relation->relate.codeBase, e4packetLen, E94425 ) ;
 | |
|    info = (CONNECTION4RELATION_DATA_OUT *)data ;
 | |
|    if ( info->relationId != relation->relationId )
 | |
|       return error4( relation->relate.codeBase, e4connection, E84305 ) ;
 | |
| 
 | |
|    pos = sizeof( CONNECTION4RELATION_DATA_OUT ) ;
 | |
|    for( relate = &relation->relate ;; )
 | |
|    {
 | |
|       #ifndef S4MEMO_OFF
 | |
|          if ( relate->data->dataFile->nFieldsMemo > 0 )
 | |
|          {
 | |
|             for ( i = 0; i < relate->data->dataFile->nFieldsMemo; i++ )
 | |
|                f4memoReset( relate->data->fieldsMemo[i].field ) ;
 | |
|          }
 | |
|       #endif
 | |
|       if ( len < (long)pos + (long)dfile4recWidth( relate->data->dataFile ) + (long)sizeof( relate->data->recNum ) + (long)sizeof(long) )
 | |
|          return error4( relation->relate.codeBase, e4packetLen, E94425 ) ;
 | |
|       memcpy( &relate->data->recNum, data + pos, sizeof( relate->data->recNum ) ) ;
 | |
|       pos += sizeof( relate->data->recNum ) ;
 | |
|       recCount = *((long *)( data + pos )) ;
 | |
|       pos += sizeof( long ) ;
 | |
|       if ( recCount < 0 )
 | |
|          return error4( relation->relate.codeBase, e4result, E84305 ) ;
 | |
|       if ( relate->data->recNum > recCount )
 | |
|          relate->data->eofFlag = 1 ;
 | |
|       else
 | |
|          relate->data->eofFlag = 0 ;
 | |
|       if ( recCount == 0 || relate->data->recNum == 0 )
 | |
|          relate->data->bofFlag = 1 ;
 | |
|       else
 | |
|          relate->data->bofFlag = 0 ;
 | |
|       memcpy( relate->data->record, data + pos, dfile4recWidth( relate->data->dataFile ) ) ;
 | |
|       pos += dfile4recWidth( relate->data->dataFile ) ;
 | |
|       if ( relate4next( &relate ) == 2 )
 | |
|          break ;
 | |
|    }
 | |
| 
 | |
|    if ( len != (long)pos )
 | |
|       return error4( relation->relate.codeBase, e4packetLen, E94425 ) ;
 | |
| 
 | |
|    return 0 ;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| int S4FUNCTION relate4bottom( RELATE4 *relate )
 | |
| {
 | |
|    RELATION4 *relation ;
 | |
|    int rc, rc2 ;
 | |
|    CODE4 *c4 ;
 | |
|    #ifdef S4CLIENT
 | |
|       CONNECTION4 *connection ;
 | |
|       CONNECTION4RELATE_BOTTOM_INFO_IN info ;
 | |
|    #else
 | |
|       #ifndef S4OFF_MULTI
 | |
|          int oldReadLock ;
 | |
|       #endif
 | |
|       long rec ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef S4VBASIC
 | |
|       if ( c4parm_check( relate, 5, E94401 ) )
 | |
|          return -1 ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef E4PARM_LOW
 | |
|       if ( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94401 ) ;
 | |
|    #endif
 | |
| 
 | |
|    c4 = relate->codeBase ;
 | |
|    if ( error4code( c4 ) < 0 )
 | |
|       return e4codeBase ;
 | |
| 
 | |
|    relation = relate->relation ;
 | |
|    relate = &relation->relate ;
 | |
| 
 | |
|    #ifdef S4CLIENT
 | |
|       if ( relation->isInitialized == 0 )
 | |
|       {
 | |
|          rc = relate4clientInit( relate ) ;
 | |
|          if ( rc != 0 )
 | |
|             return rc ;
 | |
|       }
 | |
|       #ifdef S4CB51
 | |
|          #ifndef S4OFF_MULTI
 | |
|             if ( c4->readLock )
 | |
|             {
 | |
|                rc = relate4lock( relate ) ;
 | |
|                if ( rc != 0 )
 | |
|                   return rc ;
 | |
|             }
 | |
|          #endif
 | |
|       #endif
 | |
|       #ifdef E4ANALYZE
 | |
|          if ( relate->data == 0 )
 | |
|             return error4( c4, e4parm, E94401 ) ;
 | |
|          if ( relate->data->dataFile == 0 )
 | |
|             return error4( c4, e4parm, E94401 ) ;
 | |
|       #endif
 | |
|       connection = relate->data->dataFile->connection ;
 | |
|       #ifdef E4ANALYZE
 | |
|          if ( connection == 0 )
 | |
|             return error4( c4, e4parm, E94401 ) ;
 | |
|       #endif
 | |
| 
 | |
|       connection4assign( connection, CON4RELATE_BOTTOM, 0, 0 ) ;
 | |
|       info.relationId = relation->relationId ;
 | |
|       connection4addData( connection, &info, sizeof( CONNECTION4RELATE_BOTTOM_INFO_IN ), 0 ) ;
 | |
|       connection4send( connection ) ;
 | |
|       rc = connection4receive( connection ) ;
 | |
|       if ( rc < 0 )
 | |
|          return error4stack( c4, rc, E94401 ) ;
 | |
|       rc = connection4status( connection ) ;
 | |
|       if ( rc < 0 )
 | |
|          return connection4error( connection, c4, rc, E94401 ) ;
 | |
|       rc2 = relate4unpack( relation, connection ) ;
 | |
|       if ( rc2 < 0 )
 | |
|          return error4stack( c4, rc, E94401 ) ;
 | |
|       return rc ;
 | |
|    #else
 | |
|       #ifndef S4OFF_MULTI
 | |
|          oldReadLock = c4->readLock ;
 | |
|          c4->readLock = 0 ;
 | |
|       #endif
 | |
|       if ( relation->skipBackwards == 0 )
 | |
|       {
 | |
|          relate4sortFree( relation, 0 ) ;
 | |
|          relate4skipEnable( relate, 1 ) ;
 | |
|       }
 | |
| 
 | |
|       for ( ;; )  /* used to minimize return code areas, just break out... */
 | |
|       {
 | |
|          rc = relate4topInit( relate ) ;
 | |
|          if ( rc != 0 )
 | |
|             break ;
 | |
| 
 | |
|          relate4setNotRead( relate ) ;
 | |
| 
 | |
|          relation->currentRelateLevel = 0 ;
 | |
|          relate4prevRelationList( relation, 1 ) ;
 | |
| 
 | |
|          if ( relation->inSort == relate4sortDone )
 | |
|          {
 | |
|             if ( relate4sortGetRecord( relation, relation->sortRecCount ) == r4eof )
 | |
|             {
 | |
|                rc = r4eof ;
 | |
|                break ;
 | |
|             }
 | |
|             else
 | |
|                relation->sortRecOn = relation->sortRecCount ;
 | |
|          }
 | |
|          else
 | |
|          {
 | |
|             rc = d4bottom( relate->data ) ;
 | |
|             if ( rc != 0 )
 | |
|                break ;
 | |
|             if ( relation->exprSource )
 | |
|             {
 | |
|                rec = d4recNo( relate->data ) ;
 | |
|                #ifndef S4INDEX_OFF
 | |
|                   if ( relate->dataTag )
 | |
|                   {
 | |
|                      while ( f4flagIsSetFlip( &relate->set, (unsigned long)rec ) == 0 )
 | |
|                      {
 | |
|                         #ifdef S4HAS_DESCENDING
 | |
|                            rc = (int)tfile4dskip( relate->dataTag->tagFile, -1L ) ;
 | |
|                         #else
 | |
|                            rc = (int)tfile4skip( relate->dataTag->tagFile, -1L ) ;
 | |
|                         #endif
 | |
|                         if ( rc != -1 )
 | |
|                         {
 | |
|                            if ( rc == 0 )
 | |
|                               rc = r4eof ;
 | |
|                            break ;
 | |
|                         }
 | |
|                         rec = tfile4recNo( relate->dataTag->tagFile ) ;
 | |
|                      }
 | |
|                      if ( rc == r4eof )
 | |
|                         break ;
 | |
|                   }
 | |
|                   else
 | |
|                   {
 | |
|                #endif
 | |
| 
 | |
|                if ( f4flagIsSetFlip( &relate->set, (unsigned long)rec ) == 0 )
 | |
|                {
 | |
|                   rec = d4recNo( relate->data ) - f4flagGetNextFlip( &relate->set, (unsigned long)d4recNo( relate->data), (char)-1 ) ;
 | |
|                   if ( rec == 0 )
 | |
|                   {
 | |
|                      rc = r4eof ;
 | |
|                      break ;
 | |
|                   }
 | |
|                }
 | |
|                #ifndef S4INDEX_OFF
 | |
|                   }
 | |
|                #endif
 | |
|                rc = d4go( relate->data, rec ) ;
 | |
|                if ( rc < 0 )
 | |
|                   break ;
 | |
|             }
 | |
|             relate4setNotRead( relate ) ;
 | |
|          }
 | |
|          rc = relate4readRest( relate, -1 ) ;
 | |
|          if ( rc == relate4filterRecord )
 | |
|             rc = relate4skip( relate, -1L ) ;
 | |
| 
 | |
|          if ( rc < 0 || rc == r4terminate )
 | |
|             break ;
 | |
| 
 | |
|          if ( relation->exprSource )
 | |
|          {
 | |
|             rc2 = log4true( &relation->log ) ;
 | |
|             if ( rc2 == r4terminate )
 | |
|             {
 | |
|                rc = r4terminate ;
 | |
|                break ;
 | |
|             }
 | |
|             if ( rc2 == 0 )
 | |
|             {
 | |
|                if ( relation->inSort == relate4sortSkip )  /* must temporarily disable in order to get a matching scan if available */
 | |
|                {
 | |
|                   relation->inSort = 0 ;
 | |
|                   rc = relate4skip( relate, -1L ) ;
 | |
|                   relation->inSort = relate4sortSkip ;
 | |
|                }
 | |
|                else
 | |
|                   rc = relate4skip( relate, -1L ) ;
 | |
|             }
 | |
|          }
 | |
| 
 | |
|          if ( rc == r4bof )
 | |
|             rc = r4eof ;
 | |
| 
 | |
|          break ;
 | |
|       }
 | |
| 
 | |
|       #ifndef S4OFF_MULTI
 | |
|          c4->readLock = oldReadLock ;
 | |
|       #endif
 | |
|       return rc ;
 | |
|    #endif
 | |
| }
 | |
| 
 | |
| #ifndef S4CLIENT
 | |
| static int relate4buildScanList( RELATE4 *master, RELATE4 *relate, RELATION4 *relation )
 | |
| {
 | |
|    RELATE4 *relateOn ;
 | |
|    RELATE4LIST *ptr ;
 | |
| 
 | |
|    if ( error4code( relate->codeBase ) < 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    for( relateOn = 0 ;; )
 | |
|    {
 | |
|       relateOn = (RELATE4 *)l4next( &relate->slaves, relateOn ) ;
 | |
|       if ( relateOn == 0 )
 | |
|          break ;
 | |
|       if ( relate4buildScanList( relate, relateOn, relation ) < 0 )
 | |
|          return -1 ;
 | |
|    }
 | |
| 
 | |
|    if ( master != 0 )  /* cannot have a scan without a base master */
 | |
|       if ( relate->relationType == relate4scan || relate == &relation->relate )
 | |
|       {
 | |
|          ptr = (RELATE4LIST *)mem4createAlloc( relate->codeBase, &relate->codeBase->relateListMemory, 5, sizeof(RELATE4LIST), 5, 0 ) ;
 | |
|          if ( ptr == 0 )
 | |
|             return -1 ;
 | |
|          ptr->ptr = relate ;
 | |
|          l4add( &master->relateList, ptr ) ;
 | |
|       }
 | |
|    return 0 ;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static void relate4freeRelateList( RELATE4 *relate )
 | |
| {
 | |
|    RELATE4 *slaveOn ;
 | |
|    void *ptr ;
 | |
| 
 | |
|    for( ;; )
 | |
|    {
 | |
|       ptr = l4pop( &relate->relateList ) ;
 | |
|       if ( ptr == 0 )
 | |
|          break ;
 | |
|       mem4free( relate->codeBase->relateListMemory, ptr ) ;
 | |
|    }
 | |
| 
 | |
|    for ( slaveOn = 0 ;; )
 | |
|    {
 | |
|       slaveOn = (RELATE4 *)l4next( &relate->slaves, slaveOn ) ;
 | |
|       if ( slaveOn == 0 )
 | |
|          return ;
 | |
|       relate4freeRelateList( slaveOn ) ;
 | |
|    }
 | |
| 
 | |
| }
 | |
| 
 | |
| int S4FUNCTION relate4changed( RELATE4 *relate )
 | |
| {
 | |
|    RELATION4 *relation ;
 | |
|    CODE4 *c4 ;
 | |
|    #ifndef S4CLIENT
 | |
|       int j ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef E4PARM_HIGH
 | |
|       if ( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94402 ) ;
 | |
|    #endif
 | |
| 
 | |
|    c4 = relate->codeBase ;
 | |
|    if ( error4code( c4 ) < 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    #ifndef S4CLIENT
 | |
|       u4free( relate->scanValue ) ;
 | |
|       relate->scanValue = 0 ;
 | |
|    #endif
 | |
|    relation = relate->relation ;
 | |
| 
 | |
|    #ifdef S4CLIENT
 | |
|       if ( relation->isInitialized != 0 )
 | |
|          relation->needsFreeing = 1 ;
 | |
|    #endif
 | |
| 
 | |
|    relation->isInitialized = 0 ;
 | |
|    relate4sortFree( relation, 0 ) ;
 | |
| 
 | |
|    relate4freeRelateList( &(relation->relate) ) ;
 | |
| 
 | |
|    #ifndef S4CLIENT
 | |
|       u4free( relation->relate.set.flags ) ;
 | |
|       memset( (void *)&relation->relate.set, 0, sizeof( F4FLAG ) ) ;
 | |
| 
 | |
|       if ( relation->log.expr != 0 )
 | |
|       {
 | |
|          for( j = relation->log.expr->infoN; --j >= 0; )
 | |
|          {
 | |
|             E4INFO_REPORT *info_ptr = relation->log.infoReport + j ;
 | |
|             if ( info_ptr->relateDataList != 0 )
 | |
|             {
 | |
|                u4free( (info_ptr->relateDataList->pointers) ) ;
 | |
|                mem4free( c4->dataListMemory, info_ptr->relateDataList ) ;
 | |
|             }
 | |
|          }
 | |
| 
 | |
|          expr4free( relation->log.expr ) ;
 | |
|          relation->log.expr = 0 ;
 | |
|          u4free( relation->log.infoReport ) ;
 | |
|          relation->log.infoReport = 0 ;
 | |
|       }
 | |
|       if ( relation->log.bufLen != 0 )
 | |
|       {
 | |
|          u4free( relation->log.buf ) ;
 | |
|          relation->log.bufLen = 0 ;
 | |
|          relation->log.bufPos = 0 ;
 | |
|       }
 | |
|       relation->inSort = 0 ;
 | |
|    #endif
 | |
| 
 | |
|    return 0 ;
 | |
| }
 | |
| 
 | |
| RELATE4 *S4FUNCTION relate4createSlave( RELATE4 *master, DATA4 *slaveData, char *masterExpr, TAG4 *slaveTag )
 | |
| {
 | |
|    RELATION4 *relation ;
 | |
|    RELATE4 *slave ;
 | |
|    CODE4 *c4 ;
 | |
|    int rc ;
 | |
|    #ifdef S4SERVER
 | |
|       LIST4 *oldList ;
 | |
|    #endif
 | |
| 
 | |
|    if ( master == 0 )
 | |
|       return 0 ;
 | |
| 
 | |
|    #ifdef S4VBASIC
 | |
|       if ( c4parm_check( master, 5, E94403 ) )
 | |
|          return (RELATE4 *) 0 ;
 | |
|    #endif
 | |
| 
 | |
|    c4 = master->codeBase ;
 | |
| 
 | |
|    if ( error4code( c4 ) < 0 )
 | |
|       return 0 ;
 | |
| 
 | |
|    #ifdef E4PARM_LOW
 | |
|       if ( slaveData == 0 || masterExpr == 0 )
 | |
|       {
 | |
|          error4( c4, e4parm_null, E94403 ) ;
 | |
|          return 0 ;
 | |
|       }
 | |
|    #endif
 | |
| 
 | |
|    relation = master->relation ;
 | |
| 
 | |
|    #ifdef E4MISC
 | |
|       /* check that the d4 doesn't belong to any existing relation */
 | |
|       if ( relate4lookupRelate( &relation->relate, slaveData ) != 0 )
 | |
|       {
 | |
|          error4( c4, e4parm, E84403 ) ;
 | |
|          return 0 ;
 | |
|       }
 | |
|    #endif
 | |
| 
 | |
|    relate4changed( master ) ;
 | |
| 
 | |
|    slave = (RELATE4 *)mem4createAlloc( c4, &c4->relateMemory, 5, sizeof(RELATE4), 5, 0 ) ;
 | |
|    if ( slave == 0 )
 | |
|       return 0 ;
 | |
| 
 | |
|    #ifdef S4SERVER
 | |
|       rc = relate4initRelate( slave, relation, slaveData, c4, 0 ) ;
 | |
|    #else
 | |
|       rc = relate4initRelate( slave, relation, slaveData, c4, 1 ) ;
 | |
|    #endif
 | |
|    if ( rc < 0 )
 | |
|    {
 | |
|       mem4free( c4->relateMemory, slave ) ;
 | |
|       return 0 ;
 | |
|    }
 | |
|    #ifdef S4SERVER
 | |
|       oldList = tran4dataList( code4trans( c4 ) ) ;
 | |
|       tran4dataListSet( code4trans( c4 ), &relation->localDataList ) ;
 | |
|    #endif
 | |
|    slave->masterExpr = expr4parseLow( master->data, masterExpr, 0 ) ;
 | |
|    #ifdef S4VFP_KEY
 | |
|       if ( slaveTag != 0 )
 | |
|          slave->masterExpr->vfpInfo = &slaveTag->tagFile->vfpInfo ;
 | |
|    #endif
 | |
|    #ifdef S4SERVER
 | |
|       tran4dataListSet( code4trans( c4 ), oldList ) ;
 | |
|    #endif
 | |
|    if ( slave->masterExpr == 0 )
 | |
|    {
 | |
|       mem4free( c4->relateMemory, slave ) ;
 | |
|       return 0 ;
 | |
|    }
 | |
| 
 | |
|    #ifndef S4CLIENT
 | |
|       #ifndef S4INDEX_OFF
 | |
|          if ( slaveTag != 0 )
 | |
|             if ( tfile4type( slaveTag->tagFile ) != expr4type( slave->masterExpr ) )
 | |
|             {
 | |
|                #ifndef S4SERVER
 | |
|                   error4( c4, e4relate, E84404 ) ;
 | |
|                #endif
 | |
|                mem4free( c4->relateMemory, slave ) ;
 | |
|                return 0 ;
 | |
|             }
 | |
|       #endif
 | |
|    #endif
 | |
| 
 | |
|    slave->dataTag = slaveTag ;
 | |
|    slave->master = master ;
 | |
| 
 | |
|    l4add( &master->slaves, slave ) ;
 | |
|    relate4matchLen( slave, -1 ) ; /* Set to maximum */
 | |
| 
 | |
|    return slave ;
 | |
| }
 | |
| 
 | |
| #ifdef S4CLIENT
 | |
| int S4FUNCTION relate4doAll( RELATE4 *relate )
 | |
| {
 | |
|    CONNECTION4RELATE_DO_INFO_IN info ;
 | |
|    CONNECTION4 *connection ;
 | |
|    int rc, rc2 ;
 | |
|    CODE4 *c4 ;
 | |
| 
 | |
|    #ifdef E4PARM_HIGH
 | |
|       if ( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94404 ) ;
 | |
|    #endif
 | |
| 
 | |
|    c4 = relate->codeBase ;
 | |
| 
 | |
|    #ifdef E4PARM_HIGH
 | |
|       if ( relate->master != 0 )
 | |
|          return error4( c4, e4parm, E84402 ) ;
 | |
|    #endif
 | |
| 
 | |
|    if ( relate->relation->isInitialized == 0 )  /* need to initialize on server first */
 | |
|    {
 | |
|       rc = relate4clientInit( relate ) ;
 | |
|       if ( rc != 0 )
 | |
|          return rc ;
 | |
|    }
 | |
| 
 | |
|    connection = relate->data->dataFile->connection ;
 | |
|    #ifdef E4ANALYZE
 | |
|       if ( connection == 0 )
 | |
|          return error4( c4, e4struct, E94404 ) ;
 | |
|    #endif
 | |
| 
 | |
|    connection4assign( connection, CON4RELATE_DO, 0, 0 ) ;
 | |
|    info.relationId = relate->relation->relationId ;
 | |
|    info.relateId = relate->id ;
 | |
|    info.masterStartPos = d4recNo( relate->data ) ;
 | |
|    connection4addData( connection, &info, sizeof( CONNECTION4RELATE_DO_INFO_IN ), 0 ) ;
 | |
|    connection4send( connection ) ;
 | |
|    rc = connection4receive( connection ) ;
 | |
|    if ( rc < 0 )
 | |
|       return error4stack( c4, rc, E94404 ) ;
 | |
|    rc = connection4status( connection ) ;
 | |
|    if ( rc < 0 )
 | |
|       return connection4error( connection, c4, rc, E94404 ) ;
 | |
|    rc2 = relate4unpack( relate->relation, relate->data->dataFile->connection ) ;
 | |
|    if ( rc2 < 0 )
 | |
|       return error4stack( c4, rc, E94404 ) ;
 | |
|    return rc ;
 | |
| }
 | |
| 
 | |
| int S4FUNCTION relate4doOne( RELATE4 *relate )
 | |
| {
 | |
|    CONNECTION4RELATE_DO_ONE_INFO_IN info ;
 | |
|    CONNECTION4RELATE_DO_ONE_INFO_OUT *out ;
 | |
|    CONNECTION4 *connection ;
 | |
|    int rc, saveRc ;
 | |
|    CODE4 *c4 ;
 | |
| 
 | |
|    #ifdef E4PARM_HIGH
 | |
|       if ( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94405 ) ;
 | |
|       if ( relate->master == 0 )
 | |
|          return error4( relate->codeBase, e4parm, E84405 ) ;
 | |
|    #endif
 | |
| 
 | |
|    c4 = relate->codeBase ;
 | |
| 
 | |
|    if ( relate->relation->isInitialized == 0 )  /* need to initialize on server first */
 | |
|    {
 | |
|       rc = relate4clientInit( relate ) ;
 | |
|       if ( rc != 0 )
 | |
|          return rc ;
 | |
|    }
 | |
| 
 | |
|    connection = relate->data->dataFile->connection ;
 | |
|    #ifdef E4ANALYZE
 | |
|       if ( connection == 0 )
 | |
|          return error4( 0, e4struct, E94405 ) ;
 | |
|    #endif
 | |
| 
 | |
|    connection4assign( connection, CON4RELATE_DO_ONE, 0, 0 ) ;
 | |
|    info.relationId = relate->relation->relationId ;
 | |
|    info.relateId = relate->id ;
 | |
|    info.masterStartPos = d4recNo( relate->master->data ) ;
 | |
|    connection4addData( connection, &info, sizeof( CONNECTION4RELATE_DO_ONE_INFO_IN ), 0 ) ;
 | |
|    connection4send( connection ) ;
 | |
|    rc = connection4receive( connection ) ;
 | |
|    if ( rc < 0 )
 | |
|       return error4stack( c4, rc, E94405 ) ;
 | |
|    rc = connection4status( connection ) ;
 | |
|    if ( rc < 0 )
 | |
|       return connection4error( connection, c4, rc, E94405 ) ;
 | |
| 
 | |
|    saveRc = rc ;
 | |
|    if ( saveRc != r4terminate )
 | |
|    {
 | |
|       if ( saveRc == r4eof )   /* end of file */
 | |
|       {
 | |
|          saveRc = 0 ;
 | |
|          rc = d4goEof( relate->data ) ;
 | |
|          if ( rc < 0 )
 | |
|             return error4stack( c4, rc, E94405 ) ;
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|          if ( connection4len( connection ) != sizeof( CONNECTION4RELATE_DO_ONE_INFO_OUT ) )
 | |
|             return error4stack( c4, e4packetLen, E94405 ) ;
 | |
| 
 | |
|          out = (CONNECTION4RELATE_DO_ONE_INFO_OUT *)connection4data( connection ) ;
 | |
| 
 | |
|          rc = d4go( relate->data, out->recNo ) ;
 | |
|          if ( rc < 0 )
 | |
|             return error4stack( c4, rc, E94405 ) ;
 | |
|       }
 | |
|    }
 | |
|    return saveRc ;
 | |
| }
 | |
| #ifdef S4CB51
 | |
| static int relate4dbfInRelation( RELATE4 *relate, const DATA4 *dbf )
 | |
| {
 | |
|    RELATE4 *relateOn ;
 | |
| 
 | |
|    relateOn = &relate->relation->relate ;
 | |
|    while( relateOn->master )
 | |
|       relateOn = relateOn->master ;
 | |
| 
 | |
|    do
 | |
|    {
 | |
|       if ( relateOn->data == dbf )
 | |
|          return 1 ;
 | |
|    } while( relate4next( &relateOn ) != 2 ) ;
 | |
| 
 | |
|    return 0 ;
 | |
| }
 | |
| #endif /* S4CB51 */
 | |
| #else
 | |
| /* checks if the given dbf belongs to one of the relations in relation */
 | |
| #ifdef S4CB51
 | |
| static int relate4dbfInRelation( RELATE4 *relate, const DATA4 *dbf )
 | |
| {
 | |
|    RELATE4 *relateOn ;
 | |
| 
 | |
|    relateOn = &relate->relation->relate ;
 | |
|    while( relateOn->master )
 | |
|       relateOn = relateOn->master ;
 | |
| 
 | |
|    do
 | |
|    {
 | |
|       if ( relateOn->data == dbf )
 | |
|          return 1 ;
 | |
|    } while( relate4next( &relateOn ) != 2 ) ;
 | |
| 
 | |
|    return 0 ;
 | |
| }
 | |
| #endif /* S4CB51 */
 | |
| 
 | |
| int S4FUNCTION relate4doAll( RELATE4 *relate )
 | |
| {
 | |
|    int rc ;
 | |
|    CODE4 *c4 ;
 | |
|    #ifndef S4OFF_MULTI
 | |
|       int oldReadLock ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef S4VBASIC
 | |
|       if ( c4parm_check( relate, 5, E94405 ) )
 | |
|          return -1 ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef E4PARM_HIGH
 | |
|       if ( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94405 ) ;
 | |
|    #endif
 | |
| 
 | |
|    c4 = relate->codeBase ;
 | |
| 
 | |
|    #ifdef E4ANALYZE
 | |
|       if ( relate->master != 0 )
 | |
|          return error4( c4, e4struct, E84402 ) ;
 | |
|    #endif
 | |
| 
 | |
|    if ( error4code( c4 ) < 0 )
 | |
|       return e4codeBase ;
 | |
| 
 | |
|    #ifndef S4OFF_MULTI
 | |
|       oldReadLock = c4->readLock ;
 | |
|       c4->readLock = 0 ;
 | |
|    #endif
 | |
|    relate4setNotRead( relate ) ;
 | |
|    rc = relate4readRest( relate, 0 ) ;
 | |
|    #ifndef S4OFF_MULTI
 | |
|       c4->readLock = oldReadLock ;
 | |
|    #endif
 | |
| 
 | |
|    if ( rc == relate4filterRecord )  /* no match is an error */
 | |
|    {
 | |
|       #ifndef S4SERVER
 | |
|          if ( c4->errRelate )
 | |
|             return error4describe( c4, e4lookupErr, E94404, relate->data->alias, 0, 0 ) ;
 | |
|       #endif
 | |
|       return r4terminate ;
 | |
|    }
 | |
| 
 | |
|    return rc ;
 | |
| }
 | |
| 
 | |
| int S4FUNCTION relate4doOne( RELATE4 *relate )
 | |
| {
 | |
|    int rc ;
 | |
|    CODE4 *c4 ;
 | |
|    #ifndef S4OFF_MULTI
 | |
|       int oldReadLock ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef S4VBASIC
 | |
|       if ( c4parm_check( relate, 5, E94405 ) )
 | |
|          return -1 ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef E4PARM_HIGH
 | |
|       if ( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94405 ) ;
 | |
|    #endif
 | |
| 
 | |
|    c4 = relate->codeBase ;
 | |
| 
 | |
|    #ifdef E4PARM_HIGH
 | |
|       if ( relate->master == 0 )
 | |
|          return error4( c4, e4parm, E84405 ) ;
 | |
|    #endif
 | |
| 
 | |
|    if ( error4code( c4 ) < 0 )
 | |
|       return e4codeBase ;
 | |
| 
 | |
|    if ( relate->master == 0 )   /* no master, so we must be read */
 | |
|       return 0 ;
 | |
| 
 | |
|    #ifndef S4OFF_MULTI
 | |
|       oldReadLock = c4->readLock ;
 | |
|       c4->readLock = 0 ;
 | |
|    #endif
 | |
|    relate4setNotRead( relate ) ;
 | |
|    rc = relate4lookup( relate, 0 ) ;
 | |
|    #ifndef S4OFF_MULTI
 | |
|       c4->readLock = oldReadLock ;
 | |
|    #endif
 | |
| 
 | |
|    relate->isRead = relate->master->isRead ;  /* we are read if master is read */
 | |
|    if ( rc == relate4filterRecord )  /* no match is an error */
 | |
|    {
 | |
|       #ifndef S4SERVER
 | |
|          if ( c4->errRelate )
 | |
|             return error4describe( c4, e4info, E94405, relate->data->alias, 0, 0 ) ;
 | |
|       #endif
 | |
|       return r4terminate ;
 | |
|    }
 | |
|    return rc ;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| int S4FUNCTION relate4eof( RELATE4 *relate )
 | |
| {
 | |
|    #ifdef E4PARM_HIGH
 | |
|       if ( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94406 ) ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef E4MISC
 | |
|       if ( relate->relation->isInitialized == 0 )
 | |
|       {
 | |
|          error4( relate->codeBase, e4info, E84406 ) ;
 | |
|          return -1 ;
 | |
|       }
 | |
|    #endif
 | |
| 
 | |
|    #ifndef S4CLIENT
 | |
|       if ( relate->relation->inSort == relate4sortDone )
 | |
|          return relate->relation->sortEofFlag ;
 | |
|       else
 | |
|    #endif
 | |
|       return d4eof( relate->relation->relate.data ) ;
 | |
| }
 | |
| 
 | |
| int S4FUNCTION relate4errorAction( RELATE4 *relate, const int code )
 | |
| {
 | |
|    int rc ;
 | |
| 
 | |
|    #ifdef E4PARM_HIGH
 | |
|       if ( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94407 ) ;
 | |
|       if ( code != relate4blank && code != relate4skipRec && code != relate4terminate )
 | |
|          return error4( relate->codeBase, e4parm, E84407 ) ;
 | |
|    #endif
 | |
| 
 | |
|    rc = relate->errorAction ;
 | |
|    relate->errorAction = code ;
 | |
|    return rc ;
 | |
| }
 | |
| 
 | |
| int S4FUNCTION relate4freeRelate( RELATE4 *relate, const int closeFiles )
 | |
| {
 | |
|    int rc ;
 | |
|    RELATE4 *relateOn ;
 | |
|    CODE4 *c4 ;
 | |
|    #ifdef S4SERVER
 | |
|       LIST4 *oldList ;
 | |
|    #endif
 | |
| 
 | |
|    rc = 0 ;
 | |
|    if ( relate->master == 0 )
 | |
|       return relate4free( relate, closeFiles ) ;
 | |
| 
 | |
|    c4 = relate->codeBase ;
 | |
| 
 | |
|    relate4changed( relate ) ;
 | |
| 
 | |
|    if( closeFiles )
 | |
|    {
 | |
|       #ifdef S4SERVER
 | |
|          oldList = tran4dataList( code4trans( c4 ) ) ;
 | |
|          tran4dataListSet( code4trans( c4 ), &relate->relation->localDataList ) ;
 | |
|       #endif
 | |
|       if( d4close( relate->data ) < 0 )
 | |
|          rc = -1 ;
 | |
|       #ifdef S4SERVER
 | |
|          tran4dataListSet( code4trans( c4 ), oldList ) ;
 | |
|       #endif
 | |
|       relate->data = 0 ;
 | |
|    }
 | |
| 
 | |
|    for( ;; )
 | |
|    {
 | |
|       relateOn = (RELATE4 *)l4last( &relate->slaves) ;
 | |
|       if ( relateOn == 0 )
 | |
|          break ;
 | |
|       if( relate4freeRelate( relateOn, closeFiles ) < 0 )
 | |
|          rc = -1 ;
 | |
|    }
 | |
| 
 | |
|    expr4free( relate->masterExpr ) ;
 | |
|    u4free( relate->scanValue ) ;
 | |
|    relate->scanValue = 0 ;
 | |
|    #ifndef S4CLIENT
 | |
|       u4free( relate->set.flags ) ;
 | |
|       relate->set.flags = 0 ;
 | |
|    #endif
 | |
| 
 | |
|    l4remove( &relate->master->slaves, relate ) ;
 | |
|    mem4free( c4->relateMemory, relate ) ;
 | |
|    relate = 0 ;
 | |
| 
 | |
|    return rc ;
 | |
| }
 | |
| 
 | |
| int S4FUNCTION relate4free( RELATE4 *relate, const int closeFiles )
 | |
| {
 | |
|    int rc ;
 | |
|    RELATION4 *relation ;
 | |
|    RELATE4 *relateOn ;
 | |
|    CODE4 *c4 ;
 | |
|    #ifdef S4CLIENT
 | |
|       CONNECTION4RELATE_FREE_INFO_IN info ;
 | |
|       CONNECTION4 *connection ;
 | |
|    #endif
 | |
|    #ifdef S4SERVER
 | |
|       LIST4 *oldList ;
 | |
|    #endif
 | |
| 
 | |
|    if ( relate == 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    rc = 0 ;
 | |
| 
 | |
|    #ifdef S4VBASIC
 | |
|       if ( c4parm_check( relate, 5, E94408 ) )
 | |
|          return -1 ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef S4CB51
 | |
|       #ifndef S4SINGLE
 | |
|          relate4unlock( relate ) ;
 | |
|       #endif
 | |
|    #endif
 | |
| 
 | |
|    relate4changed( relate ) ;
 | |
|    relation = relate->relation ;
 | |
|    relate = &relation->relate ;
 | |
| 
 | |
|    c4 = relate->codeBase ;
 | |
| 
 | |
|    #ifdef S4CLIENT
 | |
|       #ifdef E4ANALYZE
 | |
|          if ( relate->data == 0 )
 | |
|             return error4( c4, e4struct, E94408 ) ;
 | |
|          if ( relate->data->dataFile == 0 )
 | |
|             return error4( c4, e4struct, E94408 ) ;
 | |
|       #endif
 | |
| 
 | |
|       if ( relation->relationId != 0 )   /* if not freed on the server */
 | |
|       {
 | |
|          connection = relate->data->dataFile->connection ;
 | |
|          #ifdef E4ANALYZE
 | |
|             if ( connection == 0 )
 | |
|                return error4( c4, e4struct, E94408 ) ;
 | |
|          #endif
 | |
|          connection4assign( connection, CON4RELATE_FREE, 0, 0 ) ;
 | |
|          info.relationId = relation->relationId ;
 | |
|          connection4addData( connection, &info, sizeof( CONNECTION4RELATE_FREE_INFO_IN ), 0 ) ;
 | |
|          connection4send( connection ) ;
 | |
|          rc = connection4receive( connection ) ;
 | |
|          if ( rc < 0 )
 | |
|             return error4stack( c4, rc, E94408 ) ;
 | |
|          rc = connection4status( connection ) ;
 | |
|          if ( rc != 0 )
 | |
|             return connection4error( connection, c4, rc, E94408 ) ;
 | |
|       }
 | |
|    #endif
 | |
|    #ifdef S4SERVER
 | |
|       if( closeFiles || relate->freeData == 1 )
 | |
|    #else
 | |
|       if( closeFiles )
 | |
|    #endif
 | |
|    {
 | |
|       #ifdef S4SERVER
 | |
|          oldList = tran4dataList( code4trans( c4 ) ) ;
 | |
|          tran4dataListSet( code4trans( c4 ), &relation->localDataList ) ;
 | |
|       #endif
 | |
|       if( d4close( relate->data ) < 0 )
 | |
|          rc = -1 ;
 | |
|       #ifdef S4SERVER
 | |
|          tran4dataListSet( code4trans( c4 ), oldList ) ;
 | |
|       #endif
 | |
|       relate->data = 0 ;
 | |
|    }
 | |
| 
 | |
|    for( relateOn = 0 ;; )
 | |
|    {
 | |
|       relateOn = (RELATE4 *)l4last( &relate->slaves ) ;
 | |
|       if ( relateOn == 0 )
 | |
|          break ;
 | |
|       if( relate4freeRelate( relateOn, closeFiles ) < 0 )
 | |
|          rc = -1 ;
 | |
|    }
 | |
| 
 | |
| /*   mem4release( relation->relateMemory ) ;                      */
 | |
| /*   mem4release( relation->relateListMemory ) ;     */
 | |
| /*   mem4release( relation->relateDataListMemory ) ; */
 | |
| 
 | |
|    relate4sortFree( relation, 1 ) ;
 | |
|    u4free( relation->exprSource ) ;
 | |
| 
 | |
|    mem4free( c4->relationMemory, relation ) ;
 | |
| 
 | |
|    return rc ;
 | |
| }
 | |
| 
 | |
| #ifdef S4SERVER
 | |
| static DATA4 *relate4dataOpen( RELATE4 *relate, DATA4 *oldData )
 | |
| {
 | |
|    DATA4 *d4 ;
 | |
|    int oldAccessMode, oldReadOnly ;
 | |
|    CODE4 *c4 ;
 | |
| 
 | |
|    #ifdef E4PARM_LOW
 | |
|       if ( relate == 0 || oldData == 0 )
 | |
|       {
 | |
|          error4( 0, e4parm_null, E94427 ) ;
 | |
|          return 0 ;
 | |
|       }
 | |
|    #endif
 | |
| 
 | |
|    c4 = relate->codeBase ;
 | |
| 
 | |
|    relate->dataOld = oldData ;
 | |
| 
 | |
|    oldAccessMode = c4->accessMode ;
 | |
|    c4->accessMode = OPEN4DENY_NONE ;
 | |
|    oldReadOnly = c4->readOnly ;
 | |
|    c4->readOnly = 1 ;
 | |
|    d4 = d4openClone( oldData ) ;
 | |
|    if ( d4 == 0 )
 | |
|    {
 | |
|       error4( c4, e4info, E94409 ) ;
 | |
|       return 0 ;
 | |
|    }
 | |
|    #ifdef S4SERVER
 | |
|       d4->trans = &c4->currentClient->trans ;
 | |
| /*      l4add( tran4dataList( &c4->currentClient->trans ), d4 ) ;*/
 | |
|    #endif
 | |
|    c4->accessMode = oldAccessMode ;
 | |
|    c4->readOnly = oldReadOnly ;
 | |
| 
 | |
|    return d4 ;
 | |
| }
 | |
| #endif /* S4SERVER */
 | |
| 
 | |
| RELATE4 *S4FUNCTION relate4init( DATA4 *master )
 | |
| {
 | |
|    RELATION4 *relation ;
 | |
|    CODE4 *c4 ;
 | |
|    int rc ;
 | |
| 
 | |
|    #ifdef S4VBASIC
 | |
|       if ( c4parm_check( master, 5, E94410 ) )
 | |
|          return (RELATE4 *) 0 ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef E4PARM_HIGH
 | |
|       if ( master == 0 )
 | |
|       {
 | |
|          error4( 0, e4parm_null, E94410 ) ;
 | |
|          return 0 ;
 | |
|       }
 | |
|    #endif
 | |
| 
 | |
|    c4 = master->codeBase ;
 | |
|    if ( error4code( c4 ) < 0 )
 | |
|       return 0 ;
 | |
| 
 | |
|    if ( c4->relationMemory == 0 )
 | |
|    {
 | |
|       c4->relationMemory = mem4create( c4, 5, sizeof( RELATION4 ), 5, 0 ) ;
 | |
|       if ( c4->relationMemory == 0 )
 | |
|          return 0 ;
 | |
|    }
 | |
|    relation = (RELATION4 *)mem4alloc( c4->relationMemory ) ;
 | |
|    if ( relation == 0 )
 | |
|       return 0 ;
 | |
| 
 | |
|    #ifndef S4CLIENT
 | |
|       relation->log.relation = relation ;
 | |
|       relation->log.codeBase = c4 ;
 | |
|       relation->sort.file.hand = -1 ;
 | |
|       relation->sortedFile.hand = -1 ;
 | |
|    #endif
 | |
| 
 | |
|    rc = relate4initRelate( &relation->relate, relation, master, c4, 1 ) ;
 | |
|    if ( rc < 0 )
 | |
|    {
 | |
|       mem4free( c4->relationMemory, relation ) ;
 | |
|       return 0 ;
 | |
|    }
 | |
| 
 | |
|    #ifdef S4SERVER
 | |
|       relation->relate.freeData = 1 ;
 | |
|    #endif
 | |
| 
 | |
|    return &relation->relate ;
 | |
| }
 | |
| 
 | |
| #ifdef P4ARGS_USED
 | |
|    #pragma argsused
 | |
| #endif
 | |
| static int relate4initRelate( RELATE4 *relate, RELATION4 *relation, DATA4 *data, CODE4 *c4, int doOpen )
 | |
| {
 | |
|    #ifdef S4SERVER
 | |
|       LIST4 *oldList ;
 | |
|    #endif
 | |
| 
 | |
|    relate->codeBase = c4 ;
 | |
|    relate->relationType = relate4exact ;
 | |
|    #ifdef E4ANALYZE
 | |
|       if ( relate->data != 0 )
 | |
|          error4( relate->codeBase, e4info, E84406 ) ;
 | |
|    #endif
 | |
| 
 | |
|    #ifndef S4SERVER
 | |
|       relate->data = data ;
 | |
|    #else
 | |
|       if ( doOpen == 0 )
 | |
|          relate->data = data ;
 | |
|       else
 | |
|       {
 | |
|          oldList = tran4dataList( code4trans( c4 ) ) ;
 | |
|          tran4dataListSet( code4trans( c4 ), &relation->localDataList ) ;
 | |
|          relate->data = relate4dataOpen( relate, data ) ;
 | |
|          tran4dataListSet( code4trans( c4 ), oldList ) ;
 | |
|       }
 | |
|    #endif
 | |
|    relate->errorAction = relate4blank ;
 | |
|    relate->relation = relation ;
 | |
|    if ( relate->data == 0 )
 | |
|       return error4( c4, e4info, E94410 ) ;
 | |
| 
 | |
|    relate->data->count = d4recCount( relate->data ) ;
 | |
| 
 | |
|    return 0 ;
 | |
| }
 | |
| 
 | |
| #ifdef P4ARGS_USED
 | |
|    #pragma argsused
 | |
| #endif
 | |
| int S4FUNCTION relate4lockAdd( RELATE4 *relate )
 | |
| {
 | |
|    #ifdef S4OFF_MULTI
 | |
|       return 0 ;
 | |
|    #else
 | |
|       int rc ;
 | |
|       RELATE4 *relateOn ;
 | |
| 
 | |
|       #ifdef E4PARM_HIGH
 | |
|          if ( relate == 0 )
 | |
|             return error4( 0, e4parm_null, E94411 ) ;
 | |
|       #endif
 | |
| 
 | |
|       #ifdef S4VBASIC
 | |
|          if ( c4parm_check( relate, 5, E94411 ) )
 | |
|             return -1 ;
 | |
|       #endif
 | |
| 
 | |
|       if ( error4code( relate->codeBase ) < 0 )
 | |
|          return e4codeBase ;
 | |
| 
 | |
|       rc = 0 ;
 | |
|       for( relateOn = &relate->relation->relate ;; )
 | |
|       {
 | |
|          if ( relateOn == 0 )
 | |
|             break ;
 | |
|          rc = d4lockAddFile( relateOn->data ) ;
 | |
|          if ( rc != 0 )
 | |
|             break ;
 | |
|          if ( relate4next( &relateOn ) == 2 )
 | |
|             break ;
 | |
|       }
 | |
| 
 | |
|       return rc ;
 | |
|    #endif
 | |
| }
 | |
| 
 | |
| #ifdef S4CB51
 | |
| 
 | |
| /* new form does not allow combining lockAdds spaced around relate4locks()
 | |
|    if required to do that, S4OLD_RELATE_LOCK code will allow */
 | |
| int S4FUNCTION relate4lock( RELATE4 *relate )
 | |
| {
 | |
|    int rc ;
 | |
| 
 | |
|    rc = relate4lockAdd( relate ) ;
 | |
|    if ( rc < 0 )
 | |
|       return rc ;
 | |
|    rc = code4lock( relate->codeBase ) ;
 | |
|    if ( rc == 0 )
 | |
|       relate->relation->locked = 1 ;
 | |
|    return rc ;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef S4CLIENT
 | |
| #ifdef S4CB51
 | |
| #ifdef S4OLD_RELATE_LOCK
 | |
| int S4FUNCTION relate4lock( RELATE4 *relate )
 | |
| {
 | |
|    CONNECTION4 *connection ;
 | |
|    CONNECTION4RELATE_LOCK_INFO_IN info ;
 | |
|    CONNECTION4RELATE_LOCK_INFO_OUT *out ;
 | |
|    CONNECTION4RELATE_LOCK_SUB_DATA *subData ;
 | |
|    int rc, count ;
 | |
|    CODE4 *c4 ;
 | |
|    DATA4 *data ;
 | |
| 
 | |
|    #ifdef E4PARM_HIGH
 | |
|       if ( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94411 ) ;
 | |
|    #endif
 | |
| 
 | |
|    c4 = relate->codeBase ;
 | |
| 
 | |
|    /* must ensure that client relation is registered before requesting a lock */
 | |
|    if ( relate->relation->isInitialized == 0 || relate->dataTag != relate->data->tagSelected )
 | |
|    {
 | |
|       relate->dataTag = relate->data->tagSelected ;
 | |
|       rc = relate4clientInit( relate ) ;
 | |
|       if ( rc != 0 )
 | |
|          return rc ;
 | |
|    }
 | |
| 
 | |
|    connection = relate->data->dataFile->connection ;
 | |
|    #ifdef E4ANALYZE
 | |
|       if ( connection == 0 )
 | |
|       {
 | |
|          error4( c4, e4struct, E94411 ) ;
 | |
|          return 0 ;
 | |
|       }
 | |
|    #endif
 | |
| 
 | |
|    connection4assign( connection, CON4RELATE_LOCK, 0, 0 ) ;
 | |
|    info.relationId = relate->relation->relationId ;
 | |
|    connection4addData( connection, &info, sizeof( CONNECTION4RELATE_LOCK_INFO_IN ), 0 ) ;
 | |
|    rc = connection4repeat( connection, -2, -1, -1, 0 ) ;
 | |
|    if ( rc == r4locked )
 | |
|       return r4locked ;
 | |
|    if ( rc < 0 )
 | |
|       return connection4error( connection, c4, rc, E94411 ) ;
 | |
| 
 | |
|    if ( rc == 0 )  /* add locks */
 | |
|    {
 | |
|       if ( connection4len( connection ) < sizeof(CONNECTION4RELATE_LOCK_INFO_OUT) )
 | |
|          return error4( c4, e4packetLen, E94411 ) ;
 | |
|       out = (CONNECTION4RELATE_LOCK_INFO_OUT *)connection4data( connection ) ;
 | |
|       if ( connection4len( connection ) != (long)sizeof(CONNECTION4RELATE_LOCK_INFO_OUT) + out->count * sizeof( CONNECTION4RELATE_LOCK_SUB_DATA ) )
 | |
|          return error4( c4, e4packetLen, E94411 ) ;
 | |
|       subData = (CONNECTION4RELATE_LOCK_SUB_DATA *)( (char *)connection4data( connection ) + sizeof( CONNECTION4RELATE_LOCK_INFO_OUT ) ) ;
 | |
|       for( count = out->count ; count > 0 ; count-- )
 | |
|       {
 | |
|          data = tran4data( code4trans( c4 ), subData->serverId, subData->clientId ) ;
 | |
|          if ( data == 0 )
 | |
|             return error4( c4, e4info, E94411 ) ;
 | |
|          if ( data->dataFile->fileLock != 0 )
 | |
|          {
 | |
|             #ifdef E4MISC
 | |
|                if ( data->dataFile->fileLock != data )
 | |
|                   return error4( c4, e4info, E94411 ) ;
 | |
|             #endif
 | |
|          }
 | |
|          else
 | |
|             data->dataFile->fileLock = data ;
 | |
|          subData++ ;
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    return rc ;
 | |
| }
 | |
| #endif  /* S4OLD_RELATE_LOCK */
 | |
| #endif  /* S4CB51 */
 | |
| #else
 | |
| 
 | |
| #ifdef S4OLD_RELATE_LOCK
 | |
| int S4FUNCTION relate4lock( RELATE4 *relate )
 | |
| {
 | |
|    #ifdef S4SINGLE
 | |
|       return 0 ;
 | |
|    #else
 | |
|       CODE4 *c4 ;
 | |
|       int rc, oldAttempts, count ;
 | |
|       RELATE4 *relateOn ;
 | |
|       #ifdef S4SERVER
 | |
|          RELATE4 *relateSkip ;
 | |
|          DATA4 *dataTemp ;
 | |
|       #endif
 | |
| 
 | |
|       #ifdef E4PARM_HIGH
 | |
|          if ( relate == 0 )
 | |
|             return error4( 0, e4parm_null, E94411 ) ;
 | |
|       #endif
 | |
| 
 | |
|       #ifdef S4VBASIC
 | |
|          if ( c4parm_check( relate, 5, E94411 ) )
 | |
|             return -1 ;
 | |
|       #endif
 | |
| 
 | |
|       c4 = relate->codeBase ;
 | |
|       if ( error4code( c4 ) < 0 )
 | |
|          return e4codeBase ;
 | |
| 
 | |
|       relate->relation->locked = 1 ;
 | |
| 
 | |
|       count = oldAttempts = c4->lockAttempts ;  /* take care of wait here */
 | |
|       c4->lockAttempts = 1 ;
 | |
| 
 | |
|       for( ;; )
 | |
|       {
 | |
|          rc = 0 ;
 | |
|          for( relateOn = &relate->relation->relate ;; )
 | |
|          {
 | |
|             if ( relateOn == 0 )
 | |
|                break ;
 | |
|             #ifdef S4SERVER
 | |
|                if ( relateOn->dataOld->dataFile->fileServerLock == data4serverId( relateOn->dataOld ) )  /* check for duplicate lock */
 | |
|                {
 | |
|                   dataTemp = tran4data( code4trans( c4 ), relateOn->dataOld->dataFile->fileServerLock, relateOn->dataOld->dataFile->fileClientLock ) ;
 | |
|                   if ( dataTemp == 0 )
 | |
|                      rc = r4locked ;
 | |
|                   else
 | |
|                   {
 | |
|                      /* check for data in relation via relate search */
 | |
|                      for ( relateSkip = &relate->relation->relate ;; )
 | |
|                      {
 | |
|                         if ( relateSkip == 0 )
 | |
|                         {
 | |
|                            rc = r4locked ;
 | |
|                            break ;
 | |
|                         }
 | |
|                         if ( relateSkip->dataOld == dataTemp )
 | |
|                            break ;
 | |
|                         if ( relate4next( &relateSkip ) == 2 )
 | |
|                            break ;
 | |
|                      }
 | |
|                   }
 | |
|                }
 | |
|                else
 | |
|                   rc = dfile4lockAll( relateOn->dataOld->dataFile, data4clientId( relateOn->dataOld ), data4serverId( relateOn->dataOld ), relateOn->dataOld ) ;
 | |
|             #else
 | |
|                rc = d4lockAll( relateOn->data ) ;
 | |
|             #endif
 | |
|             if ( rc != 0 )
 | |
|                break ;
 | |
|             if ( relate4next( &relateOn ) == 2 )
 | |
|                break ;
 | |
|          }
 | |
| 
 | |
|          if ( rc != r4locked )
 | |
|             break ;
 | |
| 
 | |
|          relate4unlock( relate ) ;
 | |
|          if ( count == 0 )
 | |
|             break ;
 | |
| 
 | |
|          if ( count > 0 )
 | |
|             count-- ;
 | |
| 
 | |
|          #ifdef S4TEMP
 | |
|             if ( d4displayQuit( &display ) )
 | |
|             {
 | |
|                rc = error4( c4, e4result, E84409 ) ;
 | |
|                break ;
 | |
|             }
 | |
|          #endif
 | |
| 
 | |
|          u4delayHundredth( c4->lockDelay ) ;   /* wait a second & try lock again */
 | |
|       }
 | |
| 
 | |
|       c4->lockAttempts = oldAttempts ;
 | |
|       if ( error4code( c4 ) < 0 )
 | |
|          return error4code( c4 ) ;
 | |
| 
 | |
|       return rc ;
 | |
|    #endif
 | |
| }
 | |
| #endif  /* S4OLD_RELATE_LOCK */
 | |
| 
 | |
| /* direction : -1 = look backwards, 0 = lookup only, 1 = look forwards */
 | |
| static int relate4lookup( RELATE4 *relate, const char direction )
 | |
| {
 | |
|    int rc ;
 | |
|    long recno ;
 | |
|    CODE4 *c4 ;
 | |
|    RELATION4 *relation ;
 | |
|    #ifndef S4SERVER
 | |
|       int oldGoError ;
 | |
|    #endif
 | |
|    #ifndef S4INDEX_OFF
 | |
|       char *ptr ;
 | |
|       int len ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef E4PARM_LOW
 | |
|       if ( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94412 ) ;
 | |
|       if ( direction < -1 || direction > 1 )
 | |
|          return error4( 0, e4parm, E94417 ) ;
 | |
|    #endif
 | |
| 
 | |
|    c4 = relate->codeBase ;
 | |
|    if ( error4code( c4 ) < 0 )
 | |
|       return e4codeBase ;
 | |
|    relation = relate->relation ;
 | |
| 
 | |
|    #ifdef E4MISC
 | |
|       if ( direction != 0 && relation->isInitialized == 0 )
 | |
|          return error4( c4, e4struct, E84406 ) ;
 | |
|    #endif
 | |
| 
 | |
|    relate->isRead = 1 ;
 | |
|    if ( relate->master == 0 )
 | |
|       return 0 ;
 | |
| 
 | |
|    #ifndef S4INDEX_OFF
 | |
|       d4tagSelect( relate->data, relate->dataTag ) ;
 | |
| 
 | |
|       if ( relate->dataTag == 0 )
 | |
|       {
 | |
|    #endif
 | |
|       recno = (long)expr4double( relate->masterExpr ) ;
 | |
|       if ( error4code( c4 ) < 0 )
 | |
|          return -1 ;
 | |
| 
 | |
|       if ( direction != 0 )
 | |
|          if ( f4flagIsSetFlip( &relate->set, (unsigned long)recno ) == 0 )
 | |
|             return relate4filterRecord ;
 | |
| 
 | |
|       #ifndef S4SERVER
 | |
|          oldGoError = c4->errGo ;
 | |
|          c4->errGo = 0 ;
 | |
|       #endif
 | |
|       rc = d4go( relate->data, recno ) ;
 | |
|       #ifndef S4SERVER
 | |
|          c4->errGo = oldGoError ;
 | |
|          if ( rc < 0 )
 | |
|             return error4stack( c4, rc, E94412 ) ;
 | |
|       #endif
 | |
|       if ( rc != r4entry )  /* if not error, then return */
 | |
|          return 0 ;
 | |
|       if ( relate->relationType == relate4approx )
 | |
|       {
 | |
|          d4goEof( relate->data ) ;
 | |
|          return 0 ;
 | |
|       }
 | |
|    #ifndef S4INDEX_OFF
 | |
|    }
 | |
|    else
 | |
|    {
 | |
|       len = expr4key( relate->masterExpr, &ptr ) ;
 | |
|       if ( len < 0 )
 | |
|          return -1 ;
 | |
| 
 | |
|       len = (len < relate->matchLen) ? len : relate->matchLen ;   /* min of len and match len */
 | |
| 
 | |
|       if ( relate->relationType == relate4scan )
 | |
|       {
 | |
|          #ifdef E4ANALYZE
 | |
|             if ( relate->master == 0 )
 | |
|                return error4( c4, e4struct, E84410 ) ;
 | |
|          #endif
 | |
|          if ( relate->master->scanValue == 0 )
 | |
|          {
 | |
|             relate->master->scanValueLen = len ;
 | |
|             relate->master->scanValue = (char *)u4allocEr( c4, (long)len ) ;
 | |
|             if ( relate->master->scanValue == 0 )
 | |
|                return -1 ;
 | |
|          }
 | |
|          memcpy( relate->master->scanValue, ptr, (unsigned int)len ) ;
 | |
|       }
 | |
| 
 | |
|       rc = tfile4seek( relate->dataTag->tagFile, ptr, len ) ;
 | |
|       if ( rc < 0 )
 | |
|          return -1 ;
 | |
|       if ( relate->relationType == relate4approx || rc == 0 )
 | |
|       {
 | |
|          if ( tfile4eof( relate->dataTag->tagFile) )
 | |
|          {
 | |
|             d4goEof( relate->data ) ;
 | |
|             return 0 ;
 | |
|          }
 | |
| 
 | |
|          if ( (int)direction < 0 && rc == 0 && relate->relationType == relate4scan )  /* look for last one */
 | |
|             for( ;; )
 | |
|             {
 | |
|                #ifdef S4HAS_DESCENDING
 | |
|                   if ( !tfile4dskip( relate->dataTag->tagFile, 1L ) )
 | |
|                #else
 | |
|                   if ( !tfile4skip( relate->dataTag->tagFile, 1L ) )
 | |
|                #endif
 | |
|                   break ;
 | |
|                #ifdef S4FOX
 | |
|                if ( u4keycmp( tfile4keyData( relate->dataTag->tagFile )->value, ptr, (unsigned int)len, (unsigned int)relate->dataTag->tagFile->header.keyLen, 0, &relate->dataTag->tagFile->vfpInfo ) )
 | |
|                #else
 | |
|                if ( u4memcmp( tfile4keyData( relate->dataTag->tagFile )->value, ptr, (unsigned int)len ) )
 | |
|                #endif
 | |
|                {
 | |
|                   #ifdef S4HAS_DESCENDING
 | |
|                      tfile4dskip( relate->dataTag->tagFile, -1L ) ;
 | |
|                   #else
 | |
|                      tfile4skip( relate->dataTag->tagFile, -1L ) ;
 | |
|                   #endif
 | |
|                   break ;
 | |
|                }
 | |
|             }
 | |
| 
 | |
|          recno = tfile4keyData( relate->dataTag->tagFile )->num ;
 | |
|          if ( direction != 0 )
 | |
|             if ( f4flagIsSetFlip( &relate->set, (unsigned long)recno ) == 0 )
 | |
|                return relate4filterRecord ;
 | |
|          if ( d4go( relate->data, recno ) < 0 )
 | |
|             return -1 ;
 | |
|          return 0 ;
 | |
|       }
 | |
|       else
 | |
|          recno = tfile4keyData( relate->dataTag->tagFile )->num ;
 | |
|    }
 | |
|    #endif
 | |
| 
 | |
|    switch( relate->errorAction )  /* if got here, must be error condition */
 | |
|    {
 | |
|       case relate4blank:
 | |
|          if ( d4goEof( relate->data ) < 0 )
 | |
|             return -1 ;
 | |
|          if ( direction != 0 )
 | |
|             if ( f4flagIsSetFlip( &relate->set, (unsigned long)recno ) == 0 )
 | |
|                return relate4filterRecord ;
 | |
|          return 0 ;
 | |
|       case relate4skipRec:
 | |
|          /* if a scan, and a failure, then, if the current relation set is
 | |
|             below the current position, move it to above the current position */
 | |
|          if ( relate->relationType == relate4scan )
 | |
|          {
 | |
|             if ( relate != &relation->relate )
 | |
|                while ( relate4currentIsChild( relate ) )
 | |
|                   relate4nextRelationList( relation, 0 ) ;
 | |
|          }
 | |
|          return relate4filterRecord ;
 | |
|       case relate4terminate:
 | |
|          #ifndef S4SERVER
 | |
|             if ( c4->errRelate )
 | |
|                return error4describe( c4, e4lookupErr, E94412, relate->data->alias, 0, 0 ) ;
 | |
|          #endif
 | |
|          return r4terminate ;
 | |
|       default:  /* should never get this far */
 | |
|          return error4( c4, e4info, E84411 ) ;
 | |
|    }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| RELATE4 *relate4lookupRelate( RELATE4 *relate, const DATA4 *d4 )
 | |
| {
 | |
|    RELATE4 *relateReturn, *relateOn ;
 | |
| 
 | |
|    if ( relate->data == d4 )
 | |
|       return relate ;
 | |
|    for( relateOn = 0 ;; )
 | |
|    {
 | |
|       relateOn = (RELATE4 *)l4next( &relate->slaves, relateOn) ;
 | |
|       if ( relateOn == 0 )
 | |
|          return 0 ;
 | |
|       relateReturn = relate4lookupRelate( relateOn, d4 ) ;
 | |
|       if ( relateReturn )
 | |
|          return relateReturn ;
 | |
|    }
 | |
| }
 | |
| 
 | |
| int S4FUNCTION relate4matchLen( RELATE4 *relate, const int matchLenIn )
 | |
| {
 | |
|    int len, matchLen ;
 | |
| 
 | |
|    #ifdef E4PARM_HIGH
 | |
|       if ( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94413 ) ;
 | |
|    #endif
 | |
| 
 | |
|    if ( error4code( relate->codeBase ) < 0 )
 | |
|       return e4codeBase ;
 | |
| 
 | |
|    matchLen = matchLenIn ;
 | |
|    len = expr4keyLen( relate->masterExpr ) ;
 | |
| 
 | |
|    #ifdef S4CLIPPER
 | |
|       if ( matchLen <= 0 )
 | |
|          matchLen = len ;
 | |
|    #else
 | |
|       if ( matchLen <= 0 )
 | |
|       {
 | |
|          relate->matchLen = len ;
 | |
|          return len ;
 | |
|       }
 | |
|    #endif
 | |
| 
 | |
|    #ifndef S4CLIENT
 | |
|       #ifndef S4INDEX_OFF
 | |
|          #ifndef S4CLIPPER
 | |
|             #ifdef E4MISC
 | |
|                if ( relate->dataTag )
 | |
|                   if( expr4type(  relate->dataTag->tagFile->expr ) != r4str )  /* make sure r4str only */
 | |
|                      return error4( relate->codeBase, e4relate, E84412 ) ;
 | |
|             #endif
 | |
|          #endif
 | |
|       #endif
 | |
|    #endif
 | |
| 
 | |
|    if ( matchLen >= len )
 | |
|       matchLen = len ;
 | |
| 
 | |
|    #ifndef S4CLIENT
 | |
|    #ifndef S4INDEX_OFF
 | |
|       if ( relate->dataTag )
 | |
|       {
 | |
|          len = expr4keyLen( relate->dataTag->tagFile->expr ) ;
 | |
|          if ( matchLen >= len )
 | |
|             matchLen = len ;
 | |
|       }
 | |
|    #endif
 | |
|    #endif
 | |
| 
 | |
|    relate->matchLen = matchLen ;
 | |
|    relate4changed( relate ) ;
 | |
|    return matchLen ;
 | |
| }
 | |
| 
 | |
| /* r4same = 0, r4down = 1, r4complete = 2 */
 | |
| int S4FUNCTION relate4next( RELATE4 **ptrPtr )
 | |
| {
 | |
|    RELATE4 *cur ;
 | |
|    void *nextLink ;
 | |
|    int rc ;
 | |
| 
 | |
|    #ifdef E4PARM_HIGH
 | |
|       if ( ptrPtr == 0 )
 | |
|          return error4( 0, e4parm_null, E94414 ) ;
 | |
|       if ( *ptrPtr == 0 )
 | |
|          return error4( 0, e4parm_null, E94414 ) ;
 | |
|    #endif
 | |
| 
 | |
|    cur = *ptrPtr ;
 | |
|    rc = r4down ;
 | |
| 
 | |
|    if ( cur->slaves.nLink > 0 )
 | |
|    {
 | |
|       *ptrPtr = (RELATE4 *)l4first( &cur->slaves ) ;
 | |
|       return r4down ;
 | |
|    }
 | |
| 
 | |
|    for(;;)
 | |
|    {
 | |
|       rc -- ;
 | |
|       if ( cur->master == 0 )
 | |
|       {
 | |
|          *ptrPtr = 0 ;
 | |
|          return r4complete ;
 | |
|       }
 | |
| 
 | |
|       nextLink = l4next( &cur->master->slaves, cur ) ;
 | |
|       if ( nextLink )
 | |
|       {
 | |
|          *ptrPtr = (RELATE4 *)nextLink ;
 | |
|          return rc ;
 | |
|       }
 | |
| 
 | |
|       cur = cur->master ;
 | |
|    }
 | |
| }
 | |
| 
 | |
| #ifndef S4CLIENT
 | |
| static int relate4nextRecordInScan( RELATE4 *relate )
 | |
| {
 | |
|    long nextRec ;
 | |
|    int rc ;
 | |
|    DATA4 *d4 ;
 | |
|    #ifndef S4SERVER
 | |
|       int  saveCode ;
 | |
|    #endif
 | |
|    #ifndef S4INDEX_OFF
 | |
|       B4KEY_DATA *key ;
 | |
|    #endif
 | |
|    #ifndef S4INDEX_OFF
 | |
|       char *ptr ;
 | |
|       int len ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef E4PARM_LOW
 | |
|       if ( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94415 ) ;
 | |
|    #endif
 | |
| 
 | |
|    if ( error4code( relate->codeBase ) < 0 )
 | |
|       return e4codeBase ;
 | |
| 
 | |
|    #ifdef E4MISC
 | |
|       if ( relate->relation->isInitialized == 0 )
 | |
|          return error4( relate->codeBase, e4info, E84406 ) ;
 | |
|    #endif
 | |
| 
 | |
|    if ( relate->relation->inSort == relate4sortSkip && relate->sortType == relate4sortSkip )
 | |
|       return r4eof ;
 | |
| 
 | |
|    d4 = relate->data ;
 | |
|    #ifndef S4INDEX_OFF
 | |
|       if ( relate->dataTag == 0 )
 | |
|       {
 | |
|    #endif
 | |
|          if ( d4bof( d4 ) )
 | |
|             nextRec = 1 ;
 | |
|          else
 | |
|             nextRec = d4recNo( d4 ) + 1 ;
 | |
|          nextRec += f4flagGetNextFlip( &relate->set, (unsigned long)nextRec, (char)1 ) ;
 | |
|          if ( d4recCountLessEq( d4, nextRec ) == 0 )
 | |
|             return r4eof ;
 | |
|    #ifndef S4INDEX_OFF
 | |
|       }
 | |
|       else
 | |
|          for(;;)
 | |
|          {
 | |
|             if ( d4bof( d4 ) )
 | |
|             {
 | |
|                if ( d4recCountLessEq( d4, 1L ) == 0L )  /* count == 0 */
 | |
|                   return r4eof ;
 | |
|                if ( relate->masterExpr == 0 )   /* top relate, bof */
 | |
|                   return r4bof ;
 | |
|                len = expr4key( relate->masterExpr, &ptr ) ;
 | |
|                if ( len < 0 )
 | |
|                   return -1 ;
 | |
| 
 | |
|                len = (len < relate->matchLen) ? len : relate->matchLen ;   /* min of len and match len */
 | |
| 
 | |
|                rc = (int)tfile4seek( relate->dataTag->tagFile, relate->master->scanValue, len ) ;
 | |
|                if ( rc < 0 )
 | |
|                   return -1 ;
 | |
|                if ( rc == 0 )
 | |
|                   rc = 1 ;
 | |
|                else
 | |
|                   rc = 0 ;
 | |
|             }
 | |
|             else
 | |
|                #ifdef S4HAS_DESCENDING
 | |
|                   rc = (int)tfile4dskip( relate->dataTag->tagFile, 1L ) ;
 | |
|                #else
 | |
|                   rc = (int)tfile4skip( relate->dataTag->tagFile, 1L ) ;
 | |
|                #endif
 | |
|             if ( rc < 0 )
 | |
|                return -1 ;
 | |
|             if ( rc != 1 )
 | |
|                return r4eof ;
 | |
| 
 | |
|             key = tfile4keyData( relate->dataTag->tagFile) ;
 | |
|             nextRec = key->num ;
 | |
| 
 | |
|             if ( relate->master )
 | |
|             {
 | |
|                #ifdef S4FOX
 | |
|                   if ( u4keycmp( key->value, relate->master->scanValue, (unsigned int)relate->master->scanValueLen,
 | |
|                        (unsigned int)relate->dataTag->tagFile->header.keyLen, 0, &relate->dataTag->tagFile->vfpInfo ) )
 | |
|                #else
 | |
|                   if ( u4memcmp( key->value, relate->master->scanValue, (unsigned int)relate->master->scanValueLen ) )
 | |
|                #endif
 | |
|                   return r4eof ;
 | |
|             }
 | |
| 
 | |
|             if ( f4flagIsSetFlip( &relate->set, (unsigned long)nextRec ) )
 | |
|                break ;
 | |
|          }
 | |
|    #endif
 | |
| 
 | |
|    #ifndef S4SERVER
 | |
|       saveCode = relate->codeBase->errGo ;
 | |
|       relate->codeBase->errGo = 0 ;
 | |
|    #endif
 | |
|    rc = d4go( d4, nextRec ) ;
 | |
|    #ifndef S4SERVER
 | |
|       relate->codeBase->errGo = saveCode ;
 | |
|    #endif
 | |
|    if ( rc < 0 )
 | |
|       return -1 ;
 | |
|    if ( rc == r4entry )
 | |
|       return r4eof ;
 | |
|    relate->isRead = 1 ;   /* we have updated this one */
 | |
|    return relate4skipped ;
 | |
| }
 | |
| 
 | |
| /* returns 1 if the current relation set is a child (or is itself) of the input relation */
 | |
| static int relate4currentIsChild( RELATE4 *parent )
 | |
| {
 | |
|    RELATE4 *relateOn ;
 | |
| 
 | |
|    relateOn = parent->relation->currentRelateLevel ;
 | |
| 
 | |
|    if ( relateOn == parent )
 | |
|       return 1 ;
 | |
| 
 | |
|    for ( relateOn = 0 ;; )  /* now recursively check all the descendents */
 | |
|    {
 | |
|       relateOn = (RELATE4 *)l4next( &parent->slaves, relateOn ) ;
 | |
|       if ( relateOn == 0 )
 | |
|          return 0 ;
 | |
|       if ( relate4currentIsChild( relateOn ) == 1 )
 | |
|          return 1 ;
 | |
|    }
 | |
| }
 | |
| 
 | |
| /* returns 1 if the parent is above the child at any level (grandparent, etc) */
 | |
| static int relate4parent( RELATE4 *parent, RELATE4 *child )
 | |
| {
 | |
|    RELATE4 *slaveOn, *masterOn ;
 | |
| 
 | |
|    for ( slaveOn = child ;; )
 | |
|    {
 | |
|       masterOn = slaveOn->master ;
 | |
|       if ( masterOn == 0 )
 | |
|          return 0 ;
 | |
|       if ( masterOn == parent )
 | |
|          return 1 ;
 | |
|       slaveOn = slaveOn->master ;  /* go up one level */
 | |
|    }
 | |
| }
 | |
| 
 | |
| /* returns 1 if done, 0 if positioned to a new relate */
 | |
| /* if setup is true, it just does positional work (for relate4top), no reads */
 | |
| static int relate4nextRelationList( RELATION4 *relation, int setup )
 | |
| {
 | |
|    RELATE4 *relateOn, *master ;
 | |
|    int rc, rc2 ;
 | |
| 
 | |
|    relateOn = relation->currentRelateLevel ;
 | |
| 
 | |
|    if ( relateOn == 0 )  /* means get the first one */
 | |
|       relateOn = &relation->relate ;
 | |
|    else
 | |
|       if ( setup != 1 )
 | |
|       {
 | |
|          /* first see if we are part of a scan ourselves, and if so that we are scanned */
 | |
|          if ( relateOn->relationType == relate4scan || relateOn == &relation->relate )  /* the master is an implicit scan */
 | |
|          {
 | |
|             relate4setNotRead( relateOn ) ;  /* This data file & its slaves */
 | |
|             if ( relation->inSort == relate4sortDone )
 | |
|                if ( r4dataListFind( &relation->sortDataList, relateOn ) )
 | |
|                   return relate4sortNextRecord( relation ) ;
 | |
|             rc = relate4nextRecordInScan( relateOn ) ;
 | |
|             if ( rc == relate4skipped )
 | |
|                return 0 ;
 | |
|             rc2 = relate4blankSet( relateOn, (char)1 ) ;
 | |
|             if ( rc2 == r4locked || rc2 < 0 )  /* error or locked */
 | |
|                return rc2 ;
 | |
|             if ( relateOn->master == 0 )
 | |
|                if ( d4eof( relateOn->data ) )
 | |
|                   return r4eof ;
 | |
|             /* are our siblings also scanned? */
 | |
| 
 | |
|             /* we are scanned, so fall through and check out our master */
 | |
|          }
 | |
| 
 | |
|          if ( relateOn->master == 0 )
 | |
|          {
 | |
|             relation->currentRelateLevel = 0 ;
 | |
|             return r4eof ;
 | |
|          }
 | |
| 
 | |
|          master = relateOn->master ;
 | |
| 
 | |
|          /* try our masters next slave */
 | |
|          relateOn = (RELATE4 *)l4next( &master->slaves, relateOn ) ;
 | |
|          if ( relateOn == 0 )  /* no more slaves, try the master itself */
 | |
|          {
 | |
|             relation->currentRelateLevel = master ;
 | |
|             return relate4continue ;  /* either do ourselves or go up further */
 | |
|          }
 | |
|       }
 | |
| 
 | |
|    /* we need to go down our own slave list to the bottom level and start seek */
 | |
|    while ( relateOn->slaves.nLink != 0 )
 | |
|       relateOn = (RELATE4 *)l4first( &relateOn->slaves ) ;
 | |
| 
 | |
|    /* at the bottom, so try ourselves */
 | |
|    relation->currentRelateLevel = relateOn->master ;
 | |
| 
 | |
|    if ( setup == 1 )
 | |
|       return relate4continue ;
 | |
| 
 | |
|    if ( relateOn->master == 0 )  /* done/ eof */
 | |
|       return r4eof ;
 | |
| 
 | |
|    if ( relateOn->relationType == relate4scan )
 | |
|       return relate4continue ;
 | |
| 
 | |
|    /* otherwise try our current masters other slaves--i.e. just go get next */
 | |
|    return relate4nextRelationList( relation, setup ) ;
 | |
| }
 | |
| 
 | |
| static int relate4nextScanRecord( RELATION4 *relation )
 | |
| {
 | |
|    RELATE4 *relate ;
 | |
|    int rc, rc2, tryMatches ;
 | |
|    LIST4 *relateList ;
 | |
| 
 | |
|    if ( error4code( relation->relate.codeBase ) < 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    rc = 0 ;
 | |
| 
 | |
|    for ( ;; )
 | |
|    {
 | |
|       if ( relation->currentRelateLevel == 0 )
 | |
|          relation->currentRelateLevel = &relation->relate ;
 | |
| 
 | |
|       for ( ;; )
 | |
|       {
 | |
|          relateList = &relation->currentRelateLevel->relateList ;
 | |
|          tryMatches = 1 ;
 | |
|          if ( d4eof( relation->currentRelateLevel->data ) ) /* we are at eof, so all children cannot match */
 | |
|             tryMatches = 0 ;
 | |
|          else
 | |
|          {
 | |
|             if ( relation->currentRelateLevel->master != 0 )
 | |
|                if ( d4eof( relation->currentRelateLevel->master->data ) )  /* means no matches possible */
 | |
|                   tryMatches = 0 ;
 | |
|          }
 | |
| 
 | |
|          if ( tryMatches == 1 && l4numNodes( relateList ) )
 | |
|          {
 | |
|             if ( relateList->selected == 0 )
 | |
|                relateList->selected = (LINK4 *)l4first( relateList ) ;
 | |
|             for( ;; )
 | |
|             {
 | |
|                if ( relateList->selected == 0 )  /* finished with matches for this list */
 | |
|                   break ;
 | |
|                relate = ((RELATE4LIST *)relateList->selected)->ptr ;
 | |
|                relate4setNotRead( relate ) ;  /* This data file & its slaves */
 | |
|                if ( relation->inSort == relate4sortDone )
 | |
|                   if ( r4dataListFind( &relation->sortDataList, relate ) )
 | |
|                      return relate4sortNextRecord( relation ) ;
 | |
| 
 | |
|                rc = relate4nextRecordInScan( relate ) ;
 | |
|                if ( rc == relate4skipped )
 | |
|                   return 0 ;
 | |
|                if ( rc < 0 )
 | |
|                   return rc ;
 | |
|                rc2 = relate4blankSet( relate, (char)1 ) ;
 | |
|                if ( rc2 == r4locked || rc2 < 0 )  /* error or locked */
 | |
|                   return rc2 ;
 | |
|                if ( relate->master == 0 )
 | |
|                   if ( d4eof( relate->data ) )
 | |
|                      return r4eof ;
 | |
|                relateList->selected =(LINK4 *)l4next( relateList, relateList->selected ) ;
 | |
|             }
 | |
|          }
 | |
|          rc = relate4nextRelationList( relation, 0 ) ;
 | |
|          if ( rc != relate4continue )
 | |
|             return rc ;
 | |
|       }
 | |
| 
 | |
| /*
 | |
|       for ( ;; )
 | |
|       {
 | |
|          if ( d4eof( relation->relate.data ) )
 | |
|          {
 | |
|             rc = r4eof ;
 | |
|             break ;
 | |
|          }
 | |
|          if ( relation->inSort == relate4sortDone )
 | |
|             if ( r4dataListFind( &relation->sortDataList, &relation->relate ) )
 | |
|             {
 | |
|                relate4setNotRead( &relation->relate ) ;
 | |
|                return relate4sortNextRecord( relation ) ;
 | |
|             }
 | |
|          rc = relate4nextRecordInScan( &relation->relate ) ;
 | |
|          if ( rc < 0 )
 | |
|             return rc ;
 | |
|          if ( rc != relate4skipped )
 | |
|          {
 | |
|             rc = r4bof ;
 | |
|             break ;
 | |
|          }
 | |
|          relate4setNotRead( &relation->relate ) ;
 | |
|          rc = relate4lookup( &relation->relate, 1 ) ;
 | |
|          if ( rc != 0 )
 | |
|             continue ;
 | |
|          rc = relate4readRest( &relation->relate, 1 ) ;
 | |
|          if ( rc == 0 || rc < 0 || rc == r4terminate )
 | |
|             return rc ;
 | |
|          rc = 0 ;
 | |
|       }
 | |
|       if ( rc != 0 )
 | |
|          break ;
 | |
| */
 | |
|    }
 | |
| 
 | |
| /*   d4goEof( relation->relate.data ) ; */
 | |
| /*   return r4eof ;                     */
 | |
| }
 | |
| 
 | |
| /* returns 1 if done, 0 if positioned to a new relate */
 | |
| /* if setup is true, it just does positional work (for relate4top), no reads */
 | |
| static int relate4prevRelationList( RELATION4 *relation, int setup )
 | |
| {
 | |
|    RELATE4 *relateOn, *master ;
 | |
|    int rc, rc2 ;
 | |
| 
 | |
|    relateOn = relation->currentRelateLevel ;
 | |
| 
 | |
|    if ( relateOn == 0 )  /* means get the first one */
 | |
|       relateOn = &relation->relate ;
 | |
|    else
 | |
|       if ( setup != 1 )
 | |
|       {
 | |
|          /* first see if we are part of a scan ourselves, and if so that we are scanned */
 | |
|          if ( relateOn->relationType == relate4scan || relateOn == &relation->relate )  /* the master is an implicit scan */
 | |
|          {
 | |
|             relate4setNotRead( relateOn ) ;  /* This data file & its slaves */
 | |
|             if ( relation->inSort == relate4sortDone )
 | |
|                if ( r4dataListFind( &relation->sortDataList, relateOn ) )
 | |
|                   return relate4sortPrevRecord( relation ) ;
 | |
| 
 | |
|             rc = relate4prevRecordInScan( relateOn ) ;
 | |
|             if ( rc == relate4skipped )
 | |
|             {
 | |
|                if ( relate4eof( relateOn ) )
 | |
|                {
 | |
|                   if ( relation->inSort == relate4sortDone && relation->sortEofFlag == 1 )
 | |
|                   {
 | |
|                      relation->sortRecOn-- ;  /* move off eof on sort part */
 | |
|                      relation->sortEofFlag = 0 ;
 | |
|                   }
 | |
|                   else
 | |
|                   {
 | |
|                      rc = d4go( relation->relate.data, d4recCount( relation->relate.data ) ) ;
 | |
|                      if ( rc < 0 )
 | |
|                         return rc ;
 | |
|                   }
 | |
|                }
 | |
|                return 0 ;
 | |
|             }
 | |
|             if ( rc < 0 )
 | |
|                return rc ;
 | |
|             rc2 = relate4blankSet( relateOn, (char)-1 ) ;
 | |
|             if ( rc2 == r4locked || rc2 < 0 )  /* error or locked */
 | |
|                return rc2 ;
 | |
|             if ( relateOn->master == 0 )
 | |
|             {
 | |
|                if ( d4bof( relateOn->data ) )
 | |
|                   return r4bof ;
 | |
|                if ( d4eof( relateOn->data ) )
 | |
|                   return r4eof ;
 | |
|             }
 | |
|             /* are our siblings also scanned? */
 | |
| 
 | |
|             /* we are scanned, so fall through and check out our master */
 | |
|          }
 | |
| 
 | |
|          if ( relateOn->master == 0 )
 | |
|          {
 | |
|             relation->currentRelateLevel = 0 ;
 | |
|             return r4bof ;
 | |
|          }
 | |
| 
 | |
|          master = relateOn->master ;
 | |
| 
 | |
|          /* try our masters prev slave */
 | |
|          relateOn = (RELATE4 *)l4prev( &master->slaves, relateOn ) ;
 | |
|          if ( relateOn == 0 )  /* no more slaves, try the master itself */
 | |
|          {
 | |
|             relation->currentRelateLevel = master ;
 | |
|             return relate4continue ;  /* either do ourselves or go up further */
 | |
|          }
 | |
|       }
 | |
| 
 | |
|    /* we need to go down our own slave list to the bottom level and start seek */
 | |
|    while ( relateOn->slaves.nLink != 0 )
 | |
|       relateOn = (RELATE4 *)l4last( &relateOn->slaves ) ;
 | |
| 
 | |
|    /* at the bottom, so try ourselves */
 | |
|    relation->currentRelateLevel = relateOn->master ;
 | |
| 
 | |
|    if ( setup == 1 )
 | |
|       return relate4continue ;
 | |
| 
 | |
|    if ( relateOn->master == 0 )  /* done / bof */
 | |
|       return r4bof ;
 | |
| 
 | |
|    if ( relateOn->relationType == relate4scan )
 | |
|       return relate4continue ;
 | |
| 
 | |
|    /* otherwise try our current masters other slaves--i.e. just go get next */
 | |
|    return relate4prevRelationList( relation, setup ) ;
 | |
| }
 | |
| 
 | |
| static int relate4prevRecordInScan( RELATE4 *relate )
 | |
| {
 | |
|    long nextRec ;
 | |
|    int  rc ;
 | |
|    DATA4 *d4 ;
 | |
|    #ifndef S4SERVER
 | |
|       int  saveCode ;
 | |
|    #endif
 | |
|    #ifndef S4INDEX_OFF
 | |
|       B4KEY_DATA *key ;
 | |
|       #ifdef S4HAS_DESCENDING
 | |
|          unsigned short int oldDesc ;
 | |
|          int len ;
 | |
|          char *ptr ;
 | |
|       #endif
 | |
|    #endif
 | |
| 
 | |
|    #ifdef E4PARM_LOW
 | |
|       if ( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94416 ) ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef E4MISC
 | |
|       if ( relate->relation->isInitialized == 0 )
 | |
|          return error4( relate->codeBase, e4info, E84406 ) ;
 | |
|    #endif
 | |
| 
 | |
|    d4 = relate->data ;
 | |
| 
 | |
|    #ifndef S4INDEX_OFF
 | |
|    if ( relate->dataTag == 0 )
 | |
|    {
 | |
|    #endif
 | |
|       nextRec = d4recNo( d4 ) - 1 ;
 | |
|       nextRec -= f4flagGetNextFlip( &relate->set, (unsigned long)nextRec, (char)-1 ) ;
 | |
|       if ( nextRec <= 0 )
 | |
|          return r4bof ;
 | |
|       if ( d4recCountLessEq( d4, nextRec ) == 0 )
 | |
|          return r4eof ;
 | |
|    #ifndef S4INDEX_OFF
 | |
|    }
 | |
|    else
 | |
|       for(;;)
 | |
|       {
 | |
|          if ( relate4eof( relate ) )   /* if eof in relate, just leave on last tag entry */
 | |
|             rc = tfile4eof( relate->dataTag->tagFile ) ? 0 : -1 ;
 | |
|          else
 | |
|          {
 | |
|             if ( d4eof( d4 ) == 1 )
 | |
|             {
 | |
|                if ( d4recCountLessEq( d4, 1L ) == 0L )  /* count == 0 */
 | |
|                   return r4bof ;
 | |
|                if ( relate->masterExpr == 0 )   /* top relate, bof */
 | |
|                   return r4eof ;
 | |
|                #ifdef S4HAS_DESCENDING
 | |
|                   len = expr4key( relate->masterExpr, &ptr ) ;
 | |
|                   if ( len < 0 )
 | |
|                      return -1 ;
 | |
|                   len = (len < relate->matchLen) ? len : relate->matchLen ;   /* min of len and match len */
 | |
|                   oldDesc = relate->dataTag->tagFile->header.descending ;
 | |
|                   tfile4descending( relate->dataTag->tagFile, ((unsigned short int)(1 - oldDesc)) ) ;  /* invert the descending */
 | |
|                   rc = (int)tfile4seek( relate->dataTag->tagFile, relate->master->scanValue, len ) ;
 | |
|                   tfile4descending( relate->dataTag->tagFile, oldDesc ) ;
 | |
|                #else
 | |
|                   /* need to find the last matching entry, without seek */
 | |
|                   rc = (int)tfile4bottom( relate->dataTag->tagFile ) ;
 | |
|                   if ( rc == 0 )
 | |
|                   {
 | |
|                      rc = -1 ;
 | |
|                      while ( rc == -1 )
 | |
|                      {
 | |
|                         key = tfile4keyData( relate->dataTag->tagFile) ;
 | |
|                         if ( u4memcmp( key->value, relate->master->scanValue, (unsigned int)relate->master->scanValueLen ) == 0 )
 | |
|                         {
 | |
|                            rc = 0 ;
 | |
|                            break ;
 | |
|                         }
 | |
|                         if ( u4memcmp( key->value, relate->master->scanValue, (unsigned int)relate->master->scanValueLen ) < 0 )
 | |
|                            return r4bof ;
 | |
|                         rc = (int)tfile4skip( relate->dataTag->tagFile, -1L ) ;
 | |
|                      }
 | |
|                   }
 | |
|                #endif
 | |
|                if ( rc < 0 )
 | |
|                   return -1 ;
 | |
|                if ( rc == 0 )
 | |
|                   rc = -1 ;
 | |
|                else
 | |
|                   rc = 0 ;
 | |
|             }
 | |
|             else
 | |
|                #ifdef S4HAS_DESCENDING
 | |
|                   rc = (int)tfile4dskip( relate->dataTag->tagFile, -1L ) ;
 | |
|                #else
 | |
|                   rc = (int)tfile4skip( relate->dataTag->tagFile, -1L ) ;
 | |
|                #endif
 | |
|          }
 | |
|          if ( rc > 0 )
 | |
|             return -1 ;
 | |
|          if ( rc != -1L )
 | |
|             return r4bof ;
 | |
| 
 | |
|          key = tfile4keyData( relate->dataTag->tagFile) ;
 | |
|          nextRec = key->num ;
 | |
| 
 | |
|          if ( relate->master )
 | |
|             #ifdef S4FOX
 | |
|                if ( u4keycmp( key->value, relate->master->scanValue, (unsigned int)relate->master->scanValueLen,
 | |
|                     (unsigned int)relate->dataTag->tagFile->header.keyLen, 0, &relate->dataTag->tagFile->vfpInfo ) )
 | |
|             #else
 | |
|                if ( u4memcmp( key->value, relate->master->scanValue, (unsigned int)relate->master->scanValueLen ) )
 | |
|             #endif
 | |
|                return r4bof ;
 | |
| 
 | |
|          if ( f4flagIsSetFlip( &relate->set, (unsigned long)nextRec ) )
 | |
|             break ;
 | |
|       }
 | |
|    #endif
 | |
| 
 | |
|    #ifndef S4SERVER
 | |
|       saveCode = relate->codeBase->errGo ;
 | |
|       relate->codeBase->errGo = 0 ;
 | |
|    #endif
 | |
|    rc = d4go( d4, nextRec ) ;
 | |
|    #ifndef S4SERVER
 | |
|       relate->codeBase->errGo = saveCode ;
 | |
|    #endif
 | |
|    if ( rc < 0 )
 | |
|       return -1 ;
 | |
|    if ( rc == r4entry )
 | |
|       return r4eof ;
 | |
|    relate->isRead = 1 ;   /* we have updated this one */
 | |
|    return relate4skipped ;
 | |
| }
 | |
| 
 | |
| static int relate4prevScanRecord( RELATION4 *relation )
 | |
| {
 | |
|    RELATE4 *relate ;
 | |
|    int rc, rc2, tryMatches ;
 | |
|    LIST4 *relateList ;
 | |
| 
 | |
|    if ( error4code( relation->relate.codeBase ) < 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    rc = 0 ;
 | |
| 
 | |
|    for ( ;; )
 | |
|    {
 | |
|       if ( relation->currentRelateLevel == 0 )
 | |
|          relation->currentRelateLevel = &relation->relate ;
 | |
| 
 | |
|       for ( ;; )
 | |
|       {
 | |
|          relateList = &relation->currentRelateLevel->relateList ;
 | |
|          tryMatches = 1 ;
 | |
|          if ( d4eof( relation->currentRelateLevel->data ) ) /* we are at eof, so all children cannot match */
 | |
|             tryMatches = 0 ;
 | |
|          else
 | |
|          {
 | |
|             if ( relation->currentRelateLevel->master != 0 )
 | |
|                if ( d4eof( relation->currentRelateLevel->master->data ) )  /* means no matches possible */
 | |
|                   tryMatches = 0 ;
 | |
|          }
 | |
| 
 | |
|          if ( tryMatches == 1 && l4numNodes( relateList ) )
 | |
|          {
 | |
|             if ( relateList->selected == 0 )
 | |
|                relateList->selected = (LINK4 *)l4last( relateList ) ;
 | |
|             for (;; )
 | |
|             {
 | |
|                if ( relateList->selected == 0 )  /* finished with matches for this list */
 | |
|                   break ;
 | |
|                relate = ((RELATE4LIST *)l4last( relateList ))->ptr ;
 | |
|                if ( relate4eof( relate ) )  /* at eof means we must read this record */
 | |
|                   if ( relation->inSort != relate4sortDone )
 | |
|                   {
 | |
|                      rc = relate4bottom( relate ) ;
 | |
|                      if ( rc == r4eof )   /* no records, so can't skip back */
 | |
|                         return r4bof ;
 | |
|                      else
 | |
|                         return rc ;
 | |
|                   }
 | |
| 
 | |
|                relate = ((RELATE4LIST *)relateList->selected)->ptr ;
 | |
|                relate4setNotRead( relate ) ;  /* This data file & its slaves */
 | |
|                if ( relation->inSort == relate4sortDone )
 | |
|                   if ( r4dataListFind( &relation->sortDataList, relate ) )
 | |
|                      return relate4sortPrevRecord( relation ) ;
 | |
| 
 | |
|                rc = relate4prevRecordInScan(relate) ;
 | |
|                if ( rc == relate4skipped )
 | |
|                {
 | |
|                   if ( relate4eof( relate ) )
 | |
|                   {
 | |
|                      if ( relation->inSort == relate4sortDone && relation->sortEofFlag == 1 )
 | |
|                      {
 | |
|                         relation->sortRecOn-- ;  /* move off eof on sort part */
 | |
|                         relation->sortEofFlag = 0 ;
 | |
|                      }
 | |
|                      else
 | |
|                      {
 | |
|                         rc = d4go( relation->relate.data, d4recCount( relation->relate.data ) ) ;
 | |
|                         if ( rc < 0 )
 | |
|                            return rc ;
 | |
|                      }
 | |
|                   }
 | |
|                   return 0 ;
 | |
|                }
 | |
|                if ( rc < 0 )
 | |
|                   return rc ;
 | |
|                rc2 = relate4blankSet( relate, (char)-1 ) ;
 | |
|                if ( rc2 == r4locked || rc2 < 0 )  /* error or locked */
 | |
|                   return rc2 ;
 | |
|                if ( relate->master == 0 )
 | |
|                {
 | |
|                   if ( d4bof(relate->data) )
 | |
|                      return r4bof ;
 | |
|                   if ( d4eof(relate->data) )
 | |
|                      return r4eof ;
 | |
|                }
 | |
|                relateList->selected =(LINK4 *)l4prev( relateList, relateList->selected ) ;
 | |
|             }
 | |
|          }
 | |
|          rc = relate4prevRelationList( relation, 0 ) ;
 | |
|          if ( rc != relate4continue )
 | |
|             return rc ;
 | |
|       }
 | |
| /*
 | |
|       for ( ;; )
 | |
|       {
 | |
|          if ( d4bof( relation->relate.data ) || d4eof( relation->relate.data ) )
 | |
|          {
 | |
|             rc = r4bof ;
 | |
|             break ;
 | |
|          }
 | |
|          if ( relation->inSort == relate4sortDone )
 | |
|             if ( r4dataListFind( &relation->sortDataList, &relation->relate ) )
 | |
|             {
 | |
|                relate4setNotRead( &relation->relate ) ;
 | |
|                return relate4sortPrevRecord( relation ) ;
 | |
|             }
 | |
|          rc = relate4prevRecordInScan( &relation->relate ) ;
 | |
|          if ( rc < 0 )
 | |
|             return rc ;
 | |
|          if ( rc != relate4skipped )
 | |
|          {
 | |
|             rc = r4bof ;
 | |
|             break ;
 | |
|          }
 | |
|          relate4setNotRead( &relation->relate ) ;
 | |
|          rc = relate4lookup( &relation->relate, -1 ) ;
 | |
|          if ( rc != 0 )
 | |
|             continue ;
 | |
|          rc = relate4readRest( &relation->relate, -1 ) ;
 | |
|          if ( rc == 0 || rc < 0 || rc == r4terminate )
 | |
|             return rc ;
 | |
|          rc = 0 ;
 | |
|       }
 | |
|       if ( rc != 0 )
 | |
|          break ;
 | |
| */
 | |
|    }
 | |
| /*   return r4bof ; */
 | |
| }
 | |
| #endif
 | |
| 
 | |
| int S4FUNCTION relate4querySet( RELATE4 *relate, const char *expr )
 | |
| {
 | |
|    int len ;
 | |
| 
 | |
|    if ( relate == 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    #ifdef S4VBASIC
 | |
|       if ( c4parm_check( relate, 5, E94428 ) )
 | |
|          return -1 ;
 | |
|    #endif
 | |
| 
 | |
|    if ( error4code( relate->codeBase ) < 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    relate4changed( relate ) ;
 | |
|    u4free( relate->relation->exprSource ) ;
 | |
|    relate->relation->exprSource = 0 ;
 | |
|    if ( expr == 0 )
 | |
|       return 0 ;
 | |
|    if ( expr[0] == 0 )
 | |
|       return 0 ;
 | |
|    len = strlen( expr ) + 1 ;
 | |
|    relate->relation->exprSource = (char *)u4allocEr( relate->codeBase, (long)len ) ;
 | |
|    if ( relate->relation->exprSource == 0 )
 | |
|       return -1 ;
 | |
|    memcpy( relate->relation->exprSource, expr, (unsigned int)len ) ;
 | |
|    return 0 ;
 | |
| }
 | |
| 
 | |
| #ifndef S4CLIENT
 | |
| int relate4readIn( RELATE4 *relate )
 | |
| {
 | |
|    int rc ;
 | |
| 
 | |
|    if ( error4code( relate->codeBase ) < 0 )
 | |
|       return -1 ;
 | |
|    if ( relate->isRead )
 | |
|       return 0 ;
 | |
|    if ( relate->master )
 | |
|       if ( relate->master->isRead == 0 )
 | |
|       {
 | |
|          rc = relate4readIn( relate->master ) ;
 | |
|          if ( rc == relate4filterRecord || rc == r4terminate )
 | |
|             return rc ;
 | |
|       }
 | |
| 
 | |
|    return relate4lookup( relate, 1 ) ;
 | |
| }
 | |
| 
 | |
| /* direction : -1 = look backwards, 0 = lookup only, 1 = look forwards */
 | |
| static int relate4readRest( RELATE4 *relate, char direction )
 | |
| {
 | |
|    RELATE4 *slave ;
 | |
|    int rc, scanDone ;
 | |
| 
 | |
|    #ifdef E4PARM_LOW
 | |
|       if ( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94417 ) ;
 | |
|       if ( direction < -1 || direction > 1 )
 | |
|          return error4( 0, e4parm, E94417 ) ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef E4ANALYZE
 | |
|       if ( error4code( relate->codeBase ) < 0 )
 | |
|          return e4codeBase ;
 | |
|    #endif
 | |
| 
 | |
|    rc = 0 ;
 | |
| 
 | |
|    if ( relate->isRead == 0 )
 | |
|    {
 | |
|       rc = relate4lookup( relate, direction );
 | |
|       if ( rc < 0 || rc == relate4filterRecord || rc == r4terminate )
 | |
|          return rc ;
 | |
|    }
 | |
| 
 | |
|    scanDone = 0 ;
 | |
|    for( slave = 0 ;; )
 | |
|    {
 | |
|       if ( direction == 1 )
 | |
|          slave = (RELATE4 *)l4next( &relate->slaves, slave ) ;
 | |
|       else
 | |
|          slave = (RELATE4 *)l4prev( &relate->slaves, slave ) ;
 | |
|       if ( slave == 0 )
 | |
|          break ;
 | |
|       if ( slave->isRead == 0 )
 | |
|          if ( slave->relationType == relate4scan )
 | |
|          {
 | |
|             /* if the currentRelateLevel is an upward master of ourselves,
 | |
|                then make our master the currentRelateLevel so that the next
 | |
|                skip will scan through us */
 | |
|             if ( relate4parent( slave->relation->currentRelateLevel, slave->master ) )
 | |
|                slave->relation->currentRelateLevel = slave->master ;
 | |
|             if ( direction == 1 )
 | |
|             {
 | |
|                d4top( slave->data ) ;
 | |
|                #ifndef S4OFF_INDEX
 | |
|                   tfile4top( slave->dataTag->tagFile ) ;
 | |
|                #endif
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                d4bottom( slave->data ) ;
 | |
|                #ifndef S4OFF_INDEX
 | |
|                   tfile4bottom( slave->dataTag->tagFile ) ;
 | |
|                #endif
 | |
|             }
 | |
|          }
 | |
|    }
 | |
|    for( slave = 0 ;; )
 | |
|    {
 | |
|       if ( direction == 1 )
 | |
|          slave = (RELATE4 *)l4next( &relate->slaves, slave ) ;
 | |
|       else
 | |
|          slave = (RELATE4 *)l4prev( &relate->slaves, slave ) ;
 | |
|       if ( slave == 0 )
 | |
|          return 0 ;
 | |
|       if ( slave->relationType == relate4scan && scanDone == 1 )
 | |
|       {
 | |
|          if ( slave->isRead == 0 )
 | |
|          {
 | |
|             relate4blankSet( slave, (char)(-direction) ) ;  /* do reverse of direction */
 | |
|             slave->isRead = 1 ;
 | |
|             rc = relate4readRest( slave, direction ) ;
 | |
|          }
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|          rc = relate4readRest( slave, direction ) ;
 | |
|          if ( slave->relationType == relate4scan && rc == 0 )
 | |
|          {
 | |
|             switch ( direction )
 | |
|             {
 | |
|                case -1:
 | |
|                   if ( !d4bof( slave->data ) )
 | |
|                      scanDone = 1 ;
 | |
|                   break ;
 | |
|                case 1:
 | |
|                   if ( !d4eof( slave->data ) )
 | |
|                      scanDone = 1 ;
 | |
|                   break ;
 | |
|                default:
 | |
|                   break ;
 | |
|             }
 | |
|          }
 | |
|       }
 | |
|       if ( rc < 0 || rc == relate4filterRecord || rc == r4terminate )
 | |
|          return rc ;
 | |
|    }
 | |
| }
 | |
| 
 | |
| static void relate4setNotRead( RELATE4 *relate )
 | |
| {
 | |
|    RELATE4 *slaveOn ;
 | |
| 
 | |
|    if ( relate->isRead )
 | |
|    {
 | |
|       relate->isRead = 0 ;
 | |
|       for( slaveOn = 0 ;; )
 | |
|       {
 | |
|          slaveOn = (RELATE4 *)l4next(&relate->slaves,slaveOn) ;
 | |
|          if ( slaveOn == 0 )
 | |
|             return ;
 | |
|          relate4setNotRead( slaveOn ) ;
 | |
|       }
 | |
|    }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| int S4FUNCTION relate4skip( RELATE4 *relate, const long numSkip )
 | |
| {
 | |
|    int rc ;
 | |
|    long numskip ;
 | |
|    RELATION4 *relation ;
 | |
|    CODE4 *c4 ;
 | |
|    #ifdef S4CLIENT
 | |
|       CONNECTION4RELATE_SKIP_INFO_IN info ;
 | |
|       CONNECTION4 *connection ;
 | |
|       int saveRc ;
 | |
|    #else
 | |
|       int sortStatus, rc2 ;
 | |
|       signed char sign ;
 | |
|       #ifdef S4REPORT
 | |
|          #ifdef S4WINDOWS
 | |
|             char countstring[22];
 | |
|             static long int scanrecCount = 0, selectrecCount = 0, sortrecCount = 0;
 | |
|             HWND statwin;
 | |
|          #endif
 | |
|       #endif
 | |
|       #ifndef S4SINGLE
 | |
|          int oldReadLock ;
 | |
|       #endif
 | |
|    #endif
 | |
| 
 | |
|    #ifdef S4VBASIC
 | |
|       if ( c4parm_check( relate, 5, E94418 ) )
 | |
|          return -1 ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef E4PARM_HIGH
 | |
|       if ( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94418 ) ;
 | |
|    #endif
 | |
| 
 | |
|    c4 = relate->codeBase ;
 | |
| 
 | |
|    if ( error4code( c4 ) < 0 )
 | |
|       return e4codeBase ;
 | |
| 
 | |
|    numskip = numSkip ;
 | |
|    relation = relate->relation ;
 | |
| 
 | |
|    if ( relation->isInitialized == 0 )
 | |
|       return error4( c4, e4info, E84406 ) ;
 | |
| 
 | |
|    relate = &relation->relate ;
 | |
| 
 | |
|    #ifdef S4CLIENT
 | |
|       #ifdef E4ANALYZE
 | |
|          if ( relate->data == 0 )
 | |
|             return error4( c4, e4struct, E94418 ) ;
 | |
|          if ( relate->data->dataFile == 0 )
 | |
|             return error4( c4, e4struct, E94418 ) ;
 | |
|       #endif
 | |
|       connection = relate->data->dataFile->connection ;
 | |
|       #ifdef E4ANALYZE
 | |
|          if ( connection == 0 )
 | |
|             return error4( c4, e4struct, E94418 ) ;
 | |
|       #endif
 | |
| 
 | |
|       connection4assign( connection, CON4RELATE_SKIP, 0, 0 ) ;
 | |
|       info.relationId = relation->relationId ;
 | |
|       info.numSkips = numskip ;
 | |
|       connection4addData( connection, &info, sizeof( CONNECTION4RELATE_SKIP_INFO_IN ), 0 ) ;
 | |
|       connection4send( connection ) ;
 | |
|       saveRc = connection4receive( connection ) ;
 | |
|       if ( saveRc < 0 )
 | |
|          return error4stack( c4, saveRc, E94418 ) ;
 | |
|       saveRc = connection4status( connection ) ;
 | |
|       if ( saveRc < 0 )
 | |
|          return connection4error( connection, c4, saveRc, E94418 ) ;
 | |
| 
 | |
|       rc = relate4unpack( relation, relate->data->dataFile->connection ) ;
 | |
|       if ( rc < 0 )
 | |
|          return error4stack( c4, rc, E94418 ) ;
 | |
|       return saveRc ;
 | |
|    #else
 | |
|       if ( numskip < 0 )
 | |
|       {
 | |
|          if ( relation->skipBackwards == 0 )
 | |
|             return error4( c4, e4info, E84417 ) ;
 | |
|          sign = -1 ;
 | |
|       }
 | |
|       else
 | |
|          sign = 1 ;
 | |
| 
 | |
|       sortStatus = 0 ;
 | |
|       rc = 0 ;
 | |
|       #ifndef S4SINGLE
 | |
|          /* suspend auto read locking from within the relate */
 | |
|          oldReadLock = c4->readLock ;
 | |
|          c4->readLock = 0 ;
 | |
|       #endif
 | |
|       for( ; numskip ; )
 | |
|       {
 | |
|          #ifdef S4REPORT
 | |
|          #ifdef S4WINDOWS
 | |
|             if( GetWindowWord( c4->hWnd, 8 ) == 666 )
 | |
|                statwin = c4->hWnd ;
 | |
| 
 | |
|             if ( statwin )
 | |
|             {
 | |
|                if ( GetWindowWord( statwin, 6 ) == 0 )
 | |
|                {
 | |
|                   SetWindowWord( statwin, 6, 1 ) ;
 | |
|                   scanrecCount = sortrecCount = selectrecCount = 0 ;
 | |
|                }
 | |
| 
 | |
|                scanrecCount++ ;
 | |
|                if ( scanrecCount < 20 || ( scanrecCount % 20 ) == 0 )
 | |
|                {
 | |
|                   c4ltoa45( scanrecCount, countstring, sizeof( countstring ) -1 ) ;
 | |
|                   countstring[21] = 0 ;
 | |
|                   SendMessage( (HWND)GetWindowWord( statwin, 0 ), WM_SETTEXT, 0, (LPARAM)((LPSTR)countstring ) ) ;
 | |
|                }
 | |
|             }
 | |
|          #endif
 | |
|          #endif
 | |
| 
 | |
|          if ( sign > 0 )
 | |
|          {
 | |
|             rc = relate4nextScanRecord( relation ) ;
 | |
|             if ( rc == r4eof )
 | |
|                break ;
 | |
|          }
 | |
|          else
 | |
|          {
 | |
|             rc = relate4prevScanRecord( relation ) ;
 | |
|             if ( rc == r4bof )
 | |
|                break ;
 | |
|          }
 | |
| 
 | |
|          #ifdef S4SINGLE
 | |
|             if ( rc < 0 || rc == r4terminate )
 | |
|          #else
 | |
|             if ( rc < 0 || rc == r4locked || rc == r4terminate )
 | |
|          #endif
 | |
|                break ;
 | |
| 
 | |
|          rc = relate4readRest( relate, sign ) ;
 | |
|          if ( rc == relate4filterRecord )
 | |
|             continue ;
 | |
| 
 | |
|          if ( rc < 0 || rc == r4terminate )
 | |
|             break ;
 | |
| 
 | |
|          if ( relation->exprSource )
 | |
|          {
 | |
|             rc2 = log4true(&relation->log ) ;
 | |
|             if ( rc2 == r4terminate )
 | |
|             {
 | |
|                rc = r4terminate ;
 | |
|                break ;
 | |
|             }
 | |
|             if ( rc2 == 0 )
 | |
|             {
 | |
|                if ( relation->inSort == relate4sortSkip )  /* must temporarily disable in order to get a matching scan if available */
 | |
|                {
 | |
|                   sortStatus = 1 ;
 | |
|                   relation->inSort = 0 ;
 | |
|                }
 | |
|                continue ;
 | |
|             }
 | |
|          }
 | |
| 
 | |
|          numskip -= sign ;
 | |
|       }
 | |
|       #ifndef S4SINGLE
 | |
|          /* suspend auto read locking from within the relate */
 | |
|          c4->readLock = oldReadLock ;
 | |
|       #endif
 | |
| 
 | |
|       #ifdef S4WINDOWS
 | |
|       #ifdef S4REPORT
 | |
|          if(GetWindowWord( c4->hWnd, 8 ) == 666 )
 | |
|             statwin = c4->hWnd;
 | |
| 
 | |
|          if ( statwin )
 | |
|          {
 | |
|             selectrecCount++;
 | |
|             if ( selectrecCount < 20 || (selectrecCount % 20) == 0 )
 | |
|             {
 | |
|                c4ltoa45(selectrecCount,countstring,sizeof(countstring)-1);
 | |
|                countstring[21] = 0;
 | |
|                SendMessage((HWND)GetWindowWord(statwin,2),WM_SETTEXT,0,(LPARAM)((LPSTR)countstring));
 | |
|             }
 | |
|             if ( relation->inSort )
 | |
|             {
 | |
|                sortrecCount++;
 | |
|                if( sortrecCount < 20 || (sortrecCount % 20) == 0)
 | |
|                {
 | |
|                   c4ltoa45(sortrecCount,countstring,sizeof(countstring)-1);
 | |
|                   countstring[21] = 0;
 | |
|                   SendMessage((HWND)GetWindowWord(statwin,4),WM_SETTEXT,0,(LPARAM)((LPSTR)countstring));
 | |
|                }
 | |
|             }
 | |
|          }
 | |
|       #endif
 | |
|       #endif
 | |
| 
 | |
|       if ( sortStatus == 1 )
 | |
|          relation->inSort = relate4sortSkip ;
 | |
|       return rc ;
 | |
|    #endif
 | |
| }
 | |
| 
 | |
| int S4FUNCTION relate4skipEnable( RELATE4 *relate, const int doEnable )
 | |
| {
 | |
|    if ( relate == 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    if ( error4code( relate->codeBase ) < 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    if ( relate->relation->skipBackwards != (char) doEnable )
 | |
|    {
 | |
|       relate->relation->skipBackwards = (char) doEnable ;
 | |
|       relate4changed( relate ) ;
 | |
|    }
 | |
|    return 0 ;
 | |
| }
 | |
| 
 | |
| static void relate4sortFree( RELATION4 *relation, const int deleteSort )
 | |
| {
 | |
|    if ( relation == 0 )
 | |
|       return ;
 | |
| 
 | |
|    #ifndef S4CLIENT
 | |
|       sort4free( &relation->sort ) ;
 | |
|       u4free( relation->otherData ) ;
 | |
|       relation->otherData = 0 ;
 | |
|       if ( relation->sortedFile.hand >= 0 )
 | |
|          file4close( &relation->sortedFile ) ;
 | |
|       r4dataListFree( &relation->sortDataList ) ;
 | |
|       relation->inSort = 0 ;
 | |
|    #endif
 | |
|    if ( deleteSort )
 | |
|    {
 | |
|       u4free( relation->sortSource ) ;
 | |
|       relation->sortSource = 0 ;
 | |
|    }
 | |
| }
 | |
| 
 | |
| #ifndef S4CLIENT
 | |
| static int relate4sort( RELATE4 *relate )
 | |
| {
 | |
|    EXPR4 *sortExpr ;
 | |
|    int rc, i, len ;
 | |
|    long j, zero ;
 | |
|    char nDbf, *sortKey ;
 | |
|    R4DATA_LIST *r4data ;
 | |
|    RELATION4 *relation ;
 | |
|    CODE4 *c4 ;
 | |
|    #ifdef S4SERVER
 | |
|       LIST4 *oldList ;
 | |
|    #endif
 | |
| 
 | |
|    zero = 0L ;
 | |
| 
 | |
|    #ifdef E4PARM_LOW
 | |
|       if ( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94419 ) ;
 | |
|    #endif
 | |
| 
 | |
|    c4 = relate->codeBase ;
 | |
| 
 | |
|    #ifdef E4ANALYZE
 | |
|       if ( error4code( c4 ) < 0 )
 | |
|          return e4codeBase ;
 | |
|    #endif
 | |
| 
 | |
|    relation = relate->relation ;
 | |
|    relate = &relation->relate ;
 | |
|    rc = 0 ;
 | |
|    #ifdef S4SERVER
 | |
|       oldList = tran4dataList( code4trans( c4 ) ) ;
 | |
|       tran4dataListSet( code4trans( c4 ), &relation->localDataList ) ;
 | |
|    #endif
 | |
|    sortExpr = expr4parseLow( relate->data, relation->sortSource, 0 ) ;
 | |
|    #ifdef S4SERVER
 | |
|       tran4dataListSet( code4trans( c4 ), oldList ) ;
 | |
|    #endif
 | |
| 
 | |
|    relation->inSort = relate4sortSkip ;
 | |
|    relation->sortDoneFlag = 0 ;
 | |
| 
 | |
|    rc = relate4top( relate ) ;
 | |
|    if ( rc )   /* no records satisfy the relate, or error */
 | |
|    {
 | |
|       expr4free( sortExpr ) ;
 | |
|       return rc ;
 | |
|    }
 | |
| 
 | |
|    len = expr4key( sortExpr, &sortKey ) ;
 | |
|    if ( len <= 0 )
 | |
|    {
 | |
|       expr4free( sortExpr ) ;
 | |
|       return -1 ;
 | |
|    }
 | |
| 
 | |
|    #ifdef E4ANALYZE
 | |
|       if ( relation->sortDataList.nLink != 0 )
 | |
|          return error4( c4, e4struct, E84413 ) ;
 | |
|    #endif
 | |
| 
 | |
|    if ( r4dataListBuild( &relation->sortDataList, relate, sortExpr, relate4exact ) < 0 )
 | |
|    {
 | |
|       expr4free( sortExpr ) ;
 | |
|       return -1 ;
 | |
|    }
 | |
| 
 | |
|    if ( r4dataListMassage( &relation->sortDataList ) < 0 )
 | |
|    {
 | |
|       expr4free( sortExpr ) ;
 | |
|       return -1 ;
 | |
|    }
 | |
| 
 | |
|    nDbf = (char)relation->sortDataList.nLink ;
 | |
| 
 | |
|    relation->sortOtherLen = (int)(nDbf * sizeof( long )) ;
 | |
|    relation->otherData = (char *)u4alloc( (long)relation->sortOtherLen ) ;
 | |
|    if ( relation->otherData == 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    rc = sort4initFree( &relation->sort, c4, len, relation->sortOtherLen, relate ) ;
 | |
|    if ( rc )
 | |
|    {
 | |
|       expr4free( sortExpr ) ;
 | |
|       return rc ;
 | |
|    }
 | |
| 
 | |
|    #ifndef S4FOX
 | |
|    #ifndef S4CLIPPER
 | |
|       switch( expr4type( sortExpr ) )
 | |
|       {
 | |
|          #ifdef S4NDX
 | |
|             case r4num:
 | |
|             case r4num_doub:
 | |
|             case r4date:
 | |
|             case r4date_doub:
 | |
|                relation->sort.cmp = (S4CMP_FUNCTION *)t4cmpDoub ;
 | |
|                break ;
 | |
|          #endif
 | |
|          #ifdef S4MDX
 | |
|             case r4num:
 | |
|                relation->sort.cmp = (S4CMP_FUNCTION *)c4bcdCmp ;
 | |
|                break ;
 | |
|             case r4date:
 | |
|                relation->sort.cmp = (S4CMP_FUNCTION *)t4cmpDoub ;
 | |
|                break ;
 | |
|          #endif
 | |
|          default:
 | |
|             break ;
 | |
|       }
 | |
|    #endif
 | |
|    #endif
 | |
| 
 | |
|    /* call relate4top() again in case free-ups occurred */
 | |
|    rc = relate4top( relate ) ;
 | |
|    if ( rc )   /* no records satisfy the relate, or error */
 | |
|    {
 | |
|       expr4free( sortExpr ) ;
 | |
|       return rc ;
 | |
|    }
 | |
| 
 | |
|    for ( j = 0L, rc = 0 ; !rc ; j++, rc = relate4skip( relate, 1L ) )
 | |
|    {
 | |
|       for ( i = 0, r4data = 0 ;; i++ )
 | |
|       {
 | |
|          r4data = (R4DATA_LIST *)l4next( &relation->sortDataList, r4data ) ;
 | |
|          if ( r4data == 0 )
 | |
|             break ;
 | |
|          if ( d4eof( r4data->data ) || d4bof( r4data->data ) )   /* relate4blank case */
 | |
|             memcpy( relation->otherData + i * sizeof(long), (void *)&zero, sizeof( long ) ) ;
 | |
|          else
 | |
|             memcpy( relation->otherData + i * sizeof(long), (void *)&r4data->data->recNum, sizeof( long ) ) ;
 | |
|       }
 | |
|       if ( expr4key( sortExpr, &sortKey ) < 0 )
 | |
|       {
 | |
|          expr4free( sortExpr ) ;
 | |
|          u4free( relation->otherData ) ;
 | |
|          relation->otherData = 0 ;
 | |
|          return -1 ;
 | |
|       }
 | |
|       if ( sort4put( &relation->sort, j, sortKey, relation->otherData ) < 0 )
 | |
|       {
 | |
|          expr4free( sortExpr ) ;
 | |
|          u4free( relation->otherData ) ;
 | |
|          relation->otherData = 0 ;
 | |
|          return -1 ;
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    expr4free( sortExpr ) ;
 | |
| 
 | |
|    if ( rc < 0 || rc == r4terminate )
 | |
|    {
 | |
|       u4free( relation->otherData ) ;
 | |
|       relation->otherData = 0 ;
 | |
|       return rc ;
 | |
|    }
 | |
| 
 | |
|    relation->sortRecCount = j ;
 | |
|    relation->inSort = relate4sortDone ;
 | |
| 
 | |
|    if ( relation->skipBackwards )
 | |
|       if ( file4tempLow( &relation->sortedFile, c4, 1 ) < 0 )
 | |
|       {
 | |
|          u4free( relation->otherData ) ;
 | |
|          relation->otherData = 0 ;
 | |
|          return -1 ;
 | |
|       }
 | |
| 
 | |
|    if ( sort4getInitFree( &relation->sort, relate ) < 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    relation->sortRecOn = relation->sortFilePos = relation->sortRecTo = 0L ;
 | |
| 
 | |
|    return 0 ;
 | |
| }
 | |
| 
 | |
| static int relate4sortGetRecord( RELATION4 *relation, const long num )
 | |
| {
 | |
|    int len, i, rc ;
 | |
|    char *key ;
 | |
|    char *other = 0 ;
 | |
|    R4DATA_LIST *linkOn ;
 | |
|    long j, numLeft ;
 | |
|    #ifdef S4DATA_ALIGN
 | |
|       long longPtr ;
 | |
|    #endif
 | |
| 
 | |
|    if ( error4code( relation->relate.codeBase ) < 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    if ( num <= 0 )
 | |
|       return r4bof ;
 | |
| 
 | |
|    relation->sortEofFlag = 0 ;
 | |
|    numLeft = num - relation->sortRecTo ;
 | |
| 
 | |
|    if ( numLeft <= 0 )  /* already read, so just return from file */
 | |
|    {
 | |
|       if ( relation->skipBackwards == 0 )
 | |
|          return -1 ;
 | |
|       len = file4read( &relation->sortedFile, ( num - 1 ) * relation->sortOtherLen, relation->otherData, (unsigned int)relation->sortOtherLen ) ;
 | |
|       if ( len != relation->sortOtherLen )  /* free up and exit */
 | |
|          return -1 ;
 | |
|       other = relation->otherData ;
 | |
|    }
 | |
|    else
 | |
|       while ( numLeft-- )
 | |
|       {
 | |
|          if ( relation->sortDoneFlag == 1 )  /* sort is finished, therefore must be eof */
 | |
|             return r4eof ;
 | |
| 
 | |
|          rc = sort4get( &relation->sort, &j, (void **)&key, (void **)&other ) ;
 | |
|          if ( rc )  /* no more items, or error */
 | |
|          {
 | |
|             sort4free( &relation->sort ) ;
 | |
|             if ( rc == 1 )
 | |
|             {
 | |
|                relation->sortEofFlag = 1 ;
 | |
|                relation->sortDoneFlag = 1 ;
 | |
|                return r4eof ;
 | |
|             }
 | |
|             else
 | |
|                return rc ;
 | |
|          }
 | |
|          relation->sortRecTo++ ;
 | |
|          if ( relation->skipBackwards )
 | |
|          {
 | |
|             file4write( &relation->sortedFile, relation->sortFilePos, other, (unsigned int)relation->sortOtherLen ) ;
 | |
|             relation->sortFilePos += relation->sortOtherLen ;
 | |
|          }
 | |
|       }
 | |
| 
 | |
|    /* now read the database records in */
 | |
|    for ( i = 0, linkOn = 0 ;; i++ )
 | |
|    {
 | |
|       linkOn = (R4DATA_LIST *)l4next( &relation->sortDataList, linkOn ) ;
 | |
|       if ( linkOn == 0 )
 | |
|          return 0 ;
 | |
|       /* note that the sort positions all blanks to eof whereas in non-sort it is not
 | |
|          consistent whether it is positioned bof or eof.  For sort it doesn't matter
 | |
|          because further skips all go through the sort, therefore we already know the
 | |
|          record order thus it doesn't need to be calculated.  This is a small efficience. */
 | |
|       #ifdef S4DATA_ALIGN
 | |
|          memcpy( &longPtr, (void *)(other + i*sizeof(longPtr)), sizeof(longPtr) ) ;
 | |
|          if ( longPtr == 0 )  /* relate4blank case */
 | |
|             rc = d4goEof( linkOn->data ) ;
 | |
|          else
 | |
|             rc = d4go( linkOn->data, longPtr ) ;
 | |
|       #else
 | |
|          if ( *((long *)(other) + i ) == 0 )  /* relate4blank case */
 | |
|             rc = d4goEof( linkOn->data ) ;
 | |
|          else
 | |
|             rc = d4go( linkOn->data, *((long *)(other) + i ) ) ;
 | |
|       #endif
 | |
|       if ( rc < 0 )
 | |
|          return rc ;
 | |
| 
 | |
|       linkOn->relate->isRead = 1 ;
 | |
|    }
 | |
| }
 | |
| 
 | |
| static int relate4sortNextRecord( RELATION4 *relation )
 | |
| {
 | |
|    int rc ;
 | |
| 
 | |
|    if ( error4code( relation->relate.codeBase ) < 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    rc = relate4sortGetRecord( relation, relation->sortRecOn + 1 ) ;
 | |
|    if ( rc == 0 )
 | |
|       relation->sortRecOn++ ;
 | |
|    if ( rc == r4eof )
 | |
|       relation->sortRecOn = relation->sortRecCount + 1 ;
 | |
|    return rc ;
 | |
| }
 | |
| 
 | |
| static int relate4sortPrevRecord( RELATION4 *relation )
 | |
| {
 | |
|    int rc ;
 | |
| 
 | |
|    if ( error4code( relation->relate.codeBase ) < 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    rc = relate4sortGetRecord( relation, relation->sortRecOn - 1 ) ;
 | |
|    if ( rc == 0 )
 | |
|       relation->sortRecOn-- ;
 | |
|    return rc ;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| int S4FUNCTION relate4sortSet( RELATE4 *relate, const char *expr )
 | |
| {
 | |
|    RELATION4 *relation ;
 | |
|    int len ;
 | |
| 
 | |
|    if ( relate == 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    #ifdef S4VBASIC
 | |
|       if ( c4parm_check( relate, 5, E94429 ) )
 | |
|          return -1 ;
 | |
|    #endif
 | |
| 
 | |
|    if ( error4code( relate->codeBase ) < 0 )
 | |
|       return -1 ;
 | |
| 
 | |
|    relation = relate->relation ;
 | |
|    relate = &relation->relate ;
 | |
|    relate4changed( relate ) ;
 | |
|    u4free( relation->sortSource ) ;
 | |
|    relation->sortSource = 0 ;
 | |
|    if ( expr )
 | |
|       if ( expr[0] )
 | |
|       {
 | |
|          len = strlen( expr ) ;
 | |
|          relation->sortSource = (char *)u4allocEr( relate->codeBase, (long)len + 1L ) ;
 | |
|          if ( relation->sortSource == 0 )
 | |
|             return -1 ;
 | |
|          memcpy( relation->sortSource, expr, (unsigned int)len ) ;
 | |
|       }
 | |
| 
 | |
|    return 0 ;
 | |
| }
 | |
| 
 | |
| #ifdef S4CLIENT
 | |
| static int relate4add( CONNECTION4 *connection, RELATE4 *relate, unsigned int *relatePos, unsigned int *flexPos, char *relateData )
 | |
| {
 | |
|    int len, savePos ;
 | |
|    RELATE4 *slaveOn ;
 | |
|    CONNECTION4RELATE *info ;
 | |
|    TAG4 *tag ;
 | |
| 
 | |
|    /* add this relate's info */
 | |
|    savePos = *relatePos ;
 | |
|    info = (CONNECTION4RELATE *)( relateData + *relatePos ) ;
 | |
|    info->matchLen = relate->matchLen ;
 | |
|    info->relationType = relate->relationType ;
 | |
|    info->errorAction = relate->errorAction ;
 | |
|    info->numSlaves = relate->slaves.nLink ;
 | |
|    info->clientId = data4clientId( relate->data ) ;
 | |
| 
 | |
|    tag = relate->dataTag ;
 | |
|    if ( tag == 0 )
 | |
|       info->dataTagName.offset = 0 ;
 | |
|    else
 | |
|    {
 | |
|       len = strlen( tag->tagFile->alias ) + 1 ;
 | |
|       info->dataTagName.offset = *flexPos ;
 | |
|       connection4addData( connection, tag->tagFile->alias, len, 0 ) ;
 | |
|       *flexPos += len ;
 | |
|    }
 | |
| 
 | |
|    if ( relate->masterExpr == 0 )
 | |
|       info->masterExpr.offset = 0 ;
 | |
|    else
 | |
|    {
 | |
|       len = strlen( relate->masterExpr->source ) + 1 ;
 | |
|       info->masterExpr.offset = *flexPos ;
 | |
|       connection4addData( connection, relate->masterExpr->source, len, 0 ) ;
 | |
|       *flexPos += len ;
 | |
|    }
 | |
| 
 | |
|    if ( relate->data == 0 )
 | |
|       info->dataAccessName.offset = 0 ;
 | |
|    else
 | |
|    {
 | |
|       len = strlen( dfile4name( relate->data->dataFile ) ) + 1 ;
 | |
|       info->dataAccessName.offset = *flexPos ;
 | |
|       connection4addData( connection, dfile4name( relate->data->dataFile ), len, 0 ) ;
 | |
|       *flexPos += len ;
 | |
|    }
 | |
| 
 | |
|    *relatePos += sizeof( CONNECTION4RELATE ) ;
 | |
| 
 | |
|    /* and do it's slaves:  */
 | |
|    for ( slaveOn = 0 ;; )
 | |
|    {
 | |
|       slaveOn = (RELATE4 *)l4next( &relate->slaves, slaveOn ) ;
 | |
|       if ( slaveOn == 0 )
 | |
|          break ;
 | |
|       relate4add( connection, slaveOn, relatePos, flexPos, relateData ) ;
 | |
|    }
 | |
| 
 | |
|    return 0 ;
 | |
| }
 | |
| 
 | |
| static int relate4clientInit( RELATE4 *master )
 | |
| {
 | |
|    CONNECTION4RELATE_INIT_INFO_IN info ;
 | |
|    CONNECTION4RELATE_INIT_INFO_OUT *out ;
 | |
|    int rc, relateCount, i ;
 | |
|    RELATE4 *relateOn ;
 | |
|    CONNECTION4 *connection ;
 | |
|    unsigned int relatePos, flexPos, exprLen, sortLen ;
 | |
|    char *relateData ;
 | |
|    CODE4 *c4 ;
 | |
| 
 | |
|    #ifdef E4PARM_LOW
 | |
|       if ( master == 0 )
 | |
|          return error4( 0, e4parm_null, E94421 ) ;
 | |
|    #endif
 | |
| 
 | |
|    c4 = master->codeBase ;
 | |
|    connection = master->data->dataFile->connection ;
 | |
|    relateCount = 1 ;
 | |
| 
 | |
|    memset( &info, 0, sizeof( CONNECTION4RELATE_INIT_INFO_IN ) ) ;
 | |
| 
 | |
|    for( relateOn = master ;; relateCount++ )
 | |
|    {
 | |
|       if ( relate4next( &relateOn ) == 2 )
 | |
|          break ;
 | |
|       #ifdef E4MISC
 | |
|          if ( relateOn->data != 0 )
 | |
|          {
 | |
|             if ( connection == 0 )
 | |
|                connection = relateOn->data->dataFile->connection ;
 | |
|             else
 | |
|                if ( connection != relateOn->data->dataFile->connection )   /* multi-servers not supported on relations */
 | |
|                   return error4( c4, e4notSupported, E84414 ) ;
 | |
|          }
 | |
|       #endif
 | |
|    }
 | |
| 
 | |
|    #ifdef E4ANALYZE
 | |
|       if ( connection == 0 )
 | |
|          return error4( c4, e4struct, E94421 ) ;
 | |
|    #endif
 | |
|    if ( relateCount == 0 || connection == 0 )
 | |
|       return error4( c4, e4relate, E84415 ) ;
 | |
| 
 | |
|    if ( master->relation->needsFreeing == 1 )
 | |
|       info.relationId = master->relation->relationId ;
 | |
|    else
 | |
|       info.relationId = 0 ;
 | |
|    info.relateOffset = sizeof( CONNECTION4RELATE_INIT_INFO_IN ) ;
 | |
|    info.flexOffset = info.relateOffset + relateCount * sizeof( CONNECTION4RELATE ) ;
 | |
|    info.relation.skipBackwards = master->relation->skipBackwards ;
 | |
|    info.bitmapDisable = master->relation->bitmapDisable ;
 | |
|    info.masterClientId = master->data->clientId ;
 | |
|    if ( master->relation->exprSource == 0 )
 | |
|    {
 | |
|       info.relation.exprSource.offset = 0 ;
 | |
|       exprLen = 0 ;
 | |
|    }
 | |
|    else
 | |
|    {
 | |
|       exprLen = strlen( master->relation->exprSource ) + 1 ;
 | |
|       info.relation.exprSource.offset = info.flexOffset ;
 | |
|    }
 | |
|    if ( master->relation->sortSource == 0 )
 | |
|    {
 | |
|       info.relation.sortSource.offset = 0 ;
 | |
|       sortLen = 0 ;
 | |
|    }
 | |
|    else
 | |
|    {
 | |
|       sortLen = strlen( master->relation->sortSource ) + 1 ;
 | |
|       info.relation.sortSource.offset = info.flexOffset + exprLen ;
 | |
|    }
 | |
| 
 | |
|    connection4assign( connection, CON4RELATE_INIT, 0, 0 ) ;
 | |
|    connection4addData( connection, &info, sizeof( CONNECTION4RELATE_INIT_INFO_IN ), 0 ) ;
 | |
|    relateData = (char *)u4allocFree( c4, relateCount * sizeof( CONNECTION4RELATE ) ) ;
 | |
|    if ( relateData == 0 )
 | |
|       return error4stack( c4, e4memory, E94421 ) ;
 | |
|    connection4addData( connection, relateData, relateCount * sizeof( CONNECTION4RELATE ), 0 ) ;
 | |
|    if ( exprLen != 0 )
 | |
|       connection4addData( connection, master->relation->exprSource, exprLen, 0 ) ;
 | |
|    if ( sortLen != 0 )
 | |
|       connection4addData( connection, master->relation->sortSource, sortLen, 0 ) ;
 | |
| 
 | |
|    relatePos = 0 ;
 | |
|    flexPos = info.flexOffset + exprLen + sortLen ;
 | |
|    rc = relate4add( connection, master, &relatePos, &flexPos, relateData ) ;
 | |
|    if ( rc < 0 )
 | |
|    {
 | |
|       u4free( relateData ) ;
 | |
|       return error4stack( c4, rc, E94421 ) ;
 | |
|    }
 | |
| 
 | |
|    connection4send( connection ) ;
 | |
|    u4free( relateData ) ;
 | |
|    rc = connection4receive( connection ) ;
 | |
|    if ( rc < 0 )
 | |
|       return error4stack( c4, rc, E94421 ) ;
 | |
|    rc = connection4status( connection ) ;
 | |
|    if ( rc < 0 )
 | |
|       return connection4error( connection, c4, rc, E84401 ) ;
 | |
|    if ( rc != 0 )
 | |
|       return rc ;
 | |
|    if ( connection4len( connection ) != (long)sizeof( CONNECTION4RELATE_INIT_INFO_OUT ) + relateCount * (long)sizeof( relateOn->id ) )
 | |
|       return error4stack( c4, e4packetLen, E94421 ) ;
 | |
|    out = (CONNECTION4RELATE_INIT_INFO_OUT *)connection4data( connection ) ;
 | |
|    master->relation->relationId = out->relationId ;
 | |
| 
 | |
|    #ifdef E4ANALYZE
 | |
|       if ( sizeof( relateOn->id ) != sizeof( unsigned short int ) )
 | |
|          return error4( c4, e4struct, E94421 ) ;
 | |
|    #endif
 | |
| 
 | |
|    out++ ;   /* go to the end of out for the variable length data */
 | |
| 
 | |
|    for( relateOn = master, i = 0 ;; i++ )
 | |
|    {
 | |
|       if ( relateOn == 0 )
 | |
|          break ;
 | |
|       relateOn->id = *((unsigned short int *)(((char *)out) + i * sizeof( relateOn->id ) ) ) ;
 | |
|       rc = relate4next( &relateOn ) ;
 | |
|       if ( rc == 2 )
 | |
|          break ;
 | |
|    }
 | |
| 
 | |
|    master->relation->isInitialized = 1 ;
 | |
| 
 | |
|    return 0 ;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifndef S4CLIENT
 | |
| static int relate4topInit( RELATE4 *relate )
 | |
| {
 | |
|    RELATION4 *relation ;
 | |
|    int rc ;
 | |
|    CODE4 *c4 ;
 | |
|    #ifndef S4OPTIMIZE_OFF
 | |
|       int has_opt ;
 | |
|    #endif
 | |
|    #ifdef S4SERVER
 | |
|       LIST4 *oldList ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef S4VBASIC
 | |
|       if ( c4parm_check( relate, 5, E94422 ) )
 | |
|          return -1 ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef E4PARM_HIGH
 | |
|       if( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94422 ) ;
 | |
|    #endif
 | |
| 
 | |
|    c4 = relate->codeBase ;
 | |
|    if ( error4code( c4 ) < 0 )
 | |
|       return e4codeBase ;
 | |
| 
 | |
|    relation = relate->relation ;
 | |
|    relate = &relation->relate ;
 | |
| 
 | |
|    rc = 0 ;
 | |
| 
 | |
|    if ( relation->inSort == relate4sortDone )
 | |
|       if ( relation->skipBackwards == 0 )
 | |
|          relate4changed( relate ) ;
 | |
| 
 | |
|    relate->dataTag = relate->data->tagSelected ;
 | |
|    if ( relation->isInitialized == 0 )
 | |
|    {
 | |
|       #ifndef S4OPTIMIZE_OFF
 | |
|          has_opt = (char)c4->hasOpt ;
 | |
|       #endif
 | |
|       if ( rc < 0 )
 | |
|          return rc ;
 | |
|       relation->bitmapsFreed = 0 ;
 | |
|       if ( relation->exprSource )
 | |
|       {
 | |
|          #ifdef S4SERVER
 | |
|             oldList = tran4dataList( code4trans( c4 ) ) ;
 | |
|             tran4dataListSet( code4trans( c4 ), &relation->localDataList ) ;
 | |
|          #endif
 | |
|          relation->log.expr = expr4parseLow( relate->data, relation->exprSource, 0 ) ;
 | |
|          #ifdef S4SERVER
 | |
|             tran4dataListSet( code4trans( c4 ), oldList ) ;
 | |
|          #endif
 | |
|          if ( relation->log.expr == 0 )
 | |
|             return -1 ;
 | |
| 
 | |
|          if ( log4bitmapDo( &relation->log ) < 0 )
 | |
|             relation->bitmapsFreed = 1 ;
 | |
|          log4determineEvaluationOrder( &relation->log ) ;
 | |
|       }
 | |
| 
 | |
|       if ( relate4buildScanList( 0, relate, relation ) < 0 )
 | |
|          return -1 ;
 | |
| /*      relation->relateList.selected = (LINK4 *)l4first( &relation->relateList) ;*/
 | |
| 
 | |
|       relation->isInitialized = 1 ;
 | |
|       if ( relation->sortSource )
 | |
|       {
 | |
|          rc = relate4sort( relate ) ;
 | |
|          if ( rc < 0 || rc == r4terminate )
 | |
|             return rc ;
 | |
|       }
 | |
| 
 | |
|       #ifndef S4OPTIMIZE_OFF
 | |
|          if ( has_opt )
 | |
|             code4optRestart( c4 ) ;
 | |
|       #endif
 | |
|    }
 | |
| 
 | |
|    return 0 ;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| int S4FUNCTION relate4top( RELATE4 *relate )
 | |
| {
 | |
|    RELATION4 *relation ;
 | |
|    int rc ;
 | |
|    CODE4 *c4 ;
 | |
|    DATA4 *d4 ;
 | |
|    #ifdef S4CLIENT
 | |
|       CONNECTION4RELATE_TOP_INFO_IN info ;
 | |
|       CONNECTION4 *connection ;
 | |
|       int saveRc ;
 | |
|    #else
 | |
|       long rec ;
 | |
|       int rc2 ;
 | |
|       #ifndef S4OFF_MULTI
 | |
|          int oldReadLock ;
 | |
|       #endif
 | |
|    #endif
 | |
| 
 | |
|    #ifdef S4VBASIC
 | |
|       if ( c4parm_check( relate, 5, E94422 ) )
 | |
|          return -1 ;
 | |
|    #endif
 | |
| 
 | |
|    #ifdef E4PARM_HIGH
 | |
|       if( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94422 ) ;
 | |
|    #endif
 | |
| 
 | |
|    c4 = relate->codeBase ;
 | |
|    if ( error4code( c4 ) < 0 )
 | |
|       return e4codeBase ;
 | |
| 
 | |
|    relation = relate->relation ;
 | |
|    relate = &relation->relate ;
 | |
|    d4 = relate->data ;
 | |
| 
 | |
|    rc = 0 ;
 | |
| 
 | |
|    #ifdef S4CLIENT
 | |
|       #ifdef E4ANALYZE
 | |
|          if ( d4 == 0 )
 | |
|             return error4( c4, e4struct, E94422 ) ;
 | |
|          if ( d4->dataFile == 0 )
 | |
|             return error4( c4, e4struct, E94422 ) ;
 | |
|       #endif
 | |
|       if ( relation->isInitialized == 0 || relate->dataTag != d4->tagSelected )
 | |
|       {
 | |
|          relate->dataTag = d4->tagSelected ;
 | |
|          rc = relate4clientInit( relate ) ;
 | |
|          if ( rc != 0 )
 | |
|             return rc ;
 | |
|       }
 | |
|       #ifdef S4CB51
 | |
|          #ifndef S4OFF_MULTI
 | |
|             if ( c4->readLock )
 | |
|             {
 | |
|                rc = relate4lock( relate ) ;
 | |
|                if ( rc != 0 )
 | |
|                   return rc ;
 | |
|             }
 | |
|          #endif
 | |
|       #endif
 | |
|       connection = d4->dataFile->connection ;
 | |
|       #ifdef E4ANALYZE
 | |
|          if ( connection == 0 )
 | |
|             return error4( c4, e4struct, E94422 ) ;
 | |
|       #endif
 | |
| 
 | |
|       connection4assign( connection, CON4RELATE_TOP, 0, 0 ) ;
 | |
|       info.relationId = relation->relationId ;
 | |
|       connection4addData( connection, &info, sizeof( CONNECTION4RELATE_TOP_INFO_IN ), 0 ) ;
 | |
|       connection4send( connection ) ;
 | |
|       saveRc = connection4receive( connection ) ;
 | |
|       if ( saveRc < 0 )
 | |
|          return error4stack( c4, saveRc, E94422 ) ;
 | |
|       saveRc = connection4status( connection ) ;
 | |
|       if ( saveRc < 0 )
 | |
|          return connection4error( connection, c4, saveRc, E94422 ) ;
 | |
| 
 | |
|       rc = relate4unpack( relation, d4->dataFile->connection ) ;
 | |
|       if ( rc != 0 )
 | |
|          return error4stack( c4, rc, E94422 ) ;
 | |
|       return saveRc ;
 | |
|    #else
 | |
|       #ifndef S4OFF_MULTI
 | |
|          oldReadLock = c4->readLock ;
 | |
|          c4->readLock = 0 ;
 | |
|       #endif
 | |
| 
 | |
|       for ( ;; )  /* used to minimize return code areas, just break out... */
 | |
|       {
 | |
|          rc = relate4topInit( relate ) ;
 | |
|          if (  rc != 0 )
 | |
|             break ;
 | |
| 
 | |
|          relate4setNotRead( relate ) ;
 | |
| 
 | |
|          relation->currentRelateLevel = 0 ;
 | |
|          relate4nextRelationList( relation, 1 ) ;
 | |
| 
 | |
|          if ( relation->inSort == relate4sortDone )
 | |
|          {
 | |
|             relation->sortRecOn = 0 ;
 | |
|             rc = relate4sortNextRecord( relation ) ;
 | |
|          }
 | |
|          else
 | |
|             rc = d4top( d4 ) ;
 | |
| 
 | |
|          if ( rc )    /* eof or error */
 | |
|             break ;
 | |
| 
 | |
|          if ( relation->exprSource )
 | |
|          {
 | |
|             rec = d4recNo( d4 ) ;
 | |
|             if ( f4flagIsSetFlip( &relate->set, (unsigned long)rec ) == 0 )
 | |
|             {
 | |
|                #ifndef S4INDEX_OFF
 | |
|                   if ( relate->dataTag )
 | |
|                   {
 | |
|                      while ( f4flagIsSetFlip( &relate->set, (unsigned long)rec ) == 0 )
 | |
|                      {
 | |
|                         #ifdef S4HAS_DESCENDING
 | |
|                            rc = (int)tfile4dskip( relate->dataTag->tagFile, 1L ) ;
 | |
|                         #else
 | |
|                            rc = (int)tfile4skip( relate->dataTag->tagFile, 1L ) ;
 | |
|                         #endif
 | |
|                         if ( rc != 1 )
 | |
|                         {
 | |
|                            if ( rc == 0 )
 | |
|                            {
 | |
|                               d4goEof( d4 ) ;
 | |
|                               rc = r4eof ;
 | |
|                            }
 | |
|                            break ;
 | |
|                         }
 | |
|                         rec = tfile4recNo( relate->dataTag->tagFile ) ;
 | |
|                      }
 | |
|                      if ( rc == r4eof )
 | |
|                         break ;
 | |
|                   }
 | |
|                   else
 | |
|                   {
 | |
|                #endif
 | |
|                   rec = f4flagGetNextFlip( &relate->set, 1L, 1 ) + 1L ;
 | |
|                   if ( d4recCountLessEq( d4, rec ) == 0 )
 | |
|                   {
 | |
|                      d4goEof( d4 ) ;
 | |
|                      rc = r4eof ;
 | |
|                      break ;
 | |
|                   }
 | |
|                #ifndef S4INDEX_OFF
 | |
|                   }
 | |
|                #endif
 | |
|             }
 | |
|             rc = d4go( d4, rec ) ;
 | |
|             if ( rc != 0 )
 | |
|                break ;
 | |
|          }
 | |
| 
 | |
|          rc = relate4readRest( relate, 1 ) ;
 | |
|          if ( rc == relate4filterRecord )
 | |
|          {
 | |
|             rc = relate4skip( relate, 1L ) ;
 | |
|             break ;
 | |
|          }
 | |
| 
 | |
|          if ( rc < 0 || rc == r4terminate )
 | |
|             break ;
 | |
| 
 | |
|          if ( relation->exprSource )
 | |
|          {
 | |
|             rc2 = log4true( &relation->log ) ;
 | |
|             if ( rc2 == r4terminate )
 | |
|             {
 | |
|                rc = r4terminate ;
 | |
|                break ;
 | |
|             }
 | |
|             if ( rc2 == 0 )
 | |
|             {
 | |
|                if ( relation->inSort == relate4sortSkip )  /* must temporarily disable in order to get a matching scan if available */
 | |
|                {
 | |
|                   relation->inSort = 0 ;
 | |
|                   rc = relate4skip( relate, 1L ) ;
 | |
|                   relation->inSort = relate4sortSkip ;
 | |
|                }
 | |
|                else
 | |
|                   rc = relate4skip( relate, 1L ) ;
 | |
|             }
 | |
|          }
 | |
|          break ;
 | |
|       }
 | |
| 
 | |
|       #ifndef S4OFF_MULTI
 | |
|          c4->readLock = oldReadLock ;
 | |
|       #endif
 | |
|       return rc ;
 | |
|    #endif
 | |
| }
 | |
| 
 | |
| int S4FUNCTION relate4type( RELATE4 *relate, int relateType )
 | |
| {
 | |
|    int rc ;
 | |
| 
 | |
|    #ifdef E4PARM_HIGH
 | |
|       if ( relate == 0 )
 | |
|          return error4( 0, e4parm_null, E94423 ) ;
 | |
|       if ( relateType != relate4exact && relateType != relate4scan && relateType != relate4approx )
 | |
|          return error4( relate->codeBase, e4parm, E84416 ) ;
 | |
|    #endif
 | |
|    rc = relate->relationType ;
 | |
|    if ( rc != relateType )
 | |
|    {
 | |
|       relate->relationType = relateType ;
 | |
|       relate4changed( relate ) ;
 | |
|    }
 | |
|    return rc ;
 | |
| }
 | |
| 
 | |
| #ifdef S4CB51
 | |
| int S4FUNCTION relate4unlock( RELATE4 *relate )
 | |
| {
 | |
|    #ifndef S4SINGLE
 | |
|       DATA4 *dataOn ;
 | |
| 
 | |
|       #ifdef E4PARM_HIGH
 | |
|          if ( relate == 0 )
 | |
|             return error4( 0, e4parm_null, E94424 ) ;
 | |
|       #endif
 | |
| 
 | |
|       if ( !relate->relation->locked )
 | |
|          return 0 ;
 | |
| 
 | |
|       for ( dataOn = (DATA4 *)l4first( tran4dataList( code4trans( relate->codeBase ) ) ) ;
 | |
|             dataOn ; dataOn = (DATA4 *)l4next( tran4dataList ( code4trans( relate->codeBase ) ), dataOn ) )
 | |
|          if ( relate4dbfInRelation( relate, dataOn ) )
 | |
|             d4unlock( dataOn ) ;
 | |
| 
 | |
|       relate->relation->locked = 0 ;
 | |
|    #endif
 | |
|    return 0 ;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef S4VB_DOS
 | |
| 
 | |
| RELATE4 *S4FUNCTION relate4createSlave_v( RELATE4 *master, DATA4 *slave, char *masterExpr, TAG4 *slaveTag )
 | |
| {
 | |
|    return relate4createSlave( master, slave, c4str( masterExpr ), slaveTag ) ;
 | |
| }
 | |
| 
 | |
| char * relate4masterExpr_v( RELATE4 *r4 )
 | |
| {
 | |
|    #ifdef S4VBASIC
 | |
|       if ( c4parm_check( r4, 5, "relate4masterExpr():" ) ) return 0 ;
 | |
|    #endif
 | |
| 
 | |
|    return v4str(r4->masterExpr->source) ;
 | |
| }
 | |
| 
 | |
| int S4FUNCTION relate4querySet_v ( RELATE4 *relate, char *expr )
 | |
| {
 | |
|    return relate4querySet( relate, c4str(expr) ) ;
 | |
| }
 | |
| 
 | |
| int S4FUNCTION relate4sortSet_v ( RELATE4 *relate, char *expr )
 | |
| {
 | |
|    return relate4sortSet( relate, c4str( expr ) ) ;
 | |
| }
 | |
| 
 | |
| #endif
 |