/*

  My brother is writing this, I have no idea how it works, or what state it is in..

  It is supposed to be an HBBS clone of the brilliant MultiTop for AmiExpress...


*/

/* **** HBBS Door Code****************************************************** */

/*
  DoorName
  ========

    HBBSMultiTop

  Version
  =======

    1.0, release  1, 26/12/1995

  Options
  =======

    N_ND->ActiveDoor->SystemOptions
    -------------------------------



    Command Line Arguments
    ----------------------

  ToDo
  ====


    BestUsers list (i.e.  highest uploaders, comparing actualuploadbytes to uploadbytes)

       if users actual upload bytes is higher than the uploadbytes they get nukes alot
       if users actual upload bytes is LESS then the uploadbytes they get awarded alot

       Handle  AUP    UP   RATIO

       sid     8   /  10 = 0.8    awarded
       Hydra   10  /  10 = 1      not nuked, not awarded
       Fred    10  /  8  = 1.25   nuked

       therefore lower ratio = better user.

    BestCallers list (i.e. list of users comparing upload to download bytes)

       Handle  UP     DN   RATIO

       Fred    10  /  8  = 1.25   uploaded more than downloaded
       Hydra   10  /  10 = 1      same uploads as downloads
       sid     8   /  10 = 0.8    downloaded more that uploaded



*/

/* **** Includes *********************************************************** */

#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/alib_protos.h>

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <time.h>

#include <HBBS/ANSI_Codes.h>
#include <HBBS/Defines.h>
#include <HBBS/types.h>
#include <HBBS/Access.h>
#include <HBBS/structures.h>
#include <HBBS/hbbscommon_protos.h>
#include <HBBS/hbbscommon_pragmas.h>
#include <HBBS/Hbbsnode_protos.h>
#include <HBBS/Hbbsnode_pragmas.h>

/* **** Definitions ******************************************************** */

#define BENS_HEADER "+------------------------------------------------------+\r\n|            Zippy Search V1 by Ben Clifton            |\r\n|           (C) 1995 Ruby Knight Productions           |\r\n+------------------------------------------------------+\r\n"

/* **** Variables ********************************************************** */


struct Library *HBBSCommonBase  = NULL;
struct Library *HBBSNodeBase    = NULL;

struct BBSGlobalData *BBSGlobal = NULL;
struct NodeData *N_ND           = NULL;
int    N_NodeNum                = -1;

long __stack=16*1024; // increse this in 4k incrments if you suffer from
                      // random/suprious crashings after or during the running
                      // of your door.

int    gargc;         // these are just copies of main()'s argc and argv..
char   **gargv;

char tmpstr[BIG_STR];

/* **** Functions ********************************************************** */


#ifdef __SASC
int CXBRK(void) { return(0); }
int _CXBRK(void) { return(0); }
void chkabort(void) {}
#endif

static VOID cleanup(ULONG num)
{
  if (HBBSCommonBase)
  {
    HBBS_CleanUpCommon();
    CloseLibrary (HBBSCommonBase);
  }

  if (num) printf("Door Error = %d\n",num);

  exit(0);
}

static VOID init( void )
{
  if(!(HBBSCommonBase = OpenLibrary("HBBSCommon.library",0)))
  {
    cleanup(1);
  }

  if (!(HBBS_InitCommon()))
  {
    cleanup(2);
  }
}

void Usage ( void )
{
  Printf( "HBBSMultitop by Hydra^cP!\n");
  Printf("\n"
         "Usage : HBBSMultiTop (<design> <output>...)\n"
         "\n"
         "  design  - Full path to your design file.\n"
         "  output  - Full path to your output file.\n"
         "E.G.  HBBSMultiTop in.1 out.1 in.2 out.2\n");
}



struct UserInfo
{
  char *Handle;
  char *Group;
  V_HUGENUM UploadBytes;
  V_HUGENUM DownloadBytes;
  V_HUGENUM Messages;
  V_HUGENUM Calls;
  V_BIGNUM UploadFiles;
  V_BIGNUM DownloadFiles;
  V_BIGNUM CPSUp;
  V_BIGNUM CPSDown;

};

struct UserNode
{
  struct Node node;
  struct UserInfo *info;
};

struct List *TopBUploaders=NULL;
struct List *TopBDownloaders=NULL;
struct List *TopFUploaders=NULL;
struct List *TopFDownloaders=NULL;
struct List *BestMsgWriters=NULL;
struct List *BestCallers=NULL;
struct List *BestCPSUp=NULL;
struct List *BestCPSDown=NULL;

#define I_TopBUploader 0
#define I_TopBDownloader 1
#define I_BestMsgWriters 2
#define I_BestCallers 3
#define I_TopFUploader 4
#define I_TopFDownloader 5
#define I_BestCPSUp 6
#define I_BestCPSDown 7

#define NUM_LISTS 8  // if you change this search for NUM_LISTS below and update accordingly

V_HUGENUM TotalBytesUp=0;
V_HUGENUM TotalBytesDown=0;
V_HUGENUM TotalFilesUp=0;
V_HUGENUM TotalFilesDown=0;
V_HUGENUM TotalCalls=0;
V_HUGENUM TotalMessages=0;
V_BIGNUM BestEverCPSUp=0;
V_BIGNUM BestEverCPSDown=0;


struct List *UserList=NULL;
char *outfilename=NULL, *infilename=NULL;
int LoadedUsers=0; // contains the amount of user details we've got loaded
                   // after CreateLists() has completed

BOOL CreateLists( void )
{

  // right...  complex...

  /*

    this routine allocated a load of structures in memory with info about all the
    users in the user data file.

    It creates a list called UserList with a pointer to each of the allocated structures

    it then creates other lists which point to the same data in memory except
    that the order of the other lists is sorted by TopBUploaders/Downloaders etc..

    this way we don't use loads of memory creating loads of lists and loads of COPIES
    of the information.  we just have the info loaded once in memory and all the
    lists point to the same data just in different orders.

    when it comes to freeing the memory we free the nodes in UserList first then
    we just FreeStrList() the other lists..

  */

  struct UserData *tmpuser;
  struct UserNode *tmpusernode,*in;
  struct UserInfo *tmpinfo;

  V_BIGNUM UserID=1;

  BOOL Done=FALSE,Error=FALSE,OK;

  if (tmpuser=AllocVec(sizeof(struct UserData),MEMF_PUBLIC|MEMF_CLEAR))
  {

    UserList=HBBS_CreateList();

    TopBUploaders=HBBS_CreateList();
    TopBDownloaders=HBBS_CreateList();
    TopFUploaders=HBBS_CreateList();
    TopFDownloaders=HBBS_CreateList();
    BestMsgWriters=HBBS_CreateList();
    BestCallers=HBBS_CreateList();
    BestCPSUp=HBBS_CreateList();
    BestCPSDown=HBBS_CreateList();

    do
    {
      if (HBBS_LoadUser(UserID,NULL,NULL,tmpuser))
      {
        UserID++;

        if (tmpuser->Status==USER_VALIDATED) // ignore deleted/new users etc..
        {
          if (tmpinfo=AllocVec(sizeof(struct UserInfo),MEMF_PUBLIC|MEMF_CLEAR))
          {
             Error=TRUE;

            if (tmpinfo->Handle=DupStr(tmpuser->Handle))
            {
              if (tmpinfo->Group=DupStr(tmpuser->Group))
              {

                // copy just the info we need..

                tmpinfo->UploadBytes   = tmpuser->UploadBytes;
                tmpinfo->DownloadBytes = tmpuser->DownloadBytes;
                tmpinfo->UploadFiles   = tmpuser->UploadFiles;
                tmpinfo->DownloadFiles = tmpuser->DownloadFiles;
                tmpinfo->Calls         = tmpuser->CallsMade;
                tmpinfo->Messages      = tmpuser->MessagesWritten;
                tmpinfo->CPSUp         = tmpuser->BestCPSUp;
                tmpinfo->CPSDown       = tmpuser->BestCPSDown;

                // increment totals

                TotalBytesUp   += (double)tmpuser->UploadBytes;
                TotalBytesDown += (double)tmpuser->DownloadBytes;
                TotalFilesUp   += (double)tmpuser->UploadFiles;
                TotalFilesDown += (double)tmpuser->DownloadFiles;
                TotalCalls     += (double)tmpuser->CallsMade;
                TotalMessages  += (double)tmpuser->MessagesWritten;

                if (tmpinfo->CPSUp > BestEverCPSUp) BestEverCPSUp = tmpinfo->CPSUp;
                if (tmpinfo->CPSDown > BestEverCPSDown) BestEverCPSDown = tmpinfo->CPSDown;

//              no where did that #define go for max()
//                BestEverCPSUp = max(tmpinfo->CPSUp,BestEverCPSUp);
//                BestEverCPSDown = max(tmpinfo->CPSDown,BestEverCPSDown);

                // add the node to the complete UserList

                if (tmpusernode=AllocVec(sizeof(struct UserNode),MEMF_PUBLIC|MEMF_CLEAR))
                {
                  tmpusernode->info=tmpinfo;

                  AddHead(UserList,(struct Node *)tmpusernode);

                  // add the node to the BestCPSUp inserting the node in the correct place.
                  if (tmpusernode=AllocVec(sizeof(struct UserNode),MEMF_PUBLIC|MEMF_CLEAR))
                  {
                    tmpusernode->info=tmpinfo;
                    OK=FALSE;
                    // we have to start at the end of the list because Insert() adds it after
                    // specified node and there is no function to add a node to a list before another
                    // node.
                    for (in = (struct UserNode*)BestCPSUp->lh_TailPred; in->node.ln_Pred && !OK ; in = (struct UserNode*)in->node.ln_Pred)
                    {
                      if (in->info->CPSUp > tmpinfo->CPSUp)
                      {
                        Insert(BestCPSUp,(struct Node*)tmpusernode,(struct Node*)in);
                        OK=TRUE;
                      }
                    }
                    if (!OK) // not added to the list, so list must be empty!
                    {
                      AddHead(BestCPSUp,(struct Node*)tmpusernode);
                    }

                    // add the node to the BestCPSDown inserting the node in the correct place.
                    if (tmpusernode=AllocVec(sizeof(struct UserNode),MEMF_PUBLIC|MEMF_CLEAR))
                    {
                      tmpusernode->info=tmpinfo;
                      OK=FALSE;
                      // we have to start at the end of the list because Insert() adds it after
                      // specified node and there is no function to add a node to a list before another
                      // node.
                      for (in = (struct UserNode*)BestCPSDown->lh_TailPred; in->node.ln_Pred && !OK ; in = (struct UserNode*)in->node.ln_Pred)
                      {
                        if (in->info->CPSDown > tmpinfo->CPSDown)
                        {
                          Insert(BestCPSDown,(struct Node*)tmpusernode,(struct Node*)in);
                          OK=TRUE;
                        }
                      }
                      if (!OK) // not added to the list, so list must be empty!
                      {
                        AddHead(BestCPSDown,(struct Node*)tmpusernode);
                      }

                      // add the node to the TopBUploaders inserting the node in the correct place.
                      if (tmpusernode=AllocVec(sizeof(struct UserNode),MEMF_PUBLIC|MEMF_CLEAR))
                      {
                        tmpusernode->info=tmpinfo;
                        OK=FALSE;
                        // we have to start at the end of the list because Insert() adds it after
                        // specified node and there is no function to add a node to a list before another
                        // node.
                        for (in = (struct UserNode*)TopBUploaders->lh_TailPred; in->node.ln_Pred && !OK ; in = (struct UserNode*)in->node.ln_Pred)
                        {
                          if (in->info->UploadBytes > tmpinfo->UploadBytes)
                          {
                            Insert(TopBUploaders,(struct Node*)tmpusernode,(struct Node*)in);
                            OK=TRUE;
                          }
                        }
                        if (!OK) // not added to the list, so list must be empty!
                        {
                          AddHead(TopBUploaders,(struct Node*)tmpusernode);
                        }


                        // add the node to the TopBDownloaders inserting the node in the correct place.

                        if (tmpusernode=AllocVec(sizeof(struct UserNode),MEMF_PUBLIC|MEMF_CLEAR))
                        {
                          tmpusernode->info=tmpinfo;

                          OK=FALSE;

                          for (in = (struct UserNode*)TopBDownloaders->lh_TailPred; in->node.ln_Pred && !OK ; in = (struct UserNode*)in->node.ln_Pred)
                          {
                            if (in->info->DownloadBytes > tmpinfo->DownloadBytes)
                            {
                              Insert(TopBDownloaders,(struct Node*)tmpusernode,(struct Node*)in);
                              OK=TRUE;
                            }
                          }

                          if (!OK) // not added to the list, so list must be empty!
                          {
                            AddHead(TopBDownloaders,(struct Node*)tmpusernode);
                          }

                          // add the node to the TopFUploaders inserting the node in the correct place.


                          if (tmpusernode=AllocVec(sizeof(struct UserNode),MEMF_PUBLIC|MEMF_CLEAR))
                          {
                            tmpusernode->info=tmpinfo;

                            OK=FALSE;

                            // we have to start at the end of the list because Insert() adds it after
                            // specified node and there is no function to add a node to a list before another
                            // node.

                            for (in = (struct UserNode*)TopFUploaders->lh_TailPred; in->node.ln_Pred && !OK ; in = (struct UserNode*)in->node.ln_Pred)
                            {
                              if (in->info->UploadFiles > tmpinfo->UploadFiles)
                              {
                                Insert(TopFUploaders,(struct Node*)tmpusernode,(struct Node*)in);
                                OK=TRUE;
                              }
                            }

                            if (!OK) // not added to the list, so list must be empty!
                            {
                              AddHead(TopFUploaders,(struct Node*)tmpusernode);
                            }

                            // add the node to the TopFDownloaders inserting the node in the correct place.

                            if (tmpusernode=AllocVec(sizeof(struct UserNode),MEMF_PUBLIC|MEMF_CLEAR))
                            {
                              tmpusernode->info=tmpinfo;

                              OK=FALSE;

                              for (in = (struct UserNode*)TopFDownloaders->lh_TailPred; in->node.ln_Pred && !OK ; in = (struct UserNode*)in->node.ln_Pred)
                              {
                                if (in->info->DownloadFiles > tmpinfo->DownloadFiles)
                                {
                                  Insert(TopFDownloaders,(struct Node*)tmpusernode,(struct Node*)in);
                                  OK=TRUE;
                                }
                              }

                              if (!OK) // not added to the list, so list must be empty!
                              {
                                AddHead(TopFDownloaders,(struct Node*)tmpusernode);
                              }

                              // *** add the node to the BestMsgWriters inserting the node in the correct place.

                              if (tmpusernode=AllocVec(sizeof(struct UserNode),MEMF_PUBLIC|MEMF_CLEAR))
                              {
                                tmpusernode->info=tmpinfo;

                                OK=FALSE;

                                for (in = (struct UserNode*)BestMsgWriters->lh_TailPred; in->node.ln_Pred && !OK ; in = (struct UserNode*)in->node.ln_Pred)
                                {
                                  if (in->info->Messages > tmpinfo->Messages)
                                  {
                                    Insert(BestMsgWriters,(struct Node*)tmpusernode,(struct Node*)in);
                                    OK=TRUE;
                                  }
                                }

                                if (!OK) // not added to the list, so list must be empty!
                                {
                                  AddHead(BestMsgWriters,(struct Node*)tmpusernode);
                                }

                                // *** add the node to the BestCallers inserting the node in the correct place.

                                if (tmpusernode=AllocVec(sizeof(struct UserNode),MEMF_PUBLIC|MEMF_CLEAR))
                                {
                                  tmpusernode->info=tmpinfo;

                                  OK=FALSE;

                                  for (in = (struct UserNode*)BestCallers->lh_TailPred; in->node.ln_Pred && !OK ; in = (struct UserNode*)in->node.ln_Pred)
                                  {
                                    if (in->info->Calls > tmpinfo->Calls)
                                    {
                                      Insert(BestCallers,(struct Node*)tmpusernode,(struct Node*)in);
                                      OK=TRUE;
                                      LoadedUsers++;
                                    }
                                  }

                                  if (!OK) // not added to the list, so list must be empty!
                                  {
                                    AddHead(BestCallers,(struct Node*)tmpusernode);
                                  }

                                  Error=FALSE;
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        } /* new/deleted check */
      }
      else
      {
        Done=TRUE;
      }
    } while (!Done && !Error);
  }
  return((BOOL)(Done && !Error));  // returns TRUE if completed without error
}

void PrintList( struct List *list)
{
  struct UserNode *in;

  for (in = (struct UserNode *) list->lh_Head; in->node.ln_Succ ; in = (struct UserNode*)in->node.ln_Succ)
  {
    printf("%-15.15s "
           "%-15.15s "
           "UB %9.0f "
           "DB %9.0f "
           "UF %3d "
           "DF %3d "
           "M %3.0f "
           "C %3.0f\n",
           in->info->Handle,
           in->info->Group,
           in->info->UploadBytes,
           in->info->DownloadBytes,
           in->info->UploadFiles,
           in->info->DownloadFiles,
           in->info->Messages,
           in->info->Calls);

  }

}


void FreeLists( void )
{
  struct UserNode *node;

  if (UserList)
  {
    while (UserList->lh_Head->ln_Succ)
    {
      node=(struct UserNode *)UserList->lh_Head;
      FreeStr(node->info->Handle);
      FreeStr(node->info->Group);
      Remove((struct Node*)node);
      FreeVec(node);
    }
  }

  FreeStrList(TopBUploaders);   // this still works because we used AllocVec() for the nodes..
  FreeStrList(TopBDownloaders);
  FreeStrList(TopFUploaders);
  FreeStrList(TopFDownloaders);
  FreeStrList(BestMsgWriters);
  FreeStrList(BestCallers);
  FreeStrList(BestCPSUp);
  FreeStrList(BestCPSDown);

}

short fposition(char *substr,char *str,ULONG startpos) // *C* include in hbbscommon
{
  // returns an character offset of a substring in a string
  // this version is CASE SENSITIVE
  // returns -1 if substring not found in the string

  char *whstr;
  return((short)((whstr=strstr(str+startpos,substr)) ? (short)((LONG)whstr-(LONG)str) : -1));
}


void CreateOutput( void )
{

  // the parsing routines of this bit of code are taken from hbbs:source/node/node_misc.c

  BPTR inFH, outFH;
  UBYTE *tmpstr,buffer[BIG_STR+1],mystr[BIG_STR],checkstr[20],formatstr[20],replacestr[BIG_STR],outstr[BIG_STR];
  BOOL Done;
  short startpos,endpos;

  struct UserNode *nodeptr[NUM_LISTS];
  int nodenum[NUM_LISTS] = {0,0,0,0,0,0,0,0}; // these increment as each list is accessed

  nodeptr[I_TopBUploader]=(struct UserNode *)TopBUploaders->lh_Head;
  nodeptr[I_TopBDownloader]=(struct UserNode *)TopBDownloaders->lh_Head;
  nodeptr[I_TopFUploader]=(struct UserNode *)TopFUploaders->lh_Head;
  nodeptr[I_TopFDownloader]=(struct UserNode *)TopFDownloaders->lh_Head;
  nodeptr[I_BestMsgWriters]=(struct UserNode *)BestMsgWriters->lh_Head;
  nodeptr[I_BestCallers]=(struct UserNode *)BestCallers->lh_Head;
  nodeptr[I_BestCPSUp]=(struct UserNode *)BestCPSUp->lh_Head;
  nodeptr[I_BestCPSDown]=(struct UserNode *)BestCPSDown->lh_Head;



  if (inFH=Open(infilename,MODE_OLDFILE))
  {
    if (outFH=Open(outfilename,MODE_NEWFILE))
    {
      while (FGets(inFH,buffer,BIG_STR))
      {
        // process the input file a line at a time
        // replacing @^@ sequences with replaced information from the lists we
        // have created

        Done=FALSE;

        tmpstr=mystr;
        strcpy(tmpstr,buffer);
        buffer[0]=0;

        while (!Done)
        {
          Done=TRUE;

          // is there a sequence introducer in the buffer ?

          if ((startpos=position("@^",tmpstr))>=0)
          {
            // yup, but is it followed by a sequence terminator ?
            if ((endpos=fposition("@",tmpstr,startpos+2))>=0)
            {
              Done=FALSE;
              // get the sequence from the buffer.. missing off the introducer
              // and the terminator, i.e. the bit between the @^ and the @

              strftcpy(checkstr,tmpstr,startpos+2,endpos-1);

              // copy the normal text to the output buffer.
              if (startpos) strncat(buffer,tmpstr,startpos);

              // modify our temporary buffer..
              tmpstr=tmpstr+endpos+1;

              if (endpos-1 > startpos+2) // skip @^@'s
              {
                formatstr[0] = 0;

                // check for formatting information
                // if we have @^-20^blah@ copy the "-20" part to formatstr
                // and the "blah" part to checkstr

                if ((startpos = position("^",checkstr))>=0)
                {
                  strNcpy(formatstr,checkstr,startpos);
                  strfcpy(checkstr,checkstr,startpos+1);
                }

                StripSpaces(checkstr);

                // at this point, formatstr contains any printf() formatting details
                // and checkstr contains the parameter that we check for.
                // tmpstr points to the character after the @^@ sequence
                // which is added the the output buffer after we add the results
                // of the replaced checkstr.


                // now we fill "replacestr" with replacements!

                replacestr[0]=0;

                // reference table for checkstr's

                // TBUH = top bytes uploader handle
                // TBUB = top bytes uploader bytes
                // TBUF = top bytes uploader files
                // TBUG = top bytes uploader group

                // TBDH = top bytes downloader handle
                // TBDB = top bytes downloader bytes
                // TBDF = top bytes downloader files
                // TBDG = top bytes downloader group

                // TFUH = top files uploader handle
                // TFUB = top files uploader bytes
                // TFUF = top files uploader files
                // TFUG = top files uploader group

                // TFDH = top files downloader handle
                // TFDB = top files downloader bytes
                // TFDF = top files downloader files
                // TFDG = top files downloader group

                // TMWH = top message writer handle
                // TMWB = top message writer bytes
                // TMWF = top message writer messages

                // TCH = top caller handle
                // TCB = top caller bytes
                // TCC = top caller calls

                // BCUH = Best CPS Up Handle
                // BCUG = Best CPS Up Group
                // BCUC = Best CPS Up CPS

                // BCDH = Best CPS Down Handle
                // BCDG = Best CPS Down Group
                // BCDC = Best CPS Down CPS

                // TBU  = Total Bytes Uploaded
                // TFU  = Total Files Uploaded
                // TBD  = Total Bytes Downloaded
                // TFD  = Total Files Downloaded
                // TC   = Total Calls
                // TM   = Total Messages
                // BCU  = Best Ever CPS Up
                // BCD  = Best Ever CPS Down

                if (stricmp("TBUH",checkstr)==0)
                {
                  // ************ TOP BYTE UPLOADERS *****************************

                  // each time we are asked for a handle we increment nodenum
                  // so that next time we are asked for a handle we use the
                  // next user in out list.

                  if (nodenum[I_TopBUploader] < LoadedUsers)
                  {
                    nodeptr[I_TopBUploader]=(struct UserNode *)GetNode(TopBUploaders,nodenum[I_TopBUploader]);

                    // we take our information from the nodeptr[] that we have just set

                    strcpy(replacestr,nodeptr[I_TopBUploader]->info->Handle);
                  }

                  nodenum[I_TopBUploader]++;
                }
                else
                if (stricmp("TBUG",checkstr)==0)
                {
                  if (nodenum[I_TopBUploader] <= LoadedUsers) strcpy(replacestr,nodeptr[I_TopBUploader]->info->Group);
                }
                else
                if (stricmp("TBUB",checkstr)==0)
                {
                  if (nodenum[I_TopBUploader] <= LoadedUsers) sprintf(replacestr,"%-10.0f",nodeptr[I_TopBUploader]->info->UploadBytes);
                }
                else
                if (stricmp("TBUF",checkstr)==0)
                {
                  if (nodenum[I_TopBUploader] <= LoadedUsers) sprintf(replacestr,"%-10.0ld",nodeptr[I_TopBUploader]->info->UploadFiles);
                }
                else
                if (stricmp("TBDH",checkstr)==0)
                {
                  // ************ TOP BYTE DOWNLOADERS ***************************

                  // each time we are asked for a handle we increment nodenum
                  // so that next time we are asked for a handle we use the
                  // next user in out list.

                  if (nodenum[I_TopBDownloader] < LoadedUsers)
                  {
                    nodeptr[I_TopBDownloader]=(struct UserNode *)GetNode(TopBDownloaders,nodenum[I_TopBDownloader]);

                    // we take our information from the nodeptr[] that we have just set

                    strcpy(replacestr,nodeptr[I_TopBDownloader]->info->Handle);
                  }

                  nodenum[I_TopBDownloader]++;
                }
                else
                if (stricmp("TBDG",checkstr)==0)
                {
                  if (nodenum[I_TopBDownloader] <= LoadedUsers) strcpy(replacestr,nodeptr[I_TopBDownloader]->info->Group);
                }
                else
                if (stricmp("TBDB",checkstr)==0)
                {
                  if (nodenum[I_TopBDownloader] <= LoadedUsers) sprintf(replacestr,"%-10.0f",nodeptr[I_TopBDownloader]->info->DownloadBytes);
                }
                else
                if (stricmp("TBDF",checkstr)==0)
                {
                  if (nodenum[I_TopBDownloader] <= LoadedUsers) sprintf(replacestr,"%-10.0ld",nodeptr[I_TopBDownloader]->info->DownloadFiles);
                }
                else

                if (stricmp("TFUH",checkstr)==0)
                {
                  // ************ TOP FILE UPLOADERS *****************************

                  // each time we are asked for a handle we increment nodenum
                  // so that next time we are asked for a handle we use the
                  // next user in out list.

                  if (nodenum[I_TopFUploader] < LoadedUsers)
                  {
                    nodeptr[I_TopFUploader]=(struct UserNode *)GetNode(TopFUploaders,nodenum[I_TopFUploader]);

                    // we take our information from the nodeptr[] that we have just set

                    strcpy(replacestr,nodeptr[I_TopFUploader]->info->Handle);
                  }

                  nodenum[I_TopFUploader]++;
                }
                else
                if (stricmp("TFUG",checkstr)==0)
                {
                  if (nodenum[I_TopFUploader] <= LoadedUsers) strcpy(replacestr,nodeptr[I_TopFUploader]->info->Group);
                }
                else
                if (stricmp("TFUB",checkstr)==0)
                {
                  if (nodenum[I_TopFUploader] <= LoadedUsers) sprintf(replacestr,"%-10.0f",nodeptr[I_TopFUploader]->info->UploadBytes);
                }
                else
                if (stricmp("TFUF",checkstr)==0)
                {
                  if (nodenum[I_TopFUploader] <= LoadedUsers) sprintf(replacestr,"%-10.0ld",nodeptr[I_TopFUploader]->info->UploadFiles);
                }
                else

                if (stricmp("TFDH",checkstr)==0)
                {
                  // ************ TOP FILE DOWNLOADERS ***************************

                  // each time we are asked for a handle we increment nodenum
                  // so that next time we are asked for a handle we use the
                  // next user in out list.

                  if (nodenum[I_TopFDownloader] < LoadedUsers)
                  {
                    nodeptr[I_TopFDownloader]=(struct UserNode *)GetNode(TopFDownloaders,nodenum[I_TopFDownloader]);

                    // we take our information from the nodeptr[] that we have just set

                    strcpy(replacestr,nodeptr[I_TopFDownloader]->info->Handle);
                  }

                  nodenum[I_TopFDownloader]++;
                }
                else
                if (stricmp("TFDG",checkstr)==0)
                {
                  if (nodenum[I_TopFDownloader] <= LoadedUsers) strcpy(replacestr,nodeptr[I_TopFDownloader]->info->Group);
                }
                else
                if (stricmp("TFDB",checkstr)==0)
                {
                  if (nodenum[I_TopFDownloader] <= LoadedUsers) sprintf(replacestr,"%-10.0f",nodeptr[I_TopFDownloader]->info->DownloadBytes);
                }
                else
                if (stricmp("TFDF",checkstr)==0)
                {
                  if (nodenum[I_TopFDownloader] <= LoadedUsers) sprintf(replacestr,"%-10.0ld",nodeptr[I_TopFDownloader]->info->DownloadFiles);
                }
                else

                if (stricmp("TMWH",checkstr)==0)
                {
                  // ************ TOP MESSAGE WRITER ***************************

                  // each time we are asked for a handle we increment nodenum
                  // so that next time we are asked for a handle we use the
                  // next user in out list.

                  if (nodenum[I_BestMsgWriters] < LoadedUsers)
                  {
                    nodeptr[I_BestMsgWriters]=(struct UserNode *)GetNode(BestMsgWriters,nodenum[I_BestMsgWriters]);

                    // we take our information from the nodeptr[] that we have just set

                    strcpy(replacestr,nodeptr[I_BestMsgWriters]->info->Handle);
                  }

                  nodenum[I_BestMsgWriters]++;
                }
                else
                if (stricmp("TMWG",checkstr)==0)
                {
                  if (nodenum[I_BestMsgWriters] <= LoadedUsers) strcpy(replacestr,nodeptr[I_BestMsgWriters]->info->Group);
                }
                else
                if (stricmp("TMWM",checkstr)==0)
                {
                  if (nodenum[I_BestMsgWriters] <= LoadedUsers) sprintf(replacestr,"%-10.0f",nodeptr[I_BestMsgWriters]->info->Messages);
                }
                else

                if (stricmp("TCH",checkstr)==0)
                {
                  // ************ TOP CALLERS ***************************

                  // each time we are asked for a handle we increment nodenum
                  // so that next time we are asked for a handle we use the
                  // next user in out list.

                  if (nodenum[I_BestCallers] < LoadedUsers)
                  {
                    nodeptr[I_BestCallers]=(struct UserNode *)GetNode(BestCallers,nodenum[I_BestCallers]);

                    // we take our information from the nodeptr[] that we have just set

                    strcpy(replacestr,nodeptr[I_BestCallers]->info->Handle);
                  }

                  nodenum[I_BestCallers]++;
                }
                else
                if (stricmp("TCG",checkstr)==0)
                {
                  if (nodenum[I_BestCallers] <= LoadedUsers) strcpy(replacestr,nodeptr[I_BestCallers]->info->Group);
                }
                else
                if (stricmp("TCC",checkstr)==0)
                {
                  if (nodenum[I_BestCallers] <= LoadedUsers) sprintf(replacestr,"%-10.0f",nodeptr[I_BestCallers]->info->Calls);
                }
                else

                if (stricmp("BCUH",checkstr)==0)
                {
                  // ************ BEST CPS UP ***************************

                  // each time we are asked for a handle we increment nodenum
                  // so that next time we are asked for a handle we use the
                  // next user in out list.

                  if (nodenum[I_BestCPSUp] < LoadedUsers)
                  {
                    nodeptr[I_BestCPSUp]=(struct UserNode *)GetNode(BestCPSUp,nodenum[I_BestCPSUp]);

                    // we take our information from the nodeptr[] that we have just set

                    strcpy(replacestr,nodeptr[I_BestCPSUp]->info->Handle);
                  }

                  nodenum[I_BestCPSUp]++;
                }
                else
                if (stricmp("BCUG",checkstr)==0)
                {
                  if (nodenum[I_BestCPSUp] <= LoadedUsers) strcpy(replacestr,nodeptr[I_BestCPSUp]->info->Group);
                }
                else
                if (stricmp("BCUC",checkstr)==0)
                {
                  if (nodenum[I_BestCPSUp] <= LoadedUsers) sprintf(replacestr,"%-10.0f",nodeptr[I_BestCPSUp]->info->CPSUp);
                }
                else

                if (stricmp("BCUH",checkstr)==0)
                {
                  // ************ BEST CPS UP ***************************

                  // each time we are asked for a handle we increment nodenum
                  // so that next time we are asked for a handle we use the
                  // next user in out list.

                  if (nodenum[I_BestCPSDown] < LoadedUsers)
                  {
                    nodeptr[I_BestCPSDown]=(struct UserNode *)GetNode(BestCPSDown,nodenum[I_BestCPSDown]);

                    // we take our information from the nodeptr[] that we have just set

                    strcpy(replacestr,nodeptr[I_BestCPSDown]->info->Handle);
                  }

                  nodenum[I_BestCPSDown]++;
                }
                else
                if (stricmp("BCUG",checkstr)==0)
                {
                  if (nodenum[I_BestCPSDown] <= LoadedUsers) strcpy(replacestr,nodeptr[I_BestCPSDown]->info->Group);
                }
                else
                if (stricmp("BCUC",checkstr)==0)
                {
                  if (nodenum[I_BestCPSDown] <= LoadedUsers) sprintf(replacestr,"%-10.0f",nodeptr[I_BestCPSDown]->info->CPSDown);
                }
                else

                if (stricmp("TBU",checkstr)==0)
                {
                  // ************ TOTALS ******************************************
                  sprintf(replacestr,"%-10.0f",TotalBytesUp);
                }
                else
                if (stricmp("TBD",checkstr)==0)
                {
                  sprintf(replacestr,"%-10.0f",TotalBytesDown);
                }
                else
                if (stricmp("TFU",checkstr)==0)
                {
                  sprintf(replacestr,"%-10.0f",TotalFilesUp);
                }
                else
                if (stricmp("TFD",checkstr)==0)
                {
                  sprintf(replacestr,"%-10.0f",TotalFilesDown);
                }
                else
                if (stricmp("TC",checkstr)==0)
                {
                  sprintf(replacestr,"%-10.0f",TotalCalls);
                }
                else
                if (stricmp("TM",checkstr)==0)
                {
                  sprintf(replacestr,"%-10.0f",TotalMessages);
                }
                else
                if (stricmp("BCU",checkstr)==0)
                {
                  sprintf(replacestr,"%-10.0f",BestEverCPSUp);
                }
                else
                if (stricmp("BCD",checkstr)==0)
                {
                  sprintf(replacestr,"%-10.0f",BestEverCPSDown);
                }

                // now using the print format spec we add the info to out output buffer

                StripSpaces(replacestr);

                sprintf(outstr,"%%%ss",formatstr);
                sprintf(buffer+strlen(buffer),outstr,replacestr);

              }
            }
          }

        }
        strcat(buffer,tmpstr);
        Write(outFH,buffer,strlen(buffer));
//        Write(outFH,"\n",1);
      }
      Close(outFH);
    }
    else puts("Cant open output file");

    Close(inFH);
  }
  else puts("Cant open input file");
}


void UtilMain( void )
{
  short loop;

  if( gargc < 2 )
  {
    Usage();
  }
  else
  {
    if (gargv[1][0] == '?')
    {
      Usage();
    }
    else
    {
      if (gargc % 2 == 0) // if odd amount of paramaters
      {
        Usage();
      }
      else
      {
        printf("Creating Lists in memory..");
        if (CreateLists())
        {
          puts("Done!");

          for (loop=1;loop+1 < gargc ;loop+=2)
          {
            infilename=gargv[loop];
            outfilename=gargv[loop+1];

            printf("Processing %s -> %s\n",infilename, outfilename);
            CreateOutput();
          }

        }
        else puts("failed");

        FreeLists();
      }
    }
  }
}

int main(int argc,char **argv)
{
  gargc=argc;
  gargv=argv;

  init();

  UtilMain();

  cleanup(0);
}

/* **** End Of File ******************************************************** */
