/*

Copyright (C) 2000 - 2006 Christian Kreibich <christian@whoop.org>.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies of the Software and its documentation and acknowledgment shall be
given in the documentation and software packages that this Software was
used.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <errno.h>

#include <libnd_prefs.h>
#include <libnd_packet.h>
#include <libnd_trace.h>
#include <libnd_tcpdump.h>
#include <libnd_tpm.h>
#include <libnd_tp.h>


static GList *observers;


#ifdef DEBUG
static void
tp_dump(LND_TracePart *tp)
{
  struct bpf_timeval tv1, tv2;

  if (!tp)
    return;

  tv1.tv_sec = 0;
  tv1.tv_usec = 0;
  tv2.tv_sec = 0;
  tv2.tv_usec = 0;

  if (tp->pl)
    {
      tv1 = tp->pl->ph.ts;
      tv2 = tp->pl_end->ph.ts;
    }

  D(("Part %lu -- %lu (%lu.%lu -- %lu.%lu) %i, delta %i\n",
     tp->start.offset, tp->end.offset,
     tv1.tv_sec, tv1.tv_usec, tv2.tv_sec, tv2.tv_usec,
     tp->num_packets, (int) tp->size));
}
#else
#define tp_dump(X)
#endif


static void
tp_packet_remove(LND_Packet *packet)
{
  if (packet->next)
    {
      if (packet->prev)
	{
	  /* It's a normal packet (not first or last) --> just cut it out */
	  packet->prev->next = packet->next;
	  packet->next->prev = packet->prev;
	}
      else
	{
	  /* It's got a next packet, but no previous one --> first packet */
	  if (packet->part)
	    packet->part->pl = packet->next;

	  packet->next->prev = NULL;
	}
    }
  else if (packet->prev)
    {
      /* It's got no next one, but a previous one --> last packet */
      packet->prev->next = NULL;

      if (packet->part)
	packet->part->pl_end = packet->prev;
    }
  else if (packet->part)
    {
      /* It's the only packet in the trace */
      packet->part->pl = NULL;
      packet->part->pl_end = NULL;
    }
}


LND_TracePart *
libnd_tp_new(LND_TPM *tpm, LND_TracePart *parent, off_t offset)
{
  LND_TracePart *tp;

  D_ENTER;
  
  if (parent && offset > parent->size)
    {
      D(("Requested offset out of bounds\n"));
      D_RETURN_(NULL);
    }

  tp = g_new0(LND_TracePart, 1);
  D_ASSERT_PTR(tp);

  D(("New trace part on %p, at %lu, address %p\n",
     parent, (long unsigned) offset, tp));

  tp->tpm          = tpm;
  tp->start.tp     = parent;
  tp->start.offset = offset;
  tp->end.tp       = parent;
  tp->end.offset   = offset;
  tp->out_filename = libnd_misc_get_tmpfile(tpm->trace->filename);

  if (parent && parent->in_filename)
    {
      if (! (tp->pcn = pcapnav_open_offline(parent->in_filename)))
	{
	  D(("Could not read parent part..\n"));
	  libnd_tp_free(tp);
	  D_RETURN_(NULL);
	}
      
      if (pcapnav_set_offset(tp->pcn, offset) < 0)
	{
	  D(("Could not jump to start point: %s\n", strerror(errno)));
	  libnd_tp_free(tp);
	  D_RETURN_(NULL);
	}

      /* Set start timestamp of the new part */
      pcapnav_get_timestamp(tp->pcn, &tp->start_ts);
      tp->end_ts = tp->start_ts;
      
      D(("New part at %lu of %s\n", (long unsigned) offset, parent->in_filename));
      tp->next_part = libnd_tp_find_part_after_offset(parent, offset, NULL);

      if (! (tp->pd = pcap_dump_open(pcapnav_pcap(tp->pcn), tp->out_filename)))
	{
	  D(("Could not create swap file dumper.\n"));
	  libnd_tp_free(tp);
	  D_RETURN_(NULL);
	}
    }
  
  D_RETURN_(tp);
}


static void
tp_safe_delete_output(LND_TracePart *tp)
{
  gboolean trace_okay = TRUE;
  gboolean tpm_okay = TRUE;
  
  if (!tp || !tp->tpm)
    return;
  
  
  if (tp->tpm->trace &&
      tp->out_filename && tp->tpm->trace->filename)
    {
      if (! strcmp(tp->out_filename, tp->tpm->trace->filename))
	trace_okay = FALSE;
    } 
  
  if (tp->out_filename && tp->tpm->output_name)
    if (! strcmp(tp->out_filename, tp->tpm->output_name))
      tpm_okay = FALSE;
  
  if (tpm_okay && trace_okay)
    {
      D(("Deleting output file '%s'\n", tp->out_filename));
      unlink(tp->out_filename);
    }
}


void
libnd_tp_free(LND_TracePart *tp)
{
  LND_Packet *p, *p2;
  GList *part;

  D_ENTER;

  if (!tp)
    D_RETURN;
  
  if (tp != tp->tpm->base)
    {
      if (tp->in_filename)
	{
	  /* If the trace doesn't have a filename, or when the input
	   * filename differs from the trace's filename, delete the
	   * input file.
	   */
	  if (!tp->tpm->trace->filename || strcmp(tp->in_filename, tp->tpm->trace->filename))
	    {
	      D(("Deleting input file '%s'\n", tp->in_filename));
	      unlink(tp->in_filename);
	    }
	}
    }

  tp_safe_delete_output(tp);

  g_free(tp->in_filename);
  g_free(tp->out_filename);
  
  if (tp->pd)
    pcap_dump_close(tp->pd);
  if (tp->pcn)
    pcapnav_close(tp->pcn);

  /* Free trace parts on top of this one, if any */
  for (part = tp->parts; part; part = g_list_next(part))
    libnd_tp_free((LND_TracePart *) part->data);

  g_list_free(tp->parts);
  g_list_free(tp->bw_parts);

  /* Free loaded packets, if any */
  p = tp->pl;
  while (p)
    {
      p2 = p;
      p = p->next;
      libnd_packet_free(p2);
    }

  D(("Freeing trace part at %p\n", tp));
  g_free(tp);
  D_RETURN;
}


gboolean        
libnd_tp_set_input_file(LND_TracePart *tp, const char *filename)
{
  D_ENTER;

  if (!tp || !filename || !filename[0])
    {
      D(("Invalid input\n"));
      D_RETURN_(FALSE);
    }

  if (tp->parts || tp->bw_parts)
    {
      D(("Not plateau\n"));
      D_RETURN_(FALSE);
    }

  libnd_tp_clear_packetlists(tp);
  tp->next_part = NULL;

  g_free(tp->in_filename);
  tp->in_filename = g_strdup(filename);
    
  if (! (tp->pcn = pcapnav_open_offline(filename)))
    {
      D(("Could not open file.\n"));
      D_RETURN_(FALSE);
    }
  
  tp->start.tp = tp->end.tp = NULL;
  tp->start.offset = 0;
  tp->end.offset = pcapnav_get_size(tp->pcn);
  tp->size = tp->end.offset;
  pcapnav_get_timespan(tp->pcn, &tp->start_ts, &tp->end_ts);

  D(("Size of trace part %p with file %s: %lu\n",
     tp, filename, (long unsigned) tp->size));

  D_RETURN_(TRUE);
}


gboolean        
libnd_tp_set_output_file(LND_TracePart *tp, const char *filename)
{
  D_ENTER;

  if (!tp || !filename || !filename[0])
    {
      D(("Invalid input\n"));
      D_RETURN_(FALSE);
    }

  tp_safe_delete_output(tp);

  g_free(tp->out_filename);
  tp->out_filename = strdup(filename);
  
  if (! (tp->pd = pcap_dump_open(pcapnav_pcap(tp->pcn), tp->out_filename)))
    {
      D(("Could not create swap file dumper.\n"));
      D_RETURN_(FALSE);
    }

  D_RETURN_(TRUE);
}


LND_TracePart *
libnd_tp_find_part_after_offset(const LND_TracePart *tp,
				off_t offset,
				const LND_TracePart *tp_dontuse)
{
  GList *l;
  LND_TracePart *part;

  if (!tp)
    return NULL;

  for (l = tp->parts; l; l = g_list_next(l))
    {
      part = (LND_TracePart *) l->data;
      
      if (part->start.offset >= offset &&
	  tp_dontuse != part)
	return part;
    }
  
  return NULL;
}


void              
libnd_tp_init_packets(LND_TracePart *tp)
{
  int i;
  LND_Packet *p;
  
  D_ENTER;

  if (!tp)
    D_RETURN;
    
  libnd_filter_list_init(tp->tpm->trace->filters, tp->tpm->trace);

  /* Initialize all LND_Packets internally: */
  for (i = 0, p = tp->pl; p; p = p->next, i++)
    {
      libnd_packet_init(p);
      libnd_packet_update_proto_state(p, i);

      if (!libnd_packet_is_complete(p) && tp->tpm && tp->tpm->trace)
	tp->tpm->trace->incomplete = TRUE;
    }
  
  libnd_filter_list_cleanup(tp->tpm->trace->filters);

  D_RETURN;
}


void               
libnd_tp_write_packet(LND_TracePart *tp, const LND_Packet *packet)
{
  if (!tp || !packet)
    return;

  tp->size += pcapnav_get_pkthdr_size(tp->pcn) + packet->ph.caplen;
  libnd_packet_dump(packet, tp->pd);
}


void               
libnd_tp_set_dirty(LND_TracePart *tp, gboolean dirty)
{
  D_ENTER;

  if (dirty != tp->dirty)
    {
      tp->dirty = dirty;

      /* When a trace part is marked dirty, the trace is dirty as well.
       * However when a part is marked clean, there could still be other
       * parts that are dirty. We could do this properly using a "dirty counter"
       * I guess ... 
       */
      if (dirty)
	libnd_trace_set_dirty(tp->tpm->trace, dirty);
      libnd_tp_tell_observers(tp, LND_TP_DIRTY_STATE, NULL);
    }
  
  D_RETURN;
}


void
libnd_tp_sync(LND_TracePart *tp)
{
  LND_Packet *packet = tp->pl, *next_packet;

  D_ENTER;

  if (!tp || !tp->pd)
    {
      D(("No dumper in part %p, not syncing.\n", tp));
      D_RETURN;
    }

  if (!tp->dirty)
    {
      D(("Trace part not dirty, not syncing.\n"));
      D_RETURN;
    }

  /* Write all packets out to temp file. We don't use
   * libnd_tp_write_packet(), as that would modify the
   * trace's size which is undesired in this case.
   */
  while (packet)
    {
      libnd_packet_dump(packet, tp->pd);

      next_packet = packet->next;
      libnd_packet_free(packet);
      packet = next_packet;
    }

  tp->pl = NULL;

  /* Now adjust input and output files. In the future,
   * read from the file we so far have written to, and
   * create a new output file.
   */
  g_free(tp->in_filename);
  tp->in_filename = tp->out_filename;
  tp->out_filename = libnd_misc_get_tmpfile(tp->tpm->trace->filename);

  D(("Trace part %p synced. In: %s. Out: %s\n", tp, tp->in_filename, tp->out_filename));

  if (tp->pd)
    {
      pcap_dump_close(tp->pd);
      tp->pd = NULL;
    }
  
  if (tp->pcn)
    {
      pcapnav_close(tp->pcn);
      tp->pcn = NULL;
    }

  libnd_tp_set_dirty(tp, FALSE);
  libnd_tpm_add_part(tp->tpm, tp);

  D_RETURN;
}


void
libnd_tp_insert_packets(LND_TracePart *tp, LND_Packet *packet, int index)
{
  off_t size_change = 0;
  int   num_packets = 0;
  LND_Packet *p, *p_last;

  D_ENTER;
  
  if (!tp || !packet)
    D_RETURN;

  libnd_packet_tell_observers(packet, LND_PACKET_INSERT_PRE, NULL);

  /* Find the last packet of the chain -- O(n) unfortunately. While
   * doing this,
   * - fix the trace part pointers,
   * - count the number of packets,
   * - calculate the size increase.
   */
  p = packet;

  do {
    p_last = p;
    p_last->part = tp;
    num_packets++;
    size_change += pcapnav_get_pkthdr_size(tp->pcn) + p_last->ph.caplen;
    p = p->next;
  } while (p);
  
  if (index < 0 || (u_int) index >= tp->num_packets)
    {
      if (tp->pl_end)
	{
	  tp->pl_end->next = packet;
	  packet->prev = tp->pl_end;
	}
      else
	tp->pl = packet;

      tp->pl_end = p_last;
    }
  else
    {
      /* Scan to desired index in packet list */
      for (p = tp->pl; p; p = p->next, index--)
	if (index == 0)
	  break;

      D_ASSERT_PTR(p);

      /* p now is the packet before which we want to insert our chain. */
      packet->prev = p->prev;
      if (!p->prev)
	tp->pl = packet;
      
      p_last->next = p;
      if (p->prev)
	p->prev->next = packet;
      p->prev = p_last;
    }

  libnd_tp_set_dirty(tp, TRUE);

  tp->num_packets += num_packets;
  tp->tpm->size   += size_change;
  tp->size        += size_change;
  D(("Size change: +%i bytes, new packets: %i\n", (int) size_change, num_packets));
  
  libnd_packet_tell_observers(packet, LND_PACKET_INSERT_POST, NULL);
  
  D_RETURN;
}


LND_Packet *
libnd_tp_remove_packet(LND_TracePart *tp, int index)
{
  LND_Packet *packet;

  if (!tp)
    return NULL;

  if ((u_int) index >= tp->num_packets || index < 0)
    return NULL;
  
  for (packet = tp->pl; packet; packet = packet->next, index--)
    {
      if (index == 0)
	break;
    }

  if (!packet)
    return NULL;

  libnd_packet_tell_observers(packet, LND_PACKET_DELETE_PRE, NULL);

  tp_packet_remove(packet);

  /* This packet may be part of the selection, update accordingly: */
  if (packet->sel_next || packet->sel_prev)
    {
      if (packet->part)
	packet->part->sel.size--;

      packet->part->sel.last_valid = FALSE;

      /* Same pattern as above: */

      if (packet->sel_next)
	{
	  if (packet->sel_prev)
	    {
	      /* selected before and after -- just cut it out */
	      packet->sel_prev->sel_next = packet->sel_next;
	      packet->sel_next->sel_prev = packet->sel_prev;
	    }
	  else
	    {
	      /* No selections before this one -- new first selected */
	      if (packet->part)
		packet->part->sel.pl = packet->sel_next;
	      
	      packet->sel_next->sel_prev = NULL;
	    }
	}
      else if (packet->sel_prev)
	packet->sel_prev->sel_next = NULL;
      else if (packet->part)
	packet->part->sel.pl = NULL;
    }

  libnd_tp_set_dirty(packet->part, TRUE);
  packet->part->num_packets--;
  packet->part->tpm->size -= pcapnav_get_pkthdr_size(packet->part->pcn) + packet->ph.caplen;
  packet->part->size      -= pcapnav_get_pkthdr_size(packet->part->pcn) + packet->ph.caplen;
  D(("Size change: -%i bytes.\n", pcapnav_get_pkthdr_size(tp->pcn) + packet->ph.caplen));

  libnd_packet_tell_observers(packet, LND_PACKET_DELETE_POST, NULL);

  return packet;
}


void
libnd_tp_clear_selection(LND_TracePart *tp)
{
  LND_Packet *p, *p2;

  D_ENTER;

  if (!tp)
    D_RETURN;

  D(("Clearing selection\n"));
  p = tp->sel.pl;

  while (p)
    {
      p2 = p;
      p = p->sel_next;
      
      p2->sel_next = NULL;
      p2->sel_prev = NULL;
    }

  tp->sel.pl   = NULL;
  tp->sel.size = 0;
  tp->sel.last = NULL;
  tp->sel.last_index = 0;
  tp->sel.last_valid = FALSE;

  libnd_tp_tell_observers(tp, LND_TP_SEL_CLR, NULL);
  D_RETURN;
}


void    
libnd_tp_full_selection(LND_TracePart *tp)
{
  LND_Packet *p, *p_last = NULL;

  D_ENTER;
  
  if (!tp || !tp->pl)
    D_RETURN;

  /* Select everything in the packet structures: */
  
  p_last = p = tp->pl;
  tp->sel.pl = p_last;
  p_last->sel_prev = NULL;
  p_last->sel_next = NULL;
  
  for (p = p->next; p; p = p->next)
    {
      p_last->sel_next = p;
      p->sel_prev = p_last;
      p->sel_next = NULL;
      p_last = p;
    }
  
  tp->sel.size = tp->num_packets;
  tp->sel.last_valid = FALSE;

  libnd_tp_tell_observers(tp, LND_TP_SEL_ALL, NULL);
  D_RETURN;
}


LND_Packet  *
libnd_tp_packet_get_nth(const LND_TracePart *tp, int n)
{
  LND_Packet *p;
  int        i = 0;
  
  if (!tp)
    return NULL;

  p = tp->pl;
  while (p)
    {
      if (i == n)
	return p;

      p = p->next;
      i++;
    }

  return NULL;
}


void            
libnd_tp_move_packet(LND_TracePart *tp, int from_index, int to_index)
{
  LND_Packet *p, *p2;

  D_ENTER;
  
  if (!tp)
    D_RETURN;
  
  /* There's an off-by-one evilness if we move downward */
  if (to_index > from_index)
    to_index++;
  
  D(("%i --> %i\n", from_index, to_index));
  
  p  = libnd_tp_packet_get_nth(tp, from_index);
  p2 = libnd_tp_packet_get_nth(tp, to_index);
  
  if (p)
    {
      if (p->prev)
	{
	  if (p->next)
	    {
	      p->prev->next = p->next;
	      p->next->prev = p->prev;
	    }
	  else
	    {
	      p->prev->next = NULL;
	      tp->pl_end = p->prev;
	    }
	  
	}
      else if (p->next)
	{
	  tp->pl = p->next;
	  p->next->prev = NULL;
	}
      else
	{
	  /* No prev and no next -- moving doesn't make
	     much sense, so we return. */
	  D_RETURN;
	}
      
      if (p2)
	{
	  p->next = p2;
	  p->prev = p2->prev;
	  
	  if (p2->prev)
	    p2->prev->next = p;
	  else
	    tp->pl = p;
	  
	  p2->prev = p;
	}
      else
	{
	  tp->pl_end->next = p;
	  p->prev = tp->pl_end;
	  tp->pl_end = p;
	  p->next = NULL;
	}
      
      libnd_tp_set_dirty(tp, TRUE);
      libnd_packet_update_proto_state(p, to_index);
      tp->sel.last_valid = FALSE;
    }

  D_RETURN;
}


LND_Packet *
libnd_tp_select_packet(LND_TracePart *tp, int index)
{
  LND_Packet   *packet, *sel = NULL, *sel_last = NULL;
  int          i;

  D_ENTER;

  if (!tp)
    D_RETURN_(NULL);

  D(("Selecting %i. packet\n", index));

  packet = tp->pl;
  sel = tp->sel.pl;
  i = 0;

  if (!sel)
    {
      D(("No selection yet  -- we can speed things up\n"));
      
      if ( (packet = libnd_tp_packet_get_nth(tp, index)))
	{
	  tp->sel.pl = packet;
	  packet->sel_next = NULL;
	  packet->sel_prev = NULL;
	  tp->sel.size++;

	  tp->sel.last = packet;
	  tp->sel.last_index = index;
	  tp->sel.last_valid = TRUE;

	  goto return_packet;
	}
    }

  /* We already have something selected */

  if (tp->sel.last_valid && index >= tp->sel.last_index)
    {
      if (index == tp->sel.last_index)
	{
	  D(("Already selected  -- do nothing\n"));
	  packet = tp->sel.last;
	  goto return_packet;
	}
      
      sel_last = packet = tp->sel.last;
      sel = sel_last->sel_next;
      i = tp->sel.last_index;

      D(("Speeding up, from index %i\n", i));
    }
  
  while (packet)
    {
      if (i == index)
	{
	  if (packet == sel)
	    {
	      D(("Packet already selected (%p/%p)\n",
		 packet->sel_prev, packet->sel_next));

	      tp->sel.last = packet;
	      tp->sel.last_index = index;
	      tp->sel.last_valid = TRUE;

	      goto return_packet;
	    }

	  if (sel_last)
	    {
	      packet->sel_prev = sel_last;
	      packet->sel_next = sel_last->sel_next;
	      if (packet->sel_next)
		packet->sel_next->sel_prev = packet;
	      sel_last->sel_next = packet;
	    }
	  else /* It's before the first selected packet */
	    {
	      packet->sel_next = tp->sel.pl;
	      tp->sel.pl->sel_prev = packet;
	      packet->sel_prev = NULL;
	      tp->sel.pl = packet;
	    }

	  tp->sel.size++;
	  tp->sel.last = packet;
	  tp->sel.last_index = index;
	  tp->sel.last_valid = TRUE;

	  goto return_packet;
	}
      
      if (packet == sel)
	{
	  sel_last = packet;
	  sel = packet->sel_next;
	}
      
      i++;
      packet = packet->next;
    }

  D_RETURN_(NULL);

 return_packet:
  libnd_tp_tell_observers(tp, LND_TP_PACKET_SEL, (void *) index);
  D_RETURN_(packet);
}


LND_Packet *
libnd_tp_unselect_packet(LND_TracePart *tp, int index)
{
  LND_Packet *packet = NULL;
  
  if (!tp)
    return NULL;

  tp->sel.last_valid = FALSE;

  if ( (packet = libnd_tp_packet_get_nth(tp, index)))
    {      
      if (packet->sel_next)
	{
	  if (packet->sel_prev)
	    {
	      packet->sel_next->sel_prev = packet->sel_prev;
	      packet->sel_prev->sel_next = packet->sel_next;
	    }
	  else
	    {
	      tp->sel.pl = packet->sel_next;
	      packet->sel_next->sel_prev = NULL;
	    }
	}
      else /* Last packet in selection */
	{
	  if (packet->sel_prev) /* Not single selected packet */
	    packet->sel_prev->sel_next = NULL;
	  else
	    tp->sel.pl = NULL;
	}
      
      packet->sel_next = packet->sel_prev = NULL;
      tp->sel.size--;
      libnd_tp_tell_observers(tp, LND_TP_PACKET_UNSEL, (void *) index);
    }
  
  return packet;
}


guint           
libnd_tp_get_sel_size(LND_TracePart *tp)
{
  if (!tp)
    return 0;

  return tp->sel.size;
}


void        
libnd_tp_sel_delete(LND_TracePart *tp)
{
  LND_Packet        *p, *p2;
  int               index;

  if (!tp)
    return;

  if (tp->sel.size == 0)
    return;

  index = tp->num_packets - 1;
  p = tp->sel.pl;

  while (p)
    {
      p2 = p;
      p  = p->sel_next;

      tp_packet_remove(p2);

      /* ... and clean up the packet, without the GUI updates.
       * This also adjusts tp->size!
       */
      libnd_packet_free(p2);

      tp->num_packets--;
      tp->tpm->size -= pcapnav_get_pkthdr_size(tp->pcn) + p2->ph.caplen;
      tp->size      -= pcapnav_get_pkthdr_size(tp->pcn) + p2->ph.caplen;
    }

  tp->sel.size = 0;
  tp->sel.pl  = NULL;
  tp->sel.last_valid = FALSE;

  libnd_tp_set_dirty(tp, TRUE);
  libnd_tp_tell_observers(tp, LND_TP_SEL_DEL, NULL);
}


void        
libnd_tp_sel_filter(LND_TracePart *tp)
{
  LND_Packet        *p;

  if (!tp || !tp->tpm->trace)
    return;

  for (p = tp->sel.pl; p; p = p->sel_next)
    libnd_packet_set_filtered(p, TRUE);
}


void        
libnd_tp_sel_unfilter(LND_TracePart *tp)
{
  LND_Packet        *p;

  if (!tp || !tp->tpm->trace)
    return;
  
  for (p = tp->sel.pl; p; p = p->sel_next)
    libnd_packet_set_filtered(p, FALSE);
}


LND_Packet *
libnd_tp_clear_packetlists(LND_TracePart *tp)
{
  LND_Packet *result;

  if (!tp)
    return NULL; 

  result = tp->pl;

  tp->pl = tp->pl_end = NULL;
  libnd_tp_clear_selection(tp);

  /* Does NOT reset num_packets! This is used when packets
   * are swapped out -- even in that state, we still want to
   * know how many packets this trace part actually has.
   */

  return result;
}



LND_TracePartObserver *
libnd_tp_observer_new(void)
{
  return g_new0(LND_TracePartObserver, 1);
}

void               
libnd_tp_observer_free(LND_TracePartObserver *ob)
{
  g_free(ob);
}

void               
libnd_tp_add_observer(LND_TracePartObserver *ob)
{
  if (!ob)
    return;

  observers = g_list_prepend(observers, ob);
}

void               
libnd_tp_del_observer(LND_TracePartObserver *ob)
{
  if (!ob)
    return;

  observers = g_list_remove(observers, ob);
}

void               
libnd_tp_tell_observers(LND_TracePart *tp,
			LND_TracePartObserverOp op,
			void *data)
{
  GList *l;
  LND_TracePartObserver *ob;
  int index;

  if (!tp)
    return;

  for (l = observers; l; l = g_list_next(l))
    {
      ob = (LND_TracePartObserver *) l->data;

      switch (op)
	{
	case LND_TP_PACKET_SEL:
	  index = (int) data;
	  if (ob->tp_packet_sel)
	    ob->tp_packet_sel(tp, index);
	  break;

	case LND_TP_PACKET_UNSEL:     
	  index = (int) data;
	  if (ob->tp_packet_unsel)
	    ob->tp_packet_unsel(tp, index);
	  break;
	  
	case LND_TP_SEL_CLR:
	  if (ob->tp_sel_clr)
	    ob->tp_sel_clr(tp);
	  break;
	  
	case LND_TP_SEL_ALL:
	  if (ob->tp_sel_all)
	    ob->tp_sel_all(tp);
	  break;
	  
	case LND_TP_SEL_DEL:
	  if (ob->tp_sel_del)
	    ob->tp_sel_del(tp);
	  break;

	case LND_TP_DIRTY_STATE:
	  if (ob->tp_dirty_state)
	    ob->tp_dirty_state(tp);
	  break;
	  
	default:
	  D(("Unknown trace part operation\n"));
	}
    }
  
  return;
  TOUCH(data);
}


syntax highlighted by Code2HTML, v. 0.9.1