/* 1200, Wed 3 Feb 99

   FD_PARSE.C:  Scanner/parser for fd_filter and fd_extract

   Copyright (C) 1992-2002 by Nevil Brownlee,
   CAIDA | University of Auckland */

/*
 * $Log: fd_parse.c,v $
 * Revision 1.1.1.2.2.11  2002/02/23 01:57:17  nevil
 * Moving srl examples to examples/ directory.  Modified examples/Makefile.in
 *
 * Revision 1.1.1.2.2.6  2000/08/08 19:44:42  nevil
 * 44b8 release
 *
 * Revision 1.1.1.2.2.4  2000/06/06 03:38:08  nevil
 * Combine NEW_ATR with TCP_ATR, various bug fixes
 *
 * Revision 1.1.1.2.2.1  2000/01/12 02:57:03  nevil
 * Implement 'packet pair matched' turnaroundtime distribution attributes.
 * Fix ASN-related bugs in NeTraMet, distribution-related bugs in fd_filter.
 *
 * Revision 1.1.1.2  1999/10/03 21:06:14  nevil
 * *** empty log message ***
 *
 * Revision 1.1.1.1.2.9  1999/09/22 05:38:34  nevil
 * Improve code to work properly on 64-bit machines
 * - Add OS=ALPHA handling to configure.in
 * - Clean up the Alpha compiler warnings
 * - Change all the snmp-related code to use Bit32 instead of unsigned long
 *
 * Revision 1.1.1.1.2.8  1999/09/14 00:46:46  nevil
 * 4.3 Release ..
 *  - Implement -D option to run NeMaC as a daemon
 *  - Tidy up the on-line help displays
 *  - Make IPv4 the default PeerType for sfmt_attribute()
 *
 * Revision 1.1.1.1.2.7  1999/05/18 03:36:24  nevil
 * Implement IPv6 in NeTraMet, and its manager/collectors.
 * - This is controlled by the V6 #define
 * - NeTraMet recognises v6 packets and fishes through their extension
 *     headers until it finds the actual payload.
 * - NeMaC et al display v6 addresses in the fom specified by RFC 2373
 * - fd_util and fd_extract allow colons in addresses (for defining tags)
 *
 * Revision 1.1.1.1.2.6  1999/02/15 21:24:05  nevil
 * Distribution file for 4.3b9
 *
 * Revision 1.1.1.1.2.5  1999/02/03 04:41:42  nevil
 * Implementation of TCP attributes, part 5
 *
 * Revision 1.1.1.1.2.4  1999/01/27 04:26:12  nevil
 * Minor corrections to fix compiler warnings
 *
 * Revision 1.1.1.1.2.3  1999/01/20 04:01:32  nevil
 * Implementation of TCP attributes, part 4
 *
 * Revision 1.1.1.1.2.2  1999/01/08 01:37:07  nevil
 * Implement TCPdata and DScodepoint attribute
 *
 * Revision 1.1.1.1.2.1  1998/11/26 04:13:49  nevil
 * Fix problems in fd_filter and fd_extract when dealing with 'new'
 * attributes, especially those used with NETFLOW. In particular:
 * - Make sure attribs[] is referred to via attr_ix[]
 * - Get the separators correctly from a #Format record (no quotes)
 * - fd_extract wasn't writing out its data for the last sample!
 *
 * Revision 1.1.1.1  1998/11/16 03:57:27  nevil
 * Import of NeTraMet 4.3b3
 *
 * Revision 1.1.1.1  1998/11/16 03:22:00  nevil
 * Import of release 4.3b3
 *
 * Revision 1.1.1.1.2.1  1998/11/11 23:14:36  nevil
 * Only include malloc.h if we HAVE_MALLOC_H
 *
 * Revision 1.1.1.1  1998/10/28 20:31:23  nevil
 * Import of NeTraMet 4.3b1
 *
 * Revision 1.1.3.2  1998/10/18 23:44:04  nevil
 * Added Nicolai's patches, some 'tidying up' of the source
 *
 * Revision 1.1.3.1  1998/10/13 02:48:17  nevil
 * Import of Nicolai's 4.2.2
 *
 * Revision 1.1.1.1  1998/08/24 12:09:28  nguba
 * NetraMet 4.2 Original Distribution
 *
 * Revision 1.4  1998/07/21 00:43:57  rtfm
 * Change attrib numbers for 'New Attribs' I-D
 * First release version of SRL
 *
 * Revision 1.3  1998/05/07 04:28:53  rtfm
 * Implement NetFlowMet, the Cisco NetFlow RTFM meter
 */

#if HAVE_CONFIG_H
#include <ntm_conf.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>

#include <string.h>
#if HAVE_MALLOC_H
# include <malloc.h>
#endif
#include <sys/param.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>

#include "ausnmp.h"
#include "asn1.h"

#include "fd_data.h"
#include "nmc_c64.h"

#if NEW_ATR
#define TEST_DELTA  0
struct distribution *dl INIT(NULL);

struct distribution *get_dist(void)  /* Get an unused distribution */
{
   struct distribution *d;
   if (dl != NULL) {
      d = dl;  dl = dl->next;
      }
   else {
      d = (struct distribution *)malloc(sizeof(struct distribution));
      }
   return d;
   }

void free_dist(struct distribution *d)  /* Return distribution to free list */
{
   d->next = dl;
   dl = d;
   }
#endif  /* NEW_ATR */

char *gnbr(unsigned int *n,char *s)  /* Get nbr from 's', */
{
   unsigned int v = 0, b = 10, d;
   while (*s == ' ') ++s;
   if (*s == 'x' || *s == 'X') {
      ++s;  b = 16;
      }
   else if (*s == '0') {
      ++s;  b = 8;
      }
   for (;;) {
      d = *s;
      if (b == 16) {
         if (!isxdigit(d)) break;
         }
      else {
         if (!isdigit(d)) break;
         if (b == 8 && d > '7') break;
         }
      if (d <= '9') d -= '0';
      else if (d <= 'F') d -= ('A'-10);
      else d -= ('a'-10);
      v = v*b + d;  ++s;
      }
   *n = v;  return s;
   }

char *gcstring(char *s, int *len)  /* Get string from s */
{
   static char escin[] = {
      'b', 'f', 'n', 'r', 't', 'v','\\','\'','\"','\?', 0 };
   static char escout[] ={
      '\b','\f','\n','\r','\t','\v','\\','\'','\"','\?' };
   unsigned int c,j;
   char sbuf[80], *t = sbuf, *rp;
   while (*s) {
      if (*s == '\\') {
         ++s;  c = *s;
         if (c == '0' || c == 'x' || c == 'X') {  /* Octal or hex nbr */
            s = gnbr(&c,s);  *t++ = c;
	    }
         else if (isdigit(c)) {  /* Octal number */
            --s;  *s = '0';  /* Make gnbr use base 8 */
            s = gnbr(&c,s);  *t++ = c;
	    }
         else {
            j = 0;  do {
               if (c == escin[j]) break;
	       } while (escin[++j] != 0);
            if (escin[j] != 0) {
               *t++ = escout[j];  ++s;
	       }
	    }
         }
      else *t++ = *s++;
      }
   *t = '\0';  *len = t-sbuf;
   rp = (char *)malloc(*len+1);  strcpy(rp,sbuf);
   return rp;
   }

int parse_open(char *fn)  /* Open file and initialise parser */
{
   if ((rfp = fopen(fn, "r")) == NULL) return 0;
   rferrors = rule_line = 0;  ic = '\n';
   return 1;
   }

void p_error(char *msg)
{
   if (!iblisted) {
      fprintf(stderr,"line %d: %s\n", rule_line,inbuf);
      iblisted = 1;
      }
   fprintf(stderr,"%s !!!\n", msg);
   ++rferrors;
   }

int nextchar(void)
{
   char *p;
   lic = ic;
   for (;;) {
      if (lic == '\n') {
	 if (fgets(inbuf, sizeof(inbuf), rfp) == NULL) return ic = EOF;
	 iblisted = 0;  ++rule_line;
	 ibp = inbuf;
	 }
      ic = *ibp++;
      if (ic == '#') lic = '\n';  /* Ignore comments */
      else return ic;
      }
   }

int nexttoken(void)
{
   for (;;) {
      if (isalnum(ic) || ic == '_') return 1;
      if (ic == ';' || ic == EOF) return 0;
      nextchar();
      }
   }

int relop_next(void)
{
   int op;
   for (;;) {
      if (isalnum(ic) || ic == '_' || ic == ';' || ic == EOF)
         return 0;
      if (ic == '=' || ic == '!') {
         op = ic;  nextchar();
         switch (op) {
         case '=':
            return OP_EQ;
         case '!':
            return OP_NE;
            }
         }
      nextchar();
      }
   }

void getfdstring(unsigned char *arg)
{
   for (;;) {
      if (isxdigit(ic)) break;  /* Addresses start with a (hex) digit */
#if V6
      if (ic == ':') break;  /* v6 addresses may start with a colon */
#endif
      nextchar();
      }
   if (ic != EOF) do {
      *arg++ = toupper(ic);
      nextchar();  /* Addresses may include . (decimal) or - (hex) */
      } while (isxdigit(ic) || ic == '.' || ic == '-'
#if V6
         || ic == ':'
#endif
         );
   *arg = '\0';  return;

   }

int wordis(char *p, char *w)
{
   if (strlen(p) != strlen(w)) return 0;  /* Lengths differ */
   return strcmp(p,w) == 0;
   }

int getword(void)
{
   char wbuf[50], *wp = wbuf;
   int j;
   for (;;) {
      *wp++ = tolower(ic);
      nextchar();
      if (ic == EOF) return EOF;
      if (!isalnum(ic) && ic != '_') break;
      }
   *wp = '\0';

   if (wordis(wbuf,"set")) return RF_SET;
   if (wordis(wbuf,"format")) return RF_FORMAT;
   if (wordis(wbuf,"tag")) return RF_TAG;
   if (wordis(wbuf,"statistics")) return RF_STATS;
   if (wordis(wbuf,"column")) return RF_COLUMN;
   if (wordis(wbuf,"time")) return RF_TIME;

   if (wordis(wbuf,"scale")) return EX_SCALE;
   if (wordis(wbuf,"kilo")) return EX_KILO;
   if (wordis(wbuf,"mega")) return EX_MEGA;
   if (wordis(wbuf,"elapsed")) return EX_ELAPSED;
   if (wordis(wbuf,"clock")) return EX_CLOCK;
   if (wordis(wbuf,"seconds")) return EX_SECS;
   if (wordis(wbuf,"minutes")) return EX_MINS;
   if (wordis(wbuf,"hours")) return EX_HOURS;
   if (wordis(wbuf,"days")) return EX_DAYS;

   if (wordis(wbuf,"ip")) return AT_IP4;
   if (wordis(wbuf,"clns")) return AT_CLNS;
   if (wordis(wbuf,"decnet")) return AT_DECNET;
   if (wordis(wbuf,"ipx")) return AT_NOVELL;
   if (wordis(wbuf,"novell")) return AT_NOVELL;
   if (wordis(wbuf,"ethertalk")) return AT_ETHERTALK;
   if (wordis(wbuf,"other")) return AT_OTHER;

   if (wordis(wbuf,"icmp")) return PT_ICMP;
   if (wordis(wbuf,"tcp")) return PT_TCP;
   if (wordis(wbuf,"udp")) return PT_UDP;
   if (wordis(wbuf,"ospf")) return PT_OSPF;

   if (wordis(wbuf,"ftpdata")) return WNP_FTPDATA;
   if (wordis(wbuf,"ftp")) return WNP_FTP;
   if (wordis(wbuf,"telnet")) return WNP_TELNET;
   if (wordis(wbuf,"smtp")) return WNP_SMTP;
   if (wordis(wbuf,"domain")) return WNP_DOMAIN;
   if (wordis(wbuf,"nntp")) return WNP_NNTP;
   if (wordis(wbuf,"ntp")) return WNP_NTP;
   if (wordis(wbuf,"snmp")) return WNP_SNMP;

   for (j = 0; j != SZ_ATTRIBS; ++j) {
      if (wordis(wbuf,attribs[j].name)) return attribs[j].index;
      }
   sprintf(ebuf, "Unknown word %s", wbuf);
   p_error(ebuf);
   return 0;
   }



int getnbr(void)  /* Words return negative numbers */
{
   int v = 0;
   for (;;) {
      if (ic == EOF) return EOF;
      if (isdigit(ic)) break;
      if (isalpha(ic)) return getword();
      if (ic == '\'') {
         v = nextchar();
         if (nextchar() != '\'')
            p_error("Missing closing quote");
         return v;
         }
      nextchar();
      }
   for (;;) {
      v = v*10 + ic-'0';
      if (nextchar() == EOF) return EOF;
      if (!isdigit(ic)) break;
      }
   return v;
   }

void scan_format(struct flow_stats *fs, int single_record, int list)
{
   char sbuf[32], *sp;
   int a, n, len, x;

   memset (fs, 0, sizeof(struct flow_stats));

   for (a = 0; ; ++a) {
      n = getnbr();     
      if (n < 0 || n > LASTATTRIB || attr_ix[n] == 0) {
         sprintf(ebuf, "Attribute %d not allowed in format",n);
         p_error(ebuf);
         }
      else {
         fs->format[a] = n;  fs->required[n] = 1;
#if NEW_ATR
         if (DISTRIB_ATTRIB(n))
            fs->distrib_bits |= 1 << n-DISTRIB_BASE;
         else if (DELTA_ATTRIB(n))
            fs->delta_bits |= 1 << n-DELTA_BASE;
#endif
         }

      if (single_record) {
         for (sp = sbuf; ; ) {
            if (ic == '\n' || islower(ic))
	       break;  /* Start of next attribute name */
            *sp++ = ic;
            nextchar();
            }
         *sp = '\0';
	 fs->separator[a] = (char *)malloc(strlen(sp)+1);
         strcpy(fs->separator[a],sbuf);
         if (ic == '\n') break;  /* End of #Format record */
         }
      else {
         fs->separator[a] = " ";  /* Default */
         while (isspace(ic)) nextchar();  /* Skip blanks */

         if (ic == ';') break;  /* End of format statement */

         if (!isalnum(ic)) {  /* Separator */
            if (ic == '\"') {  /* Separator string */
               nextchar();
               for (sp = sbuf; ; ) {
                  if (ic == '\"') break;
                  *sp++ = ic;
	          nextchar();
                  }
               nextchar();
               }
            else {
               sp = sbuf;  *sp++ = ' ';
               for (;;) {
                  if (isspace(ic)) break;
                  *sp++ = ic;
	          nextchar();
                  }
               *sp++ = ' ';
	       }
            *sp = '\0';
	    fs->separator[a] = gcstring(sbuf, &len);
            while (isspace(ic)) nextchar();  /* Skip blanks */
            }

         if (ic == ';') break;  /* End of format statement */
         }
      }
   fs->n_attr = a+1;
   if (list && !quiet) {
      fprintf(stderr,"Format: ");
      for (n = 0; n != fs->n_attr; ++n) {
         a = fs->format[n];
         x = attr_ix[a];  /* Index into attribs[] */
         fprintf(stderr,attribs[x].name);
         if (n != fs->n_attr-1
               || strcmp(fs->separator[n]," ") != 0)
            fprintf(stderr, fs->separator[n]);
         }
      fprintf(stderr,"\n");
      }
   }

val attrib_ptr(struct flow_info *fip, int a)
{
   val v;

   switch (a) {
   case FTFLOWINDEX:
      v.intval = &fip->FlowIndex;
      break;
   case FTLOWINTERFACE:
      v.intval = &fip->LowInterface;
      break;
   case FTLOWADJACENTTYPE:
      v.intval = &fip->LowAdjType;
      break;
   case FTLOWADJACENTADDRESS:
      v.charval = fip->LowAdjAddress;
      break;
   case FTLOWADJACENTMASK:
      v.charval = fip->LowAdjMask;
      break;
   case FTLOWPEERTYPE:
      v.intval = &fip->LowPeerType;
      break;
   case FTLOWPEERADDRESS:
      v.charval = fip->LowPeerAddress;
      break;
   case FTLOWPEERMASK:
      v.charval = fip->LowPeerMask;
      break;
   case FTLOWTRANSTYPE:
      v.intval = &fip->LowTransType;
      break;
   case FTLOWTRANSADDRESS:
      v.intval = &fip->LowTransAddress;
      break;
   case FTLOWTRANSMASK:
      v.intval = &fip->LowTransMask;
      break;
   case FTHIINTERFACE:
      v.intval = &fip->HighInterface;
      break;
   case FTHIADJACENTTYPE:
      v.intval = &fip->HighAdjType;
      break;
   case FTHIADJACENTADDRESS:
      v.charval = fip->HighAdjAddress;
      break;
   case FTHIADJACENTMASK:
      v.charval = fip->HighAdjMask;
      break;
   case FTHIPEERTYPE:
      v.intval = &fip->HighPeerType;
      break;
   case FTHIPEERADDRESS:
      v.charval = fip->HighPeerAddress;
      break;
   case FTHIPEERMASK:
      v.charval = fip->HighPeerMask;
      break;
   case FTHITRANSTYPE:
      v.intval = &fip->HighTransType;
      break;
   case FTHITRANSADDRESS:
      v.intval = &fip->HighTransAddress;
      break;
   case FTHITRANSMASK:
      v.intval = &fip->HighTransMask;
      break;
   case FTRULESET:
      v.intval = &fip->FlowRuleSet;
      break;
   case FTUPOCTETS:
      v.c64val = &fip->FwdBytes;
      break;
   case FTUPPDUS:
      v.c64val = &fip->FwdPackets;
      break;
   case FTDOWNOCTETS:
      v.c64val = &fip->BackBytes;
      break;
   case FTDOWNPDUS:
      v.c64val = &fip->BackPackets;
      break;
   case FTFIRSTTIME:
      v.intval = &fip->FirstTime;
      break;
   case FTLASTTIME:
      v.intval = &fip->LastTime;
      break;
   case FTSOURCECLASS:
      v.intval = &fip->LowClass;
      break;
   case FTDESTCLASS:
      v.intval = &fip->HighClass;
      break;
   case FTFLOWCLASS:
      v.intval = &fip->FlowClass;
      break;
   case FTSOURCEKIND:
      v.intval = &fip->LowKind;
      break;
   case FTDESTKIND:
      v.intval = &fip->HighKind;
      break;
   case FTFLOWKIND:
      v.intval = &fip->FlowKind;
      break;
   case FTDSCODEPOINT:
      v.intval = &fip->DSCodePoint;
      break;
   case FTUPOCTETS_D:
      v.c64val = &fip->upbci;
      break;
   case FTUPPDUS_D:
      v.c64val = &fip->uppci;
      break;
   case FTDOWNOCTETS_D:
      v.c64val = &fip->dnbci;
      break;
   case FTDOWNPDUS_D:
      v.c64val = &fip->dnpci;
      break;
   case FTTAGNBR:
      v.intval = &fip->TagNbr;
      break;

#if defined(NETFLOW)
   case FTMETERID:
      v.intval = &fip->MeterId;
      break;
   case FTLOWROUTEASN:
      v.intval = &fip->LowRouteASN;
      break;
   case FTLOWROUTEPREFIX:
      v.intval = &fip->LowRoutePrefix;
      break;
   case FTHIROUTEASN:
      v.intval = &fip->HighRouteASN;
      break;
   case FTHIROUTEPREFIX:
      v.intval = &fip->HighRoutePrefix;
      break;
#endif

#if NEW_ATR
   case FTTCPDATA:
      v.sfval = &fip->sfd;
      break;
   case FTTCPDATA_D:
      v.sfval = &fip->sfd_rate;
      break;

   case FTTOLOSTPDUS:
      v.intval = &fip->ToLostPDUs;
      break;
   case FTFROMLOSTPDUS:
      v.intval = &fip->FromLostPDUs;
      break;
   case FTTOLOSTPDUS_D:
      v.intval = &fip->d_ToLostPDUs;
      break;
   case FTFROMLOSTPDUS_D:
      v.intval = &fip->d_FromLostPDUs;
      break;
   case FTTOPQOVERFLOWS:
      v.intval = &fip->ToPQOverflows;
      break;
   case FTFROMPQOVERFLOWS:
      v.intval = &fip->FromPQOverflows;
      break;
   case FTTOPQOVERFLOWS_D:
      v.intval = &fip->d_ToPQOverflows;
      break;
   case FTFROMPQOVERFLOWS_D:
      v.intval = &fip->d_FromPQOverflows;
      break;

   case FTTOPACKETSIZE:
   case FTFROMPACKETSIZE:
   case FTTOINTERARRIVALTIME:
   case FTFROMINTERARRIVALTIME:
/*     case FTTOTURNAROUNDTIME:
       case FTFROMTURNAROUNDTIME: */
   case FTTOBITRATE:
   case FTFROMBITRATE:
   case FTTOPDURATE:
   case FTFROMPDURATE:

   case FTTOTCPSIZE:
   case FTFROMTCPSIZE:
   case FTTOTCPTIME:
   case FTFROMTCPTIME:
   case FTTOTCPRATE1:
   case FTFROMTCPRATE1:
   case FTTOTCPRATE2:
   case FTFROMTCPRATE2:

   case FTTOTURNAROUNDTIME1:
   case FTFROMTURNAROUNDTIME1:
   case FTTOTURNAROUNDTIME2:
   case FTFROMTURNAROUNDTIME2:
   case FTTOTURNAROUNDTIME3:
   case FTFROMTURNAROUNDTIME3:
   case FTTOTURNAROUNDTIME4:
   case FTFROMTURNAROUNDTIME4:

   case FTTOFLOWOCTETS:
   case FTFROMFLOWOCTETS:
   case FTTOFLOWPDUS:
   case FTFROMFLOWPDUS:
   case FTFLOWTIME:
      v.distval = &fip->dist;  /* Start of flow's distrib chain */
      break;

   case FTTOPACKETSIZE_D:
   case FTFROMPACKETSIZE_D:
   case FTTOINTERARRIVALTIME_D:
   case FTFROMINTERARRIVALTIME_D:
/*   case FTTOTURNAROUNDTIME_D:
     case FTFROMTURNAROUNDTIME_D: */
   case FTTOBITRATE_D:
   case FTFROMBITRATE_D:
   case FTTOPDURATE_D:
   case FTFROMPDURATE_D:

   case FTTOTCPSIZE_D:
   case FTFROMTCPSIZE_D:
   case FTTOTCPTIME_D:
   case FTFROMTCPTIME_D:
   case FTTOTCPRATE1_D:
   case FTFROMTCPRATE1_D:
   case FTTOTCPRATE2_D:
   case FTFROMTCPRATE2_D:

   case FTTOTURNAROUNDTIME1_D:
   case FTFROMTURNAROUNDTIME1_D:
   case FTTOTURNAROUNDTIME2_D:
   case FTFROMTURNAROUNDTIME2_D:
   case FTTOTURNAROUNDTIME3_D:
   case FTFROMTURNAROUNDTIME3_D:
   case FTTOTURNAROUNDTIME4_D:
   case FTFROMTURNAROUNDTIME4_D:

   case FTTOFLOWOCTETS_D:
   case FTFROMFLOWOCTETS_D:
   case FTTOFLOWPDUS_D:
   case FTFROMFLOWPDUS_D:
   case FTFLOWTIME_D:
      v.distval = &fip->dist_rate;
      break;
#endif  /* NEW_ATR */

   default:
      v.intval = NULL;
      break;
      }
   return v;
   }

/* get_value() and put_value() are simplified routines for working
   with flow data files.  Strings are delimited by blanks! */

char *get_value(val v, int a, char *pb)
{
   unsigned long val;
   int j;
   unsigned char *p;
#if NEW_ATR
   struct distribution *d, *ld;
#endif  /* NEW_ATR */
   char buf[30];  /* ???????? */

   if (*pb == '\0') return NULL;
   while (isspace(*pb)) pb++;
   switch (a) {  /* counter64 attributes */
   case FTUPOCTETS:
   case FTUPPDUS:
   case FTDOWNOCTETS:
   case FTDOWNPDUS:
   case FTUPOCTETS_D:
   case FTUPPDUS_D:
   case FTDOWNOCTETS_D:
   case FTDOWNPDUS_D:
      pb = getc64(v.c64val, pb);
      break;

#if NEW_ATR
   case FTTCPDATA:
   case FTTCPDATA_D:
      sscanf(pb, "%u", &v.sfval->n_subflows);
      while (isdigit(*pb)) ++pb;
      while (isspace(*pb)) ++pb;
      if (v.sfval->n_subflows == 0)
         break;  /* No TCPdata values */

      sscanf(pb, "%u", &v.sfval->mx_active_subflows);
      while (isdigit(*pb)) ++pb;
      while (isspace(*pb)) ++pb;
      pb = getc64(&v.sfval->ToTCPLenOctets, pb);
      while (isspace(*pb)) ++pb;
      pb = getc64(&v.sfval->FromTCPLenOctets, pb);
      while (isspace(*pb)) ++pb;
      pb = getc64(&v.sfval->ToTCPSeqOctets, pb);
      while (isspace(*pb)) ++pb;
      pb = getc64(&v.sfval->FromTCPSeqOctets, pb);
      while (isspace(*pb)) ++pb;
      pb = getc64(&v.sfval->ToTCPAckOctets, pb);
      while (isspace(*pb)) ++pb;
      pb = getc64(&v.sfval->FromTCPAckOctets, pb);
      while (isspace(*pb)) ++pb;
      sscanf(pb, "%u", &v.sfval->ToTCPDecrSeq);
      while (isdigit(*pb)) ++pb;
      while (isspace(*pb)) ++pb;
      sscanf(pb, "%u", &v.sfval->FromTCPDecrSeq);
      while (isdigit(*pb)) ++pb;
      break;

   case FTTOPACKETSIZE:
   case FTFROMPACKETSIZE:
   case FTTOINTERARRIVALTIME:
   case FTFROMINTERARRIVALTIME:
/*   case FTTOTURNAROUNDTIME:
     case FTFROMTURNAROUNDTIME: */
   case FTTOBITRATE:
   case FTFROMBITRATE:
   case FTTOPDURATE:
   case FTFROMPDURATE:

   case FTTOTCPSIZE:
   case FTFROMTCPSIZE:
   case FTTOTCPTIME:
   case FTFROMTCPTIME:
   case FTTOTCPRATE1:
   case FTFROMTCPRATE1:
   case FTTOTCPRATE2:
   case FTFROMTCPRATE2:

   case FTTOTURNAROUNDTIME1:
   case FTFROMTURNAROUNDTIME1:
   case FTTOTURNAROUNDTIME2:
   case FTFROMTURNAROUNDTIME2:
   case FTTOTURNAROUNDTIME3:
   case FTFROMTURNAROUNDTIME3:
   case FTTOTURNAROUNDTIME4:
   case FTFROMTURNAROUNDTIME4:

   case FTTOFLOWOCTETS:
   case FTFROMFLOWOCTETS:
   case FTTOFLOWPDUS:
   case FTFROMFLOWPDUS:
   case FTFLOWTIME:

   case FTTOPACKETSIZE_D:
   case FTFROMPACKETSIZE_D:
   case FTTOINTERARRIVALTIME_D:
   case FTFROMINTERARRIVALTIME_D:
/*   case FTTOTURNAROUNDTIME_D:
     case FTFROMTURNAROUNDTIME_D: */
   case FTTOBITRATE_D:
   case FTFROMBITRATE_D:
   case FTTOPDURATE_D:
   case FTFROMPDURATE_D:

   case FTTOTCPSIZE_D:
   case FTFROMTCPSIZE_D:
   case FTTOTCPTIME_D:
   case FTFROMTCPTIME_D:
   case FTTOTCPRATE1_D:
   case FTFROMTCPRATE1_D:
   case FTTOTCPRATE2_D:
   case FTFROMTCPRATE2_D:

   case FTTOTURNAROUNDTIME1_D:
   case FTFROMTURNAROUNDTIME1_D:
   case FTTOTURNAROUNDTIME2_D:
   case FTFROMTURNAROUNDTIME2_D:
   case FTTOTURNAROUNDTIME3_D:
   case FTFROMTURNAROUNDTIME3_D:
   case FTTOTURNAROUNDTIME4_D:
   case FTFROMTURNAROUNDTIME4_D:

   case FTTOFLOWOCTETS_D:
   case FTFROMFLOWOCTETS_D:
   case FTTOFLOWPDUS_D:
   case FTFROMFLOWPDUS_D:
   case FTFLOWTIME_D:
      if (*v.distval == NULL) {
         ld = d = NULL;
         }
      else for (d = *v.distval; ; ) {  /* Find end of chain */
         if (d->selector == a) break;
         ld = d;  d = d->next;
         if (d == NULL) break;
         }
      if (d == NULL) {
         if ((d = get_dist()) == NULL) {
            fprintf(stderr, "Couldn't get space for distribution!\n");
            exit(12);
            }
#if TEST_DELTA
         fprintf(stderr, "get_val(0x%0lx, %d) -> 0x%0lx\n", v.distval,a, d);
#endif
         if (ld == NULL) *v.distval = d;  /* First dist in chain */
         else ld->next = d;
         d->next = NULL;  d->selector = a;  /* Attribute number */
         }

      sscanf(pb, "%u", &d->Transform);  while (isdigit(*pb)) ++pb;
      if (d->Transform == 0) break;  /* No data available */

      while (isspace(*pb)) ++pb;
      sscanf(pb, "%u", &d->ScaleFactor);  while (isdigit(*pb)) ++pb;
      while (isspace(*pb)) ++pb;
      sscanf(pb, "%u", &d->LowerLimit);  while (isdigit(*pb)) ++pb;
      while (isspace(*pb)) ++pb;
      sscanf(pb, "%u", &d->UpperLimit);  while (isdigit(*pb)) ++pb;
      while (isspace(*pb)) ++pb;
      sscanf(pb, "%u", &d->Buckets);  while (isdigit(*pb)) ++pb;
      while (isspace(*pb)) ++pb;
      sscanf(pb, "%u", &d->Parameter1);  while (isdigit(*pb)) ++pb;
      while (isspace(*pb)) ++pb;
      sscanf(pb, "%u", &d->Parameter2);  while (isdigit(*pb)) ++pb;
      while (isspace(*pb)) ++pb;
      sscanf(pb, "%u", &d->Parameter3);  while (isdigit(*pb)) ++pb;
      while (isspace(*pb)) ++pb;
      for (j = 0; j != d->Buckets+1; ++j) {
         sscanf(pb, "%u", &d->counts[j]);  while (isdigit(*pb)) ++pb;
         while (isspace(*pb)) ++pb;
         }
      break;
#endif  /* NEW_ATR */

   default:
      if (string_value(a)) {
         for (j = 0, p = v.charval; 
               *pb && *pb != ' ' && *pb != ';' && j < STR_LEN;  ++j)
            *p++ = *pb++;
         *p = '\0';
         }
      else {  /* Unsigned int */
         sscanf(pb, "%u", v.intval);
         while (isdigit(*pb)) ++pb;
         }
      }
   return pb;
   }

void put_value(FILE *out, int a, val v)
{
   char buf[30];
#if NEW_ATR
   struct distribution *d;
   int j;
#endif  /* NEW_ATR */

   switch (a) {  /* counter64 attributes */
   case FTUPOCTETS:
   case FTUPPDUS:
   case FTDOWNOCTETS:
   case FTDOWNPDUS:
   case FTUPOCTETS_D:
   case FTUPPDUS_D:
   case FTDOWNOCTETS_D:
   case FTDOWNPDUS_D:
      putc64(buf, v.c64val);
      fprintf(out,"%s",buf);
      break;

#if NEW_ATR
   case FTTCPDATA:
   case FTTCPDATA_D:
      if (v.sfval->n_subflows == 0) {  /* No TCPdata values */
         fprintf(out,"0 ");
         break;
         }
      fprintf(out,"%u %u ",
         v.sfval->n_subflows, v.sfval->mx_active_subflows);
      putc64(buf, &v.sfval->ToTCPLenOctets);
      fprintf(out,"%s ",buf);
      putc64(buf, &v.sfval->FromTCPLenOctets);
      fprintf(out,"%s ",buf);
      putc64(buf, &v.sfval->ToTCPSeqOctets);
      fprintf(out,"%s ",buf);
      putc64(buf, &v.sfval->FromTCPSeqOctets);
      fprintf(out,"%s ",buf);
      putc64(buf, &v.sfval->ToTCPAckOctets);
      fprintf(out,"%s ",buf);
      putc64(buf, &v.sfval->FromTCPAckOctets);
      fprintf(out,"%s ",buf);
      fprintf(out,"%u %u",
         v.sfval->ToTCPDecrSeq, v.sfval->FromTCPDecrSeq);
      break;

   case FTTOPACKETSIZE:
   case FTFROMPACKETSIZE:
   case FTTOINTERARRIVALTIME:
   case FTFROMINTERARRIVALTIME:
/*    case FTTOTURNAROUNDTIME:
      case FTFROMTURNAROUNDTIME: */
   case FTTOBITRATE:
   case FTFROMBITRATE:
   case FTTOPDURATE:
   case FTFROMPDURATE:

   case FTTOTCPSIZE:
   case FTFROMTCPSIZE:
   case FTTOTCPTIME:
   case FTFROMTCPTIME:
   case FTTOTCPRATE1:
   case FTFROMTCPRATE1:
   case FTTOTCPRATE2:
   case FTFROMTCPRATE2:

   case FTTOTURNAROUNDTIME1:
   case FTFROMTURNAROUNDTIME1:
   case FTTOTURNAROUNDTIME2:
   case FTFROMTURNAROUNDTIME2:
   case FTTOTURNAROUNDTIME3:
   case FTFROMTURNAROUNDTIME3:
   case FTTOTURNAROUNDTIME4:
   case FTFROMTURNAROUNDTIME4:

   case FTTOFLOWOCTETS:
   case FTFROMFLOWOCTETS:
   case FTTOFLOWPDUS:
   case FTFROMFLOWPDUS:
   case FTFLOWTIME:

   case FTTOPACKETSIZE_D:
   case FTFROMPACKETSIZE_D:
   case FTTOINTERARRIVALTIME_D:
   case FTFROMINTERARRIVALTIME_D:
/*   case FTTOTURNAROUNDTIME_D:
     case FTFROMTURNAROUNDTIME_D: */
   case FTTOBITRATE_D:
   case FTFROMBITRATE_D:
   case FTTOPDURATE_D:
   case FTFROMPDURATE_D:

   case FTTOTCPSIZE_D:
   case FTFROMTCPSIZE_D:
   case FTTOTCPTIME_D:
   case FTFROMTCPTIME_D:
   case FTTOTCPRATE1_D:
   case FTFROMTCPRATE1_D:
   case FTTOTCPRATE2_D:
   case FTFROMTCPRATE2_D:

   case FTTOTURNAROUNDTIME1_D:
   case FTFROMTURNAROUNDTIME1_D:
   case FTTOTURNAROUNDTIME2_D:
   case FTFROMTURNAROUNDTIME2_D:
   case FTTOTURNAROUNDTIME3_D:
   case FTFROMTURNAROUNDTIME3_D:
   case FTTOTURNAROUNDTIME4_D:
   case FTFROMTURNAROUNDTIME4_D:

   case FTTOFLOWOCTETS_D:
   case FTFROMFLOWOCTETS_D:
   case FTTOFLOWPDUS_D:
   case FTFROMFLOWPDUS_D:
   case FTFLOWTIME_D:
      for (d = *v.distval; d != NULL; d = d->next) {
         if (d->selector == a) break;
         }
      if (d == NULL) {
         fprintf(stderr, "Couldn't find distribution attrib %d\n", a);
         exit(12);
         }

      fprintf(out,"%u",d->Transform);
      if (d->Transform == 0) break;  /* No data available */

      fprintf(out," %u %u %u  %u %u %u %u ",
         d->ScaleFactor, d->LowerLimit, d->UpperLimit,
         d->Buckets, d->Parameter1, d->Parameter2, d->Parameter3);
      for (j = 0; j != d->Buckets+1; ++j)
         fprintf(out," %u", d->counts[j]);
      break;
#endif  /* NEW_ATR */

   default:
      if (string_value(a)) {
         fprintf(out,"%s",v.charval);
         }
      else {
         fprintf(out,"%u",*v.intval);
	 }
      }
   }
