/**************************************************************************** 
** File: display.c
**
** Author: Mike Borella
**
** Comments: Displaying functions
**
** $Id: display.c,v 1.34 2007/06/25 10:12:37 farooq-i-azam Exp $
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Library General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
*****************************************************************************/

#include "display.h"

#define LABEL_LENGTH            22
#define MULTILINE_LENGTH        23
#define MULTILINE_STRING_LENGTH 48
#define BUF_SIZE                128
#define HOLDER_LEN              64

int packet_displayed;
int start_of_packet;

extern struct arg_t * my_args;

/*----------------------------------------------------------------------------
**
** display_header_line()
**
** Writes out a line of separators (separators defined in headerline_t)
**
**----------------------------------------------------------------------------
*/

inline void display_header_line(headerline_t hl)
{
  /* Check the layer */
  if (check_layer())
    return;
  
  switch(hl)
    {
    case HEADERLINE_DASHES:
      printf("--------------------------------------------------------------------------\n");
      break;
    case HEADERLINE_STARS:
      printf("**************************************************************************\n");
      break;
    case HEADERLINE_EQUALS:
      printf("==========================================================================\n");
      break;
    case HEADERLINE_DOTS:
      printf("..........................................................................\n");
      break;
    default:
      error_fatal("unknown header line type");
    }
}

/*----------------------------------------------------------------------------
**
** display_whitespace_pad()
**
** Pad the output with a pre-determined number of spaces from the label to
** the output. The input parameter is the length of the label. 
**
**----------------------------------------------------------------------------
*/

inline void display_whitespace_pad(int length)
{
  int i;

  /* Check the layer */
  if (check_layer())
    return;

  for (i=0; i < LABEL_LENGTH+1-length; i++)
    putchar(' ');
}

/*----------------------------------------------------------------------------
**
** display_header_banner_ts()
**
** Writes out a generic header banner with timestamp for protocol headers.
**
** ISSUE: Do we want to limit the length of the banner string so that it 
** doesn't run off the screen?
**
**----------------------------------------------------------------------------
*/

inline void display_header_banner_ts(char *banner, char *ts)
{
  /* Check the layer */
  if (check_layer())
    return;

  if (start_of_packet)
    {
      display_header_line(HEADERLINE_STARS);
      start_of_packet = 0;
    }
  else
    display_header_line(HEADERLINE_DASHES);

  printf("                        %s", banner);
  printf("\n");
  if (ts != NULL)
    {
      display_whitespace_pad ( 0 );
      printf ( " %s ", ts );
    }
  
  display_header_line(HEADERLINE_DASHES);
}

/*----------------------------------------------------------------------------
**
** display_header_banner()
**
** Writes out a generic header banner for protocol headers.
**
**----------------------------------------------------------------------------
*/

inline void display_header_banner(char * banner)
{
  display_header_banner_ts(banner, NULL);
}

/*----------------------------------------------------------------------------
**
** display()
**
** Display a protocol field or some other string according to our rules
**
** Parameters:
**   label - always printed, must be null terminated string
**   content - the stuff to display, usually the value of the field
**   length - the number of bytes to display from the content parameter
**   format - the specific format to use when displaying
**
**----------------------------------------------------------------------------
*/

inline void display(char * label, u_int8_t * content, u_int32_t length, 
	     display_t format)
{
  int label_len;
  char label_truncated[LABEL_LENGTH+1];

  /* Check the layer */
  if (check_layer())
    return;

  /*
   * Do sanity checks here
   */

  label_len = strlen(label);
  if (label_len >= LABEL_LENGTH) 
    {
      strncpy(label_truncated, label, LABEL_LENGTH);
      label_truncated[LABEL_LENGTH] = '\0';
      label_len = LABEL_LENGTH;
      printf("%s:", label_truncated); 
    }
  else
    printf("%s:", label);

  /*
   * Do the actual displaying
   */
  
  display_whitespace_pad(label_len);
  display_minimal(content, length, format);

  /*
   * Trailing printf()
   */

  printf("\n");

}

/*----------------------------------------------------------------------------
**
** display_minimal()
**
** Display a protocol field or some other string in minimal mode.
** This means that there is no label, no padding, and no carriage return.
**
** Parameters:
**   content - the stuff to display, usually the value of the field
**   length - the number of bytes to display from the content parameter
**   format - the specific format to use when displaying
**
**----------------------------------------------------------------------------
*/

inline void display_minimal(u_int8_t * content, u_int32_t length, 
			    display_t format)
{
  int i,j;
  int              seen_a_one;
  u_int8_t         bit_holder;
  struct in_addr * ip_holder;
  u_int8_t       * ptr;
  u_int8_t       * temp;
  double           sum;
  u_int32_t        multiplier;
  int              len;

  /* Check the layer */
  if (check_layer())
    return;

  /*
   * We have displayed part of this packet, so we need a carriage return.
   */

  packet_displayed = 1;

  /*
   * Depending on the display format, we may do different things
   */

  switch(format)
    {
    case DISP_DEC:
      temp = (u_int8_t *) my_malloc(length);
      memcpy(temp, content, length);
#if !defined(WORDS_BIGENDIAN)
      reverse_byte_order(temp, length);
#endif
      sum = 0.0;
      multiplier = 1;
      for (i = length-1; i >= 0; i--)
	{
	  ptr = temp+i;
	  sum += (double) (multiplier * *ptr);
	  multiplier = multiplier * 256;
	}
      printf("%.0f", sum);
      my_free(temp);
      break;

    case DISP_DOTTEDDEC:
      ip_holder = (struct in_addr *) content;
      for (i=0; i<4; i++)
	if (i==3)
	  printf("%d", content[i]);
	else
	  printf("%d.", content[i]);
      break;

    case DISP_HEX:
      printf("0x");
      for (i=0; i < length; i++)
	{
	  if (content[i] < 16)
	    printf("0%x", content[i]);
	  else
	    printf("%x", content[i]);
	}
      break;

    case DISP_HEX_MULTILINE:
      printf("0x");
      for (i=0; i < length; i++)
	{
	  if ((i > 0) && (i % MULTILINE_LENGTH == 0))
	    {
	      printf("\n");
	      display_whitespace_pad(-3);
	    }
	  if (content[i] < 16)
	    printf("0%x", content[i]);
	  else
	    printf("%x", content[i]);
	}
      break;
  
    case DISP_HEXCOLONS:
      for (i=0; i < length; i++)
	{
	  if (content[i] < 16)
	    printf("0%x", content[i]);
	  else
	    printf("%x", content[i]);
	  if (i != length-1)
	    printf(":");
	}
      break;
      
    case DISP_HEXCOLONS4:
      for (i=0; i < length; i++)
	{
	  if ( content [ i ] < 0x10 )
	    printf("0%x", content[i]);
	  else
	    printf("%x", content[i]);

	  if (i % 2 == 1 && i != length - 1)
	    printf(":");
	}      
      break;

    case DISP_BINNLZ:
      seen_a_one = 0;
      for (i=0; i < length; i++)
	{
	  bit_holder = 128;
	  for (j=1; j<=8; j++)
	    {
	      if (content[i] & bit_holder)
		{
		  seen_a_one = 1;
		  printf("1");
		}
	      else
		{
		  if (seen_a_one || (i == length -1 && j == 8))
		    printf("0");
		}
	      bit_holder = bit_holder >> 1;
	    }
	}
      break;

    case DISP_BIN:
      for (i = 0; i < length; i++)
        {
          bit_holder = 0x80;
          for (j = 1; j <= 8; j++)
            {
              if (content[i] & bit_holder)
                printf ( "1" );
              else
                printf ( "0" );

              bit_holder = bit_holder >> 1;
            } 
        }
      break;

    case DISP_STRING:
      /* 
       * Rather than just ass-u-ming that we've been handed a properly
       * null terminated string, we created one of our own.
       *
       * Make sure that ONLY printable characters are sent in this call,
       * Otherwise, very strnage bugs will occur! For example, send this 
       * code a \n and just try to figure out why your output is screwy...
       * 
       */

      ptr = (u_int8_t *) my_malloc(length+1);
      memcpy(ptr, content, length);
      ptr[length] = '\0';

      /* Clean out extra control characters */
      len = strlen(ptr);
      while((ptr[len-1] == '\n' || ptr[len-1] == '\r') && len >= 1)
	{
	  ptr[len-1] = '\0';
	  len--;
	}

      /* 
       * Let's make sure that the string contains only printable
       * characters
       */
      
      if (!isprint_str(ptr, len))
	{
	  my_free(ptr);
	  break;
	}

      /* print it then free it */
      printf("%s", ptr);
      my_free(ptr);
      break;

    case DISP_STRING_MULTILINE:
      /* I'm sure that this could be made much more efficient... */
      for (i=0; i < length; i++)
	{
	  if ((i > 0) && (i % MULTILINE_STRING_LENGTH == 0))
	    {
	      printf("\n");
	      display_whitespace_pad(-3);
	    }
	  printf("%c", content[i]);
	}
      break;

    default: /* oops, this shouldn't happen... */
      break;
    }
}

/*----------------------------------------------------------------------------
**
** display_minimal_string()
**
** Same as display_minimal() but we assume that the content argument is 
** a string.
**
**----------------------------------------------------------------------------
*/

inline void display_minimal_string(u_int8_t * content)
{
  display_minimal(content, strlen(content), DISP_STRING);
}

/*----------------------------------------------------------------------------
**
** display_string()
**
** Same as display() but we assume that the content argument is 
** a string.  We also assume multiline support is required.
**
**----------------------------------------------------------------------------
*/

inline void display_string(u_int8_t * label, u_int8_t * content)
{
  display(label, content, strlen(content), DISP_STRING_MULTILINE);
}

/*----------------------------------------------------------------------------
**
** display_strmap()
**
** Display an item from a strmap_t array, with proper expansion of its
** textual representation.
**
**----------------------------------------------------------------------------
*/

inline void display_strmap(char * label, int content, strmap_t map[])
{
  char holder[HOLDER_LEN];

  snprintf(holder, HOLDER_LEN, "%d (%s)", content, map2str(map, content));
  holder[HOLDER_LEN-1] = '\0';
  display_string(label, holder);
}

/*----------------------------------------------------------------------------
**
** display_strmap_hex()
**
** Display an item from a strmap_t array, with proper expansion of its
** textual representation.  The number appears in hex format rather than 
** decimal.
**
**----------------------------------------------------------------------------
*/

inline void display_strmap_hex(char * label, int content, strmap_t map[])
{
  char holder[HOLDER_LEN];

  snprintf(holder, HOLDER_LEN, "0x%x (%s)", content, map2str(map, content));
  holder[HOLDER_LEN-1] = '\0';
  display_string(label, holder);
}

/*----------------------------------------------------------------------------
**
** display_ipv4()
**
** Same as display() but we assume that the content argument is 
** a 4-byte IPv4 address.
**
**----------------------------------------------------------------------------
*/

inline void display_ipv4(u_int8_t * label, u_int8_t * addr)
{
  display(label, addr, 4, DISP_DOTTEDDEC);
}

/*----------------------------------------------------------------------------
**
** display_ipv6()
**
** Same as display() but we assume that the content argument is 
** a 16-byte IPv6 address.
**
**----------------------------------------------------------------------------
*/

inline void display_ipv6(u_int8_t * label, u_int8_t * addr)
{
  display(label, addr, 16, DISP_HEXCOLONS4);
}

/*----------------------------------------------------------------------------
**
** display_minimal_ipv4()
**
** Same as display_minimal() but we assume that the content argument is 
** a 4-byte IPv4 address.
**
**----------------------------------------------------------------------------
*/

inline void display_minimal_ipv4(u_int8_t * content)
{
  display_minimal(content, 4, DISP_DOTTEDDEC);
}

/*----------------------------------------------------------------------------
**
** display_minimal_ipv6()
**
** Same as display_minimal() but we assume that the content argument is 
** a 16-byte IPv6 address.
**
**----------------------------------------------------------------------------
*/

inline void display_minimal_ipv6(u_int8_t * content)
{
  display_minimal(content, 16, DISP_HEXCOLONS4);
}

/*----------------------------------------------------------------------------
**
** display_minimal_cr()
**
** Display a carriage return (actually, this will work for regular mode as 
** well).
**
**----------------------------------------------------------------------------
*/

inline void display_minimal_cr(void)
{
  /* Check the layer */
  if (check_layer())
    return;

  printf("\n");
}

