/* @(#)marker.c	1.5	2/10/93 */
/*******************************************************************************

This code was written and designed by 
Andrew Gene HALL of I.N. Services Pty. Ltd., Scarborough WA, Australia,
for the 
University of Western Australia, Crawley WA, Australia.

*******************************************************************************/

#include <string.h>
#include "utilitys.h"
#include "marker.h"

/* compare_2_Data_Seqs
 * Compare the names associated with two given nodes of Data_Seq.
 * Return the <1, 0, >1 for the first being alphabetically less than, equal to,
 * or greater than the second.
 */
int             compare_2_Data_Seqs(
Data_Seq        *node1,
Data_Seq        *node2)
{
   if( node1 && node2 ) {
      return( strcmp( node1->name, node2->name ) );
   }
   else {
      ERROR_MSG("compare_2_Data_Seqs: could not compare null pointers.");
      return( 0 );
   }
} /* compare_2_Data_Seqs */

/* First_Time_Found
 * Use tsearch to remember which Data nodes we've already got.
 * Return true if this data hasn't been recorded before.
 */	
Boolean First_Time_Found(
Data_Seq	*d,
Data_Seq	**root)
{                                                                           
   register Data_Seq    **found = NULL;                                      
   register Data_Seq    *t = NULL;                                      
                                                                              
   found = (Data_Seq **)tsearch((char *)d,(char **)root,compare_2_Data_Seqs);
   if( (*found) == d )
      return( TRUE );
   else {
      return( FALSE );
   }
} /* First_Time_Found */

int	compare_2_DI( 
Data_Item	*p1,
Data_Item	*p2)
{
   if( p1 < p2 ) return( -1 );
   else if( p1 == p2 ) return( 0 );
   else return( 1 );
} /* compare_2_DI */

/* Mark_LVS
 * Mark all the data items in a LVS.
 */
void	Mark_LVS(
List_Value_Seq	*list,
Data_Item	**Root_Data_Item,
char		**Frame_Codes)
{
   for( ; list; list = list->next) {
      switch( list->tag ) {

         case DATA_ITEM:
              Mark_Data_Item( list->element.data_item, Root_Data_Item );
              if( list->element.data_item->tag == FRAME_CODE_DI )
                 /* save the reference for later */
                 (void)tsearch(list->element.data_item->value,Frame_Codes,strcmp);
              break;

	 case SUB_LOOP:
              Mark_LVS(list->element.sub_loop, Root_Data_Item, Frame_Codes);
              break;

         default:
              ERROR_MSG("Mark_LVS(): Unknown domain in LVS.");
      }
   }
} /* Mark_LVS */

/* refs_to_SB_from_LVS
 * Find all references in a list value sequence to a set of given Save Blocks.
 */
Boolean	refs_to_SB_from_LVS(
List_Value_Seq  *data,
char            **frame_codes,
Data_Item       **Root_Data_Item)
{
   register Boolean     ref_found = FALSE;

   while( data ) {
      switch( data->tag ) {

      case DATA_ITEM:
           if( data->element.data_item->tag == FRAME_CODE_DI ) 
              if( tfind( data->element.data_item->value, frame_codes, strcmp ))
                 /* found a referencing data_item */
                 /* check in case we've already found it before */
                 if( ! (Data_Item **)tfind((char *)data->element.data_item,
                                           (char **)Root_Data_Item,
                                           compare_2_DI) ) {
                    Mark_Data_Item( data->element.data_item, Root_Data_Item);
                    ref_found = TRUE;
                 }
           break;

      case SUB_LOOP:
           if( refs_to_SB_from_LVS( data->element.sub_loop, 
                                    frame_codes, Root_Data_Item) )
              ref_found = TRUE;
           break;

      default:
           ERROR_MSG("refs_to_SB_from_LVS(): Unknown domain in LVS.");
      }
      data = data->next;
   }
   return( ref_found );
} /* refs_to_SB_from_LVS */

/* refs_to_SB
 * Find all references in a data sequence to set of given Save Blocks.
 * Update the set of found Data Items
 * Return whether any fresh references were found or not.
 */
Boolean	refs_to_SB(
Data_Seq	*start_data,
char		**frame_codes,
Data_Item	**Root_Data_Item)
{
   register Data_Seq	*data = start_data;
   register Boolean	ref_found = FALSE;

   while( data ) {
      switch( data->tag ) {

      case DATA_ITEM:
           if( data->data.data_item->tag == FRAME_CODE_DI )
              if( tfind( data->data.data_item->value, frame_codes, strcmp ) )
                 /* found a referencing data_item */
                 /* check in case we've already found it before */
                 if( ! (Data_Item **)tfind((char *)data->data.data_item,
                                           (char **)Root_Data_Item,
                                           compare_2_DI) ) {
                    Mark_Data_Item( data->data.data_item, Root_Data_Item);
                    ref_found = TRUE;
                 }
           break;

      case LOOP:
           if( refs_to_SB_from_LVS( data->data.loop.values, 
                                            frame_codes, Root_Data_Item) )
              ref_found = TRUE;
           break;

      case SAVE_BLOCK:
           if( refs_to_SB( data->data.save_block, frame_codes, Root_Data_Item)){
              char	*s;

              /* add this save_frame to the set of those being checked for */
              CONVERT_TO_FRAME_CODE( s, data->name);
              (void)tsearch( s, frame_codes, strcmp);
              /* start the search again this time including this block */
              refs_to_SB( start_data, frame_codes, Root_Data_Item);
              return( TRUE );
           }
           break;

      default:
           ERROR_MSG("refs_to_SB(): Unknown Domain in Sequence.");
      }
      data = data->next;
   }
   return( ref_found );
} /* refs_to_SB */

/* refs_by_frame_codes
 * Scan a data_sequence to find any Save_Blocks referenced by a set of
   frame codes.
 * Update the marked data items.
*/
void	refs_by_frame_codes(
Data_Seq	*data,
Data_Item	**Root_Data_Item,
char		**Frame_Codes,
char		**save_refs,
char		**Known_SBs)
{
   register Data_Seq	*d;
   char	*s;

   for( d = data; d; d = d->next)
      /* find the next Save Block */
      if( d->tag == SAVE_BLOCK )
         /* if its not already known */
         if( ! tfind( d->name, Known_SBs, strcmp) ) {
            /* check to see if its referenced */
            CONVERT_TO_FRAME_CODE(s, d->name);
            if( tfind( s, Frame_Codes, strcmp) ) {
               register Data_Seq	*pos;

               /* mark the entire contents of the save block,
                  updating any references it makes */
               for( pos = d->data.save_block; pos; pos = pos->next)
                  switch( pos->tag ) {
                  case DATA_ITEM:
                       Mark_Data_Item( pos->data.data_item, Root_Data_Item );
                       if( pos->data.data_item->tag == FRAME_CODE_DI )
                          /* save the reference for later */
                          (void)tsearch(pos->data.data_item->value,Frame_Codes,strcmp);
                       break;

                  case LOOP:
                       Mark_LVS(pos->data.loop.values,Root_Data_Item,Frame_Codes);
                       break;

                  default:
                       ERROR_MSG("refs_by_frame_codes: unknown domain in sequence.");
                  }

               /* mark this block as known */
               tsearch( d->name, Known_SBs, strcmp);

               /* Add the save_frame to those to trace later */
               (void)tsearch( s, save_refs, strcmp);

               /* restart the search */
               refs_by_frame_codes(data,Root_Data_Item,Frame_Codes,save_refs,
                                   Known_SBs);

               /* terminate the scanning,
                  as the recursion would of done the rest */
               return;
            }
         }
   return;
} /* refs_by_frame_codes */

/* isa_Marked_Data_Item
 * Check that a given data item has been previously marked
 */
Boolean	isa_Marked_Data_Item(
Data_Item	*d,
Data_Item	**Root_Data_Items)
{
   if( tfind( (char *)d, (char **)Root_Data_Items, compare_2_DI) )
      return( TRUE );
   else
      return( FALSE );
} /* isa_Marked_Data_Item */

/* get_Marked_LVS
 * Return an LVS of those previously marked.
 */
List_Value_Seq	*get_Marked_LVS(
List_Value_Seq	*list,
Data_Item	**Root_Data_Item)
{
   List_Value_Seq	*tmp_list, *last_list, *tmp;

   tmp_list = last_list = NULL;
   for( ; list; list = list->next)
      switch( list->tag ) {

         case DATA_ITEM:
              if( isa_Marked_Data_Item(list->element.data_item,Root_Data_Item)){
                 /* add a list node */
                 if( last_list ) {
                    Malloc(List_Value_Seq,last_list->next);
                    last_list = last_list->next;
                 }
                 else {
                    Malloc( List_Value_Seq, last_list );
                    tmp_list = last_list;
                 }
                 /* add the data */
                 last_list->tag = DATA_ITEM;
                 last_list->element.data_item = list->element.data_item;
                 last_list->next = NULL;
              }
              break;

	 case SUB_LOOP:
              if( tmp = get_Marked_LVS(list->element.sub_loop,Root_Data_Item)) {
                 /* add a list node */
                 if( last_list ) {
                    Malloc(List_Value_Seq,last_list->next);
                    last_list = last_list->next;
                 }
                 else {
                    Malloc( List_Value_Seq, last_list );
                    tmp_list = last_list;
                 }
                 /* add the data */
                 last_list->tag = SUB_LOOP;
                 last_list->element.sub_loop = tmp;
                 last_list->next = NULL;
              }
              break;

         default:
              ERROR_MSG("get_Marked_LVS(): Unknown domain in LVS.");
      }
   return( tmp_list );
} /* get_Marked_LVS */

/* get_Marked_DS
 * Return a Data Sequence of those Data Items previousely marked.
 */
Data_Seq	*get_Marked_DS(
Data_Seq	*data,
Data_Item	**Root_Data_Item,
char		**Known_Save_Blocks)
{
   Data_Seq	*tmp_ds, *last_ds;

   tmp_ds = last_ds = NULL;
   for( ; data; data = data->next)
      switch( data->tag ) {
         case DATA_ITEM:
              if( isa_Marked_Data_Item( data->data.data_item,Root_Data_Item) ) {
                 /* add a data node */
                 if( last_ds ) {
                    Malloc( Data_Seq, last_ds->next);
                    last_ds = last_ds->next;
                 }
                 else {
                    Malloc( Data_Seq, last_ds );
                    tmp_ds = last_ds;
                 }
                 /* add the data */
                 last_ds->name = data->name;
                 last_ds->tag = DATA_ITEM;
                 last_ds->data.data_item = data->data.data_item;
                 last_ds->next = NULL;
              }
              break;

         case LOOP: {
              List_Value_Seq	*tmp;

              if( tmp = get_Marked_LVS( data->data.loop.values,Root_Data_Item)){
                 /* add a data node */
                 if( last_ds ) {
                    Malloc( Data_Seq, last_ds->next);
                    last_ds = last_ds->next;
                 }
                 else {
                    Malloc( Data_Seq, last_ds );
                    tmp_ds = last_ds;
                 }
                 /* add the data */
                 last_ds->name = data->name;
                 last_ds->tag = LOOP;
                 last_ds->data.loop.packet_members = data->data.loop.packet_members;
                 last_ds->data.loop.values = tmp;
                 last_ds->next = NULL;
              }
              } break;

         case SAVE_BLOCK: {
              Data_Seq	*tmp;

              if( tmp = get_Marked_DS( data->data.save_block,Root_Data_Item,Known_Save_Blocks) ) {
                 /* add a data node */
                 if( last_ds ) {
                    Malloc( Data_Seq, last_ds->next);
                    last_ds = last_ds->next;
                 }
                 else {
                    Malloc( Data_Seq, last_ds );
                    tmp_ds = last_ds;
                 }
                 /* add the data */
                 last_ds->name = data->name;
                 last_ds->tag = SAVE_BLOCK;
                 last_ds->data.save_block = tmp;
                 last_ds->next = NULL;
              }
              } break;

         default:
              ERROR_MSG("get_Marked_DS(): Unknown Domain in Data Sequence.");
      }
   return( tmp_ds );
} /* get_Marked_DS */

