/*
** $VER: sana2meter.c 1.98 (04 Jun 1996)
**
** (C) Copyright 1996 Marius Gröger
**     All Rights Reserved
**
** $HISTORY:
**
** 04 Jun 1996 : 001.009 :  memory for SpecialStatRecords was allocated
**                          badly causing memory trashing
** 02 Jun 1996 : 001.008 :  support for statically linked gtlayout
** 05 May 1996 : 001.007 :  Screen jumping, Stay-front
** 04 May 1996 : 001.006 :  CPS display
** 25 Apr 1996 : 001.005 :  some parts moved to support.c, sana2meter.h
** 20 Apr 1996 : 001.004 :  support for multiple devices
** 15 Apr 1996 : 001.003 :  support for broken gadtools V39
** 14 Apr 1996 : 001.002 :  failing DoIO()'s will turn off further displaying
**                          of the corresponding values
** 10 Apr 1996 : 001.001 :  packet type tracking
** 26 Mar 1996 : 001.000 :  created
*/

#define DEBUG 0
#define USE_EXTREADARGS 1
#define CATALOG_VERSION 5

/*F*/ /* includes */

#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
#include <pragmas/dos_pragmas.h>
#include <clib/utility_protos.h>
#include <pragmas/utility_pragmas.h>
#include <clib/exec_protos.h>
#include <pragmas/exec_sysbase_pragmas.h>
#include <clib/intuition_protos.h>
#include <pragmas/intuition_pragmas.h>
#include <clib/gadtools_protos.h>
#include <pragmas/gadtools_pragmas.h>
#include <clib/icon_protos.h>
#include <pragmas/icon_pragmas.h>
#include <clib/locale_protos.h>
#include <pragmas/locale_pragmas.h>
#include <gtlayout/gtlayout.h>
#include <exec/libraries.h>
#include <exec/devices.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <workbench/startup.h>
#include <utility/tagitem.h>
#include <netinclude:devices/sana2.h>
#define AsmPools
#include <pools/pool_lib.h>

#if (USE_EXTREADARGS != 0)
#include <extrdargs.h>
#endif

#include "compiler.h"
#include "debug.h"
#define CATCOMP_NUMBERS
#include "locale.h"
#include "sana2meter.h"
#include "constdata.h"

#include <strings.h>
#include <stddef.h>
/*E*/

/*F*/ /* private symbols */
PRIVATE BOOL parseargs(GD gd);
PRIVATE VOID cleanup(GD gd);
PRIVATE BOOL opendevs(GD gd);
PRIVATE GD createenv(ULONG argc, UBYTE **argv);
PRIVATE VOID getspeciallabels(GD gd);
PRIVATE struct GlobalDevData *makedevice(GD gd, STRPTR devicename);
PRIVATE VOID dummys2copy(VOID);
/*E*/
/*F*/ /* exported symbols */
PUBLIC VOID SAVEDS STDARGS main(ULONG argc, UBYTE **argv);
PUBLIC VOID saveconfig(GD gd);
PUBLIC VOID updateupdate(GD gd, struct GlobalDevData *gdd);
PUBLIC UWORD updateglobalstats(GD gd, struct GlobalDevData *gdd);
PUBLIC UWORD updatespecialstats(GD gd, struct GlobalDevData *gdd);
PUBLIC UWORD updatetracktype(GD gd, struct GlobalDevData *gdd);
PUBLIC VOID updateactives(GD gd);
/*E*/
/*F*/ /* imported symbols */
IMPORT struct Library *openlib(GD gd, STRPTR name, LONG version);
IMPORT ASM STRPTR GetString(REG(a0) struct LocaleInfo *li, REG(d0) LONG stringNum);
IMPORT STDARGS VOID exit(LONG);
IMPORT STDARGS VOID sprintf(char *, char *, ...);
IMPORT VOID message(GD gd, LONG, LONG, LONG, ...);
IMPORT VOID *allocpvec(GD gd, ULONG len);
IMPORT VOID freepvec(GD gd, VOID *mem);
IMPORT VOID *allocppvec(GD gd, ULONG len);
IMPORT VOID freeppvec(GD gd, VOID *mem);
IMPORT BOOL preparemenu(GD gd);
IMPORT BOOL dowindow(GD gd, DoWindowCtrl *ctrl);
IMPORT VOID checkmenu(GD gd);
IMPORT VOID updategui(GD gd, BOOL complete);
IMPORT VOID closegui(GD gd);
IMPORT BOOL opengui(GD gd);
IMPORT ULONG strntolong(GD gd, STRPTR s, LONG *number, LONG n);
IMPORT STRPTR AddToolType(GD gd, struct DiskObject *dobj, char *tool);
IMPORT STRPTR *DupToolTypes(GD gd, struct DiskObject *dobj);
IMPORT void UnDupToolTypes(GD gd, struct DiskObject *dobj, STRPTR *oldtt);
/*E*/

/*F*/ /* constants for parsing */
PRIVATE const UBYTE TEMPLATE[] = "DEV=DEVICE,"            \
                                 "U=UNIT/K/N,"            \
                                 "S=SHOW/K,"              \
                                 "TT=TRACKTYPE/K,"        \
                                 "FORCE/S,"               \
                                 "OFF/S,"                 \
                                 "FROM/K,"                \
                                 "RF=REFRESH/K/N,"        \
                                 "CPSA=CPSAGING/K/N,"     \
                                 "L=LIST/S,"              \
                                 "FONT=FONTNAME/K,"       \
                                 "FONTSIZE/K/N,"          \
                                 "TOP/K/N,"               \
                                 "LEFT/K/N,"              \
                                 "SCREEN=PUBSCREEN/K,"    \
                                 "NA=NOACTIVATE/S,"       \
                                 "W2B=WINDOWTOBACK/S,"    \
                                 "SF=STAYFRONT/S,"        \
                                 "WINDOW/K";

enum
{
   ARG_DEVICE,
   ARG_UNIT,
   ARG_SHOW,
   ARG_TRACKTYPE,
   ARG_FORCE,
   ARG_OFF,
   ARG_FROM,
   ARG_REFRESH,
   ARG_CPSAGING,
   ARG_LIST,
   ARG_FONT,
   ARG_FONTSIZE,
   ARG_TOP,
   ARG_LEFT,
   ARG_PUBSCREEN,
   ARG_NOACTIVATE,
   ARG_WINDOWTOBACK,
   ARG_STAYFRONT,
   ARG_WINDOW,
   ARG_COUNT
};


#define DEF_UNIT 0
#define DEF_CPSAGING 20
#define DEF_REFRESH 2
#define DEF_FONTSIZE 8
#define DEF_LEFT 30
#define DEF_TOP 20

PRIVATE UBYTE KEYDELIMITERS[]   = ",;| ";
PRIVATE UBYTE TTMASKSEPARATOR[] = ":/";
PRIVATE UBYTE TTMASKACTIVE[]    = "+xX#";
PRIVATE UBYTE TTMASKINACTIVE[]  = "-oO.";

/*E*/

/*F*/ PRIVATE struct GlobalDevData *makedevice(GD gd, STRPTR devicename)
{
   const struct NewWatch * nw;
   struct Watch *w;
   struct Sana2SpecialStatRecord *sr;
   struct GlobalDevData *gdd;
   struct PublicDevData *pdd;
   BOOL rc = FALSE;

   if (gdd = allocpvec(gd, sizeof(struct GlobalDevData) + strlen(devicename)))
   {
      strcpy((char*)gdd->gd_Device, devicename);
      gdd->gd_Unit = DEF_UNIT;
      gdd->gd_Active = 1;
      NewList((struct List*)&gdd->gd_Watches);

      if (pdd = gdd->gd_Public = allocppvec(gd, sizeof(struct PublicDevData) +
                   (NUM_SPECIALSTATS-1)*sizeof(struct Sana2SpecialStatRecord)))
      {
         sr = pdd->pd_S2SpecialStatRecs;

         rc = TRUE;

         if (gdd->gd_StdWatches = allocpvec(gd, (NUM_SPECIALSTATS+NUM_STDWATCHES)*sizeof(struct Watch)))
         {
            /* create all the watches from the NewWatch array */
            for (w = gdd->gd_StdWatches, nw = stdwatches; nw->nw_Member != MEMBER_NONE; ++nw, ++w)
            {
               w->w_Member = nw->nw_Member;
               w->w_Key = nw->nw_Key;
               w->w_ShortKey = nw->nw_ShortKey;
               if (!(nw->nw_Flags & WF_NOLABEL))
                  w->w_Label = GetString(&gd->gd_LocaleInfo, nw->nw_LabelID);
               w->w_Flags = nw->nw_Flags;

               if (nw->nw_Member == MEMBER_GLOBALSTATS)
                  w->w_Value = (ULONG*)((UBYTE*)&pdd->pd_S2DeviceStats + nw->nw_StructOffset);
               else if (nw->nw_Member == MEMBER_DEVICEQUERY)
                  w->w_Value = (ULONG*)((UBYTE*)&pdd->pd_S2DeviceQuery + nw->nw_StructOffset);
               else if (nw->nw_Member == MEMBER_SPECIALSTATS)
               {
                  w->w_Value = &sr->Count;
                  ++sr;
               }
               else
                  w->w_Value = NULL;

               AddTail((struct List*)&gdd->gd_Watches, (struct Node*)&w->w_Link);
            }
         } else rc = FALSE;
      }

      if (!rc)
      {
         if (gdd->gd_StdWatches) freepvec(gd, gdd->gd_StdWatches);
         if (gdd->gd_Public) freepvec(gd, gdd->gd_Public);
         freepvec(gd, gdd);
         gdd = NULL;
      }
   }

   return gdd;
}
/*E*/
/*F*/ PRIVATE BOOL preparetts(GD gd)
{
   struct NewWatch *nw;
   struct TrackType *tt;
   struct Watch *w;
   BOOL rc = TRUE, first;
   ULONG numtt, i;
   struct GlobalDevData *gdd;
   struct PublicDevData *pdd;

   SCANDEVS(gdd, &gd->gd_Devices)
   {
      pdd = gdd->gd_Public;
      if (gdd->gd_TrackWatches = allocpvec(gd, NUM_TTWATCHES*gdd->gd_NumTT*sizeof(struct Watch)))
      {
         w = gdd->gd_TrackWatches;
         for (tt=pdd->pd_TrackedTypes, numtt = gdd->gd_NumTT; numtt; ++tt,  --numtt)
         {
            for (i=0, first=TRUE, nw=(struct NewWatch *)ttwatches; nw->nw_Member != MEMBER_NONE;
                                                                        ++i, ++nw, first=FALSE)
            {
               w->w_Member = nw->nw_Member;
               w->w_TrackType = tt;
               w->w_Key = first ? tt->tt_Key : NULL;
               w->w_Label = GetString(&gd->gd_LocaleInfo, nw->nw_LabelID);
               w->w_Flags = nw->nw_Flags | (first ? WF_FIRSTTT : 0);
               if (tt->tt_Flags & TTF_INITIAL_SETUP)
               {
                  if (tt->tt_Flags & ttmaskflags[i])
                     w->w_Flags |= WF_ACTIVE;
                  else
                     w->w_Flags &= ~WF_ACTIVE;
               }
               tt->tt_Flags |= TTF_DROPFIRSTCPS;
               w->w_Value = (ULONG*)((UBYTE*)tt + nw->nw_StructOffset);
               AddTail((struct List*)&gdd->gd_Watches, (struct Node*)&w->w_Link);

               ++w;
            }
         }
      }
   }
   return rc;
}
/*E*/
/*F*/ PRIVATE VOID getspeciallabels(GD gd)
{
   struct Watch *w;
   BOOL specials;
   struct GlobalDevData *gdd;
   struct PublicDevData *pdd;

   SCANDEVS(gdd, &gd->gd_Devices)
   {
      pdd = gdd->gd_Public;
      specials = FALSE;

      gdd->gd_ValidSpecialStats = 0;

      SCANWATCHES(w, &gdd->gd_Watches)
      {
         if (!specials)
         {
            if (w->w_Member == MEMBER_SPECIALSTATS)
               specials = TRUE;
         }
         if (specials)
         {
            if ((gdd->gd_ValidSpecialStats >= NUM_SPECIALSTATS) ||
                (gdd->gd_ValidSpecialStats >= pdd->pd_S2SpecialStats.RecordCountSupplied))
               break;
            w->w_Label = pdd->pd_S2SpecialStatRecs[gdd->gd_ValidSpecialStats].String;
            ++gdd->gd_ValidSpecialStats;
         }
      }
   }

   return ;
}
/*E*/

/*F*/ PUBLIC VOID updateactives(GD gd)
{
   struct GlobalDevData *gdd;

   gd->gd_NeedTimer = 0;
   gd->gd_NumActives = 0;
   SCANDEVS(gdd, &gd->gd_Devices)
   {
      d(("device <%s>\n", gdd->gd_Device));

      if (!gdd->gd_GetGlobalStats && !gdd->gd_GetSpecialStats &&
           !gdd->gd_GetTrackType && !gdd->gd_ShowDevQuery)
         gdd->gd_Active = 0;
      else
      {
         if (gdd->gd_Active)
         {
            if (gdd->gd_GetGlobalStats || gdd->gd_GetSpecialStats || gdd->gd_GetTrackType)
               gd->gd_NeedTimer = 1;

            ++gd->gd_NumActives;
         }
      }
      d(("GetGlobalStats %ld, GetSpecialStats %ld, GetTrackType %ld, Active %ld\n",
         gdd->gd_GetGlobalStats, gdd->gd_GetSpecialStats, gdd->gd_GetTrackType, gdd->gd_Active));

   }
   d(("NeedTimer %ld, NumActives %ld\n", gd->gd_NeedTimer, gd->gd_NumActives));
}
/*E*/
/*F*/ PUBLIC VOID updateupdate(GD gd, struct GlobalDevData *gdd)
{
   struct Watch *w;
   struct TrackType *tt;
   ULONG i;
   struct PublicDevData *pdd;

   pdd = gdd->gd_Public;

   gdd->gd_GetGlobalStats = 0;
   gdd->gd_GetSpecialStats = 0;
   gdd->gd_GetTrackType = 0;
   gdd->gd_ShowDevQuery = 0;

   for(i=gdd->gd_NumTT, tt=pdd->pd_TrackedTypes; i; --i, ++tt)
      tt->tt_Flags &= ~TTF_ACTIVE;

   SCANWATCHES(w, &gdd->gd_Watches)
   {
      if ((w->w_Flags & WF_ACTIVE) && (w->w_Member == MEMBER_DEVICEQUERY))
         gdd->gd_ShowDevQuery = 1;

      if ((w->w_Flags & WF_ACTIVE) && (w->w_Member == MEMBER_GLOBALSTATS))
         gdd->gd_GetGlobalStats = 1;

      if ((w->w_Flags & WF_ACTIVE) && (w->w_Member == MEMBER_SPECIALSTATS))
         gdd->gd_GetSpecialStats = 1;

      if (w->w_Member == MEMBER_TRACKTYPE)
      {
         if (w->w_Flags & WF_ACTIVE)
         {
            w->w_TrackType->tt_Flags |= TTF_ACTIVE;
            gdd->gd_GetTrackType = 1;
         }
      }
   }
   return ;
}
/*E*/
/*F*/ PUBLIC UWORD updateglobalstats(GD gd, struct GlobalDevData *gdd)
{
   struct PublicDevData *pdd;
   UWORD rc = 0;

   pdd = gdd->gd_Public;
   pdd->pd_S2Req.ios2_StatData = (void*)&pdd->pd_S2DeviceStats;
   pdd->pd_S2Req.ios2_Req.io_Command = S2_GETGLOBALSTATS;
   if (DoIO((struct IORequest*)&pdd->pd_S2Req))
   {
      struct Watch *w;

      SCANWATCHES(w, &gdd->gd_Watches)
      {
         if (w->w_Member == MEMBER_GLOBALSTATS)
            w->w_Flags = (w->w_Flags & ~WF_ACTIVE) | WF_OFF;
      }
      rc = 1;
   }
   return rc;
}
/*E*/
/*F*/ PUBLIC UWORD updatespecialstats(GD gd, struct GlobalDevData *gdd)
{
   struct PublicDevData *pdd;
   UWORD rc = 0;

   pdd = gdd->gd_Public;
   pdd->pd_S2SpecialStats.RecordCountMax = NUM_SPECIALSTATS;
   pdd->pd_S2Req.ios2_StatData = (void*)&pdd->pd_S2SpecialStats;
   pdd->pd_S2Req.ios2_Req.io_Command = S2_GETSPECIALSTATS;
   if (DoIO((struct IORequest*)&pdd->pd_S2Req))
   {
      struct Watch *w;

      SCANWATCHES(w, &gdd->gd_Watches)
      {
         if (w->w_Member == MEMBER_SPECIALSTATS)
            w->w_Flags = (w->w_Flags & ~WF_ACTIVE) | WF_OFF;
      }
      rc = 1;
   }
   return rc;
}
/*E*/
/*F*/ PUBLIC UWORD updatetracktype(GD gd, struct GlobalDevData *gdd)
{
   struct PublicDevData *pdd;
   struct TrackType *tt;
   ULONG i;
   UWORD rc = 0;
   ULONG old_ibytes, old_obytes;

   pdd = gdd->gd_Public;
   for(i=gdd->gd_NumTT, tt=pdd->pd_TrackedTypes; i; --i, ++tt)
   {
      if (tt->tt_Flags & TTF_ACTIVE)
      {
         old_ibytes = tt->tt_Stats.BytesReceived;
         old_obytes = tt->tt_Stats.BytesSent;
         pdd->pd_S2Req.ios2_StatData = (void*)&tt->tt_Stats;
         pdd->pd_S2Req.ios2_Req.io_Command = S2_GETTYPESTATS;
         if (DoIO((struct IORequest*)&pdd->pd_S2Req))
         {
            struct Watch *w;

            SCANWATCHES(w, &gdd->gd_Watches)
            {
               if (w->w_TrackType == tt)
                  w->w_Flags = (w->w_Flags & ~WF_ACTIVE) | WF_OFF;
            }
            rc = 1;
         }
         else
         {
            if (tt->tt_Flags & TTF_DROPFIRSTCPS)
               tt->tt_Flags &= ~TTF_DROPFIRSTCPS;
            else
            {
               tt->tt_OCPS = (gd->gd_CPSAging * tt->tt_OCPS +
                              gd->gd_CPSAgingComp *
                                    ((tt->tt_Stats.BytesSent - old_obytes) / gd->gd_Refresh)
                              ) / 100;

               tt->tt_ICPS = (gd->gd_CPSAging * tt->tt_ICPS +
                              gd->gd_CPSAgingComp *
                                    ((tt->tt_Stats.BytesReceived - old_ibytes) / gd->gd_Refresh)
                              ) / 100;
            }
         }
      }
   }
   return rc;
}
/*E*/

/*F*/ PRIVATE LONG nextkey(STRPTR arg, STRPTR *next)
{
   STRPTR nx;
   LONG len;

   if ((nx = strpbrk(arg, KEYDELIMITERS)) != NULL)
   {
      len = nx - arg;
      ++nx;
   }
   else
   {
      len = strlen(arg);
      nx = arg + len;
   }

   *next = nx;
   return len;
}
/*E*/
/*F*/ PRIVATE BOOL nowparse(GD gd, LONG param[ARG_COUNT], BOOL *more, BOOL fromconfig)
{
   PD pd = gd->gd_Public;
   struct GlobalDevData *gdd;
   BOOL rc = TRUE;
   LONG len;

   *more = FALSE;

   if (param[ARG_WINDOW])
      strncpy((char*)pd->pd_Console, (char*)param[ARG_PUBSCREEN], sizeof(pd->pd_Console));

   if (param[ARG_LIST])
   {
      gd->gd_List = 1;
   }
   else
   {
      if (param[ARG_FROM])
      {
         if (gd->gd_ConfigName) freepvec(gd, gd->gd_ConfigName);
         if (gd->gd_ConfigName = allocpvec(gd, strlen((char*)param[ARG_FROM])))
         {
            strcpy(gd->gd_ConfigName, (char*)param[ARG_FROM]);
            *more = TRUE;
         }
         else rc = FALSE;
      }

      /* DEVICE/A */
      if (param[ARG_DEVICE])
      {
         if (gdd = makedevice(gd, (STRPTR)param[ARG_DEVICE]))
         {
            gdd->gd_FromConfig = fromconfig ? 1 : 0;

            /* UNIT/K/N */
            if (param[ARG_UNIT])
               gdd->gd_Unit = *(LONG*)param[ARG_UNIT];

            /* OFF/S */
            gdd->gd_Active = (param[ARG_OFF] == 0);

            /* FORCE/S */
            gdd->gd_Force = (param[ARG_FORCE] != 0);

            /* SHOW/K */
            if (param[ARG_SHOW])
            {
               STRPTR arg, next;
               struct Watch *w;

               SCANWATCHES(w, &gdd->gd_Watches)
               {
                  w->w_Flags &= ~WF_ACTIVE;
               }
               arg = (STRPTR)param[ARG_SHOW];
               while(len = nextkey(arg, &next))
               {
                  SCANWATCHES(w, &gdd->gd_Watches)
                  {
                     if (!Strnicmp(w->w_Key, arg, len) || !Strnicmp(w->w_ShortKey, arg, len))
                     {
                        w->w_Flags |= WF_ACTIVE;
                        break;
                     }
                  }
                  arg = next;
               }
            }

            /* TRACKTYPE/K */
            if (param[ARG_TRACKTYPE])
            {
               STRPTR arg, next;
               WORD clen, i;
               struct PublicDevData *pdd = gdd->gd_Public;

               arg = (STRPTR)param[ARG_TRACKTYPE];
               while(len = nextkey(arg, &next))
               {
                  ++gdd->gd_NumTT;
                  arg = next;
               }

               if (pdd->pd_TrackedTypes = allocpvec(gd, gdd->gd_NumTT * sizeof(struct TrackType)))
               {
                  struct TrackType *tt = pdd->pd_TrackedTypes;
                  STRPTR fmt;

                  arg = (STRPTR)param[ARG_TRACKTYPE];
                  while(len = nextkey(arg, &next))
                  {
                     if (clen = strntolong(gd, arg, (LONG*)&tt->tt_Type, len))
                     {
                        tt->tt_Flags = 0;
                        fmt = GetString(&gd->gd_LocaleInfo, TXT_TTLABEL);
                        sprintf(tt->tt_Label, fmt, tt->tt_Type);
                        sprintf(tt->tt_Key, "$%lx", tt->tt_Type);
                        if (clen != len)
                        {
                           if (strchr(TTMASKSEPARATOR, arg[clen]) != NULL)
                           {
                              tt->tt_Flags |= TTF_INITIAL_SETUP;
                              for(++clen, i=0; clen < len; ++clen, ++i)
                              {
                                 if (strchr(TTMASKACTIVE, arg[clen]) != NULL)
                                    tt->tt_Flags |= ttmaskflags[i];
                                 else if (strchr(TTMASKINACTIVE, arg[clen]) == NULL)
                                 {
                                    d(("bad ttmask char: %lc (%02lx)\n",arg[clen],arg[clen]));
                                    rc = FALSE;
                                    break;
                                 }
                              }
                           }
                        }
                        ++tt;
                     }
                     else --gdd->gd_NumTT;

                     arg = next;
                  }
               }
            }
            AddTail((struct List*)&gd->gd_Devices, (struct Node*)gdd);
            ++gd->gd_NumDevices;
         }
      }

      if (rc)
      {
         if (!gd->gd_HadRefresh && param[ARG_REFRESH])
         {
            gd->gd_Refresh = *(LONG*)(param[ARG_REFRESH]);
            gd->gd_HadRefresh = 1;
         }

         if (!gd->gd_HadCPSAging && param[ARG_CPSAGING])
         {
            if (*(LONG*)(param[ARG_CPSAGING]) > 50 || *(LONG*)(param[ARG_CPSAGING]) < 0)
            {
               message(gd, REQ_ERROR_TITLE, REQ_ERROR_BADAGING,
                                                    REQ_ERROR_QUITBUTTON);
               rc = FALSE;
            }
            else
            {
               gd->gd_CPSAging = *(LONG*)(param[ARG_CPSAGING]);
               gd->gd_CPSAgingComp = 100 - gd->gd_CPSAging;
               gd->gd_HadCPSAging = 1;
            }
         }

         if (!gd->gd_HadFont && param[ARG_FONT])
         {
            gd->gd_CustomFont = 1;
            strncpy(pd->pd_Fontname, (char*)param[ARG_FONT], sizeof(pd->pd_Fontname));
            pd->pd_Font.tta_Name = (STRPTR)pd->pd_Fontname;
            pd->pd_Font.tta_YSize = param[ARG_FONTSIZE] ? *(LONG*)(param[ARG_FONTSIZE]) : DEF_FONTSIZE;
            pd->pd_Font.tta_Style = FS_NORMAL;
            pd->pd_Font.tta_Flags = FPF_DISKFONT;
            pd->pd_Font.tta_Tags = NULL;
            gd->gd_HadFont = 1;
         }

         if (!gd->gd_HadLeft && param[ARG_LEFT])
         {
            gd->gd_Left = *(LONG*)(param[ARG_LEFT]);
            gd->gd_HadLeft = 1;
         }

         if (!gd->gd_HadTop && param[ARG_TOP])
         {
            gd->gd_Top = *(LONG*)(param[ARG_TOP]);
            gd->gd_HadTop = 1;
         }

         if (!gd->gd_HadInactive && param[ARG_NOACTIVATE])
         {
            gd->gd_Inactive = 1;
            gd->gd_HadInactive = 1;
         }

         if (!gd->gd_HadWindowToBack && param[ARG_WINDOWTOBACK])
         {
            gd->gd_ToBack = 1;
            gd->gd_HadWindowToBack = 1;
         }

         if (!gd->gd_HadStayFront && param[ARG_STAYFRONT])
         {
            gd->gd_StayFront = 1;
            gd->gd_HadStayFront = 1;
         }

         if (!gd->gd_HadPubscreen && param[ARG_PUBSCREEN])
         {
            strncpy((char*)pd->pd_Pubscreen, (char*)param[ARG_PUBSCREEN],
                                                   sizeof(pd->pd_Pubscreen));
            gd->gd_HadPubscreen = 1;
         }
      }
   }

   return rc;
}
/*E*/
/*F*/ PRIVATE BOOL parseargs(GD gd)
{
   PD pd = gd->gd_Public;
   BOOL rc = FALSE;
   LONG param[ARG_COUNT];
   BOOL more;
#if (USE_EXTREADARGS != 0)
   struct ExtRDArgs eargs = {NULL};

   eargs.erda_Window        = (STRPTR)DEF_CONSOLE;
   eargs.erda_Template      = (STRPTR)TEMPLATE;
   eargs.erda_Parameter     = param;
   eargs.erda_FileParameter = -1;
   eargs.erda_SysBase       = SysBase;
   eargs.erda_DOSBase       = DOSBase;
   eargs.erda_IconBase      = IconBase;
#else
   struct RDArgs *rda;
#endif

   memset(param, '\0', sizeof(param));

#if (USE_EXTREADARGS != 0)
   if (ExtReadArgs(gd->gd_argc, (STRPTR*)gd->gd_argv, &eargs) == 0)
#else
   if (rda = ReadArgs((STRPTR)TEMPLATE, param, NULL))
#endif
   {
      /* some default values */
      gd->gd_CPSAging = DEF_CPSAGING;
      gd->gd_CPSAgingComp = 100 - DEF_CPSAGING;
      gd->gd_Refresh = DEF_REFRESH;
      gd->gd_CustomFont = 0;
      gd->gd_Top = DEF_TOP;
      gd->gd_Left = DEF_LEFT;
      strncpy((char*)pd->pd_Console, DEF_CONSOLE, sizeof(pd->pd_Console));

      rc = nowparse(gd, param, &more, FALSE);
      if (rc && !gd->gd_List)
      {
         BPTR file;
         struct RDArgs *mrda, *xrda;
         STRPTR line;

         while(rc && more)
         {
            if (file = Open(gd->gd_ConfigName, MODE_OLDFILE))
            {
               if (line = allocppvec(gd, 1024))
               {
                  if (mrda = AllocDosObject(DOS_RDARGS, NULL))
                  {
                     while(FGets(file, line, 1024))
                     {
                        d(("line : <%s>\n",line));
                        mrda->RDA_Source.CS_Buffer = line;
                        mrda->RDA_Source.CS_Length = strlen(line);
                        mrda->RDA_Source.CS_CurChr = 0;
                        mrda->RDA_Flags |= RDAF_NOPROMPT;
                        memset(param, '\0', sizeof(param));
                        if (xrda = ReadArgs((STRPTR)TEMPLATE, param, mrda))
                        {
                           nowparse(gd, param, &more, TRUE);
                           FreeArgs(xrda);
                        }
                        else break;
                     }
                     if (gd->gd_NumDevices == 0)
                     {
                        message(gd, REQ_ERROR_TITLE, REQ_ERROR_READARGS,
                                                            REQ_ERROR_QUITBUTTON, IoErr());
                        rc = FALSE;
                     }
                     FreeDosObject(DOS_RDARGS, mrda);
                  }
                  else
                  {
                     message(gd, REQ_ERROR_TITLE, REQ_ERROR_READARGS,
                                                         REQ_ERROR_QUITBUTTON, IoErr());
                     rc = FALSE;
                  }
                  freeppvec(gd, line);
               }
               Close(file);
            }
            else
            {
               message(gd, REQ_ERROR_TITLE, REQ_ERROR_NOCONFIG, REQ_ERROR_QUITBUTTON, gd->gd_ConfigName);
               rc = FALSE;
            }
         }
      }

      if (!gd->gd_List && IsListEmpty((struct List*)&gd->gd_Devices))
      {
         message(gd, REQ_ERROR_TITLE, REQ_ERROR_NODEVS, REQ_ERROR_QUITBUTTON);
         rc = FALSE;
      }

#if (USE_EXTREADARGS != 0)
      ExtFreeArgs(&eargs);
#else
      FreeArgs(rda);
#endif
   }
   else
   {
      if (gd->gd_CLI)
         PrintFault(IoErr(),gd->gd_ProgramName);
      else
         message(gd, REQ_ERROR_TITLE, REQ_ERROR_READARGS,
                                          REQ_ERROR_QUITBUTTON, IoErr());
   }
   return(rc);
}
/*E*/
/*F*/ PRIVATE VOID openconsole(GD gd)
{
   struct PublicData *pd = gd->gd_Public;

   if (!gd->gd_CLI)
   {
      if (gd->gd_Output = Open(pd->pd_Console, MODE_OLDFILE))
      {
         gd->gd_OldConsoleTask = SetConsoleTask(((struct FileHandle*)BADDR(gd->gd_Output))->fh_Type);
         gd->gd_OldOutput = SelectOutput(gd->gd_Output);
         gd->gd_OutputSelected = 1;
         gd->gd_CTSelected = 1;
         if (gd->gd_Input = Open("*", MODE_OLDFILE))
         {
            gd->gd_OldInput = SelectInput(gd->gd_Input);
            gd->gd_InputSelected = 1;
         }
         else d(("no input\n"));
      }
      else d(("no output\n"));
   }
}
/*E*/
/*F*/ PRIVATE VOID cleanup(GD gd)
{
   PD pd = gd->gd_Public;
   struct GlobalDevData *gdd;

   closegui(gd);
   if (pd && pd->pd_TReq.tr_node.io_Device) CloseDevice((struct IORequest*)&pd->pd_TReq);
   SCANDEVS(gdd, &gd->gd_Devices)
   {
      struct PublicDevData *pdd = gdd->gd_Public;
      struct TrackType *tt;
      ULONG i;

      if (pdd->pd_S2Req.ios2_Req.io_Device)
      {
         for(i=gdd->gd_NumTT, tt=pdd->pd_TrackedTypes; i; --i, ++tt)
         {
            if (tt->tt_Flags & TTF_TRACKED)
            {
               pdd->pd_S2Req.ios2_PacketType = tt->tt_Type;
               pdd->pd_S2Req.ios2_Req.io_Command = S2_UNTRACKTYPE;
               DoIO((struct IORequest*)&pdd->pd_S2Req);
            }
         }
         CloseDevice((struct IORequest*)&pdd->pd_S2Req);
      }
   }
   if (gd->gd_S2Port) DeleteMsgPort(gd->gd_S2Port);
   if (gd->gd_TPort) DeleteMsgPort(gd->gd_TPort);
   if (gd->gd_LocaleInfo.li_Catalog) CloseCatalog(gd->gd_LocaleInfo.li_Catalog);
#ifndef LINK_LIB
   if (LocaleBase) CloseLibrary(LocaleBase);
#endif
   if (GTLayoutBase) CloseLibrary(GTLayoutBase);
   if (GadToolsBase) CloseLibrary(GadToolsBase);
   if (UtilityBase) CloseLibrary(UtilityBase);
   if (IntuitionBase) CloseLibrary(IntuitionBase);
   if (gd->gd_InputSelected) SelectInput(gd->gd_OldInput);
   if (gd->gd_OutputSelected) SelectOutput(gd->gd_OldOutput);
   if (gd->gd_CTSelected) SetConsoleTask(gd->gd_OldConsoleTask);
   if (gd->gd_Input) Close(gd->gd_Input);
   if (gd->gd_Output) Close(gd->gd_Output);
   if (DOSBase) CloseLibrary(DOSBase);
   if (gd->gd_Public) FreeVec(gd->gd_Public);
   if (gd->gd_PubPool) AsmDeletePool(gd->gd_PubPool, (struct ExecBase*)SysBase);
   if (gd->gd_AnyPool) AsmDeletePool(gd->gd_AnyPool, (struct ExecBase*)SysBase);
   FreeVec(gd);
   return ;
}
/*E*/
/*F*/ PRIVATE VOID dummys2copy(VOID) {}
/*E*/
/*F*/ PRIVATE BOOL opendevs(GD gd)
{
   PD  pd = gd->gd_Public;
   struct GlobalDevData *gdd;
   struct PublicDevData *pdd;
   BOOL rc = FALSE;
   struct TrackType *tt;
   ULONG i;
   static const struct TagItem dummybuffermgnt[] =
   {
      S2_CopyToBuff, (ULONG)&dummys2copy,
      S2_CopyFromBuff, (ULONG)&dummys2copy,
      TAG_DONE
   };

   gd->gd_We = FindTask(NULL);

   if (gd->gd_TPort = CreateMsgPort())
   {
      pd->pd_TReq.tr_node.io_Message.mn_ReplyPort = gd->gd_TPort;
      if (!OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest*)&pd->pd_TReq, 0L))
      {
         pd->pd_TReq.tr_node.io_Command = TR_ADDREQUEST;
         gd->gd_time.tv_secs = gd->gd_Refresh;
         gd->gd_time.tv_micro = 0;
         gd->gd_TimerOff = 1;

         rc = TRUE;

         if (gd->gd_S2Port = CreateMsgPort())
         {
            SCANDEVS(gdd, &gd->gd_Devices)
            {
               pdd = gdd->gd_Public;

               sprintf(gdd->gd_Title, "%s/%ld", FilePart(gdd->gd_Device), gdd->gd_Unit);

               pdd->pd_S2Req.ios2_Req.io_Message.mn_ReplyPort = gd->gd_S2Port;
               if (gdd->gd_Force)
                  pdd->pd_S2Req.ios2_BufferManagement = (VOID*)dummybuffermgnt;

               if (!OpenDevice(gdd->gd_Device, gdd->gd_Unit, (struct IORequest*)&pdd->pd_S2Req, 0L))
               {
                  pdd->pd_S2DeviceQuery.SizeAvailable = sizeof(pdd->pd_S2DeviceQuery);
                  pdd->pd_S2Req.ios2_StatData = &pdd->pd_S2DeviceQuery;
                  pdd->pd_S2Req.ios2_Req.io_Command = S2_DEVICEQUERY;
                  d(("S2_DEVICEQUERY\n"));
                  if (DoIO((struct IORequest*)&pdd->pd_S2Req) == 0)
                  {
                     for(i=gdd->gd_NumTT, tt=pdd->pd_TrackedTypes; i; --i, ++tt)
                     {
                        pdd->pd_S2Req.ios2_PacketType = tt->tt_Type;
                        pdd->pd_S2Req.ios2_Req.io_Command = S2_TRACKTYPE;
                        d(("S2_TRACKTYPE %ld\n", tt->tt_Type));
                        if (DoIO((struct IORequest*)&pdd->pd_S2Req) == 0)
                        {
                           tt->tt_Flags |= TTF_TRACKED;
                        }
                        else if ((pdd->pd_S2Req.ios2_Req.io_Error == S2ERR_BAD_STATE) &&
                                 (pdd->pd_S2Req.ios2_WireError == S2WERR_ALREADY_TRACKED))
                        {
                           tt->tt_Flags &= ~TTF_TRACKED;
                        }
                        else
                        {
                           message(gd, REQ_ERROR_TITLE, REQ_ERROR_TRACK, REQ_ERROR_QUITBUTTON,
                                             pdd->pd_S2Req.ios2_PacketType,
                                             pdd->pd_S2Req.ios2_Req.io_Error,
                                             pdd->pd_S2Req.ios2_WireError);
                           rc = FALSE;
                           break;
                        }
                     }
                  }
                  else
                  {
                     message(gd, REQ_ERROR_TITLE, REQ_ERROR_QUERY, REQ_ERROR_QUITBUTTON,
                                             gdd->gd_Device, gdd->gd_Unit);
                     rc = FALSE;
                  }
               }
               else
               {
                  message(gd, REQ_ERROR_TITLE, REQ_ERROR_OPENDEV, REQ_ERROR_QUITBUTTON,
                                             gdd->gd_Device, gdd->gd_Unit);
                  rc = FALSE;
               }
               if (!rc) break;
            }
         }
         else message(gd, REQ_ERROR_TITLE, REQ_ERROR_NOMSGPORT, REQ_ERROR_QUITBUTTON);
      }
      else message(gd, REQ_ERROR_TITLE, REQ_ERROR_OPENDEV, REQ_ERROR_QUITBUTTON,
                              TIMERNAME, UNIT_VBLANK);
   }
   else message(gd, REQ_ERROR_TITLE, REQ_ERROR_NOMSGPORT, REQ_ERROR_QUITBUTTON);

   return rc;
}
/*E*/
/*F*/ PRIVATE GD createenv(ULONG argc, UBYTE **argv)
{
   GD gd, new;
   BOOL ok = FALSE;

   {
      LOCALSYSBASE;
      new = AllocVec(sizeof(struct GlobalData), MEMF_ANY | MEMF_CLEAR);
      new->gd_SysBase = SysBase;
   }

   if (gd = new)
   {
      gd->gd_argv = argv;
      gd->gd_argc = argc;
      NewList((struct List*)&gd->gd_Devices);
      if (gd->gd_Public = AllocVec(sizeof(struct PublicData),
                                                MEMF_PUBLIC | MEMF_CLEAR))
      {
         if (DOSBase = openlib(gd, "dos.library", 37))
         {
            if (LocaleBase = openlib(gd, "locale.library", 38))
               gd->gd_LocaleInfo.li_Catalog = OpenCatalog(NULL, "sana2meter.catalog",
                                                               OC_Version, CATALOG_VERSION, TAG_DONE);

            if (UtilityBase = openlib(gd, "utility.library", 37))
            {
               if (IntuitionBase = openlib(gd, "intuition.library", 37))
               {
                  if (GadToolsBase = openlib(gd, "gadtools.library", 37))
                  {
#ifndef LINK_LIB
                     if (GTLayoutBase = openlib(gd, "gtlayout.library", 24))
                     {
#endif
                        GetProgramName(new->gd_ProgramName, sizeof(new->gd_ProgramName));

                        if (!(gd->gd_CLI = (Cli() != NULL)))
                           IconBase = openlib(gd, "icon.library", 0);

                        if (gd->gd_AnyPool = AsmCreatePool(ANYPOOLKIND, ANYPOOLPUDDLE, ANYPOOLTHRESH,
                                                      (struct ExecBase*)SysBase))
                        {
                           if (gd->gd_PubPool = AsmCreatePool(PUBPOOLKIND, PUBPOOLPUDDLE, PUBPOOLTHRESH,
                                                         (struct ExecBase*)SysBase))
                           {
                              if (parseargs(gd))
                              {
                                 if (preparetts(gd))
                                 {
                                    openconsole(gd);
                                    ok = TRUE;
                                 }
                              }
                           }
                           else message(gd, REQ_ERROR_TITLE, REQ_ERROR_NOPOOL, REQ_ERROR_QUITBUTTON);
                        }
                        else message(gd, REQ_ERROR_TITLE, REQ_ERROR_NOPOOL, REQ_ERROR_QUITBUTTON);
#ifndef LINK_LIB
                     }
#endif
                  }
               }
            }
         }
         else d(("no dos.library\n"));
      }
      else d(("out of memory\n"));
   }
   else d(("out of memory\n"));

   if (!ok && new)
   {
      cleanup(new);
      new = NULL;
   }
   return new;
}
/*E*/

/*F*/PUBLIC VOID saveconfig(GD gd)
{
#define MAXTOOLTYPE 2048
   BPTR old, new, cfgfile = 0;
   struct DiskObject *dob;
   STRPTR tooltype;
   STRPTR *oldtt;
   STRPTR progname;
   struct GlobalDevData *gdd;
   BOOL doicon;

   if (!IconBase)
      IconBase = openlib(gd, "icon.library", 0);

   if (IconBase)
   {
      if (gd->gd_CLI)
      {
         progname = gd->gd_ProgramName;
         new = Lock("PROGDIR:", ACCESS_READ);
      }
      else
      {
         struct WBArg *wbarg = ((struct WBStartup *)gd->gd_argv)->sm_ArgList;
         progname = wbarg->wa_Name;
         new = DupLock(wbarg->wa_Lock);
      }

      if(new)
      {
         if (tooltype = allocpvec(gd, MAXTOOLTYPE))
         {
            old = CurrentDir(new);

            doicon = FALSE;
            if (dob = GetDiskObject(progname))
            {
               if (oldtt = DupToolTypes(gd, dob))
                  doicon = TRUE;
               else
                  FreeDiskObject(dob);
            }

            SCANDEVS(gdd, &gd->gd_Devices)
            {
               if (gdd->gd_FromConfig || !doicon)
               {
                  if (!cfgfile) cfgfile = Open(gd->gd_ConfigName, MODE_NEWFILE);
                  if (cfgfile)
                  {
                     sprintf(tooltype, "DEVICE=\"%s\" UNIT=%ld%s%s",
                                    gdd->gd_Device,
                                    gdd->gd_Unit,
                                    gdd->gd_Force ? " FORCE" : "",
                                    gdd->gd_Active ? "" : " OFF");

                     strcat(tooltype, " SHOW=\"");
                     {
                        struct Watch *w;
                        BOOL first = TRUE;

                        SCANWATCHES(w, &gdd->gd_Watches)
                        {
                           if ((w->w_Member != MEMBER_TRACKTYPE) && (w->w_Flags & WF_ACTIVE) && w->w_Key)
                           {
                              if (first)
                                 first = FALSE;
                              else
                                 strcat(tooltype, ",");
                              strcat(tooltype, w->w_Key);
                           }
                        }
                     }
                     strcat(tooltype, "\"");
                     if (gdd->gd_NumTT)
                     {
                        strcat(tooltype, " TRACKTYPE=\"");
                        {
                           struct Watch *w;
                           BOOL first = TRUE;

                           SCANWATCHES(w, &gdd->gd_Watches)
                           {
                              if (w->w_Member == MEMBER_TRACKTYPE)
                              {
                                 if (w->w_Key)
                                 {
                                    if (first)
                                       first = FALSE;
                                    else
                                       strcat(tooltype, ",");

                                    sprintf(&tooltype[strlen(tooltype)], "%s%lc",
                                          w->w_Key, (LONG)TTMASKSEPARATOR[0]);
                                 }
                                 sprintf(&tooltype[strlen(tooltype)], "%lc",
                                    (w->w_Flags & WF_ACTIVE) ? TTMASKACTIVE[0] : TTMASKINACTIVE[0]);
                              }
                           }
                        }
                        strcat(tooltype, "\"");
                     }
                     strcat(tooltype, "\n");
                     Write(cfgfile, tooltype, strlen(tooltype));
                  }
                  else
                  {
                     message(gd, REQ_ERROR_TITLE, REQ_ERROR_NOCONFIG,
                                                   REQ_ERROR_QUITBUTTON, gd->gd_ConfigName);
                  }
               }
               else
               {
                  /* DEVICE, UNIT */
                  sprintf(tooltype, "DEVICE=%s", gdd->gd_Device);
                  AddToolType(gd, dob, tooltype);

                  sprintf(tooltype, "UNIT=%ld", gdd->gd_Unit);
                  AddToolType(gd, dob, tooltype);

                  sprintf(tooltype, "%sFORCE%s",
                              gdd->gd_Force ? "(" : "", gdd->gd_Force ? ")" : "");
                  AddToolType(gd, dob, tooltype);

                  sprintf(tooltype, "%sOFF%s",
                              gdd->gd_Active ? "(" : "", gdd->gd_Active ? ")" : "");
                  AddToolType(gd, dob, tooltype);

                     /* SHOW watches */
                  strcpy(tooltype, "SHOW=");
                  {
                     struct Watch *w;
                     BOOL first = TRUE;

                     SCANWATCHES(w, &gdd->gd_Watches)
                     {
                        if ((w->w_Member != MEMBER_TRACKTYPE) && (w->w_Flags & WF_ACTIVE) && w->w_Key)
                        {
                           if (first)
                              first = FALSE;
                           else
                              strcat(tooltype, ",");
                           strcat(tooltype, w->w_Key);
                        }
                     }
                  }
                  AddToolType(gd, dob, tooltype);

                  /* TRACKTYPE watches */
                  strcpy(tooltype, "TRACKTYPE=");
                  {
                     struct Watch *w;
                     BOOL first = TRUE;

                     SCANWATCHES(w, &gdd->gd_Watches)
                     {
                        if (w->w_Member == MEMBER_TRACKTYPE)
                        {
                           if (w->w_Key)
                           {
                              if (first)
                                 first = FALSE;
                              else
                                 strcat(tooltype, ",");

                              sprintf(&tooltype[strlen(tooltype)], "%s%lc",
                                    w->w_Key, (LONG)TTMASKSEPARATOR[0]);
                           }
                           sprintf(&tooltype[strlen(tooltype)], "%lc",
                              (w->w_Flags & WF_ACTIVE) ? TTMASKACTIVE[0] : TTMASKINACTIVE[0]);
                        }
                     }
                     if (first)
                        strcpy(tooltype, "(TRACKTYPE=)");
                  }
                  AddToolType(gd, dob, tooltype);

                  if (gd->gd_ConfigName)
                     sprintf(tooltype, "FROM=%s", gd->gd_ConfigName);
                  else
                     strcpy(tooltype, "(FROM=)");
                  AddToolType(gd, dob, tooltype);
               }
            }

            if (doicon)
            {
               /* window position */
               sprintf(tooltype, "LEFT=%ld", gd->gd_Handle->Window->LeftEdge);
               AddToolType(gd, dob, tooltype);
               sprintf(tooltype, "TOP=%ld", gd->gd_Handle->Window->TopEdge);
               AddToolType(gd, dob, tooltype);

               /* refresh interval */
               sprintf(tooltype, "REFRESH=%ld", gd->gd_Refresh);
               AddToolType(gd, dob, tooltype);

               /* CPS display aging */
               sprintf(tooltype, "CPSAGING=%ld", gd->gd_CPSAging);
               AddToolType(gd, dob, tooltype);

               /* public screen */
               if (gd->gd_Public->pd_Pubscreen[0])
                  sprintf(tooltype, "PUBSCREEN=%s", gd->gd_Public->pd_Pubscreen);
               else
                  strcpy(tooltype, "(PUBSCREEN=)");
               AddToolType(gd, dob, tooltype);

               /* window-in-front enforcing */
               sprintf(tooltype, "%sSTAYFRONT%s",
                           gd->gd_StayFront ? "" : "(", gd->gd_StayFront ? "" : ")");
               AddToolType(gd, dob, tooltype);
            }
            else
            {
               /* window position */
               sprintf(tooltype, "LEFT=%ld TOP=%ld REFRESH=%ld CPSAGING=%ld\n",
                                                     gd->gd_Handle->Window->LeftEdge,
                                                     gd->gd_Handle->Window->TopEdge,
                                                     gd->gd_Refresh,
                                                     gd->gd_CPSAging);
               Write(cfgfile, tooltype, strlen(tooltype));

               if (gd->gd_Public->pd_Pubscreen[0])
               {
                  sprintf(tooltype, "PUBSCREEN=\"%s\"\n", gd->gd_Public->pd_Pubscreen);
                  Write(cfgfile, tooltype, strlen(tooltype));
               }

               /* window-in-front enforcing */
               if (gd->gd_StayFront)
               {
                  sprintf(tooltype, "STAYFRONT\n");
                  Write(cfgfile, tooltype, strlen(tooltype));
               }
            }

            if (cfgfile) Close(cfgfile);

            if (doicon)
            {
               PutDiskObject(progname, dob);
               UnDupToolTypes(gd, dob, oldtt);
               FreeDiskObject(dob);
            }

            UnLock(CurrentDir(old));

            freepvec(gd, tooltype);
         }
      }
      CloseLibrary(IconBase);
   }

   return ;
}
/*E*/

/*F*/ PUBLIC VOID SAVEDS STDARGS main(ULONG argc, UBYTE **argv)
{
   int rc = RETURN_FAIL;
   ULONG m_wait, m_tport, m_s2port, m_recv, m_window;
   BOOL running;
   GD gd;
   PD pd;


   if (!(gd = createenv(argc, argv))) goto leave;
   pd = gd->gd_Public;

   /* initialize gtlayout */
#ifdef LINK_LIB
   LT_Init();
#endif

   if (gd->gd_List)
   {
      const struct NewWatch *nw;
      Printf("\033[1mTable of keywords (abbrev'd form in parenthesis):\033[0m\n");
      for (nw = stdwatches; nw->nw_Member != MEMBER_NONE; nw++)
         Printf("%s (%s)\n", nw->nw_Key, nw->nw_ShortKey);
      rc = RETURN_OK;
      goto leave;
   }

   if (!opendevs(gd)) goto leave;

   {
      struct GlobalDevData *gdd;
      UWORD fail;

      SCANDEVS(gdd, &gd->gd_Devices)
      {
         updateupdate(gd, gdd);
         fail = updateglobalstats(gd, gdd);
         fail += updatespecialstats(gd, gdd);
         fail += updatetracktype(gd, gdd);
         if (fail)
         {
            updateupdate(gd, gdd);
            updateglobalstats(gd, gdd);
            updatespecialstats(gd, gdd);
            updatetracktype(gd, gdd);
         }
      }
      updateactives(gd);
   }
   getspeciallabels(gd);

   if (!preparemenu(gd)) goto leave;

   if (!opengui(gd)) goto leave;

   updategui(gd, TRUE);

   m_wait = SIGBREAKF_CTRL_C |
            (m_tport = 1 << gd->gd_TPort->mp_SigBit) |
            (m_s2port = 1 << gd->gd_S2Port->mp_SigBit) |
            (m_window = 1 << gd->gd_Handle->Window->UserPort->mp_SigBit);

   if (gd->gd_NeedTimer)
   {
      d(("starting timer\n"));
      pd->pd_TReq.tr_time = gd->gd_time;
      SendIO((struct IORequest*)&pd->pd_TReq);
      gd->gd_TimerOff = 0;
   }

   rc = RETURN_OK;
   for(running = TRUE; running;)
   {
      d(("Wait()\n"));
      m_recv = Wait(m_wait);

      if (m_recv & SIGBREAKF_CTRL_C)
      {
         running = FALSE;
      }

      if (m_recv & m_window)
      {
         DoWindowCtrl ctrl;

         running = dowindow(gd, &ctrl);
         if (running && (ctrl & DOWCTRL_UPDATE))
         {
            struct GlobalDevData *gdd;
            UWORD fail;

            ctrl &= ~DOWCTRL_REOPEN;

            if (!gd->gd_TimerOff)
               AbortIO((struct IORequest*)&pd->pd_TReq);

            closegui(gd);
            SCANDEVS(gdd, &gd->gd_Devices)
            {
               fail = 0;
               updateupdate(gd, gdd);
               if (gdd->gd_GetGlobalStats) fail = updateglobalstats(gd, gdd);
               if (gdd->gd_GetSpecialStats) fail += updatespecialstats(gd, gdd);
               if (gdd->gd_GetTrackType) fail += updatetracktype(gd, gdd);
               if (fail) updateupdate(gd, gdd);
               /* rh(c)slip seems to leave a DoIO() with the port bit set */
               SetSignal(0, m_s2port);
            }
            updateactives(gd);
            if (!opengui(gd)) running = FALSE;

            if (gd->gd_NeedTimer && gd->gd_TimerOff)
            {
               d(("starting timer\n"));
               pd->pd_TReq.tr_time = gd->gd_time;
               SendIO((struct IORequest*)&pd->pd_TReq);
               gd->gd_TimerOff = 0;
            }
         }

         if (running && (ctrl & DOWCTRL_REOPEN))
         {
            closegui(gd);
            if (!opengui(gd)) running = FALSE;
         }

         if (running && (ctrl & DOWCTRL_SHOWWINDOW))
         {
            LT_ShowWindow(gd->gd_Handle, TRUE);
         }
      }

      if (m_recv & m_s2port)
      {
         d(("spurious signal from SANA2 MsgPort (mask = %08lx)\n", m_recv));
      }

      if (m_recv & m_tport)
      {
         struct GlobalDevData *gdd;
         UWORD fail = 0;

         if (!gd->gd_TimerOff)
            WaitIO((struct IORequest*)&pd->pd_TReq);

         if (gd->gd_NumActives)
         {
            SCANDEVS(gdd, &gd->gd_Devices)
            {
               if (gdd->gd_GetGlobalStats) fail = updateglobalstats(gd, gdd);
               if (gdd->gd_GetSpecialStats) fail += updatespecialstats(gd, gdd);
               if (gdd->gd_GetTrackType) fail += updatetracktype(gd, gdd);
               /* rh(c)slip seems to leave a DoIO() with the port bit set */
               SetSignal(0, m_s2port);
            }
            if (fail)
            {
               closegui(gd);
               updateupdate(gd, gdd);
               updateactives(gd);
               if (!opengui(gd)) running = FALSE;
            }
            else
               updategui(gd, FALSE);
            if (gd->gd_NeedTimer)
            {
               d(("starting timer\n"));
               pd->pd_TReq.tr_time = gd->gd_time;
               SendIO((struct IORequest*)&pd->pd_TReq);
            }
            else gd->gd_TimerOff = 1;
         }
         else gd->gd_TimerOff = 1;
      }
   }

   AbortIO((struct IORequest*)&pd->pd_TReq);
   WaitIO((struct IORequest*)&pd->pd_TReq);

leave:
   if (gd) cleanup(gd);

#ifdef LINK_LIB
   LT_Exit();
#endif

   exit(rc);
   return ;
}
/*E*/

