home *** CD-ROM | disk | FTP | other *** search
/ ftp.ee.lbl.gov / 2014.05.ftp.ee.lbl.gov.tar / ftp.ee.lbl.gov / mrdebug.tar.Z / mrdebug.tar / user.c < prev   
C/C++ Source or Header  |  1993-10-25  |  26KB  |  845 lines

  1.  
  2. #include    <stdio.h>
  3. #include    <string.h>
  4. #include        <sysexits.h>
  5.  
  6. #include       "types.h"
  7. #include       "mrhash.h"
  8. #include       "data_struct_proto.h"
  9. #include       "build_adj_proto.h"
  10. #include       "dist_vect_proto.h"
  11. #include       "user_proto.h"
  12.  
  13. /********* EXTERNAL REFERENCES ***********/
  14. extern          int       verbose;
  15. extern            int      reverse_path;
  16.  
  17. extern int disable_ifc(Network *net, NAME frm_name, NAME to_name, FILE *out );
  18.  
  19. /****************************************************************
  20.  
  21.   FUNCTION: parse_user_input( Network *net, NAME source, NAME dest )
  22.  
  23.   DESCRIPTION:
  24.        This function requests user input and resets the appropriate
  25.        variables for the next tree printout.
  26.  
  27. ********************************************************************/
  28.  
  29. int      parse_user_input( Network *net, NAME source, NAME dest, long *max_thresh,
  30.                Ifc_list *subnets, FILE **out )
  31. {
  32.    char       cur_input[MAXLINE],
  33.               other[MAXLINE],
  34.               other2[MAXLINE];
  35.    char       ans;
  36.    int        args;
  37.    long       number, number2;
  38.    Node      *node1;
  39.    HENTRY_t  *ht_entry_p;
  40.    Interface *cur_ifc = NULL;
  41.    NAME       frm_name, to_name;
  42.    Change_item *cur_change;
  43.    FILE       *net_out;
  44.  
  45.    if( verbose )
  46.       {
  47.       printf( "\n\nCurrent source: %s\n", source );
  48.       printf( "Current destination: %s\n", dest );
  49.       printf( "\nEnter command:  ");
  50.       }
  51.    else
  52.       printf( "\n> ");
  53.    strcpy( frm_name, "\0" );
  54.    strcpy( to_name, "\0" );
  55.    if( gets( cur_input ) == NULL )
  56.       {
  57.       printf( "\n");
  58.       exit(0);
  59.       }
  60.    ans = NULL;
  61.    args = sscanf( cur_input, "%c%s%s", &ans, &frm_name, &to_name );
  62.    switch( ans )
  63.       {
  64.        case 's':
  65.      /*
  66.      **     Specify a new source for the multicast
  67.      */
  68.      if( args == 2 )
  69.         strcpy( source, frm_name );
  70.      else
  71.         {
  72.         printf( "Enter the name of the source interface: ");
  73.         gets( cur_input );
  74.         sscanf( cur_input, "%s", source );
  75.         }
  76.      return FIND_TREE;
  77.      break;
  78.        case 'd':
  79.      /*
  80.      **     Specify a new destination for the multicast
  81.      */
  82.      if( args == 2 )
  83.         strcpy( dest, frm_name );
  84.      else
  85.         {
  86.         printf( "Enter the name of the destination interface: ");
  87.         gets( cur_input );
  88.         sscanf( cur_input, "%s", dest );
  89.         }
  90.      return FALSE;
  91.      break;
  92.        case 'o':
  93.      if( *out != stdout )
  94.         fclose( *out );
  95.      if( args == 1 )
  96.         {
  97.         printf( "Enter the name of the output file (and a to append): ");
  98.         gets( cur_input );
  99.         args = sscanf( cur_input, "%s%s", frm_name, to_name );
  100.         args++;
  101.         }
  102.      if( args > 2 && to_name[0] == 'a' )
  103.         {
  104.         strcpy( to_name, "a" );
  105.         if( verbose )
  106.            printf( "\nOutput will be appended to file %s\n", frm_name );
  107.         }
  108.      else if( args > 1 )
  109.         {
  110.         strcpy( to_name, "w" );
  111.         if( verbose )
  112.            printf( "\nOutput will be written to file %s\n", frm_name );
  113.         }
  114.  
  115.      if( args >= 2 )
  116.         {
  117.         if( !(*out = fopen( frm_name, to_name )))
  118.            {
  119.            printf( "Unable to open %s output will go to the terminal\n",
  120.                frm_name );
  121.            *out = stdout;
  122.            }
  123.         }
  124.      else
  125.         {
  126.         if( verbose )
  127.            printf( "\nOutput will be printed to the terminal\n");
  128.         *out = stdout;
  129.         }
  130.      break;
  131.        case 't':
  132.        case 'u':
  133.      /*
  134.      **     Print the multicast tree or unreachables for the current source.
  135.      **     Take into account the ttl if specified.
  136.      */
  137.      *max_thresh = -1;
  138.      if( args >= 3 )
  139.         {
  140.         strcpy( source, frm_name );
  141.         sscanf( to_name, "#%ld", max_thresh );
  142.         }
  143.      else if( args == 2 )
  144.         {
  145.         if( frm_name[0] == '#' )
  146.            sscanf( frm_name, "#%ld", max_thresh );
  147.         else
  148.            strcpy( source, frm_name );
  149.         }
  150.      if( ans == 't' )
  151.         return PRINT_TREE;
  152.      else
  153.         return PRINT_UNREACH;
  154.      break;
  155.        case 'p':
  156.      /*
  157.      **     Print the multicast path from the current source to
  158.      **     the current destination.
  159.      */
  160.      if( args == 3 )
  161.         {
  162.         strcpy( source, frm_name );
  163.         strcpy( dest, to_name );
  164.         }
  165.      else if( args == 2 )
  166.         strcpy( dest, frm_name );
  167.      return PRINT_PATH;
  168.      break;
  169.        case 'l':
  170.          /*
  171.      **     Reverse the direction of the metric used in calculating
  172.      **     trees and paths.
  173.      */
  174.      if( reverse_path )
  175.         reverse_path = FALSE;
  176.      else
  177.         reverse_path = TRUE;
  178.      if( verbose )
  179.         {
  180.         if( reverse_path )
  181.             printf( "Now using the forward direction metrics\n");
  182.         else
  183.             printf( "Now using the reverse direction metrics\n");
  184.         }
  185.      return FIND_TREE;
  186.      break;
  187.        case 'e':
  188.      /*
  189.      **     Print out explanatory text defining the output formats
  190.      */
  191.      explain_output( stdout );
  192.      break;
  193.        case 'n':
  194.      /*
  195.      **     Print out the adjacency list for a specified node
  196.      */
  197.      if( args < 2 )
  198.         {
  199.         printf( "Enter interface name: " );
  200.         gets( cur_input );
  201.         sscanf( cur_input, "%s", frm_name );
  202.         }
  203.      ht_entry_p = find_actual_name( net, frm_name, subnets );
  204.      if( ht_entry_p != NULL )
  205.         print_node( net, ht_entry_p->index, *out );
  206.      break;
  207.        case 'h':
  208.      /*
  209.      **    Print the history of changes made to the network starting
  210.      **    with the most recent and going back.  If an argument is
  211.      **    supplied, then only print that many changes.
  212.      */
  213.      number = -1;
  214.      if( args > 1 )
  215.         sscanf( frm_name, "%ld", &number );
  216.      print_changes( &net->changes, number, *out );
  217.      break;
  218.        case 'r':
  219.      /*
  220.      **    Disable a tunnel or interface, the type is determined
  221.      **    based on the number of input arguments supplied.  1
  222.      **    implies a subnet interface and 2 imply the tunnel between
  223.      **    the specified interfaces.
  224.      */
  225.      if( args > 1 )
  226.         args--;
  227.      else
  228.         {
  229.         printf( "Specify interface to be removed or two for a tunnel: ");
  230.         gets( cur_input );
  231.         args = sscanf( cur_input, "%s%s", frm_name, to_name );
  232.         }
  233.      if( args < 1 || args > 2 )
  234.         {
  235.         printf( "\nIncorrect input specified\n");
  236.         return FALSE;
  237.         break;
  238.         }
  239.      if( args == 2 )
  240.         {/*
  241.          **   Disable the specified tunnel
  242.          */
  243.         ht_entry_p = find_actual_name( net, frm_name, subnets );
  244.         strcpy( frm_name, ht_entry_p->key_p );
  245.         ht_entry_p = find_actual_name( net, to_name, subnets );
  246.         strcpy( to_name, ht_entry_p->key_p );
  247.         if( !disable_ifc( net, frm_name, to_name, *out ) )
  248.            return FALSE;
  249.         else
  250.            {
  251.            cur_change = push_change( &net->changes, DISABLE_TUNNEL, frm_name, to_name );
  252.            return FIND_TREE;
  253.            }
  254.         }
  255.      if( args == 1 )
  256.         {/*
  257.          **  Disable the specified interface and all tunnels associated
  258.          **  with it.
  259.          */
  260.         ht_entry_p = find_actual_name( net, frm_name, subnets );
  261.         strcpy( frm_name, ht_entry_p->key_p );
  262.         
  263.         if( ht_entry_p != NULL )
  264.            {
  265.            node1 = &(net->adj_list[ht_entry_p->index]);
  266.            cur_ifc = find_name( &node1->interfaces, frm_name );
  267.            }
  268.         if( cur_ifc == NULL )
  269.            {
  270.            printf( "\nInterface %s not found\n", frm_name );
  271.            return FALSE;
  272.            }
  273.         cur_change = push_change( &net->changes, DISABLE_IFC, frm_name, "\0" );
  274.         for( ; cur_ifc != NULL; cur_ifc = cur_ifc->next )
  275.            {
  276.            if( !(strcmp( cur_ifc->name, frm_name)))
  277.           disable_ifc( net, frm_name, cur_ifc->to_name, *out );
  278.            else
  279.           return FIND_TREE;
  280.            }
  281.         return FIND_TREE;
  282.         }
  283.      break;
  284.        case 'a':
  285.      /*
  286.      **    Add a tunnel or interface.  If the tunnel already
  287.      **    exists, it is simply reenabled.  When a subnet interface
  288.      **    is added, a search for all interfaces on the subnet is
  289.      **    undertaken and edges to and from all such interfaces are added.
  290.      */
  291.      if( args > 1 )
  292.         args--;
  293.      else
  294.         {
  295.         /*printf( "Warning only known interfaces can be specified\n");*/
  296.         printf( "Enter interface to add or two for a tunnel:");
  297.         gets( cur_input );
  298.         args = sscanf( cur_input, "%s%s", frm_name, to_name );
  299.         }
  300.      if( args < 1 || args > 2 )
  301.         {
  302.         printf( "\nInvalid input specified\n");
  303.         return FALSE;
  304.         break;
  305.         }
  306.      ht_entry_p = find_actual_name( net, frm_name, subnets );
  307.      if( ht_entry_p != NULL )
  308.         if( !compare_names( ht_entry_p->key_p, frm_name ) &&
  309.            (net->adj_list[ht_entry_p->index].type & SUBNET) )
  310.            ht_entry_p = NULL;
  311.            
  312.      if( ht_entry_p == NULL )
  313.         {
  314.         if( !add_new_node( net, frm_name, subnets ))
  315.            return FALSE;
  316.         else
  317.             ht_entry_p = find_actual_name( net, frm_name, subnets );
  318.         }
  319.      strcpy( frm_name, ht_entry_p->key_p );
  320.      node1 = &(net->adj_list[ht_entry_p->index]);
  321.      cur_ifc = find_name( &node1->interfaces, frm_name );
  322.  
  323.      if( args == 2 )
  324.         {
  325.         ht_entry_p = find_actual_name( net, to_name, subnets );
  326.         if( ht_entry_p != NULL )
  327.            if( !compare_names( ht_entry_p->key_p, to_name ) &&
  328.            (net->adj_list[ht_entry_p->index].type & SUBNET))
  329.           ht_entry_p = NULL;
  330.            
  331.         if( ht_entry_p == NULL )
  332.            {
  333.            if( !add_new_node( net, to_name, subnets ))
  334.           return FALSE;
  335.            else
  336.           ht_entry_p = find_actual_name( net, to_name, subnets );
  337.            }
  338.         strcpy( to_name, ht_entry_p->key_p );
  339.         cur_ifc = find_ifc( net, cur_ifc, frm_name, to_name );
  340.         if( cur_ifc != NULL )
  341.            {
  342.            printf( "\nenabling interface from %s to %s\n", frm_name, to_name );
  343.            if( *out != stdout )
  344.           fprintf(*out, "\nenabling interface from %s to %s\n", frm_name, to_name );
  345.            cur_ifc->type &= ~(DOWN|DISABLED);
  346.            }
  347.         else
  348.            {
  349.            printf( "Enter edge characteristics in [] separated by /'s:  ");
  350.            gets( other );
  351.            printf( "\nAdding edge %s to %s as %s \n", frm_name, to_name, other );
  352.            if( *out != stdout )
  353.           fprintf(*out, "\nAdding edge %s to %s as %s \n", frm_name, to_name, other );
  354.            strcpy( other2, other );
  355.            add_ifc( net, frm_name, to_name, other, FALSE);
  356.            }
  357.         cur_ifc = find_ifc( net, NULL, to_name, frm_name );
  358.         if( cur_ifc != NULL )
  359.            {
  360.            printf( "enabling edge from %s to %s\n", to_name, frm_name );
  361.            if( *out != stdout )
  362.           fprintf( *out,"enabling edge from %s to %s\n", to_name, frm_name );
  363.            cur_ifc->type &= ~(DOWN|DISABLED);
  364.            }
  365.         else
  366.            {
  367.            ht_entry_p = lookup_htable( &net->name_map, to_name );
  368.            printf( "Adding edge %s to %s as %s\n", to_name, frm_name, other2 );
  369.            if( *out != stdout )
  370.           fprintf(*out, "Adding edge %s to %s as %s\n", to_name, frm_name, other2 );
  371.            add_ifc( net, to_name, frm_name, other2, FALSE );
  372.            }
  373.         cur_change = push_change( &net->changes, ADD_TUNNEL, frm_name, to_name );
  374.         }
  375.      else if( args == 1 )
  376.         {
  377.         cur_change = push_change( &net->changes, ADD_IFC, frm_name, "\0" );
  378.         add_subnet_conn( net, cur_ifc, frm_name, *out );
  379.         }
  380.      return FIND_TREE;
  381.      break;
  382.        case 'c':
  383.      /*
  384.      **      Change a metric or threshold for a tunnel or a
  385.      **      subnet interface.
  386.      */
  387.      if( args == 1 )
  388.         {
  389.         printf( "Specify interface to be changed or two for a tunnel: ");
  390.         gets( cur_input );
  391.         args = sscanf( cur_input, "%s%s", frm_name, to_name );
  392.         args++;
  393.         }
  394.      if( args < 2 || args > 3 )
  395.         {
  396.         printf( "\nIncorrect input specified\n");
  397.         return FALSE;
  398.         break;
  399.         }
  400.      if( args == 3 )
  401.         {/*
  402.          **     The interface to be changed is a tunnel
  403.          */
  404.         ht_entry_p = find_actual_name( net, frm_name, subnets );
  405.         strcpy( frm_name, ht_entry_p->key_p );
  406.         ht_entry_p = find_actual_name( net, to_name, subnets );
  407.         strcpy( to_name, ht_entry_p->key_p );
  408.  
  409.         cur_ifc = find_ifc( net, NULL, frm_name, to_name );
  410.         }
  411.      if( args == 2 )
  412.         {/*
  413.          **      The interface to be changed is a subnet interface
  414.          */
  415.         ht_entry_p = find_actual_name( net, frm_name, subnets );
  416.         strcpy( frm_name, ht_entry_p->key_p );
  417.         if( ht_entry_p == NULL )
  418.            {
  419.            printf( "\nInvalid interface name specified %s\n", frm_name );
  420.            cur_ifc = NULL;
  421.            }
  422.         else
  423.            {
  424.            node1 = &(net->adj_list[ht_entry_p->index]);
  425.            cur_ifc = find_conn_subnet( &node1->interfaces, frm_name );
  426.            if( cur_ifc != NULL )
  427.           strcpy( to_name, cur_ifc->to_name );
  428.            else
  429.           printf( "\nInvalid interface name specified %s\n", frm_name );
  430.            }
  431.         }
  432.      if( cur_ifc != NULL )
  433.            {
  434.            printf( "Current metric = %ld and threshold = %ld\n",
  435.                cur_ifc->metric, cur_ifc->threshold);
  436.            printf( "Enter new metric and new threshold: ");
  437.            gets( cur_input );
  438.            args = sscanf( cur_input, "%ld%ld", &number, &number2 );
  439.            if( args >= 1 )
  440.           {/*
  441.            **     Changing the metric for this interface
  442.            */
  443.           cur_change = push_change( &net->changes, CHANGE_METRIC,
  444.                         frm_name, to_name );
  445.           cur_change->val1 = cur_ifc->metric;
  446.           cur_change->val2 = number;
  447.           printf( "\nChanging metric of %s %s from %ld to %ld\n", frm_name,
  448.               to_name, cur_change->val1, cur_change->val2);
  449.           if( *out != stdout )
  450.              fprintf(*out, "\nChanging metric of %s %s from %ld to %ld\n", frm_name,
  451.                  to_name, cur_change->val1, cur_change->val2 );
  452.           cur_ifc->metric = number;
  453.               }
  454.            else
  455.           printf( "\nNo new values specified\n");
  456.            if( args == 2 )
  457.           {/*
  458.            **     Changing the threshold for this interface
  459.            */
  460.           cur_change = push_change( &net->changes, CHANGE_THRESH,
  461.                         frm_name, to_name );
  462.           cur_change->val1 = cur_ifc->threshold;
  463.           cur_change->val2 = number2;
  464.           printf( "Changing threshold of %s %s from %ld to %ld\n", frm_name,
  465.               to_name, cur_change->val1, cur_change->val2);
  466.           if( *out != stdout )
  467.              fprintf(*out, "Changing threshold of %s %s from %ld to %ld\n", frm_name,
  468.                  to_name, cur_change->val1, cur_change->val2);
  469.           cur_ifc->threshold = number2;
  470.               }
  471.            }
  472.      return FIND_TREE;
  473.      break;
  474.        case 'v':
  475.      /*
  476.      **      This option is used to toggle the verbose mode of the program
  477.      */
  478.      if( verbose )
  479.         verbose = FALSE;
  480.      else
  481.         verbose = TRUE;
  482.      return FALSE;
  483.      break;
  484.        case 'w':
  485.      if( args == 1 )
  486.         {
  487.         printf( "Enter the name of the network file to write: ");
  488.         gets( cur_input );
  489.         args = sscanf( cur_input, "%s", frm_name );
  490.         args++;
  491.         }
  492.      else if( args > 1 )
  493.         {
  494.         if( verbose )
  495.            printf( "\nNetwork description will be written to file %s\n", frm_name );
  496.         }
  497.  
  498.      if( args >= 2 )
  499.         {
  500.         if( !(net_out = fopen( frm_name, "w" )))
  501.            printf( "Unable to open %s for output\n", frm_name );
  502.         else
  503.            print_network( net_out, net );
  504.         }
  505.      return FALSE;
  506.      break;
  507.        case 'q':
  508.      /*
  509.      **      Exit the program
  510.      */
  511.      return DONE;
  512.      break;
  513.        case '?':
  514.      printf( "Command Options:\n");
  515.      printf( "\ts.  Specify a new source\n");
  516.      printf( "\td.  Specify a new destination\n" );
  517.      printf( "\to.  Specify a new output file\n" );
  518.      printf( "\tt.  Print source tree ( #ttl can be specified as an argument)\n");
  519.      printf( "\tp.  Print path from source to destination\n");
  520.      printf( "\tu.  Print unreachable interfaces\n" );
  521.      printf( "\tn.  Print the adjacency list for a node\n" );
  522.      printf( "\th.  Print the change history\n" );
  523.      printf( "\tl.  Change the link metric direction used\n");
  524.      printf( "\te.  Explain output symbols\n" );
  525.      printf( "\tr.  Remove a tunnel  or interface from the network\n" );
  526.      printf( "\ta.  Add a tunnel or interface to the network\n" );
  527.      printf( "\tc.  Change a metric and threshold\n" );
  528.      printf( "\tv.  Toggle verbose mode\n" );
  529.      printf( "\tw.  Write the current network to a network input file\n" );
  530.      printf( "\tq.  Quit this program\n" );
  531.      printf( "\t?.  Print this list\n" );
  532.      return FALSE;
  533.      break;
  534.        default:
  535.      printf( "\nInvalid command, use ? to get commands\n");
  536.      return FALSE;
  537.      break;
  538.       }
  539.    return 0;
  540. }
  541.  
  542. /**************************************************************************
  543.  
  544.   FUNCTION: disable_ifc( net, cur_ifc, frm_name, to_name )
  545.  
  546.   DESCRIPTION:
  547.        This function removes an edge from the network by changing the
  548.        type fields of its forward and backward interfaces to disabled.
  549.        This function is only called for tunnels.
  550.  
  551. **************************************************************************/
  552. int    disable_ifc( Network *net, NAME frm_name, NAME to_name, FILE *out )
  553. {
  554.    Interface *cur_ifc;
  555.    
  556.    cur_ifc = find_ifc( net, NULL, frm_name, to_name );
  557.    if( cur_ifc != NULL )
  558.       {
  559.      printf( "\ndisabling interface from %s to %s\n", frm_name, to_name );
  560.      if( out != stdout )
  561.         fprintf(out, "\ndisabling interface from %s to %s\n", frm_name, to_name );
  562.      cur_ifc->type |= DISABLED;
  563.       }
  564.    else
  565.       {
  566.      printf( "\nInterface %s to %s not found\n", frm_name, to_name );
  567.      return FALSE;
  568.       }
  569.    cur_ifc = find_ifc( net, NULL, to_name, frm_name );
  570.    if( cur_ifc != NULL )
  571.       {
  572.      printf( "disabling interface from %s to %s\n", to_name, frm_name );
  573.      if( out != stdout )
  574.         fprintf(out,"disabling interface from %s to %s\n", to_name, frm_name );
  575.      cur_ifc->type |= DISABLED;
  576.       }
  577.    else
  578.       {
  579.       printf( "Backward interface not found\n");
  580.       return FALSE;
  581.       }
  582.    return TRUE;
  583. }
  584.  
  585.  
  586. /***********************************************************************
  587.  
  588.   FUNCTION: add_subnet_conn( net, ifc, frm_name )
  589.  
  590.   DESCRIPTION:
  591.         This function enables all interface edges with the specified name
  592.     and it also enables the backward edges.  This includes the subnet
  593.     connection and all tunnels.
  594.  
  595. *********************************************************************/
  596.  
  597. void  add_subnet_conn( Network *net, Interface *frm_ifc, NAME frm_name, FILE *out )
  598. {
  599.    Interface  *back_ifc;
  600.  
  601.    for( ;frm_ifc != NULL; frm_ifc = frm_ifc->next )
  602.       {/*
  603.        **   If this is the last interface with this name, then we are done.
  604.        */
  605.       if( strcmp( frm_ifc->name, frm_name ))
  606.      return;
  607.       /*
  608.       **     Enable the forward interface
  609.       */
  610.       frm_ifc->type &= ~(DOWN | DISABLED);
  611.       printf( "\nEnabling interface %s to %s\n", frm_name, frm_ifc->to_name );
  612.       if( out != stdout )
  613.      fprintf( out, "\nEnabling interface %s to %s\n", frm_name, frm_ifc->to_name );
  614.       /*
  615.       **    Now to enable the backward interface
  616.       */
  617.       back_ifc = find_ifc( net, NULL, frm_ifc->to_name, frm_ifc->name );
  618.       if( back_ifc != NULL )
  619.      {
  620.      printf( "Enabling interface from %s to %s\n", back_ifc->name,
  621.          frm_name );
  622.      if( out != stdout )
  623.         fprintf(out, "Enabling interface from %s to %s\n", back_ifc->name,
  624.             frm_name );
  625.      back_ifc->type &= ~(DOWN|DISABLED);
  626.          }
  627.       else
  628.      printf( "backward interface not found\n");
  629.       }
  630. }
  631.  
  632. /*******************************************************************
  633.  
  634.   FUNCTION: push_change( Change_stack *stack, u_long type, NAME frm_name, NAME to_name )
  635.  
  636.   DESCRIPTION:
  637.        This function pushes a network change onto a stack for later
  638.        retrieval.  It returns a pointer to the change.
  639.  
  640. **********************************************************************/
  641.  
  642. Change_item *push_change( Change_stack *stack, u_long type, NAME frm_name, NAME to_name )
  643. {
  644.    Change_item *cur_change;
  645.    
  646.    cur_change = (Change_item *)calloc(1, sizeof(Change_item));
  647.    cur_change->type = type;
  648.    strcpy( cur_change->frm_name, frm_name );
  649.    strcpy( cur_change->to_name, to_name );
  650.    if( stack->number == 0 )
  651.       {
  652.       stack->last = cur_change;
  653.       cur_change->next = NULL;
  654.       }
  655.    else
  656.       {
  657.       cur_change->next = stack->last;
  658.       stack->last = cur_change;
  659.       }
  660.    stack->number++;
  661.    return cur_change;
  662. }
  663.  
  664. /*********************************************************************
  665.  
  666.   FUNCTION: extract_change( Change_stack *stack, u_long type, NAME frm_name,
  667.                             NAME to_name )
  668.  
  669.   DESCRIPTION:
  670.         This function finds the specified change and returns it.  It
  671.     also removes it from the stack.
  672.  
  673. **********************************************************************/
  674.  
  675. Change_item  *extract_change( Change_stack *stack, u_long type, NAME frm_name,
  676.                   NAME to_name )
  677. {
  678.    Change_item *cur_chng, *prev_chng;
  679.  
  680.    cur_chng = stack->last;
  681.    if( cur_chng->type == type && !(strcmp( frm_name, cur_chng->frm_name ))
  682.        && !(strcmp( to_name, cur_chng->to_name )))
  683.       {
  684.       stack->last = cur_chng->next;
  685.       stack->number--;
  686.       return cur_chng;
  687.       }
  688.    prev_chng = cur_chng;
  689.    for( cur_chng = cur_chng->next; cur_chng != NULL; cur_chng = cur_chng->next )
  690.       {
  691.       if( cur_chng->type == type && !(strcmp( frm_name, cur_chng->frm_name ))
  692.      && !(strcmp( to_name, cur_chng->to_name )))
  693.      {
  694.      prev_chng->next = cur_chng->next;
  695.      stack->number--;
  696.      return cur_chng;
  697.          }
  698.       prev_chng = cur_chng;
  699.       }
  700.    return NULL;
  701. }
  702.  
  703. /*****************************************************************************
  704.  
  705.   FUNCTION: print_changes( Change_stack *stack, long number, FILE *out )
  706.  
  707.   DESCRIPTION:
  708.        This function prints out the last 'number' changes made to the
  709.        network.   It prints them starting from the most recent.
  710.  
  711. ***************************************************************************/
  712.  
  713. void    print_changes( Change_stack *stack, long number, FILE *out )
  714. {
  715.    Change_item *cur_chng;
  716.    long i, cur_num;
  717.  
  718.    fprintf( out, "\nPrinting change history most recent first:\n" );
  719.    if( number < 0 || number > stack->number )
  720.       number = stack->number;
  721.    cur_num = stack->number;
  722.    for( i = 0, cur_chng = stack->last; i < number; i++, cur_num-- )
  723.       {
  724.       switch( cur_chng->type )
  725.      {
  726.       case DISABLE_IFC:
  727.         fprintf(out, "\t%ld. Disabled interface %s\n", cur_num, cur_chng->frm_name );
  728.         break;
  729.       case DISABLE_TUNNEL:
  730.         fprintf( out,"\t%ld. Disabled tunnel %s %s\n", cur_num, cur_chng->frm_name, cur_chng->to_name );
  731.         break;
  732.       case ADD_IFC:
  733.         fprintf(out, "\t%ld. Added interface %s\n", cur_num, cur_chng->frm_name );
  734.         break;
  735.       case ADD_TUNNEL:
  736.         fprintf(out, "\t%ld. Added tunnel %s %s\n", cur_num, cur_chng->frm_name, cur_chng->to_name );
  737.         break;
  738.       case CHANGE_METRIC:
  739.         fprintf(out, "\t%ld. Changed metric of %s %s from %ld to %ld\n", cur_num,
  740.             cur_chng->frm_name, cur_chng->to_name, cur_chng->val1, cur_chng->val2);
  741.         break;
  742.       case CHANGE_THRESH:
  743.         fprintf(out, "\t%ld. Changed threshold of %s %s from %ld to %ld\n", cur_num,
  744.             cur_chng->frm_name, cur_chng->to_name, cur_chng->val1, cur_chng->val2);
  745.         break;
  746.      }
  747.       cur_chng = cur_chng->next;
  748.       }
  749.    return;
  750. }
  751.  
  752. /**************************************************************************
  753.  
  754.   FUNCTION: explain_output( FILE *out )
  755.  
  756.   DESCRIPTION:
  757.         Print a description of the output formats for each of the
  758.     options.
  759.  
  760. ***************************************************************************/
  761.  
  762. void   explain_output( FILE *out )
  763. {
  764.    fprintf(out, "\nSource trees and paths:\n");
  765.    fprintf(out, "\tinterface, tree level/metric to here/ttl to reach\n");
  766.    fprintf(out, "\t- Indentation indicates tree level\n");
  767.    fprintf( out, "\t- A destination is SUSPECT if the tunnel was only specified\n");
  768.    fprintf(out, "\t  in one direction in the input file, and the interface appeared\n");
  769.    fprintf(out, "\t  with a ? in the input file\n");
  770.    fprintf(out, "\t- A metric of -1 indicates the message will be sent to this\n");
  771.    fprintf(out, "\t  router along this path, but it is not from the parent interface\n");
  772.    fprintf(out, "\t- The unreachable interfaces list: down means\n");
  773.    fprintf(out, "\t  the entire subnet interface is down\n\n");
  774.    fprintf(out, "Adjacency list (n option):\n");
  775.    fprintf(out, "\t- All equivalent interfaces at a site are printed.\n");
  776.    fprintf(out, "\t- Mask defines subnet mask for address resolution\n");
  777.    fprintf(out, "\t- m/t lists the current metric and threshold for the interface\n");
  778.    fprintf(out, "\tType field:\n");
  779.    fprintf(out, "\t\tLow Byte (1st) gives interface type:\n");
  780.    fprintf(out, "\t\t\tTunnel = 1, Querier = 2, Srcrt = 4, Subnet = 8\n");
  781.    fprintf(out, "\t\t2nd Byte gives interface internal information:\n");
  782.    fprintf(out, "\t\t\tAssumed = 1 (this interface was assumed to be a tunnel)\n");
  783.    fprintf(out, "\t\t3rd Byte gives current interface status:\n");
  784.    fprintf(out, "\t\t\tDown = 1, Disabled = 2, Suspect = 4\n");
  785.    fprintf(out, "\t\t5th Byte gives multicast routing information:\n");
  786.    fprintf(out, "\t\t\tLeaf = 1, Child = 2, Parent = 4\n" );
  787.    fprintf(out, "\t\t\t(for explanation see S. Deering's thesis)\n");
  788.    return;
  789. }
  790.  
  791. /****************************************************************************
  792.  
  793.   FUNCTION: print_network()
  794.  
  795.   DESCRIPTION:
  796.        This function writes the current network description to a file
  797.        for later input to the mrdebug program.  It allows changes to
  798.        the network to be saved.
  799.  
  800. ****************************************************************************/
  801. void   print_network( FILE *net_out, Network *net )
  802. {
  803.    long       v;
  804.    Interface *cur_ifc;
  805.    char      *last_name;
  806.  
  807.    for( v = 0; v < net->no_nodes; v++ )
  808.       {
  809.       last_name = NULL;
  810.       if( !(net->adj_list[v].type & SUBNET))
  811.      {
  812.      for( cur_ifc = net->adj_list[v].interfaces.first; cur_ifc != NULL;
  813.           cur_ifc = cur_ifc->next )
  814.         {
  815.         if( last_name != NULL && strcmp( last_name, cur_ifc->name ))
  816.            {/*
  817.         **   There are at least two different interface names
  818.         **   at this site so print out an equivalence line.
  819.         */
  820.            fprintf( net_out, "= %s %s\n", last_name, cur_ifc->name );
  821.             }
  822.         last_name = cur_ifc->name;
  823.         if( cur_ifc->type & SUBNET )
  824.            fprintf( net_out, "> %s 0.0.0.0 [%ld/%ld", cur_ifc->name,
  825.                cur_ifc->metric, cur_ifc->threshold );
  826.         else
  827.            fprintf( net_out, "> %s %s [%ld/%ld", cur_ifc->name, cur_ifc->to_name,
  828.                 cur_ifc->metric, cur_ifc->threshold );
  829.         
  830.         if( cur_ifc->type & QUERIER )
  831.             fprintf( net_out, "/querier" );
  832.         if( cur_ifc->type & TUNNEL )
  833.             fprintf( net_out, "/tunnel" );
  834.         if( cur_ifc->type & SRCRT )
  835.             fprintf( net_out, "/srcrt" );
  836.         if( cur_ifc->type & (DOWN | DISABLED))
  837.             fprintf( net_out, "/down" );
  838.         fprintf( net_out, "]\n" );
  839.         }
  840.      }
  841.       }
  842.    fclose( net_out );
  843. }
  844.        
  845.