#include <exec/types.h>

#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <dos.h>

#include <libraries/dosextens.h>

#include <dlg/globalconfig.h>

#include <proto/exec.h>
#include <proto/dlg.h>
#include <pragmas/dlg.h>

#include "list.h"
#include "ivn.h"
#include "rprotos.h"

#define NEWROSSAREAS 1 // use common areas stuff

#include "/ross/protos.h"
#include "/ross/header.h"
#include "/zactive/active.h"

struct GConfig *GCFG;

int GetGCFG(void);

long __stack = 100000L;


#define DATAFILENAME "ACTIVITY.DAT"
#define NAMELEN 24

/// echostat structure
struct echostat
{
   List l;

   double sort_criteria;

   int startday,startmonth,startyear;

   char name[NAMELEN+1];
   long number;

   short imported[416];
   short exported[416];
   short processed[416];
   short local[416];
   short nodes[416];
};
//-

/// Keywords and helpfile
char *keywords[] =
{
"LINES"," n      - Set the number of lines for paginated output. (default 24)",
"HEADER"," n     - Set the number of blank lines before each header. (default 0)",
"FOOTER"," n     - Set the number of blank lines at the page bottom. (default 0)",
"FORMFEED","     - Specify that a form feed goes at the bottom of each page.\n",

"DAYSAGO"," n    - Set how long ago was the last day of the report. (default 0)",
"DAYS"," n       - Set the number of days of statistics to consider. (default 7)\n",

"ECHO"," s,s     - Specify a comma separated list of echo names for analysis.",
"ECHONUM"," n,n  - Specify a comma separated list of echo numbers for analysis.",
"CLASS"," n,n    - Specify a comma separated list of classes for analysis.",
"NOCLASS"," n,n  - Exclude the specified classes from the report.",
"NET"," s,s      - Exclude all network organizations but these from the report.",
"PASS","         - Exclude nonpassthru areas from the report.",
"NOPASS","       - Exclude passthru areas from the report.\n",

"PROCESSED","    - Display average messages processed per day.",
"IDLE","         - Display days since last message processed.",
"EXPORTED","     - Display average messages exported per day.",
"IMPORTED","     - Display average messages imported per day.",
"LOCAL","        - Display average local messages posted per day.",
"DUD","          - Display days since last local message posted.",
"FLUCTUATION","  - Display standard deviation/mean of messages processed.",
"NODE","         - Display average number of nodes connected to this echo.\n",

"TOP"," n        - Limit display to a specified number of entries.",
"REVERSE","      - Reverse the default order of the sort.",
"ALPHA","        - Sort alphabetically instead of on first column.\n",

"HELP","         - Get a list of keywords and usage.",
"?","            - Same as HELP.",
"QUIET","        - Suppress progress information, show results only.\n",
   NULL,NULL
};
//-

int month=0,day=0,year=0;

#define ECHONUM_NUM  100
#define CLASS_NUM    100
#define NOCLASS_NUM  100
#define AREA_NUM     500

int days=7,daysago=1,lines=24,header=0,footer=0,nopass=FALSE,formfeed=FALSE;
int top=100000;

int activity=0,idle=0,export=0,nodes=0;
int import=0,local=0,dud=0,erratic=0;
int reverse=FALSE;
int alpha=FALSE,quiet=FALSE;
char *echoname=0;
int echonum=0,echoarray[ECHONUM_NUM];
int classnum=0,classarray[CLASS_NUM];
int noclassnum=0,noclassarray[NOCLASS_NUM];
char *netstring=0;
int pass=FALSE;

int areanum=0;
struct area *areaarray[AREA_NUM];

int count=1;

struct areas *ar = NULL;
struct echostat *stats=0;

char *programname;

struct Library *DLGBase = NULL;

/// main
void main(int argc,char **argv)
{
   int i,j,k,result;
   struct area *a;
   char *temp;

   DLGBase = OpenLibrary("dlg.library",4L);
   if(!DLGBase) exit(20);

/// Load GCFG
   if(!GetGCFG())
   {
      printf("DLGTraffic: No global config structure\nDLGTraffic must be run while DLGMail is running.\n");
      CloseLibrary(DLGBase);
      exit(20);
   }
//-

   onbreak(breakcleanup);

   programname=PROGNAME;

   calcdate();

   /*
   ** First, read in options, or if no options specified, use defaults.
   */

   echonum = 0;

   if(argc>1 && (!Stricmp(argv[1],",,t")))
      i=2;
   else
      i=1;

/// Parse arguments
   while(i<argc)
   {
      result=FALSE;

      for(j=0;keywords[j]!=0;j+=2)
      {
         if(!Stricmp(keywords[j],argv[i]))
         {
            result=TRUE;
            break;
         }
      }

      if(!result)
      {
         print_usage();
         cleanup(0);
      }

///   Set up and error check keywords
      switch(j)
      {
///      LINES
         case  0:  /* LINES     */
            lines=atoi(argv[++i]);
            break;
//-

///      HEADER
         case  2:  /* HEADER    */
            header=atoi(argv[++i]);
            break;
//-

///      FOOTER
         case  4:  /* FOOTER    */
            footer=atoi(argv[++i]);
            break;
//-

///      FORMFEED
         case  6:  /* FORMFEED  */
            formfeed=TRUE;
            break;
//-

///      DAYSAGO
         case  8:  /* DAYSAGO   */
            daysago=atoi(argv[++i]);

            if(daysago<1 || daysago+days>365)
            {
               printf("Value %d exceeds range for DAYSAGO.  Must be in the range of 1 through 365-DAYS\n",daysago);
               cleanup(10);
            }
            break;
//-

///      DAYS
         case 10:  /* DAYS      */
            days=atoi(argv[++i]);

            if(days<1 || daysago+days>365)
            {
               printf("Value %d exceeds range for DAYS.  Must be in the range of 1 through 365-DAYSAGO\n",days);
               cleanup(10);
            }
            break;
//-

///      ECHO
         case 12:  /* ECHO      */
            if(echoname)
            {
               printf("All echos need to be specified in a single ECHO statement.\n");
               cleanup(10);
            }
            echoname=argv[++i];
            break;
//-

///      ECHONUM
         case 14:  /* ECHONUM   */
            if(echonum)
            {
               printf("All echos need to be specified in a single ECHONUM statement.\n");
               cleanup(10);
            }

            temp=argv[++i];

            for(k=0;k<ECHONUM_NUM;k++)
            {
               echoarray[k]=atoi(temp);

               if(echoarray[k]<=0)
               {
                  printf("Echo number cannot be less than or equal to zero!\n");
                  cleanup(10);
               }

               while(*temp)
               {
                  if(*temp++ == ',')
                  break;
               }

               if(!*temp)
                  break;
            }

            echonum=k+1;
            break;
//-

///      CLASS
         case 16:  /* CLASS     */
            if(classnum)
            {
               printf("All classes must be specified in a single CLASS statement.\n");
               cleanup(10);
            }

            temp=argv[++i];

            for(k=0;k<CLASS_NUM;k++)
            {
               classarray[k]=atoi(temp);

               while(*temp)
               {
                  if(*temp++ == ',')
                     break;
               }

               if(!*temp)
                  break;
            }

            classnum=k+1;
            break;
//-

///      NOCLASS
         case 18:  /* NOCLASS     */

            if(noclassnum)
            {
               printf("All classes must be specified in a single NOCLASS statement.\n");
               cleanup(10);
            }

            temp=argv[++i];

            for(k=0;k<NOCLASS_NUM;k++)
            {
               noclassarray[k]=atoi(temp);

               while(*temp)
               {
                  if(*temp++ == ',')
                     break;
               }

               if(!*temp)
                  break;
            }
            noclassnum=k+1;
            break;
//-

///      NET
         case 20:  /* NET    */
            if(netstring)
            {
               printf("Only one NET may be specified.\n");
               cleanup(10);
            }

            netstring=argv[++i];
            break;
//-

///      PASS
         case 22:  /* PASS    */
            if(nopass)
            {
               printf("PASS and NOPASS are mutually exclusive.\n");
               cleanup(10);
            }

            pass=TRUE;
            break;
//-

///      NOPASS
         case 24:  /* NOPASS    */
            if(pass)
            {
               printf("NOPASS and PASS are mutually exclusive.\n");
               cleanup(10);
            }

            nopass=TRUE;
            break;
//-

///      PROCESSED
         case 26:  /* PROCESSED */
            if(activity)
            {
               printf("Can't specify columns twice.\n");
               cleanup(10);
            }

            activity=count++;
            break;
//-

///      IDLE
         case 28:  /* IDLE      */
            if(idle)
            {
               printf("Can't specify columns twice.\n");
               cleanup(10);
            }

            idle=count++;
            break;
//-

///      EXPORTED
         case 30:  /* EXPORTED  */
            if(export)
            {
               printf("Can't specify columns twice.\n");
               cleanup(10);
            }

            export=count++;
            break;
//-

///      IMPORTED
         case 32:  /* IMPORTED  */
            if(import)
            {
               printf("Can't specify columns twice.\n");
               cleanup(10);
            }

            import=count++;
            break;
//-

///      LOCAL
         case 34:  /* LOCAL     */
            if(local)
            {
               printf("Can't specify columns twice.\n");
               cleanup(10);
            }

            local=count++;
            break;
//-

///      DUD
         case 36:  /* DUD       */
            if(dud)
            {
               printf("Can't specify columns twice.\n");
               cleanup(10);
            }

            dud=count++;
            break;
//-

///      FLUCTUATION
         case 38:  /* FLUCTUATION */
            if(erratic)
            {
               printf("Can't specify columns twice.\n");
               cleanup(10);
            }

            erratic=count++;
            break;
//-

///      NODE
         case 40:  /* NODE      */
            if(nodes)
            {
               printf("Can't specify columns twice.\n");
               cleanup(10);
            }

            nodes=count++;
            break;
//-

///      TOP
         case 42:  /* TOP       */
            top=atoi(argv[++i]);
            break;
//-

///      REVERSE
         case 44:  /* REVERSE   */
            reverse=TRUE;
            break;
//-

///      ALPHA
         case 46:  /* ALPHA     */
            alpha=TRUE;
            break;
//-

///      HELP or ?
         case 48:  /* HELP      */
         case 50:  /* ?         */
            print_usage();
            cleanup(0);
            break;
//-

///      QUIET
         case 52:  /* QUIET     */
            quiet=TRUE;
            break;
//-
      }
//-

      i++;
   }
//-

/// Read in areas
   /*
   ** Next, read in areas.
   */

   if(!quiet)
      printf("Reading in DLGMail.ARE and Activity.DAT files... Patience...\n\n");

   ar = _ROSSAREAS;

   if(ar == NULL)
   {
      printf("Could not lock onto resident area list!\n");
      cleanup(10);
   }

   if(!quiet)
      printf("Areas confirmed\n");
//-

/// Find areas that match criteria
   /*
   ** Find areas that match criteria.
   */

   areanum=0;

   for(a=get_area(ar,1);a;a=get_next_area(a))
   {
      if(areanum==AREA_NUM)
         break;

      if(nopass && get_passthru(a))
         continue;
      if(pass && (!get_passthru(a)))
         continue;

      if(netstring)
         if(!stringmatch(get_area_from_organization(a),netstring))
            continue;

      if(!arraymatch(get_class(a),noclassarray,noclassnum))
      {
         if(stringmatch(get_name(a),echoname))
         {
            areaarray[areanum++]=a;
         }
         else if(arraymatch(get_areanum(a),echoarray,echonum))
         {
            areaarray[areanum++]=a;
         }
         else if(arraymatch(get_class(a),classarray,classnum))
         {
            areaarray[areanum++]=a;
         }
         else if( (!echoname) && (!echonum) && (!classnum))
         {
            areaarray[areanum++]=a;
         }
      }

   }

   if(areanum==0)
   {
      printf("\nNo areas match given criteria.\n\n");
      cleanup(0);
   }
//-

   if(areanum==1)
   {
      if(count==1)
      {
         activity=1;
         local=2;
         import=3;
         export=4;
         count=5;
      }

      echoname=get_name(areaarray[0]);

      stats=get_single_stats(ar,echoname);

      if(!stats)
      {
         printf("Problem getting statistics.  Aborting.\n");
         cleanup(10);
      }

      print_single_stats(stats);
   }
   else
   {
      if(count==1)
      {
         activity=1;
         local=2;
         import=3;
         export=4;
         idle=5;
         erratic=6;
         count=7;
      }
      stats=get_multiple_stats(areaarray,areanum);

      if(!stats)
      {
         printf("Problem getting statistics.  Aborting.\n");
         cleanup(10);
      }

      print_multiple_stats();
   }

   cleanup(0);
}
//-

/// print_single_stats
void print_single_stats(struct echostat *stat)
{
   int d,m,y;
   int de,me,ye;
   int i,j,l;

   /*
   *
   * First, get to the appropriate day ago.
   *
   */

   for(i=0,d=day,m=month,y=year;i!=daysago;i++)
   {
      if(equaldates(d,m,y,stat->startday,stat->startmonth,stat->startyear))
      {
         printf("\nReport ends before echo data begins.\n\n");
         cleanup(0);
      }

      decrement(&d,&m,&y);
   }

   de=d; me=m; ye=y;

   for(i=1;i!=days;i++)
   {
      if(equaldates(d,m,y,stat->startday,stat->startmonth,stat->startyear))
      {
         days=i;
         break;
      }

      decrement(&d,&m,&y);
   }

   /*
   *
   * Now print main title.
   *
   */

   printf("\nDLGTraffic echo %s report from ",stat->name);
   unformatted_printdate(d,m,y);
   printf(" to ");
   unformatted_printdate(de,me,ye);
   printf("\n\n");

   /*
   *
   * Next, loop over the days, printing statistics for each, until all DAYS are done.
   *
   */

   print_single_header();

   for(l=4,j=0;j<days;j++,l++)
   {
      if(l>=lines-4)
      {
         print_single_footer();

         if(formfeed)
            printf("\014");

         print_single_header();
         l=0;
      }

      printdate(d,m,y);

      for(i=1;i<count;i++)
      {
         if(activity==i) print_activity(stat,d,m,y,1);
         else if(    idle==i) print_idle    (stat,d,m,y,1);
         else if(  export==i) print_export  (stat,d,m,y,1);
         else if(  import==i) print_import  (stat,d,m,y,1);
         else if(   local==i) print_local   (stat,d,m,y,1);
         else if(     dud==i) print_dud     (stat,d,m,y,1);
         else if( erratic==i) print_erratic (stat,d,m,y,1);
         else if(   nodes==i) print_nodes   (stat,d,m,y,1);
         else
         {
            printf("This should never print -- Something's wrong. 01\n");
            cleanup(20);
         }
      }
      printf("\n");
      increment(&d,&m,&y);
   }

   /*
   *
   * Finally, print the overall footer.
   *
   */

   printf("==============================================================================\n");

   if(formfeed)
      printf("\014");

}
//-

/// print_single_footer()
void print_single_footer(void)
{
   int i;

   for(i=0;i<footer;i++)
      printf("\n");
}
//-

/// print_single_header
void print_single_header(void)
{
   int i;

   for(i=0;i<header;i++)
      printf("\n");

   printf("------------------------------------------------------------------------------\n");

   printdate(1,0,0);

   for(i=1;i<count;i++)
   {
      if(activity==i) print_single_activity_header1();
      else if(    idle==i) print_single_idle_header1();
      else if(  export==i) print_single_export_header1();
      else if(  import==i) print_single_import_header1();
      else if(   local==i) print_single_local_header1();
      else if(     dud==i) print_single_dud_header1();
      else if( erratic==i) print_single_erratic_header1();
      else if(   nodes==i) print_single_nodes_header1();
      else
      {
         printf("This should never print -- Something's wrong. 00\n");
         cleanup(20);
      }
   }

   printf("\n");
   printdate(0,0,0);

   for(i=1;i<count;i++)
   {
      if(activity==i) print_single_activity_header2();
      else if(    idle==i) print_single_idle_header2();
      else if(  export==i) print_single_export_header2();
      else if(  import==i) print_single_import_header2();
      else if(   local==i) print_single_local_header2();
      else if(     dud==i) print_single_dud_header2();
      else if( erratic==i) print_single_erratic_header2();
      else if(   nodes==i) print_single_nodes_header2();
      else
      {
         printf("This should never print -- Something's wrong. 00\n");
         cleanup(20);
      }
   }

   printf("\n------------------------------------------------------------------------------\n");
}
//-

/// print_multiple_stats
void print_multiple_stats(void)
{
   int i,l,d,m,y,ds,ms,ys;
   struct echostat *s;

   /*
 *
 * First, get to the appropriate day ago.
 *
 */

   for(i=0,d=day,m=month,y=year;i!=daysago;i++)
   {
      decrement(&d,&m,&y);
   }

   ds=d; ms=m; ys=y;

   for(i=1;i!=days;i++)
   {
      decrement(&ds,&ms,&ys);
   }

   /*
 *
 * Now print main title.
 *
 */

   printf("\nDLGTraffic report averaged from ");
   unformatted_printdate(ds,ms,ys);
   printf(" to ");
   unformatted_printdate(d,m,y);
   printf("\n\n");

   /*
 *
 * Next, sort statistics.
 *
 */

   if(alpha)
   {
      sort_alpha();
   }
   else
   {
      if(activity==1) sort_activity(stats,d,m,y,days);
      else if(    idle==1) sort_idle    (stats,d,m,y,days);
      else if(  export==1) sort_export  (stats,d,m,y,days);
      else if(  import==1) sort_import  (stats,d,m,y,days);
      else if(   local==1) sort_local   (stats,d,m,y,days);
      else if(     dud==1) sort_dud     (stats,d,m,y,days);
      else if( erratic==1) sort_erratic (stats,d,m,y,days);
      else if(   nodes==1) sort_nodes   (stats,d,m,y,days);
      else
      {
         printf("This should never print -- Something's wrong. 02\n");
         cleanup(20);
      }
   }

   /*
 *
 * Next, output results with pagination.
 *
 */

   s=stats;
   print_multiple_header();

   for(l=4,i=0;s;l++,i++,s=get_next(s))
   {
      if(i==top)
      break;

      if(l>=lines-4)
      {
         print_multiple_footer();

         if(formfeed)
            printf("\014");

         print_multiple_header();
         l=0;
      }
      print_multiple_stat(s,d,m,y,days);
   }

   /*
 *
 * Finally, print the overall footer.
 *
 */

   printf("==============================================================================\n");

   if(formfeed)
      printf("\014");
}
//-

/// print_multiple_stat
void print_multiple_stat(struct echostat *stat,int d,int m, int y, int days)
{
   int i;

   print_echo_name(stat->name);

   for(i=1;i<count;i++)
   {
      if(activity==i) print_activity(stat,d,m,y,days);
      else if(    idle==i) print_idle    (stat,d,m,y,days);
      else if(  export==i) print_export  (stat,d,m,y,days);
      else if(  import==i) print_import  (stat,d,m,y,days);
      else if(   local==i) print_local   (stat,d,m,y,days);
      else if(     dud==i) print_dud     (stat,d,m,y,days);
      else if( erratic==i) print_erratic (stat,d,m,y,days);
      else if(   nodes==i) print_nodes   (stat,d,m,y,days);
      else
      {
         printf("This should never print -- Something's wrong. 04\n");
         cleanup(20);
      }
   }

   printf("\n");
}
//-

/// print_multiple_footer
void print_multiple_footer(void)
{
   int i;

   for(i=0;i<footer;i++)
      printf("\n");
}
//-

/// print_multiple_header
void print_multiple_header(void)
{
   int i;

   for(i=0;i<header;i++)
      printf("\n");

   printf("------------------------------------------------------------------------------\n");

   print_echo_name("   ECHO");

   for(i=1;i<count;i++)
   {
      if(activity==i) print_multiple_activity_header1();
      else if(    idle==i) print_multiple_idle_header1();
      else if(  export==i) print_multiple_export_header1();
      else if(  import==i) print_multiple_import_header1();
      else if(   local==i) print_multiple_local_header1();
      else if(     dud==i) print_multiple_dud_header1();
      else if( erratic==i) print_multiple_erratic_header1();
      else if(   nodes==i) print_multiple_nodes_header1();
      else
      {
         printf("This should never print -- Something's wrong. 03\n");
         cleanup(20);
      }
   }

   printf("\n");
   print_echo_name("   NAME");

   for(i=1;i<count;i++)
   {
      if(activity==i) print_multiple_activity_header2();
      else if(    idle==i) print_multiple_idle_header2();
      else if(  export==i) print_multiple_export_header2();
      else if(  import==i) print_multiple_import_header2();
      else if(   local==i) print_multiple_local_header2();
      else if(     dud==i) print_multiple_dud_header2();
      else if( erratic==i) print_multiple_erratic_header2();
      else if(   nodes==i) print_multiple_nodes_header2();
      else
      {
         printf("This should never print -- Something's wrong. 03\n");
         cleanup(20);
      }
   }

   printf("\n------------------------------------------------------------------------------\n");
}
//-

/// getnamefromnum
char *getnamefromnum(struct areas *aa,int num)
{
   struct area *a;
   int i;

   for(a=aa->first_area;a;a=get_next_area(a))
   {
      i=get_areanum(a);

      if(i==num)
         return get_name(a);
   }

   printf("\nArea number %d does not exist!\n\n",num);
   cleanup(0);

   return 0; /* Never actually return; this is just to make prototyping happy. */
}
//-

/// get_single_stats
struct echostat *get_single_stats(struct areas *aa,char *echoname)
{
   char *name,*path,*merged;
   struct echostat *e;
   struct area *a;
   int error,num;

   for(a=aa->first_area;a;a=get_next_area(a))
   {
      name=get_name(a);

      if(!Stricmp(echoname,name))
      {
         path=get_path(a);
         num=get_areanum(a);

         merged=mergepath(path,DATAFILENAME);

         /*   printf("Group=%s, Path=%s Merged=%s\n",name,path,merged); */

         e=(void *)malloc(sizeof(struct echostat));

         if(!e)
         {
            printf("Memory allocation error!\n");
            cleanup(10);
         }

         error=readstats(e,merged,name,num);

         stats=e;
         e->l.previous=0;
         e->l.next=0;

         if(error)
         {
            if(!quiet)
               printf("Error reading activity file %s!\n",merged);
            cleanup(10);
         }

         return e;
      }
   }

   printf("\nCould not find an echo named %s\n\n",echoname);
   cleanup(0);

   return 0; /* make the compiler happy. */
}
//-

/// get_multiple_stats
struct echostat *get_multiple_stats(struct area *areaarray[], int areanum)
{
   char *name,*path,*merged;
   struct echostat *e;
   struct area *a;
   int error,num;
   ListHdr lh;
   int i;

   initlisthdr(&lh);

   for(i=0;i<areanum;i++)
   {
      a=areaarray[i];

      name=get_name(a);
      path=get_path(a);
      num=get_areanum(a);

      merged=mergepath(path,DATAFILENAME);

      /*      printf("Group=%s, Path=%s Merged=%s\n",name,path,merged); */

      e=(void *)malloc(sizeof(struct echostat));

      if(!e)
      {
         printf("Memory allocation error!\n");
         cleanup(10);
      }

      error=readstats(e,merged,name,num);

      if(error)
      {
         if(!quiet)
            printf("\nError reading activity file %s!\n\n",merged);
         free(e);
         continue;
      }

      add_item_first(e,&lh);
      stats=e;
   }

   return get_first(&lh);
}
//-

#define PATHLEN 200

/// mergepath
char *mergepath(char *path1,char *path2)
{
   int i;
   static char path[PATHLEN];

   i=strlen(path1);

   if(i+strlen(path2)>PATHLEN-2)
   {
      printf("Path length exceeds maximum for activity.dat file!\n");
      cleanup(10);
   }

   if(i==0)
      strcpy(path,path2);
   else
   {
      strcpy(path,path1);

      switch(((int)path[i-1]))
      {
         case ':':
         case '/':
            strcpy(path+i,path2);
            break;
         default:
            path[i]='/';
            strcpy(path+i+1,path2);
            break;
      }
   }

   return path;
}
//-

/// readstats
int readstats(struct echostat *e,char *file,char *echoname,int echonum)
{
   FILE *fp;
   int a,i,size;
   struct EchoLog b;
   int d,m,y;

   fp=fopen(file,"r");

   if(!fp)
   {
      return 1;
   }

   size=sizeof(b);

   a=fread((void *)&b,size,1,fp);

   if(a!=1)
   {
      if(!quiet)
         printf("Attempt to read activity file %s has failed.\n",file);
      fclose(fp);
      return 1;
   }

   strncpy(e->name,echoname,NAMELEN);
   e->name[NAMELEN]=0;

   e->number=echonum;

   for(d=day,m=month,y=year ; m!=month||d!=day||y!=year-1 ; decrement(&d,&m,&y))
   {
      i=m*32+d;

      e->imported[i]  = b.el_daily[i].d_imported;
      e->exported[i]  = b.el_daily[i].d_totexported;
      e->processed[i] = b.el_daily[i].d_exported;
      e->local[i]     = b.el_daily[i].d_exported - b.el_daily[i].d_imported;
      e->nodes[i]     = b.el_daily[i].d_totnodes;

      if(b.el_created.od_year==y && b.el_created.od_month==m && b.el_created.od_date==d)
         break;
   }

   e->startday  =b.el_created.od_date;
   e->startmonth=b.el_created.od_month;
   e->startyear =b.el_created.od_year;

   fclose(fp);
   return 0;
}
//-

/// calcdate
void calcdate(void)
{
   long t;
   struct tm *p;

   time(&t);
   p=localtime(&t);

   day=p->tm_mday;
   month=p->tm_mon+1;
   year=p->tm_year+1900;

   if(year<1980)
   year+=100;
}
//-


int daysinmonth[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};

int get_month(int i) {return i/32;}
int get_day(int i) {return i%32; }

/// decrement
void decrement(int *day,int *month,int *year)
{
   if(*day==1)
   {
      if(*month==1)
      {
         (*year)--;
         *month=12;
         *day=daysinmonth[*month];
      }
      else
      {
         (*month)--;
         *day=daysinmonth[*month];
         if(*day==28)
         {
            if(!((*year)%4))
            (*day)++;
            if(*year==2000)
            (*day)=28;     /* No leap year for year 2000. */
         }
      }
   }
   else
   {
      (*day)--;
   }
}
//-

/// increment
void increment(int *day,int *month,int *year)
{
   if(*day==daysinmonth[*month])
   {
      if(*day==28)
      {
         if(!((*year)%4))
         (*day)++;
         if(*year==2000)  /* Year 2000 has no February 29 */
         {
            (*month)++;
            *day=1;
         }
      }
      else
      {
         if(*month==12)
         {
            (*year)++;
            *month=1;
            *day=1;
         }
         else
         {
            (*month)++;
            *day=1;
         }
      }
   }
   else
   {
      (*day)++;
   }
}
//-

void print_single_activity_header1(void)   {
   printf(" Messages  "); }
void print_single_idle_header1(void)       {
   printf(" Days "); }
void print_single_export_header1(void)     {
   printf(" Messages "); }
void print_single_import_header1(void)     {
   printf(" Messages "); }
void print_single_local_header1(void)      {
   printf(" Messages "); }
void print_single_dud_header1(void)        {
   printf(" Days Without "); }
void print_single_erratic_header1(void)    {
   printf(" Fluctuation "); }
void print_single_nodes_header1(void)      {
   printf("         "); }

void print_single_activity_header2(void)   {
   printf(" Processed "); }
void print_single_idle_header2(void)       {
   printf(" Idle "); }
void print_single_export_header2(void)     {
   printf(" Exported "); }
void print_single_import_header2(void)     {
   printf(" Imported "); }
void print_single_local_header2(void)      {
   printf("   Local  "); }
void print_single_dud_header2(void)        {
   printf(" Local Posts  "); }
void print_single_erratic_header2(void)    {
   printf("  (STD/Mean) "); }
void print_single_nodes_header2(void)      {
   printf("  Nodes  "); }


void print_multiple_activity_header1(void) {
   printf("  Average  "); }
void print_multiple_idle_header1(void)     {
   printf(" Days "); }
void print_multiple_export_header1(void)   {
   printf(" Average  "); }
void print_multiple_import_header1(void)   {
   printf(" Average  "); }
void print_multiple_local_header1(void)    {
   printf("  Average "); }
void print_multiple_dud_header1(void)      {
   printf(" Days Without "); }
void print_multiple_erratic_header1(void)  {
   printf(" Fluctuation "); }
void print_multiple_nodes_header1(void)    {
   printf(" Average "); }

void print_multiple_activity_header2(void) {
   printf(" Processed "); }
void print_multiple_idle_header2(void)     {
   printf(" Idle "); }
void print_multiple_export_header2(void)   {
   printf(" Exported "); }
void print_multiple_import_header2(void)   {
   printf(" Imported "); }
void print_multiple_local_header2(void)    {
   printf("   Local  "); }
void print_multiple_dud_header2(void)      {
   printf(" Local Posts  "); }
void print_multiple_erratic_header2(void)  {
   printf("  (STD/Mean) "); }
void print_multiple_nodes_header2(void)    {
   printf("  Nodes  "); }

/// sort_activity
void sort_activity(struct echostat *stat,int d,int m,int y,int num)
{
   struct echostat *s;

   for(s=stat;s;s=get_next(s))
   {
      s->sort_criteria=calc_activity(s,d,m,y,num);
   }

   sort_stats();
}
//-

/// sort_idle
void sort_idle(struct echostat *stat,int d,int m,int y,int num)
{
   struct echostat *s;

   for(s=stat;s;s=get_next(s))
   {
      s->sort_criteria=calc_idle(s,d,m,y,num);
   }

   sort_stats();
}
//-

/// sort_export
void sort_export(struct echostat *stat,int d,int m,int y,int num)
{
   struct echostat *s;

   for(s=stat;s;s=get_next(s))
   {
      s->sort_criteria=calc_export(s,d,m,y,num);
   }

   sort_stats();
}
//-

/// sort_import
void sort_import(struct echostat *stat,int d,int m,int y,int num)
{
   struct echostat *s;

   for(s=stat;s;s=get_next(s))
   {
      s->sort_criteria=calc_import(s,d,m,y,num);
   }

   sort_stats();
}
//-

/// sort_local
void sort_local(struct echostat *stat,int d,int m,int y,int num)
{
   struct echostat *s;

   for(s=stat;s;s=get_next(s))
   {
      if(s->number==0)
      s->sort_criteria=-2;
      else
      s->sort_criteria=calc_local(s,d,m,y,num);
   }

   sort_stats();
}
//-

/// sort_dud
void sort_dud(struct echostat *stat,int d,int m,int y,int num)
{
   struct echostat *s;

   for(s=stat;s;s=get_next(s))
   {
      if(s->number==0)
      s->sort_criteria=-2;
      else
      s->sort_criteria=calc_dud(s,d,m,y,num);
   }

   sort_stats();
}
//-

/// sort_erratic
void sort_erratic(struct echostat *stat,int d,int m,int y,int num)
{
   struct echostat *s;

   for(s=stat;s;s=get_next(s))
   {
      s->sort_criteria=calc_erratic(s,d,m,y,num);
   }

   sort_stats();
}
//-

/// sort_nodes
void sort_nodes(struct echostat *stat,int d,int m,int y,int num)
{
   struct echostat *s;

   for(s=stat;s;s=get_next(s))
   {
      s->sort_criteria=calc_nodes(s,d,m,y,num);
   }

   sort_stats();
}
//-

/// swap
void swap(struct echostat *s1,struct echostat *s2)
{
   void *tmp;

   if(s1->l.next && s1->l.next!=(void *)s2)
      s1->l.next->previous=(void *)s2;

   if(s1->l.previous && s1->l.previous!=(void *)s2)
      s1->l.previous->next=(void *)s2;

   if(s2->l.next && s2->l.next!=(void *)s1)
      s2->l.next->previous=(void *)s1;

   if(s2->l.previous && s2->l.previous!=(void *)s1)
      s2->l.previous->next=(void *)s1;

   tmp=s1->l.next;
   s1->l.next=s2->l.next;
   s2->l.next=tmp;

   tmp=s1->l.previous;
   s1->l.previous=s2->l.previous;
   s2->l.previous=tmp;

   if(s2->l.next==(void *)s2)
      s2->l.next=(void *)s1;

   if(s1->l.next==(void *)s1)
      s1->l.next=(void *)s2;

   if(s2->l.previous==(void *)s2)
      s2->l.previous=(void *)s1;

   if(s1->l.previous==(void *)s1)
      s1->l.previous=(void *)s2;

}
//-

/// sort_stats
void sort_stats(void)
{
   int result;
   struct echostat *sf,*sv,*smax;

   /*
 *
 * First, do the sort.  This algorithm is not perfect, but should work.
 *
 */

   for(sf=stats;sf;sf=get_next(sf))
   {
      for(smax=sf,sv=get_next(sf);sv;sv=get_next(sv))
      {
         result=(smax->sort_criteria < sv->sort_criteria);
         if(reverse)
            result=!result;

         if(result)
         {
            smax=sv;
         }

      }

      if(smax!=sf)
      {
         swap(sf,smax);
         sf=smax;
      }
   }

   /*
 *
 * After sorting, stats may no longer be pointing to the first element.
 * This needs to be fixed.
 *
 */

   sf=get_previous(stats);

   while(sf)
   {
      stats=sf;
      sf=get_previous(stats);
   }
}
//-

/// sort_alpha
void sort_alpha(void)
{
   int result;
   struct echostat *sf,*sv,*smax;

   /*
 *
 * First, do the sort.  This algorithm is not perfect, but should work.
 *
 */

   for(sf=stats;sf;sf=get_next(sf))
   {
      for(smax=sf,sv=get_next(sf);sv;sv=get_next(sv))
      {
         result=(Stricmp(smax->name,sv->name)>0);
         if(reverse)
         result=!result;

         if(result)
         {
            smax=sv;
         }
      }

      if(smax!=sf)
      {
         swap(sf,smax);
         sf=smax;
      }
   }

   /*
 *
 * After sorting, stats may no longer be pointing to the first element.
 * This needs to be fixed.
 *
 */

   sf=get_previous(stats);
   while(sf)
   {
      stats=sf;
      sf=get_previous(stats);
   }
}
//-

/// print_activity
void print_activity (struct echostat *s,int d,int m,int y,int num)
{
   printf("%8.2f   ",calc_activity(s,d,m,y,num));
}
//-

/// print_idle
void print_idle (struct echostat *s,int d,int m,int y,int num)
{
   int i;

   i=calc_idle(s,d,m,y,num);

   if(i==-1)
   printf(" 365  ");
   else
   printf("%4d  ",i);
}
//-

/// print_export
void print_export (struct echostat *s,int d,int m,int y,int num)
{
   printf("%8.2f  ",calc_export(s,d,m,y,num));
}
//-

/// print_import
void print_import (struct echostat *s,int d,int m,int y,int num)
{
   printf("%8.2f  ",calc_import(s,d,m,y,num));
}
//-

/// print_local
void print_local (struct echostat *s,int d,int m,int y,int num)
{
   double dd;

   dd=calc_local(s,d,m,y,num);

   if(s->number)
   printf("%8.2f  ",dd);
   else
   {
      if(fabs(dd)>1e-6)
      printf("%8.2f* ",dd);
      else
      printf("          ");
   }
}
//-

/// print_dud
void print_dud (struct echostat *s,int d,int m,int y,int num)
{
   int i;

   i=calc_dud(s,d,m,y,num);

   if(s->number)
   {
      if(i==-1)
      printf("   Forever    ");
      else
      printf("%7d       ",i);
   }
   else
   printf("              ");
}
//-

/// print_erratic
void print_erratic (struct echostat *s,int d,int m,int y,int num)
{
   printf("%9.2f    ",calc_erratic(s,d,m,y,num));
}
//-

/// print_nodes
void print_nodes (struct echostat *s,int d,int m,int y,int num)
{
   printf("%7.2f  ",calc_nodes(s,d,m,y,num));
}
//-

/// breakcleanup
int breakcleanup(void)
{
   printf("\n*******Break Detected!*******\n");
   cleanup(10);

   return 0; /* Make the compiler happy. */
}
//-

/// cleanup
void cleanup(int errornum)
{
   struct echostat *s,*q;

   if(stats)
   {
      if(get_previous(stats))
      printf("Memory leak detected!\n");
      for(s=stats;s;s=q)
      {
         q=get_next(s);

         free(q);
      }
   }

   if(DLGBase) CloseLibrary(DLGBase);

   exit(errornum);
}
//-

/// print_usage
void print_usage(void)
{
   int i;

   printf("\nUSAGE: %s ARGS\tValid ARGS are: (n=integer, s=string)\n\n",programname);

   for(i=0;;i+=2)
   {
      if(!keywords[i])
      break;

      printf("%s%s\n",keywords[i],keywords[i+1]);
   }
}
//-

/// calc_activity
double calc_activity(struct echostat *s,int d,int m,int y,int num)
{
   int i,j;
   int result;

   if(!dates_in_order(s->startday,s->startmonth,s->startyear,d,m,y))
   return 0;

   j=0;
   result=0;

   for(i=0;i<num;i++)
   {
      result+=s->processed[32*m+d];
      j++;

      if(equaldates(d,m,y,s->startday,s->startmonth,s->startyear))
         break;

      decrement(&d,&m,&y);
   }

   if(j==0)
      return 0;

   return (double)(((double)result)/((double)j));
}
//-

/// calc_idle
double calc_idle(struct echostat *s,int d,int m,int y,int num)
{
   int i;

   if(!dates_in_order(s->startday,s->startmonth,s->startyear,d,m,y))
   return 0;

   for(i=0;i<365-daysago;i++)
   {
      if(s->processed[32*m+d])
      break;

      if(equaldates(d,m,y,s->startday,s->startmonth,s->startyear))
      return -1;

      decrement(&d,&m,&y);
   }

   if(i==365-daysago)
   return -1;

   return ((double)i);
}
//-

/// calc_export
double calc_export(struct echostat *s,int d,int m,int y,int num)
{
   int i,j;
   int result;

   if(!dates_in_order(s->startday,s->startmonth,s->startyear,d,m,y))
   return 0;

   j=0;
   result=0;
   for(i=0;i<num;i++)
   {
      result+=s->exported[32*m+d];
      j++;

      if(equaldates(d,m,y,s->startday,s->startmonth,s->startyear))
      break;

      decrement(&d,&m,&y);
   }

   if(j==0)
   return 0;

   return (double)(((double)result)/((double)j));
}
//-

/// calc_import
double calc_import(struct echostat *s,int d,int m,int y,int num)
{
   int i,j;
   int result;

   if(!dates_in_order(s->startday,s->startmonth,s->startyear,d,m,y))
   return 0;

   j=0;
   result=0;
   for(i=0;i<num;i++)
   {
      result+=s->imported[32*m+d];
      j++;

      if(equaldates(d,m,y,s->startday,s->startmonth,s->startyear))
      break;

      decrement(&d,&m,&y);
   }

   if(j==0)
   return 0;

   return (double)(((double)result)/((double)j));
}
//-

/// calc_local
double calc_local(struct echostat *s,int d,int m,int y,int num)
{
   int i,j;
   int result;

   if(!dates_in_order(s->startday,s->startmonth,s->startyear,d,m,y))
   return 0;

   if(s->number==0)
   return 0;

   j=0;
   result=0;
   for(i=0;i<num;i++)
   {
      result+=s->local[32*m+d];
      j++;

      if(equaldates(d,m,y,s->startday,s->startmonth,s->startyear))
      break;

      decrement(&d,&m,&y);
   }

   if(j==0)
   return 0;

   return (double)(((double)result)/((double)j));
}
//-

/// calc_dud
double calc_dud(struct echostat *s,int d,int m,int y,int num)
{
   int i;

   if(!dates_in_order(s->startday,s->startmonth,s->startyear,d,m,y))
   return -1;

   if(s->number==0)
   return -1;

   for(i=0;i<365-daysago;i++)
   {
      if(s->local[32*m+d])
      break;

      if(equaldates(d,m,y,s->startday,s->startmonth,s->startyear))
      return -1;

      decrement(&d,&m,&y);
   }

   if(i==365-daysago)
   return -1;

   return ((double)i);
}
//-

/// calc_erratic
double calc_erratic(struct echostat *s,int d,int m,int y,int num)
{
   int i,j,result;
   double mean,var,tmp;
   int d1,m1,y1;

   if(!dates_in_order(s->startday,s->startmonth,s->startyear,d,m,y))
   return 0;

   if(num==1)
   return 0;

   d1=d;
   m1=m;
   y1=y;

   j=0;
   result=0;
   for(i=0;i<num;i++)
   {
      result+=s->processed[32*m1+d1];
      j++;

      if(equaldates(d1,m1,y1,s->startday,s->startmonth,s->startyear))
      break;

      decrement(&d1,&m1,&y1);
   }

   if(j<2)
   return 0;

   mean=(double)((double)result/((double)j));

   d1=d;
   m1=m;
   y1=y;

   var=0;
   for(i=0;i<j;i++)
   {
      tmp=s->processed[32*m1+d1] - mean;

      var+=tmp*tmp;

      decrement(&d1,&m1,&y1);
   }

   var=var/j;

   if(mean<1e-6)
   return 0;

   return (double)(sqrt(var)/mean);
}
//-

/// calc_nodes
double calc_nodes(struct echostat *s,int d,int m,int y,int num)
{
   int i,j;
   int result;

   if(!dates_in_order(s->startday,s->startmonth,s->startyear,d,m,y))
   return 0;

   j=0;
   result=0;
   for(i=0;i<num;i++)
   {
      result+=s->nodes[32*m+d];
      j++;

      if(equaldates(d,m,y,s->startday,s->startmonth,s->startyear))
      break;

      decrement(&d,&m,&y);
   }

   if(j==0)
   return 0;

   return (double)(((double)result)/((double)j));
}
//-

char *months[]=
{
   "",
   "January",
   "February",
   "March",
   "April",
   "May",
   "June",
   "July",
   "August",
   "September",
   "October",
   "November",
   "December"
};

/// printdate
void printdate(int day,int month,int year)
{
  if(day==0 && month==0 && year==0)
    {
      printf("%9sDate       ",months[0]);
      return;
    }

  if(day==1 && month==0 && year==0)
    {
      printf("%9s           ",months[0]);
      return;
    }

  printf("%9s %2d, %d  ",months[month],day,year);
}
//-

/// equaldates
int equaldates(int d1,int m1,int y1,int d2,int m2,int y2)
{
  return ( d1==d2 && m1==m2 && y1==y2 );
}
//-

/// unformatted_printdate
void unformatted_printdate(int day, int month, int year)
{
  printf("%s %d, %d",months[month],day,year);
}
//-

/// print_echo_name
void print_echo_name(char *s)
{
  printf("%-15.15s",s);
}
//-

/// dates_in_order
int dates_in_order(int d1, int m1, int y1, int d2, int m2, int y2)
{
  if(y2>y1)
    return TRUE;
  if(y2<y1)
    return FALSE;

  if(m2>m1)
    return TRUE;
  if(m2<m1)
    return FALSE;

  if(d2>d1)
    return TRUE;
  if(d2<d1)
    return FALSE;

  return TRUE;  /* For the same day, it is OK. */
}
//-

/// arraymatch
int arraymatch(int num,int *numarray,int numnum)
{
  int i;

  for(i=0;i<numnum;i++)
    {
      if(num==numarray[i])
   return 1;
    }

  return 0;
}
//-

/// stringmatch
int stringmatch(char *string,char *template)
{
  if(!string || !template)
    return 0;

  for(;;)
    {
      if(thisstringmatch(string,template))
   return 1;

      while( (*template)!=',' && (*template)!=0 )
   template++;

      if(*template==0)
   return 0;


      template++;
    }
}
//-

/// thisstringmatch
int thisstringmatch(char *string,char *template)
{
  if(*template=='*')
    {

      template++;
      if( (*template)==',' || (*template)==0 )
   return 1;

      for(;*string;string++)
   {
     if(thisstringmatch(string,template))
       return 1;
   }
      return 0;
    }
  else
    {
      while( charmatch(*string,*template) )
   {
     if( (*string)==0 )
       return 1;

     string++;
     template++;
   }

      if( (*template) == '*' )
   {
     return thisstringmatch(string,template);
   }
      else if( (*template) == ',' && (*string) == 0 )
   {
     return 1;
   }
      return 0;
    }
}
//-

/// charmatch
int charmatch(int c1,int c2)
{
  if(c1>='a' && c1<='z')
    c1=c1-'a'+'A';

  if(c2>='a' && c2<='z')
    c2=c2-'a'+'A';

  if(c1==c2)
    return 1;
  else
    return 0;
}
//-

/// GetGCFG
int GetGCFG(void)
{
   FILE *fp;
   char stuff[100];

   fp=fopen("ENV:GCFG","r");

   if(fp)
   {
      fgets(stuff,16,fp);
      fclose(fp);

      GCFG=(struct GConfig *)atoi(stuff);

      if(GCFG==NULL)
      {
         printf("No GCFG - Exiting");
         return(0);
      }

      if((GCFG->MARKER1 != 5551212)||(GCFG->MARKER2 != 5551212))
      {
         printf("GCFG Marker Failure - Exiting\n");
         return(0);
      }

      if(GCFG) return(1);
   }

   return(0);
}
//-

