which included commits to RCS files with non-trunk default branches. git-svn-id: svn://10.65.10.50/trunk@976 c028cbd2-c16b-5b4b-a496-9718f37d4682
		
			
				
	
	
		
			1099 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1099 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/* r4save_2.c   (c)Copyright Sequiter Software Inc., 1991-1994.  All rights reserved. */
 | 
						|
#include "d4all.h"
 | 
						|
 | 
						|
extern int file_version;
 | 
						|
extern LIST4 name_list;
 | 
						|
 | 
						|
int save4long( FILE4SEQ_WRITE *seq, long *lval );
 | 
						|
int save4short( FILE4SEQ_WRITE *seq, short *sval );
 | 
						|
int ret4long( FILE4SEQ_READ *seq, long *lval );
 | 
						|
int ret4short( FILE4SEQ_READ *seq, short *sval );
 | 
						|
 | 
						|
/************************************************************
 | 
						|
 *
 | 
						|
 * Function:
 | 
						|
 *
 | 
						|
 *  PARAMETERS:
 | 
						|
 *    DATA4* data - data file
 | 
						|
 *    char* index_name - name of the index file to be checked for
 | 
						|
 *    char* index_lookup - buffer used in the function
 | 
						|
 *    char* current - buffer used in the function
 | 
						|
 *
 | 
						|
 *  DESCRIPTION:this function performs the same operation as d4index(), but
 | 
						|
 *   checks for an index file both with and without path names
 | 
						|
 *
 | 
						|
 *  RETURNS: 1 - on finding a matching index file, 0 - on failure
 | 
						|
 *
 | 
						|
 *  By: Raymond Cypher
 | 
						|
 *
 | 
						|
 *  HISTORY:
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
/* this function performs the same operation as d4index(), but checks for an
 | 
						|
   index file both with and without path names
 | 
						|
 */
 | 
						|
int S4FUNCTION r4index_lookup_foo( DATA4 *data, char *index_name,
 | 
						|
                                   char *index_lookup, char *current )
 | 
						|
{
 | 
						|
   INDEX4 *index_on ;
 | 
						|
   int ttype;
 | 
						|
 | 
						|
 | 
						|
   if ( data == 0 || index_name == 0 )
 | 
						|
      #ifdef S4DEBUG
 | 
						|
         e4severe( e4parm, E4_D4INDEX ) ;
 | 
						|
      #else
 | 
						|
         return 0 ;
 | 
						|
      #endif
 | 
						|
 | 
						|
   ttype = report4index_type();
 | 
						|
   u4name_piece( index_lookup, 257, index_name, 1, 0 ) ;
 | 
						|
   #ifndef S4UNIX
 | 
						|
      c4upper( index_lookup ) ;
 | 
						|
   #endif
 | 
						|
 | 
						|
   if( r4cli == ttype || r4ndx == ttype )
 | 
						|
   {
 | 
						|
      if( d4tag( data, index_lookup ) )
 | 
						|
         return 1;
 | 
						|
      u4name_piece( index_lookup, 257, index_name, 0, 0 ) ;
 | 
						|
      if( d4tag( data, index_lookup ) )
 | 
						|
         return 1;
 | 
						|
   }
 | 
						|
   else
 | 
						|
   {
 | 
						|
      index_on = (INDEX4 *)l4first( &data->indexes );
 | 
						|
      while( index_on )
 | 
						|
      {
 | 
						|
         u4name_piece( current, 257, index_on->file.name, 1, 0 ) ;
 | 
						|
         #ifndef S4UNIX
 | 
						|
            c4upper( current ) ;
 | 
						|
         #endif
 | 
						|
         if ( !strcmp( current, index_lookup ) )    /* check out data->alias? */
 | 
						|
            return 1;
 | 
						|
         index_on = (INDEX4 *) l4next( &data->indexes, index_on) ;
 | 
						|
      }
 | 
						|
 | 
						|
      u4name_piece( index_lookup, 257, index_name, 0, 0 ) ;
 | 
						|
      #ifndef S4UNIX
 | 
						|
         c4upper( index_lookup ) ;
 | 
						|
      #endif
 | 
						|
 | 
						|
      index_on = (INDEX4 *)l4first( &data->indexes );
 | 
						|
      while( index_on )
 | 
						|
      {
 | 
						|
         u4name_piece( current, 257, index_on->file.name, 0, 0 ) ;
 | 
						|
         #ifndef S4UNIX
 | 
						|
            c4upper( current ) ;
 | 
						|
         #endif
 | 
						|
         if ( !strcmp( current, index_lookup ) )    /* check out data->alias? */
 | 
						|
            return 1;
 | 
						|
         index_on = (INDEX4 *) l4next( &data->indexes, index_on) ;
 | 
						|
      }
 | 
						|
 | 
						|
   }
 | 
						|
 | 
						|
   return( 0 );
 | 
						|
}
 | 
						|
 | 
						|
/* wrapper for above function */
 | 
						|
int S4FUNCTION r4index_lookup( DATA4 *data, char *index_name )
 | 
						|
{
 | 
						|
   char *index_lookup, *current;
 | 
						|
   int retvalue;
 | 
						|
 | 
						|
   index_lookup = (char *)u4alloc_free( data->code_base, 258 );
 | 
						|
   if( !index_lookup )
 | 
						|
      return 0;
 | 
						|
 | 
						|
   current = (char*)u4alloc_free( data->code_base, 258 );
 | 
						|
   if( !current )
 | 
						|
   {
 | 
						|
      u4free( index_lookup );
 | 
						|
      return 0;
 | 
						|
   }
 | 
						|
 | 
						|
   retvalue = r4index_lookup_foo( data, index_name, index_lookup, current );
 | 
						|
 | 
						|
   u4free( index_lookup );
 | 
						|
   u4free( current );
 | 
						|
   return retvalue;
 | 
						|
}
 | 
						|
 | 
						|
/************************************************************
 | 
						|
 *
 | 
						|
 * Function: r4open_data_foo()
 | 
						|
 *
 | 
						|
 *  PARAMETERS:
 | 
						|
 *    char* file_name - full name of the file, potentially including path
 | 
						|
 *    char* alias - alias to set for the data file
 | 
						|
 *    RELATE4* relate - the relate associated with the current report
 | 
						|
 *    CODE4* code_base - CODE4 structure for the app
 | 
						|
 *    char* fnbuf - a buffer used by the function
 | 
						|
 *
 | 
						|
 *  DESCRIPTION: opens a data file, if the file is already open in the relate
 | 
						|
 *   appropriate aliasing is performed to open the file a second time
 | 
						|
 *
 | 
						|
 *  RETURNS: a DATA4 pointer on success, NULL on failure
 | 
						|
 *
 | 
						|
 *  By: Raymond Cypher
 | 
						|
 *
 | 
						|
 *  HISTORY:
 | 
						|
 *
 | 
						|
 */
 | 
						|
DATA4 *r4open_data_foo( char *file_name, char *alias, RELATE4 *relate, CODE4 *code_base, char *fnbuf )
 | 
						|
{
 | 
						|
   DATA4   *old, *nnew;
 | 
						|
   RELATE4 *relate_on;
 | 
						|
   char    abuf[11];
 | 
						|
 | 
						|
   if( !file_name || !alias )
 | 
						|
      return NULL;
 | 
						|
 | 
						|
   nnew = old = NULL;
 | 
						|
   /* if no alias or no relate do a simple d4open() */
 | 
						|
   if( alias[0] == '\0' || !relate )
 | 
						|
   {
 | 
						|
      nnew = d4open( code_base, file_name );
 | 
						|
      return nnew;
 | 
						|
   }
 | 
						|
 | 
						|
   #ifndef S4UNIX
 | 
						|
      c4upper( file_name );
 | 
						|
      c4upper( alias );
 | 
						|
   #endif
 | 
						|
 | 
						|
   /* check to see if the file is already open */
 | 
						|
   relate_on = relate;
 | 
						|
   while( relate_on )
 | 
						|
   {
 | 
						|
      strcpy( fnbuf, relate_on->data->file.name );
 | 
						|
      #ifndef S4UNIX
 | 
						|
         c4upper( fnbuf );
 | 
						|
      #endif
 | 
						|
      strcpy( abuf, d4alias( relate_on->data ) );
 | 
						|
      #ifndef S4UNIX
 | 
						|
         c4upper( abuf );
 | 
						|
      #endif
 | 
						|
      if( strcmp( fnbuf, file_name ) == 0 && strcmp(abuf, alias) == 0 )
 | 
						|
         old = relate_on->data;
 | 
						|
 | 
						|
      relate4next( &relate_on );
 | 
						|
   }
 | 
						|
 | 
						|
   /* if the file is not already open do a d4open() and set the alias */
 | 
						|
   if( old == NULL )
 | 
						|
   {
 | 
						|
      nnew = d4open( code_base, file_name );
 | 
						|
      if( nnew && alias[0] != '\0' )
 | 
						|
         d4alias_set( nnew, alias );
 | 
						|
      return nnew;
 | 
						|
   }
 | 
						|
 | 
						|
   /* save the existing files alias, reset it to xxxxxxx open the new file,
 | 
						|
      set the new files alias, then restore the old files alias  */
 | 
						|
   strcpy( abuf, d4alias(old) );
 | 
						|
   d4alias_set( old, "XXXXXXXXXX" );
 | 
						|
   nnew = d4open( code_base, file_name );
 | 
						|
   if( nnew )
 | 
						|
      d4alias_set( nnew, alias );
 | 
						|
   d4alias_set( old, abuf );
 | 
						|
 | 
						|
   return nnew;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/* wrapper for above function */
 | 
						|
DATA4 *r4open_data( char *file_name, char *alias, RELATE4 *relate, CODE4 *code_base )
 | 
						|
{
 | 
						|
   DATA4 *retvalue;
 | 
						|
   char *fnbuf;
 | 
						|
 | 
						|
   fnbuf = (char *)u4alloc_free( code_base, 256 );
 | 
						|
   if( !fnbuf )
 | 
						|
      return NULL;
 | 
						|
 | 
						|
   retvalue = r4open_data_foo( file_name, alias, relate, code_base, fnbuf );
 | 
						|
 | 
						|
   u4free( fnbuf );
 | 
						|
   return retvalue;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* frees the list of N4CHANGE structures */
 | 
						|
void report4free_name_list()
 | 
						|
{
 | 
						|
   PN4CHANGE nchange;
 | 
						|
 | 
						|
   while( (nchange = (PN4CHANGE)l4pop( &name_list )) != NULL )
 | 
						|
   {
 | 
						|
      if( nchange->old_name )
 | 
						|
         u4free( nchange->old_name );
 | 
						|
 | 
						|
      if( nchange->new_name )
 | 
						|
         u4free( nchange->new_name );
 | 
						|
 | 
						|
      u4free( nchange );
 | 
						|
   }
 | 
						|
}
 | 
						|
 | 
						|
/* simple memcompare function that first capitalizes the buffers being compared */
 | 
						|
int r4memicmp_foo( char *str1, char *str2, int len, char *buffer )
 | 
						|
{
 | 
						|
   memset( buffer, 0, len + 1 );
 | 
						|
   memcpy( buffer, str1, len );
 | 
						|
   #ifndef S4UNIX
 | 
						|
      c4upper( buffer );
 | 
						|
      c4upper( str2 );
 | 
						|
   #endif
 | 
						|
   return memcmp( str1, str2, len );
 | 
						|
}
 | 
						|
 | 
						|
/* wrapper for above function */
 | 
						|
int r4memicmp( char *str1, char *str2, int len )
 | 
						|
{
 | 
						|
   char *buffer;
 | 
						|
   int retvalue;
 | 
						|
 | 
						|
   buffer = (char *)u4alloc( len+2 );
 | 
						|
   if( !buffer )
 | 
						|
      return 1;
 | 
						|
 | 
						|
   retvalue = r4memicmp_foo( str1, str2, len, buffer );
 | 
						|
   u4free( buffer );
 | 
						|
   return retvalue;
 | 
						|
}
 | 
						|
 | 
						|
/************************************************************
 | 
						|
 *
 | 
						|
 * Function: report4nchange()
 | 
						|
 *
 | 
						|
 *  PARAMETERS:
 | 
						|
 *    CODE4* c4 - apps code4 struct
 | 
						|
 *    char** psrc - pointer to a pointer to the source string
 | 
						|
 *    int can_alloc - flag specifying whether or not to allocate additional
 | 
						|
 *     memory as needed
 | 
						|
 *    int s_size - size of the source string
 | 
						|
 *
 | 
						|
 *  DESCRIPTION: in CR when opening a report file, if a data or index file
 | 
						|
 *   cannot be found the user is prompted to enter a new path and/or name.
 | 
						|
 *   If the name of a data file has changed the name must also be changed in
 | 
						|
 *   all the expressions within the report.  To do this a list of N4CHANGE
 | 
						|
 *   structures is maintained.  N4CHANGE simply contains the old and new names
 | 
						|
 *   for a data file.  This function is passed an expression source and is
 | 
						|
 *   responsible for swapping the names.
 | 
						|
 *
 | 
						|
 *  RETURNS: none
 | 
						|
 *
 | 
						|
 *  By: Raymond Cypher
 | 
						|
 *
 | 
						|
 *  HISTORY:
 | 
						|
 *
 | 
						|
 */
 | 
						|
void report4nchange( CODE4 *c4, char **psrc, int can_alloc, int s_size )
 | 
						|
{
 | 
						|
   char      *orig = NULL, *src = *psrc;
 | 
						|
   PN4CHANGE nchange;
 | 
						|
   int       pos, src_pos, orig_len;
 | 
						|
   int       old_name_len, new_name_len;
 | 
						|
   unsigned  src_size = s_size;
 | 
						|
 | 
						|
   if( strlen(src) == 0 )
 | 
						|
      return;
 | 
						|
 | 
						|
   /* for ever N4CHANGE struct in the name list */
 | 
						|
   nchange = (PN4CHANGE)l4first( &name_list );
 | 
						|
   while( nchange )
 | 
						|
   {
 | 
						|
      /* if no names continue */
 | 
						|
      if( !nchange->new_name || !nchange->old_name )
 | 
						|
         continue;
 | 
						|
 | 
						|
      old_name_len = strlen(nchange->old_name);
 | 
						|
 | 
						|
      /* create a buffer to duplicate the expression source */
 | 
						|
      orig = (char *)u4alloc_free( c4, strlen(src) + 1 );
 | 
						|
      if( !orig )
 | 
						|
         return;
 | 
						|
 | 
						|
      /* copy the expression source */
 | 
						|
      src_pos = 0;
 | 
						|
      strcpy( orig, src );
 | 
						|
      orig_len = strlen(orig);
 | 
						|
 | 
						|
      /* run through the copy of the expression source */
 | 
						|
      for( pos = 0; orig[pos]; pos++ )
 | 
						|
      {
 | 
						|
         /* we are copying the duplicated expression source back into the
 | 
						|
            original buffer, with appropriate insertions */
 | 
						|
         src[src_pos++] = orig[pos];
 | 
						|
 | 
						|
         /* if the expression source buffer is too small due to changes
 | 
						|
            do a re-allocation */
 | 
						|
         if( src_pos == (int)src_size )
 | 
						|
         {
 | 
						|
            if( !can_alloc )
 | 
						|
            {
 | 
						|
               u4free( orig );
 | 
						|
               return;
 | 
						|
            }
 | 
						|
            u4alloc_again( c4, &src, &src_size, src_size+50 );
 | 
						|
            if( !src )
 | 
						|
               return;
 | 
						|
         }
 | 
						|
 | 
						|
         /* if we're at the end of the expression source, continue */
 | 
						|
         if( (orig_len - pos) < old_name_len )
 | 
						|
            continue;
 | 
						|
 | 
						|
         /* is this piece of the expression the current old name */
 | 
						|
         if( r4memicmp( orig+pos, nchange->old_name, old_name_len ) != 0 )
 | 
						|
            continue;
 | 
						|
 | 
						|
         if( u4name_char( orig[pos+old_name_len] ) )
 | 
						|
            continue;
 | 
						|
 | 
						|
         if( pos > 0 )
 | 
						|
            if( u4name_char( orig[pos-1]) )
 | 
						|
               continue;
 | 
						|
 | 
						|
         /* insert the new name into the expression source, re-allocating space
 | 
						|
            if necessary */
 | 
						|
         new_name_len = strlen( nchange->new_name );
 | 
						|
         if( (int)src_size <= (pos+new_name_len) )
 | 
						|
         {
 | 
						|
            if( !can_alloc )
 | 
						|
            {
 | 
						|
               u4free( orig );
 | 
						|
               return;
 | 
						|
            }
 | 
						|
 | 
						|
            u4alloc_again( c4, &src, &src_size, src_size + 50 );
 | 
						|
            if( !src )
 | 
						|
            {
 | 
						|
               u4free( orig );
 | 
						|
               return;
 | 
						|
            }
 | 
						|
         }
 | 
						|
         memcpy( src+(--src_pos), nchange->new_name, new_name_len );
 | 
						|
         src_pos += new_name_len;
 | 
						|
         pos += (old_name_len - 1);
 | 
						|
      } /* end of loop through copy of expression source */
 | 
						|
 | 
						|
      /* end the string */
 | 
						|
      src[src_pos++] = '\0';
 | 
						|
 | 
						|
      /* next change name */
 | 
						|
      nchange = (PN4CHANGE)l4next( &name_list, nchange );
 | 
						|
      /* free the source duplicate buffer */
 | 
						|
      if( orig )
 | 
						|
      {
 | 
						|
         u4free( orig );
 | 
						|
         orig = NULL;
 | 
						|
      }
 | 
						|
   }
 | 
						|
 | 
						|
   if( orig )
 | 
						|
   {
 | 
						|
      u4free( orig );
 | 
						|
      orig = NULL;
 | 
						|
   }
 | 
						|
 | 
						|
   /* set the pointer to the new source buffer */
 | 
						|
   *psrc = src;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/************************************************************
 | 
						|
 *
 | 
						|
 * Function: relate4retrieve_relate_foo()
 | 
						|
 *
 | 
						|
 *  PARAMETERS:
 | 
						|
 *   FILE4SEQ_READ* seq - sequential read struture for the file i/o
 | 
						|
 *   int open_files - should the data files for the relate be opened
 | 
						|
 *   char* spath - path to look for the data files
 | 
						|
 *   the rest of the parameters are simply character buffers used in the fctn
 | 
						|
 *
 | 
						|
 *  DESCRIPTION: retrieves a relation from a file.  Note: this file and the
 | 
						|
 *   r4save.c source file are compiled directly into the CR executable, as well
 | 
						|
 *   as into the CodeBase DLL.  As a result certain sections of this code
 | 
						|
 *   are specific the the executable and call functions in the executable
 | 
						|
 *
 | 
						|
 *  RETURNS: a RELATE4 pointer on success, NULL on failure
 | 
						|
 *
 | 
						|
 *  By: Raymond Cypher
 | 
						|
 *
 | 
						|
 *  HISTORY:
 | 
						|
 *
 | 
						|
 */
 | 
						|
RELATE4 * S4FUNCTION   relate4retrieve_relate_foo( FILE4SEQ_READ *seq, int open_files, char *spath,
 | 
						|
                          char *dname_buf, char *iname_buf, char *tname_buf,
 | 
						|
                          char *str_buf, char *master_expr_buf,
 | 
						|
                          char *slave_expr_buf, char *tempname_buf )
 | 
						|
{
 | 
						|
   RELATE4 *relate = NULL, *master = NULL;
 | 
						|
   DATA4   *data;
 | 
						|
   TAG4    *tag;
 | 
						|
   INDEX4  *index;
 | 
						|
   LIST4   indexes;
 | 
						|
   CODE4   *c4;
 | 
						|
   INAME4  *iname, *nname;
 | 
						|
   EXPR4   *expr;
 | 
						|
   char alias_buf[11], *cptr;
 | 
						|
   short match_len, relation_type, sort_type, error_action, n_links, code;
 | 
						|
   int i, err_code, err_flag, repeat_flag, error_code = 0, iindex, tname_err;
 | 
						|
   long lExt;
 | 
						|
   #ifdef S4CR2
 | 
						|
      PN4CHANGE nchange;
 | 
						|
      int rc;
 | 
						|
   #endif
 | 
						|
 | 
						|
   /* reset the changed name list */
 | 
						|
   memset( &name_list, 0, sizeof(name_list) );
 | 
						|
 | 
						|
   c4 = seq->file->code_base;
 | 
						|
 | 
						|
   /* reset the index name list */
 | 
						|
   memset( &indexes, 0, sizeof(indexes) );
 | 
						|
   err_code = err_flag = 0;
 | 
						|
 | 
						|
   while( 1 )
 | 
						|
   {
 | 
						|
      /* get the data file name */
 | 
						|
      if( retrieve4string( seq, dname_buf, 256 ) < 0 )
 | 
						|
      {
 | 
						|
         /* the error codes are used to allow the continued retrieval of
 | 
						|
            the relation tree, even if a part of it is not retrievable due
 | 
						|
            to a missing data or index file */
 | 
						|
         error_code = 1;
 | 
						|
         goto CLEANUP;
 | 
						|
      }
 | 
						|
 | 
						|
      /* get the files alias */
 | 
						|
      memset( alias_buf, 0, sizeof(alias_buf) );
 | 
						|
      if( file_version >= 0x24 )
 | 
						|
      {
 | 
						|
         if( retrieve4string( seq, alias_buf, sizeof(alias_buf) ) < 0 )
 | 
						|
         {
 | 
						|
            error_code = 1;
 | 
						|
            goto CLEANUP;
 | 
						|
         }
 | 
						|
      }
 | 
						|
 | 
						|
      /* retrieve the internal flags of the RELATE4 */
 | 
						|
      ret4short( seq, &match_len );
 | 
						|
      ret4short( seq, &relation_type );
 | 
						|
      ret4short( seq, &sort_type );
 | 
						|
      ret4short( seq, &error_action );
 | 
						|
 | 
						|
      /* retrieve the names of the index files and place them in the index list
 | 
						|
       */
 | 
						|
      ret4short( seq, &n_links );
 | 
						|
      for( i = 0; i < n_links; i++ )
 | 
						|
      {
 | 
						|
         iname = (INAME4 *)u4alloc_free( c4, sizeof(INAME4) );
 | 
						|
         if( retrieve4string( seq, iname_buf, 256 ) < 0 )
 | 
						|
         {
 | 
						|
            error_code = 1;
 | 
						|
            goto CLEANUP;
 | 
						|
         }
 | 
						|
         iname->index_name = (char *)u4alloc_free( c4, strlen(iname_buf) + 1 );
 | 
						|
         iname->name_length = strlen(iname_buf) + 1;
 | 
						|
         #ifdef S4WINDOWS
 | 
						|
            lstrcpy( iname->index_name, iname_buf );
 | 
						|
         #else
 | 
						|
            strcpy( iname->index_name, iname_buf );
 | 
						|
         #endif
 | 
						|
         l4add( &indexes, iname );
 | 
						|
      }
 | 
						|
 | 
						|
      /* get the tag name */
 | 
						|
      if( retrieve4string( seq, tname_buf, 256 ) < 0 )
 | 
						|
      {
 | 
						|
         error_code = 1;
 | 
						|
         goto CLEANUP;
 | 
						|
      }
 | 
						|
 | 
						|
      /* retrieve the source for the master expression */
 | 
						|
      if( retrieve4string( seq, master_expr_buf, 1024 ) < 0 )
 | 
						|
      {
 | 
						|
         error_code = 1;
 | 
						|
         goto CLEANUP;
 | 
						|
      }
 | 
						|
 | 
						|
      /* check for changed names */
 | 
						|
      cptr = master_expr_buf;
 | 
						|
      report4nchange( c4, &cptr, 0, 1024 );
 | 
						|
 | 
						|
      /* the code indicates where the new relate should fall in the tree with
 | 
						|
         respect to the last relate.  The code used is the same as the return
 | 
						|
         values from relate4next() */
 | 
						|
      ret4short( seq, &code );
 | 
						|
 | 
						|
      if( !err_flag && !err_code )
 | 
						|
      {
 | 
						|
         repeat_flag = 1;
 | 
						|
         while( repeat_flag )
 | 
						|
         {
 | 
						|
            repeat_flag = 0;
 | 
						|
            /* see if data file is already open */
 | 
						|
            data = 0;
 | 
						|
            u4name_piece( str_buf, 256, dname_buf, 0, 0 );
 | 
						|
            data = d4data( c4, alias_buf );
 | 
						|
            /* if not already open try to open */
 | 
						|
            if( !data )
 | 
						|
            {
 | 
						|
               /* if not allowed to open, error */
 | 
						|
               if( !open_files )
 | 
						|
               {
 | 
						|
                  e4describe( c4, e4result, E4_RESULT_LCF, dname_buf, (char *)0 );
 | 
						|
                  err_flag = 1;
 | 
						|
               }
 | 
						|
 | 
						|
               /* check for an alternate path */
 | 
						|
               if( spath && spath[0] != '\0' )
 | 
						|
               {
 | 
						|
                  u4name_piece( str_buf, 256, dname_buf, 0, 1 );
 | 
						|
                  strcpy( tempname_buf, spath );
 | 
						|
                  strcat( tempname_buf, "\\" );
 | 
						|
                  strcat( tempname_buf, str_buf );
 | 
						|
                  /* open the data file */
 | 
						|
                  data = r4open_data( tempname_buf, alias_buf, master, c4 );
 | 
						|
               }
 | 
						|
               else
 | 
						|
               {
 | 
						|
                  /* open the data file */
 | 
						|
                  data = r4open_data( dname_buf, alias_buf, master, c4 );
 | 
						|
               }
 | 
						|
 | 
						|
               /* if the open failed */
 | 
						|
               if( !data )
 | 
						|
               {
 | 
						|
                  /* if this is for the CR executable interactively get an
 | 
						|
                     alternate file name */
 | 
						|
                  #ifdef S4CR2
 | 
						|
                  nchange = (PN4CHANGE)u4alloc_free( c4, sizeof(N4CHANGE) );
 | 
						|
                  if( nchange )
 | 
						|
                  {
 | 
						|
                     u4name_piece( str_buf, 256, dname_buf, 0, 0 );
 | 
						|
                     nchange->old_name = (char *)u4alloc_free( c4, strlen(str_buf)+1 );
 | 
						|
                     if( nchange->old_name )
 | 
						|
                     {
 | 
						|
                        strcpy( nchange->old_name, str_buf );
 | 
						|
                        rc = AlternateDataFile( dname_buf, 256 );
 | 
						|
                        if( rc == 0 )
 | 
						|
                        {
 | 
						|
                           u4name_piece( str_buf, 256, dname_buf, 0, 0 );
 | 
						|
                           nchange->new_name = (char *)u4alloc_free(c4,strlen(str_buf)+1 );
 | 
						|
                           if( nchange->new_name )
 | 
						|
                           {
 | 
						|
                              strcpy( nchange->new_name, str_buf );
 | 
						|
                              l4add( &name_list, nchange );
 | 
						|
                              repeat_flag  = 1;
 | 
						|
                           }
 | 
						|
                           else
 | 
						|
                           {
 | 
						|
                              u4free( nchange->old_name );
 | 
						|
                              u4free( nchange );
 | 
						|
                              err_flag = 1;
 | 
						|
                           }
 | 
						|
                        }
 | 
						|
                        else
 | 
						|
                           err_flag = 1;
 | 
						|
                     }
 | 
						|
                     else
 | 
						|
                     {
 | 
						|
                        u4free( nchange );
 | 
						|
                        err_flag = 1;
 | 
						|
                     }
 | 
						|
                  }
 | 
						|
                  else
 | 
						|
                     err_flag = 1;
 | 
						|
                  #else
 | 
						|
                  /* if not in the executable report an error */
 | 
						|
                  if( spath && spath[0] != '\0' )
 | 
						|
                     e4describe( c4, e4report, E4_REP_DFILE, tempname_buf, (char *)0 );
 | 
						|
                  else
 | 
						|
                     e4describe( c4, e4report, E4_REP_DFILE, dname_buf, (char *)0 );
 | 
						|
                  err_flag = 1;
 | 
						|
                  #endif
 | 
						|
               }
 | 
						|
               e4set( c4, 0 );
 | 
						|
            }
 | 
						|
         }
 | 
						|
      }
 | 
						|
 | 
						|
      if( !err_flag && !err_code )
 | 
						|
      {
 | 
						|
         /* loop through index file names */
 | 
						|
         iname = (INAME4 *)l4first( &indexes );
 | 
						|
         while( iname )
 | 
						|
         {
 | 
						|
            repeat_flag = 1;
 | 
						|
            while( repeat_flag )
 | 
						|
            {
 | 
						|
               repeat_flag = 0;
 | 
						|
               index = NULL;
 | 
						|
               iindex = 0;
 | 
						|
 | 
						|
               u4name_piece( str_buf, 256, iname->index_name, 0, 0 );
 | 
						|
               tname_err = c4->tag_name_error;
 | 
						|
               c4->tag_name_error = 0;
 | 
						|
 | 
						|
               /* check to see if the index is already open */
 | 
						|
               iindex = r4index_lookup( data, str_buf );
 | 
						|
               c4->tag_name_error = tname_err;
 | 
						|
               e4set( c4, 0 );
 | 
						|
 | 
						|
               /* if not already open */
 | 
						|
               if( !iindex )
 | 
						|
               {
 | 
						|
                  /* if not allowed to open files report an error */
 | 
						|
                  if( !open_files )
 | 
						|
                  {
 | 
						|
                     e4describe( c4, e4result, E4_RESULT_LCF, iname_buf, (char *)0 );
 | 
						|
                     err_flag = 1;
 | 
						|
                  }
 | 
						|
 | 
						|
                  /* by default this compile switch is defined in r4report.h
 | 
						|
                     it forces the index file name to have the default extension
 | 
						|
                     for the compile flag, regardless of the extension saved in
 | 
						|
                     the report.  IF the user is using a different extension
 | 
						|
                     he should undefine this switch */
 | 
						|
                  #ifdef S4DEFAULT_INDEX
 | 
						|
                  lExt = u4switch();
 | 
						|
                  if( lExt & 1 )
 | 
						|
                     u4name_ext( iname->index_name, strlen(iname->index_name)+1, "cdx", 1 );
 | 
						|
                  else
 | 
						|
                     if( lExt & 2 )
 | 
						|
                        u4name_ext( iname->index_name, strlen(iname->index_name)+1, "ntx", 1 );
 | 
						|
                     else
 | 
						|
                        if( lExt & 4 )
 | 
						|
                           u4name_ext( iname->index_name, strlen(iname->index_name)+1, "mdx", 1 );
 | 
						|
                        else
 | 
						|
                           if( lExt & 8 )
 | 
						|
                              u4name_ext( iname->index_name, strlen(iname->index_name)+1, "ndx", 1 );
 | 
						|
                  #endif
 | 
						|
 | 
						|
                  /* attempt to open the index file */
 | 
						|
                  if( spath && spath[0] != '\0' )
 | 
						|
                  {
 | 
						|
                     u4name_piece( str_buf, 256, iname->index_name, 0, 1 );
 | 
						|
                     strcpy( tempname_buf, spath );
 | 
						|
                     strcat( tempname_buf, "\\" );
 | 
						|
                     strcat( tempname_buf, str_buf );
 | 
						|
                     index = i4open( data, tempname_buf );
 | 
						|
                  }
 | 
						|
                  else
 | 
						|
                     index = i4open( data, iname->index_name );
 | 
						|
 | 
						|
                  /* if open fails */
 | 
						|
                  if( !index )
 | 
						|
                  {
 | 
						|
                     /* if in CR executable prompt for alternate */
 | 
						|
                     #ifdef S4CR2
 | 
						|
                     lstrcpy( str_buf, iname->index_name );
 | 
						|
                     rc = AlternateIndexFile( str_buf, 256 );
 | 
						|
                     if( rc == 0 )
 | 
						|
                     {
 | 
						|
                        repeat_flag = 1;
 | 
						|
                        u4free( iname->index_name );
 | 
						|
                        iname->index_name = NULL;
 | 
						|
                        iname->index_name = (char *)u4alloc( lstrlen(str_buf)+1 );
 | 
						|
                        if( iname->index_name )
 | 
						|
                           lstrcpy( iname->index_name, str_buf );
 | 
						|
                     }
 | 
						|
                     else
 | 
						|
                        err_flag = 1;
 | 
						|
                     #else
 | 
						|
                     /* report an error */
 | 
						|
                     if( spath && spath[0] != '\0' )
 | 
						|
                        e4describe( c4, e4report, E4_REP_IFILE, tempname_buf, (char *)0 );
 | 
						|
                     else
 | 
						|
                        e4describe( c4, e4report, E4_REP_IFILE, iname_buf, (char *)0 );
 | 
						|
                     err_flag = 1;
 | 
						|
                     #endif
 | 
						|
                  }
 | 
						|
                  e4set( c4, 0 );
 | 
						|
               }
 | 
						|
            }
 | 
						|
            iname = (INAME4 *)l4next( &indexes, iname );
 | 
						|
         }
 | 
						|
      }
 | 
						|
 | 
						|
      if( !err_flag && !err_code )
 | 
						|
      {
 | 
						|
         tag = NULL;
 | 
						|
 | 
						|
         /* if this is the first relate do a relate4init() */
 | 
						|
         if( !relate )
 | 
						|
         {
 | 
						|
            master = relate = relate4init( data );
 | 
						|
            if( !relate )
 | 
						|
               goto CLEANUP;
 | 
						|
 | 
						|
            /* set the internal flags */
 | 
						|
            relate->match_len = match_len;
 | 
						|
            relate->sort_type = sort_type;
 | 
						|
            relate->relation_type = relation_type;
 | 
						|
            relate->error_action = error_action;
 | 
						|
 | 
						|
            /* if a tag was specified set it */
 | 
						|
            if( strlen(tname_buf) )
 | 
						|
            {
 | 
						|
               repeat_flag = 1;
 | 
						|
               while( repeat_flag )
 | 
						|
               {
 | 
						|
                  repeat_flag = 0;
 | 
						|
                  tag = NULL;
 | 
						|
                  tag = d4tag( data, tname_buf );
 | 
						|
                  if( tag )
 | 
						|
                  {
 | 
						|
                     relate->data_tag = tag;
 | 
						|
                     d4tag_select( data, tag );
 | 
						|
                  }
 | 
						|
                  #ifdef S4CR2
 | 
						|
                  else
 | 
						|
                  {
 | 
						|
                     /* if the specified tag is not available and this is the
 | 
						|
                        .exe prompt for an alternate */
 | 
						|
                     e4set( c4, 0 );
 | 
						|
                     rc = AlternateTagName( tname_buf, 256 );
 | 
						|
                     if( rc == 0 )
 | 
						|
                        repeat_flag = 1;
 | 
						|
                  }
 | 
						|
                  #else
 | 
						|
                  else
 | 
						|
                     e4set( c4, 0 );
 | 
						|
                  #endif
 | 
						|
               }
 | 
						|
            }
 | 
						|
         }
 | 
						|
         else
 | 
						|
         {
 | 
						|
            /* if not the first relate do a create slave */
 | 
						|
            /* start by getting a tag pointer from the tag name */
 | 
						|
            if( strlen(tname_buf) )
 | 
						|
            {
 | 
						|
               repeat_flag = 1;
 | 
						|
               while( repeat_flag )
 | 
						|
               {
 | 
						|
                  repeat_flag = 0;
 | 
						|
                  tag = NULL;
 | 
						|
                  tag = d4tag( data, tname_buf );
 | 
						|
                  if( !tag )
 | 
						|
                  #ifdef S4CR2
 | 
						|
                  {
 | 
						|
                     e4set( c4, 0 );
 | 
						|
                     rc = AlternateTagName( tname_buf, 256 );
 | 
						|
                     if( rc == 0 )
 | 
						|
                        repeat_flag = 1;
 | 
						|
                     else
 | 
						|
                     {
 | 
						|
                        goto CLEANUP;
 | 
						|
                     }
 | 
						|
                  }
 | 
						|
                  #else
 | 
						|
                  {
 | 
						|
                     e4describe( c4, e4report, E4_REP_NOTAG, (char *)tname_buf, 0 );
 | 
						|
                     e4set( c4, 0 );
 | 
						|
                     goto CLEANUP;
 | 
						|
                  }
 | 
						|
                  #endif
 | 
						|
               }
 | 
						|
            }
 | 
						|
 | 
						|
            /* try to create the slave */
 | 
						|
            repeat_flag = 1;
 | 
						|
            while( repeat_flag )
 | 
						|
            {
 | 
						|
               repeat_flag = 0;
 | 
						|
               expr = expr4parse( master->data, master_expr_buf );
 | 
						|
               if( expr )
 | 
						|
               {
 | 
						|
                  expr4free( expr );
 | 
						|
                  relate = relate4create_slave( master, data, master_expr_buf, tag );
 | 
						|
               }
 | 
						|
               #ifdef S4CR2
 | 
						|
               else
 | 
						|
               {
 | 
						|
                  /* if create slave fales prompt for a different master expr */
 | 
						|
                  e4set( c4, 0 );
 | 
						|
                  rc = AlternateMasterExpression( master_expr_buf, master, 1024 );
 | 
						|
                  if( rc == 0 )
 | 
						|
                     repeat_flag = 1;
 | 
						|
                  else
 | 
						|
                     relate = NULL;
 | 
						|
               }
 | 
						|
               #else
 | 
						|
               else
 | 
						|
               {
 | 
						|
                  /* report an error */
 | 
						|
                  e4describe( c4, e4report, E4_REP_NOMEXPR, 0, 0 );
 | 
						|
                  e4set( c4, 0 );
 | 
						|
                  relate = NULL;
 | 
						|
               }
 | 
						|
               #endif
 | 
						|
            }
 | 
						|
 | 
						|
            if( !relate )
 | 
						|
               goto CLEANUP;
 | 
						|
            relate->match_len = match_len;
 | 
						|
            relate->sort_type = sort_type;
 | 
						|
            relate->relation_type = relation_type;
 | 
						|
            relate->error_action = error_action;
 | 
						|
         }
 | 
						|
      }
 | 
						|
 | 
						|
      /* free the index name list */
 | 
						|
      iname = (INAME4 *)l4first( &indexes );
 | 
						|
      while( iname )
 | 
						|
      {
 | 
						|
         nname = (INAME4 *)l4next( &indexes, iname );
 | 
						|
         l4remove( &indexes, iname );
 | 
						|
         u4free( iname->index_name );
 | 
						|
         u4free( iname );
 | 
						|
         iname = nname;
 | 
						|
      }
 | 
						|
 | 
						|
      /* if end of relates leave the loop */
 | 
						|
      if( code == 2 )
 | 
						|
         break;
 | 
						|
 | 
						|
      if( err_flag == 0 && err_code > 0 )
 | 
						|
      {
 | 
						|
         err_code += code;
 | 
						|
         if( err_code <= 0 )
 | 
						|
         {
 | 
						|
            err_code = 0;
 | 
						|
            code = 1;
 | 
						|
         }
 | 
						|
      }
 | 
						|
 | 
						|
      if( err_flag == 1 )
 | 
						|
      {
 | 
						|
         err_flag = 0;
 | 
						|
         err_code = 1;
 | 
						|
      }
 | 
						|
 | 
						|
      if( err_code <= 0 )
 | 
						|
      {
 | 
						|
         master = relate;
 | 
						|
         while( code++ <= 0 )
 | 
						|
            master = master->master;
 | 
						|
      }
 | 
						|
   }
 | 
						|
 | 
						|
   /* deal with the sort and query expressions */
 | 
						|
   memset( master_expr_buf, 0, 1024 );
 | 
						|
   memset( slave_expr_buf, 0, 1024 );
 | 
						|
   retrieve4string( seq, master_expr_buf, 1024 );
 | 
						|
   cptr = master_expr_buf;
 | 
						|
   report4nchange( c4, &cptr, 0, 1024 );
 | 
						|
   retrieve4string( seq, slave_expr_buf, 1024 );
 | 
						|
   cptr = slave_expr_buf;
 | 
						|
   report4nchange( c4, &cptr, 0, 1024 );
 | 
						|
   if( master )
 | 
						|
   {
 | 
						|
      repeat_flag = 1;
 | 
						|
      if( strlen( slave_expr_buf) > 0 )
 | 
						|
      while( repeat_flag )
 | 
						|
      {
 | 
						|
         repeat_flag = 0;
 | 
						|
         expr = expr4parse( master->data, slave_expr_buf );
 | 
						|
         if( expr )
 | 
						|
         {
 | 
						|
            expr4free( expr );
 | 
						|
            relate4sort_set( master, slave_expr_buf );
 | 
						|
         }
 | 
						|
         #ifdef S4CR2
 | 
						|
         else
 | 
						|
         {
 | 
						|
            e4set( c4, 0 );
 | 
						|
            rc = AlternateSortExpression( slave_expr_buf, master, 1024 );
 | 
						|
            if( rc == 0 )
 | 
						|
               repeat_flag = 1;
 | 
						|
         }
 | 
						|
         #else
 | 
						|
         else
 | 
						|
         {
 | 
						|
            e4describe( c4, e4report, E4_REP_NOSORT, 0, 0 );
 | 
						|
            e4set( c4, 0 );
 | 
						|
         }
 | 
						|
         #endif
 | 
						|
      }
 | 
						|
 | 
						|
      repeat_flag = 1;
 | 
						|
      if( strlen( master_expr_buf ) > 0 )
 | 
						|
      while( repeat_flag )
 | 
						|
      {
 | 
						|
         repeat_flag = 0;
 | 
						|
         expr = expr4parse( master->data, master_expr_buf );
 | 
						|
         if( expr )
 | 
						|
         {
 | 
						|
            expr4free( expr );
 | 
						|
            relate4query_set( master, master_expr_buf );
 | 
						|
         }
 | 
						|
         #ifdef S4CR2
 | 
						|
         else
 | 
						|
         {
 | 
						|
            e4set( c4, 0 );
 | 
						|
            rc = AlternateQueryExpression( master_expr_buf, master, 1024 );
 | 
						|
            if( rc == 0 )
 | 
						|
               repeat_flag = 1;
 | 
						|
         }
 | 
						|
         #else
 | 
						|
         else
 | 
						|
         {
 | 
						|
            e4describe( c4, e4report, E4_REP_NOQUERY, 0, 0 );
 | 
						|
            e4set( c4, 0 );
 | 
						|
         }
 | 
						|
         #endif
 | 
						|
      }
 | 
						|
   }
 | 
						|
 | 
						|
   if( relate )
 | 
						|
      return( &relate->relation->relate );
 | 
						|
   else
 | 
						|
      return NULL;
 | 
						|
 | 
						|
CLEANUP:
 | 
						|
   iname = (INAME4 *)l4first( &indexes );
 | 
						|
   while( iname )
 | 
						|
   {
 | 
						|
      nname = (INAME4 *)l4next( &indexes, iname );
 | 
						|
      l4remove( &indexes, iname );
 | 
						|
      u4free( iname->index_name );
 | 
						|
      u4free( iname );
 | 
						|
      iname = nname;
 | 
						|
   }
 | 
						|
 | 
						|
   if( master )
 | 
						|
   {
 | 
						|
      relate4free( master, open_files );
 | 
						|
   }
 | 
						|
   else
 | 
						|
   {
 | 
						|
      if( relate )
 | 
						|
         relate4free( relate, open_files );
 | 
						|
   }
 | 
						|
 | 
						|
   if( error_code )
 | 
						|
   {
 | 
						|
      e4describe( c4, e4report, E4_REP_RELERR, 0, 0 );
 | 
						|
   }
 | 
						|
   return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* wrapper for above function */
 | 
						|
RELATE4 * S4FUNCTION   relate4retrieve_relate( FILE4SEQ_READ *seq, int open_files, char *spath )
 | 
						|
{
 | 
						|
   char *dname_buf = NULL, *iname_buf = NULL, *tname_buf = NULL, *str_buf = NULL;
 | 
						|
   char *master_expr_buf = NULL, *slave_expr_buf = NULL, *tempname_buf = NULL;
 | 
						|
   RELATE4 *retvalue = NULL;
 | 
						|
 | 
						|
   dname_buf = (char *)u4alloc_free( seq->file->code_base, 256 );
 | 
						|
   iname_buf = (char *)u4alloc_free( seq->file->code_base, 256 );
 | 
						|
   tname_buf = (char *)u4alloc_free( seq->file->code_base, 256 );
 | 
						|
   str_buf = (char *)u4alloc_free( seq->file->code_base, 256 );
 | 
						|
   master_expr_buf = (char *)u4alloc_free( seq->file->code_base, 1024 );
 | 
						|
   slave_expr_buf = (char *)u4alloc_free( seq->file->code_base, 1024 );
 | 
						|
   tempname_buf = (char *)u4alloc_free( seq->file->code_base, 512 );
 | 
						|
   if( !dname_buf || !iname_buf || !tname_buf || !str_buf ||
 | 
						|
       !master_expr_buf || !slave_expr_buf || !tempname_buf )
 | 
						|
      goto R4LEAVE;
 | 
						|
 | 
						|
   retvalue = relate4retrieve_relate_foo( seq, open_files, spath,
 | 
						|
                 dname_buf, iname_buf, tname_buf, str_buf, master_expr_buf,
 | 
						|
                 slave_expr_buf, tempname_buf );
 | 
						|
 | 
						|
R4LEAVE:
 | 
						|
   if( dname_buf )
 | 
						|
      u4free( dname_buf );
 | 
						|
   if( iname_buf )
 | 
						|
      u4free( iname_buf );
 | 
						|
   if( tname_buf )
 | 
						|
      u4free( tname_buf );
 | 
						|
   if( str_buf )
 | 
						|
      u4free( str_buf );
 | 
						|
   if( master_expr_buf )
 | 
						|
      u4free( master_expr_buf );
 | 
						|
   if( slave_expr_buf )
 | 
						|
      u4free( slave_expr_buf );
 | 
						|
   if( tempname_buf )
 | 
						|
      u4free( tempname_buf );
 | 
						|
 | 
						|
   return retvalue;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/* see the CodeReporter manual */
 | 
						|
int S4FUNCTION obj4dataFieldSet( POBJ4 obj, char *fname, char ftype, int flength, int fdec )
 | 
						|
{
 | 
						|
   POUT4OBJ oobj;
 | 
						|
   PREPORT4 report;
 | 
						|
 | 
						|
   if( !obj )
 | 
						|
      return -1;
 | 
						|
 | 
						|
   report = obj->area->report;
 | 
						|
   obj->field_type = ftype;
 | 
						|
   obj->field_len = flength;
 | 
						|
   obj->field_dec = fdec;
 | 
						|
   if( fname )
 | 
						|
      u4ncpy( obj->field_name, fname, sizeof(obj->field_name) );
 | 
						|
   else
 | 
						|
 | 
						|
   oobj = (POUT4OBJ)l4first( &report->output_objs );
 | 
						|
   while( oobj )
 | 
						|
   {
 | 
						|
      if( oobj->obj == obj )
 | 
						|
      {
 | 
						|
         if( fname == NULL || ftype == 0 || flength == 0 )
 | 
						|
            l4remove( &report->output_objs, oobj );
 | 
						|
         return 0;
 | 
						|
      }
 | 
						|
      oobj = (POUT4OBJ)l4next( &report->output_objs, oobj );
 | 
						|
   }
 | 
						|
 | 
						|
   oobj = (POUT4OBJ)u4alloc_free( obj->area->report->code_base, sizeof(OUT4OBJ) );
 | 
						|
   if( !oobj )
 | 
						|
      return -1;
 | 
						|
   oobj->obj = obj;
 | 
						|
   l4add( &report->output_objs, oobj );
 | 
						|
   return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* see the CodeReporter manual */
 | 
						|
int S4FUNCTION report4dataFileSet( PREPORT4 report, char *fname )
 | 
						|
{
 | 
						|
   if( !report )
 | 
						|
      return -1;
 | 
						|
 | 
						|
   if( report->dfile_name )
 | 
						|
      u4free( report->dfile_name );
 | 
						|
   report->dfile_name = NULL;
 | 
						|
 | 
						|
   if( fname == NULL )
 | 
						|
      return 0;
 | 
						|
 | 
						|
   report->dfile_name = (char *)u4alloc_er( report->code_base, strlen(fname)+1 );
 | 
						|
   if( !report->dfile_name )
 | 
						|
      return -1;
 | 
						|
 | 
						|
   u4ncpy( report->dfile_name, fname, strlen(fname)+1 );
 | 
						|
   return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* see the CodeReporter manual */
 | 
						|
int S4FUNCTION report4dataGroup( PREPORT4 report, PGROUP4 group )
 | 
						|
{
 | 
						|
   if( !report )
 | 
						|
      return -1;
 | 
						|
 | 
						|
   report->output_group = group;
 | 
						|
   return 0;
 | 
						|
}
 |