
/*
**
**  $VER: NetInfo.c 1.8 (20.8.96)
**  NetInfo 1.0
**
**  (C) Copyright 1994-1996 by Roland 'Gizzy' Mainz
**
**
*/

#ifdef COMMENTED_OUT
#define BE_RESIDENTABLE 1 /* used when compiling with resident startup module */
#endif /* COMMENTED_OUT */

/* We did not want any names before V36 */
#define INTUI_V36_NAMES_ONLY 1

/* amiga includes */
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <utility/utility.h>
#include <utility/tagitem.h>
#include <graphics/gfxbase.h>
#include <dos/dos.h>
#include <dos/dostags.h>
#include <workbench/startup.h>
#include <workbench/icon.h>
#include <envoy/nipc.h>
#include <envoy/errors.h>

/* amiga prototypes */
#include <clib/exec_protos.h>
#include <clib/utility_protos.h>
#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>
#include <clib/icon_protos.h>
#include <clib/nipc_protos.h>
#include <clib/envoy_protos.h>
#include <clib/alib_protos.h>
#include <clib/alib_stdio_protos.h>

/* amiga pragmas */
#include <pragmas/exec_pragmas.h>
#include <pragmas/utility_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/intuition_pragmas.h>
#include <pragmas/icon_pragmas.h>
#include <pragmas/nipc_pragmas.h>
#include <pragmas/envoy_pragmas.h>
#include <pragmas/alib_pragmas.h>

/* ansi includes */
#include <string.h>
#include <limits.h>

/* SAS/C include */
#ifdef BE_RESIDENTABLE
#include <dos.h>
#endif /* BE_RESIDENTABLE */

/* version string */
#include "NetInfo_rev.h"

/* misc defines */
#ifndef NAME
#define NAME "NetInfo"
#endif /* !NAME */

/* if condition is false, replace tag with TAG_IGNORE */
#define XTAG( expr, tagid ) ((Tag)((expr)?(tagid):(TAG_IGNORE)))

/* get data from pointer only if it's NOT NULL (and cast it to ULONG) */
#define XPTRDATA( x ) ((ULONG)((x)?(*(x)):(0UL)))


#ifdef BE_RESIDENTABLE
#define SAVEA4CONTEXT
#else
#define SAVEA4CONTEXT __saveds
#endif /* BE_RESIDENTABLE */

/* hook defines */
#ifdef _DCC
#define DISPATCHERFLAGS SAVEA4CONTEXT
#define REGARGS
#else
#define DISPATCHERFLAGS SAVEA4CONTEXT __asm
#define REGARGS __asm
#endif /* _DCC */

#define REGA0 register __a0
#define REGA1 register __a1
#define REGA2 register __a2
#define REGD0 register __d0

struct QueryMessage
{
    struct Message  qmsg_Message;
    APTR            qmsg_Pool;
    struct TagItem *qmsg_Tags;
};

/* use message node's name for single-list node tracking */
#define qmsg_TrackLink qmsg_Message . mn_Node . ln_Name

struct NIPCInquiryHook
{
    struct Hook          nih_Hook;              /* NIPC hook */
    LONG                 nih_Signal;            /* signal hook will send for termination */
    ULONG                nih_DispatcherRetval;  /* dispatcher's next return value */
    LONG                 nih_Error;             /* error during hook processing */
    struct MsgPort      *nih_MsgPort;           /* msgport for query msgs */
    struct QueryMessage *nih_TrackedMessages;   /* tracked query messages, single-linked list (see qmsg_TrackList above) */
    BOOL                 nih_DumpMessages;
};

#ifdef BE_RESIDENTABLE
/* near data compiler context from register A4 */
#define nih_REGA4 nih_Hook . h_Data
#endif /* BE_RESIDENTABLE */

/* get unsigned byte from ULONG or (unsigned long long (64 bits)) */
#define GETUBYTE( ul, idx ) ((UBYTE)(((ul) >> ((idx) * CHAR_BIT)) & UCHAR_MAX))
/* get unsigned word from ULONG or (unsigned long long (64 bits)) */
#define GETUWORD( ul, idx ) ((UBYTE)(((ul) >> ((idx) * (CHAR_BIT * 2))) & USHRT_MAX))


/* prototypes */
#ifdef _DCC
                              long                  wbmain( struct WBStartup * );
                              void                  chkabort( void );
#endif /* _DCC */

                              long                  main( long, STRPTR * );

                static        void                  DefaultSettings( void );
                static        void                  FreeInitProjectResult( void );
                static        void                  ReadENVPrefs( void );

                static        void                  ClearRDA( void );
                static        void                  ScanRDA( void );
                static        void                  ScanToolTypes( STRPTR * );

                static        void                  RunTool( void );
                              BOOL                  QueryMessageInList( struct NIPCInquiryHook *, struct TagItem * );
                              void                  DisposeQueryMessageList( struct NIPCInquiryHook * );

DISPATCHERFLAGS static        ULONG                 dispatchNIPCInquiry( REGA0 struct NIPCInquiryHook *, REGA2 struct Task *, REGA1 struct TagItem * );
                              void                  PrintNIPCInquiryTagList( struct TagItem *, BPTR );

                              void                  ProcessQueryMsgPort( struct MsgPort *, BPTR );
                              BOOL                  SendQueryMessage( struct NIPCInquiryHook *, struct TagItem * );
                              struct TagItem       *CloneNIPCInquiryTagList( struct TagItem *, APTR );
                              BOOL                  SameQueryMessages( struct TagItem *, struct TagItem * );

                static        BOOL                  OpenLibStuff( void );
                static        void                  CloseLibStuff( void );

                static        BOOL                  CreateBasicResources( void );
                static        void                  DeleteBasicResources( void );

                              void                  AttemptOpenLibrary( struct Library **, STRPTR, STRPTR, ULONG );

                              STRPTR                StringSave( STRPTR );
                              STRPTR                StringSavePooled( APTR, STRPTR );

                              void                  FreeString( STRPTR );
                              void                  FreeStringPooled( APTR, STRPTR );

                              STRPTR                UpdateString( STRPTR *, STRPTR );

                              APTR                  AllocVecPooled( APTR, ULONG );
                              void                  FreeVecPooled( APTR, APTR );

                              ULONG                 GetExecAttnFlags( STRPTR );
                              void                  FPrintExecAttnFlags( BPTR, ULONG );

                              ULONG                 GetChipRevBitsFlags( STRPTR );
                              void                  FPrintChipRevBitsFlags( BPTR, ULONG );

                              BOOL                  IP2ULONG( STRPTR, ULONG * );

                              ULONG                 NumTagItems( struct TagItem * );
                              struct TagItem       *CopyTagList( struct TagItem *, struct TagItem * );
                              struct TagItem       *CloneTagItemsPooled ( struct TagItem *, APTR );
                              void                  FreeTagItemsPooled ( struct TagItem *, APTR );


/* version_string */
STRPTR versionstring = VERSTAG;

/* global vars */
/* program return values */
long main_retval,
     main_retval2;

/* shared libraries */
extern struct ExecBase *SysBase;
extern struct Library  *DOSBase;

struct Library *UtilityBase,
               *IntuitionBase,
               *IconBase,
               *NIPCBase;

/* mempool for strings */
APTR StringPool;

/* template for ReadArgs */
#define STARTUP_TEMPLATE "QUERY_IPADDR/S,"         \
                         "MATCH_IPADDR/K,"         \
                         "QUERY_REALMS/S,"         \
                         "MATCH_REALM/K,"          \
                         "QUERY_HOSTNAME/S,"       \
                         "MATCH_HOSTNAME/K,"       \
                         "QUERY_SERVICE/S,"        \
                         "MATCH_SERVICE/K,"        \
                         "QUERY_ENTITY/S,"         \
                         "MATCH_ENTITY/K,"         \
                         "QUERY_OWNER/S,"          \
                         "MATCH_OWNER/K,"          \
                         "QUERY_ATTNFLAGS/S,"      \
                         "MATCH_ATTNFLAGS/K,"      \
                         "QUERY_CHIPREVBITS/S,"    \
                         "MATCH_CHIPREVBITS/K,"    \
                         "QUERY_MAXFASTMEM/S,"     \
                         "MATCH_MAXFASTMEM/K/N,"   \
                         "QUERY_AVAILFASTMEM/S,"   \
                         "MATCH_AVAILFASTMEM/K/N," \
                         "QUERY_MAXCHIPMEM/S,"     \
                         "MATCH_MAXCHIPMEM/K/N,"   \
                         "QUERY_AVAILCHIPMEM/S,"   \
                         "MATCH_AVAILCHIPMEM/K/N," \
                         "QUERY_KICKVERSION/S,"    \
                         "MATCH_KICKVERSION/K/N,"  \
                         "QUERY_WBVERSION/S,"      \
                         "MATCH_WBVERSION/K/N,"    \
                         "QUERY_NIPCVERSION/S,"    \
                         "MATCH_NIPCVERSION/K/N,"  \
                         "MAXTIME/K/N,"            \
                         "MAXRESPONSES=NUM/K/N,"   \
                         "DUMP=ALL/S,"             \
                         "WINDOW=TO/K"

#define ENV_TEMPLATE STARTUP_TEMPLATE /* no /A specified, startup and env template can be the same */

static struct RDArgs *startuprda;

struct QueryMatch
{
    long *query;
    APTR  match;
};

static
struct
{
    struct QueryMatch  ipaddr;
    struct QueryMatch  realms;
    struct QueryMatch  hostname;
    struct QueryMatch  service;
    struct QueryMatch  entity;
    struct QueryMatch  owner;
    struct QueryMatch  attnflags;
    struct QueryMatch  chiprevbits;
    struct QueryMatch  maxfastmem;
    struct QueryMatch  availfastmem;
    struct QueryMatch  maxchipmem;
    struct QueryMatch  availchipmem;
    struct QueryMatch  kickversion;
    struct QueryMatch  wbversion;
    struct QueryMatch  nipcversion;
    long              *maxtime;
    long              *maxresponses;
    long              *dump;
    STRPTR             window;
} result;

static
struct
{
    ULONG  query_ipaddr;
    ULONG  match_ipaddr;
    ULONG  query_realms;
    STRPTR match_realm;
    ULONG  query_hostname;
    STRPTR match_hostname;
    ULONG  query_service;
    STRPTR match_service;
    ULONG  query_entity;
    STRPTR match_entity;
    ULONG  query_owner;
    STRPTR match_owner;
    ULONG  query_attnflags;
    ULONG  match_attnflags;
    ULONG  query_chiprevbits;
    ULONG  match_chiprevbits;
    ULONG  query_maxfastmem;
    ULONG  match_maxfastmem;
    ULONG  query_availfastmem;
    ULONG  match_availfastmem;
    ULONG  query_maxchipmem;
    ULONG  match_maxchipmem;
    ULONG  query_availchipmem;
    ULONG  match_availchipmem;
    ULONG  query_kickversion;
    ULONG  match_kickversion;
    ULONG  query_wbversion;
    ULONG  match_wbversion;
    ULONG  query_nipcversion;
    ULONG  match_nipcversion;
    long   maxtime;
    long   maxresponses;
    long   dump;
    STRPTR window;
} project;


#ifdef _DCC
/* disable CTRL_C break support (DICE CTRL_C abort function) */
void chkabort( void )
{
}
#endif /* _DCC */


/****** NetInfo/NetInfo ******************************************************
*
*   NAME
*       NetInfo -- Envoy network information/diagnostic tool
*
*   FORMAT
*       NetInfo [QUERY_IPADDR] [MATCH_IPADDR <ip addr>] [QUERY_REALMS]
*       [MATCH_REALM <realm name>] [QUERY_HOSTNAME]
*       [MATCH_HOSTNAME <host name>] [QUERY_SERVICE]
*       [MATCH_SERVICE <service name>] [QUERY_ENTITY]
*       [MATCH_ENTITY <entity name>] [QUERY_OWNER] [MATCH_OWNER <match owner>]
*       [QUERY_ATTNFLAGS] [MATCH_ATTNFLAGS <attn flags>]
*       [QUERY_CHIPREVBITS] [MATCH_CHIPREVBITS  <chip revision bits>]
*       [QUERY_MAXFASTMEM] [MATCH_MAXFASTMEM <mem size>] [QUERY_AVAILFASTMEM]
*       [MATCH_AVAILFASTMEM <mem size>] [QUERY_MAXCHIPMEM]
*       [MATCH_MAXCHIPMEM <mem size>] [QUERY_AVAILCHIPMEM]
*       [MATCH_AVAILCHIPMEM <mem size>] [QUERY_KICKVERSION]
*       [MATCH_KICKVERSION <version>] [QUERY_WBVERSION]
*       [MATCH_WBVERSION <version>] [QUERY_NIPCVERSION]
*       [MATCH_NIPCVERSION <version>] [MAXTIME <max time>]
*       [MAXRESPONSES|NUM <count>] [DUMP|ALL] [WINDOW|TO <file>]
*
*   TEMPLATE
*       QUERY_IPADDR/S,MATCH_IPADDR/K,QUERY_REALMS/S,MATCH_REALM/K,
*       QUERY_HOSTNAME/S,MATCH_HOSTNAME/K,QUERY_SERVICE/S,MATCH_SERVICE/K,
*       QUERY_ENTITY/S,MATCH_ENTITY/K,QUERY_OWNER/S,MATCH_OWNER/K,
*       QUERY_ATTNFLAGS/S,MATCH_ATTNFLAGS/K,QUERY_CHIPREVBITS/S,
*       MATCH_CHIPREVBITS/K,QUERY_MAXFASTMEM/S,MATCH_MAXFASTMEM/K/N,
*       QUERY_AVAILFASTMEM/S,MATCH_AVAILFASTMEM/K/N,QUERY_MAXCHIPMEM/S,
*       MATCH_MAXCHIPMEM/K/N,QUERY_AVAILCHIPMEM/S,MATCH_AVAILCHIPMEM/K/N,
*       QUERY_KICKVERSION/S,MATCH_KICKVERSION/K/N,QUERY_WBVERSION/S,
*       MATCH_WBVERSION/K/N,QUERY_NIPCVERSION/S,MATCH_NIPCVERSION/K/N,
*       MAXTIME/K/N,MAXRESPONSES=NUM/K/N,DUMP=ALL/S,WINDOW=TO/K
*
*   TOOLTYPES
*       QUERY_IPADDR/S
*       MATCH_IPADDR
*       QUERY_REALMS/S
*       MATCH_REALM
*       QUERY_HOSTNAME/S
*       MATCH_HOSTNAME
*       QUERY_SERVICE/S
*       MATCH_SERVICE
*       QUERY_ENTITY/S
*       MATCH_ENTITY
*       QUERY_OWNER/S
*       MATCH_OWNER
*       QUERY_ATTNFLAGS/S
*       MATCH_ATTNFLAGS
*       QUERY_CHIPREVBITS/S
*       MATCH_CHIPREVBITS
*       QUERY_MAXFASTMEM/S
*       MATCH_MAXFASTMEM/N
*       QUERY_AVAILFASTMEM/S
*       MATCH_AVAILFASTMEM/N
*       QUERY_MAXCHIPMEM/S
*       MATCH_MAXCHIPMEM/N
*       QUERY_AVAILCHIPMEM/S
*       MATCH_AVAILCHIPMEM/N
*       QUERY_KICKVERSION/S
*       MATCH_KICKVERSION/N
*       QUERY_WBVERSION/S
*       MATCH_WBVERSION/N
*       QUERY_NIPCVERSION/S
*       MATCH_NIPCVERSION/N
*       MAXTIME/N
*       MAXRESPONSES/N
*       NUM/N
*       DUMP
*       ALL
*       WINDOW
*       TO
*
*   PATH
*       NetInfo:
*
*   PURPOSE
*       Collects information about the ENVOY network (environment)
*
*   DESCRIPTION
*       The following (QUERY|MATCH)_#? options describes the information
*       requested. The output consists of a list (here called "tag" list)
*       for each host found.
*       The list beginns with a "<start taglist>" line and ends with
*       a "TAG_DONE" line.
*
*       QUERY_IPADDR  - This option is used for querying a machine for
*           it's Network IP address.  Note: This function is provided purely
*           for diagnostic purposes. Do NOT depend on this tag being available
*           in the future.
*
*       MATCH_IPADDR - Query a host with the specified IP address
*           address.  Please see the note above regarding QUERY_IPADDR.
*           NetInfo will ONLY accept a string in the form xxx.xxx.xxx.xxx
*           (IPv3 address)
*
*       QUERY_REALMS - Query a realmserver for the names of all known
*           Realms.
*
*       MATCH_REALM - Only query hosts that are in a specific Realm.
*
*       QUERY_HOSTNAME - Query a host for it's name.
*
*       MATCH_HOSTNAME - Only query the host with the specified
*           hostname.
*
*       QUERY_SERVICES  - Query a host or hosts for the names of all
*           services on each machine.
*
*       MATCH_SERVICE - Only query hosts that have the named (specific)
*           service
*
*       QUERY_ENTITIES - Query a host or hosts for the names of all
*           public entities on each machine.
*
*       MATCH_ENTITY - Query only hosts that have the named public entity
*
*       QUERY_OWNER - Query the owner's name
*
*       MATCH_OWNER - Query only hosts that belongs to the named owner
*
*       QUERY_ATTNFLAGS - This Tag allows you to find out what bits
*           are set in (ExecBase -> AttnFlags).
*
*       MATCH_ATTNFLAGS - Query only hosts that have the specified
*           bits set in (ExecBase -> AttnFlags). Valid args are the following
*           flags, multiple flags can be set using the '|' seperator
*           (don't forget the '"' when using the seperator because this
*           is also the PIPE symbol for some shells):
*           AFF_68010  (checks if a 68010 cpu is present)
*           AFF_68020  (checks if a 68020 cpu is present)
*           AFF_68030  (checks if a 68030 cpu is present)
*           AFF_68040  (checks if a 68040 cpu is present)
*           AFF_68881  (checks if a 68881 fpu is present)
*           AFF_68882  (checks if a 68882 fpu is present)
*           AFF_FPU40  (checks if a 68040 fpu is present)
*           ALL        (sets all flags above in one step)
*
*       QUERY_CHIPREVBITS - This Tag alllows you to find out what
*           bits are set in (GfxBase -> ChipRevBits0).
*           Note: Only the lower 8 bits of the ULONG are used.
*
*       MATCH_CHIPREVBITS - Query only hosts that have the specified
*           bits set in (GfxBase -> ChipRevBits0).
*           Valid args are the following flags, multiple flags can be set
*           using the '|' seperator (don't forget the '"' when using the
*           seperator because this is also the PIPE symbol for some shells):
*           GFXF_BIG_BLITS
*           GFXF_HR_AGNUS
*           GFXF_HR_DENISE
*           GFXF_AA_ALICE
*           GFXF_AA_LISA
*           GFXF_AA_MLISA
*           ALL (sets all flags above in one step)
*
*       QUERY_MAXFASTMEM - Query a machine for it's maximum amount
*           of FAST memory.
*
*       MATCH_MAXFASTMEM -  Query only hosts with at least the
*           specified amount of FAST memory.
*
*       QUERY_AVAILFASTMEM - Query a machine for the amount of free
*           FAST memory it has available.
*
*       MATCH_AVAILFASTMEM - Query only those hosts that have at
*           least the specified amount of FAST memory available.
*
*       QUERY_MAXCHIPMEM - Query a machine for it's maximum amount
*           of installed CHIP memory.
*
*       MATCH_MAXCHIPMEM - Query only those hosts that have at least
*           the specified amount of CHIP ram installed.
*
*       QUERY_AVAILCHIPMEM - Query a machine for the amount of free
*           CHIP memory it has available.
*
*       MATCH_AVAILCHIPMEM - Query only those hosts that have at
*           least the specified amount of CHIP memory available.
*
*       QUERY_KICKVERSION - Query a machine for the version and
*           revision of Kickstart it is running.
*
*       MATCH_KICKVERSION - Query only those hosts that are running
*           at least the specified version and revision of Kickstart.
*
*       QUERY_WBVERSION - Query a machine for the version and
*           revision of Workbench it is running.
*
*       MATCH_WBVERSION  - Query only those hosts that are running
*           at least the specified version and revision of Workbench.
*
*       MAXTIME - Maximum number of seconds for this request
*           Defaults to 600.
*
*       MAXRESPONSES
*       NUM - Maximum number of requests
*           Defaults to 20.
*
*       DUMP
*       ALL - Write all messages. Default is that double msgs are dropped,
*           e.g. not printed.
*
*       WINDOW
*       TO - Output file. Defaults to standart output if invoked from shell.
*           Defaults to "CON:////NetInfo/auto/wait/close/inactive" if invoked
*           from Workbench.
*
*   EXAMPLES
*       ; Query the hostname and owner (only one machine)
*       10.0.Ram Disk:> netinfo query_owner query_hostname num=1
*       <start taglist>
*       QUERY_HOSTNAME "MeridianNet:Meridian"
*       QUERY_OWNER "Gisburn"
*       MATCH_REALM "MeridianNet"
*       TAG_DONE
*
*   BUGS
*       See also "History" section below
*
*   HISTORY
*       V1.7
*           First public release,
*
*       V1.8
*           - Output runs now on application process instead of
*             nipc_supervisor (fixes deadlocks with rlogin.service and
*             network-based filesystems)
*           - Errors while opening the output file (Open) or starting the
*             network query (NIPCInquiryA) caused NetInfo to exit without any
*             error prompt. Fixed.
*           - Missing NUM and TO tooltypes added.
*           - Shell output redirection now works (using stdout instead
*             CONSOLE: for output).
*           - Now returns an error if closing of outputfile (WINDOW/TO option)
*             fails.
*           - (QUERY|MATCH)_IPADDR are now using IP-style address notation
*             (XXX.XXX.XXX.XXX)
*           - NetInfo now suppresses query results which are already in the
*             output. To get all messages (the previous behaviour), use the
*             DUMP option.
*           - Possiby QUERY_MACHDESC and QUERY_LIBVERSION tags were suppressed
*             in the output. Now they're treated as unknown tags.
*           - NetInfo is now pure and can be made resident (currently only
*             SAS/C, no portable way)
*           - NetInfo now supports V37 (2.0)
*           - Unused OpenLibrary( "envoy.library", ... ) removed.
*           - The default output window (if invoked from WB) has now a title
*             ("NetInfo").
*
*
*   SEE ALSO
*       Ping
*
******************************************************************************
*
*/


#ifdef _DCC
/* DICE workbench entry */
long wbmain( struct WBStartup *wbstartup )
{
    /* Call main like SAS-C */
    return( main( 0L, (STRPTR *)wbstartup ) );
}
#endif /* _DCC */


long main( long ac, STRPTR *av )
{
    LONG               numArgs,
                       x;
    struct WBStartup  *wbstartup;
    struct WBArg      *wbarg;
    struct DiskObject *tooldobj,
                      *projectdobj;
    BPTR               oldToolLock,
                       oldProjectLock;

    main_retval2 = 0L;
    main_retval = RETURN_OK;

    if( CreateBasicResources() )
    {
      DefaultSettings();

      /* check if something goes wrong (ENV error etc.) */
      if( main_retval == RETURN_OK )
      {
/* Workbench */
        if( ac == 0L )
        {
          wbstartup = (struct WBStartup *)av;

          numArgs = wbstartup -> sm_NumArgs;
          wbarg   = wbstartup -> sm_ArgList;

          if( *(wbarg[ 0 ] . wa_Name) )
          {
            if( wbarg[ 0 ] . wa_Lock )
            {
              oldToolLock = CurrentDir( (wbarg[ 0 ] . wa_Lock) );
            }

            if( tooldobj = GetDiskObjectNew( (wbarg[ 0 ] . wa_Name) ) )
            {
              /* two possible cases when started from workbench ... */
              if( numArgs < 2L )
              {
                /* ... first case, only our tool icon is given, create one project here */

                ScanToolTypes( (STRPTR *)(tooldobj -> do_ToolTypes) );

                /* check if something goes wrong (parsing error, etc.) */
                if( main_retval == RETURN_OK )
                {
                  RunTool();
                }

                FreeInitProjectResult();
              }
              else
              {
                /* ... second case, a couple of project icons are given, multiple projects will start from here */
                for( x = 1L ; x < numArgs ; x++ )
                {
                  if( wbarg[ x ] . wa_Lock )
                  {
                    oldProjectLock = CurrentDir( (wbarg[ x ] . wa_Lock) );
                  }

                  if( *(wbarg[ x ] . wa_Name) )
                  {
                    if( projectdobj = GetDiskObject( (wbarg[ x ] . wa_Name) ) )
                    {
                      ScanToolTypes( (STRPTR *)(tooldobj -> do_ToolTypes) );
                      ScanToolTypes( (STRPTR *)(projectdobj -> do_ToolTypes) );

                      /* check if something goes wrong (parsing error, etc.) */
                      if( main_retval == RETURN_OK )
                      {
                        RunTool();
                      }

                      FreeInitProjectResult();
                      DefaultSettings();

                      FreeDiskObject( projectdobj );
                    }
                  }

                  if( wbarg[ x ] . wa_Lock )
                  {
                    CurrentDir( oldProjectLock );
                  }
                }
              }

              FreeDiskObject( tooldobj );
            }

            if( wbarg[ 0 ] . wa_Lock )
            {
              CurrentDir( oldToolLock );
            }
          }
        }
        else
        {
/* CLI/Shell */
          if( startuprda = ReadArgs( STARTUP_TEMPLATE, (LONG *)(&result), NULL ) )
          {
            /* did we get a CTRL_C signal ? */
            if( !CheckSignal( SIGBREAKF_CTRL_C ) )
            {
              ScanRDA();

              /* check if something goes wrong (parsing error, etc.) */
              if( main_retval == RETURN_OK )
              {
                RunTool();
              }

              FreeInitProjectResult();
            }
            else
            {
              main_retval2 = ERROR_BREAK;
              main_retval  = RETURN_WARN;
            }

            FreeArgs( startuprda );
          }
          else
          {
            main_retval2 = IoErr();
            main_retval  = RETURN_FAIL;
          }
        }
      }

      DeleteBasicResources();
    }

    PrintFault( main_retval2, NAME );

    SetIoErr( main_retval2 );

    return( main_retval );
}


static
void DefaultSettings( void )
{
    ClearRDA();

    /* fill project fields with NULL && result field with the default values */

    memset( (void *)(&project), 0, sizeof( project ) );

    project . maxtime      = 600L; /* wait at least 10 minutes */
    project . maxresponses = 20L;  /* 20 entries should be enouth for small networks */

    if( (((struct Process *)(SysBase -> ThisTask)) -> pr_CLI) == NULL )
    {
      result . window = "CON:////" NAME "/auto/wait/close/inactive";
    }

    ReadENVPrefs();
}


static
void ReadENVPrefs( void )
{
    struct RDArgs envvarrda =
    {
      NULL,
      256L,
      0L,
      0L,
      NULL,
      0L,
      NULL,
      RDAF_NOPROMPT
    };

    TEXT varbuff[ 258 ];

    envvarrda . RDA_Source . CS_Buffer = varbuff;

    if( GetVar( NAME, varbuff, 256L, 0UL ) != (-1L) )
    {
      strcat( varbuff, "\n" );

      if( ReadArgs( ENV_TEMPLATE, (LONG *)(&result), (&envvarrda) ) )
      {
        ScanRDA();

        FreeArgs( (&envvarrda) );
      }
      else
      {
        main_retval2 = IoErr();
        main_retval  = RETURN_FAIL;
      }
    }
}


static
void ClearRDA( void )
{
    /* fill result with NULL */
    memset( (void *)(&result), 0, sizeof( result ) );
}


static
void ScanRDA( void )
{
    /* scan result field and store the values in project */
/* ipaddr */
    if( result . ipaddr . query )
    {
      project . query_ipaddr = 1UL;
    }

    if( result . ipaddr . match )
    {
      if( !IP2ULONG( (STRPTR)(result . ipaddr . match), (&(project . match_ipaddr)) ) )
      {
        main_retval2 = ERROR_BAD_NUMBER;
        main_retval  = RETURN_FAIL;
      }
    }

/* realms */
    if( result . realms . query )
    {
      project . query_realms = 1UL;
    }

    if( result . realms . match )
    {
      UpdateString( (&(project . match_realm)), (STRPTR)(result . realms . match) );
    }

/* hostname */
    if( result . hostname . query )
    {
      project . query_hostname = 1UL;
    }

    if( result . hostname . match )
    {
      UpdateString( (&(project . match_hostname)), (STRPTR)(result . hostname . match) );
    }

/* service */
    if( result . service . query )
    {
      project . query_service = 1UL;
    }

    if( result . service . match )
    {
      UpdateString( (&(project . match_service)), (STRPTR)(result . service . match) );
    }

/* entity */
    if( result . entity . query )
    {
      project . query_entity = 1UL;
    }

    if( result . entity . match )
    {
      UpdateString( (&(project . match_entity)), (STRPTR)(result . entity. match) );
    }

/* owner */
    if( result . owner . query )
    {
      project . query_owner = 1UL;
    }

    if( result . owner . match )
    {
      UpdateString( (&(project . match_owner)), (STRPTR)(result . owner . match) );
    }

/* attnflags */
    if( result . attnflags . query )
    {
      project . query_attnflags = 1UL;
    }

    if( result . attnflags . match )
    {
      project . match_attnflags = GetExecAttnFlags( (STRPTR)(result . attnflags . match) );
    }

/* chiprevbits */
    if( result . chiprevbits . query )
    {
      project . query_chiprevbits = 1UL;
    }

    if( result . chiprevbits . match )
    {
      project . match_chiprevbits = GetChipRevBitsFlags( (STRPTR)(result . chiprevbits . match) );
    }

/* maxfastmem */
    if( result . maxfastmem . query )
    {
      project . query_maxfastmem = 1UL;
    }

    if( result . maxfastmem . match )
    {
      project . match_maxfastmem = *((LONG *)(result . maxfastmem . match));
    }

/* availfastmem */
    if( result . availfastmem . query )
    {
      project . query_availfastmem = 1UL;
    }

    if( result . availfastmem . match )
    {
      project . match_availfastmem = *((LONG *)(result . availfastmem . match));
    }

/* maxchipmem */
    if( result . maxchipmem . query )
    {
      project . query_maxchipmem = 1UL;
    }

    if( result . maxchipmem . match )
    {
      project . match_maxchipmem = *((LONG *)(result . maxchipmem . match));
    }

/* availchipmem */
    if( result . availchipmem . query )
    {
      project . query_availchipmem = 1UL;
    }

    if( result . maxchipmem . match )
    {
      project . match_availchipmem = *((LONG *)(result . maxchipmem . match));
    }

/* kickversion */
    if( result . kickversion . query )
    {
      project . query_kickversion = 1UL;
    }

    if( result . kickversion . match )
    {
      project . match_kickversion = *((LONG *)(result . kickversion . match));
    }

/* wbversion */
    if( result . wbversion . query )
    {
      project . query_wbversion = 1UL;
    }

    if( result . wbversion . match )
    {
      project . match_wbversion = *((LONG *)(result . wbversion . match));
    }

/* nipcversion */
    if( result . nipcversion . query )
    {
      project . query_nipcversion = 1UL;
    }

    if( result . nipcversion . match )
    {
      project . match_nipcversion = *((LONG *)(result . nipcversion . match));
    }

    if( result . maxtime )
    {
      project . maxtime = *(result . maxtime);
    }

    if( result . maxresponses )
    {
      project . maxresponses = *(result . maxresponses);
    }

    if( result . dump )
    {
      project . dump = 1L;
    }

    if( result . window )
    {
      UpdateString( (&(project . window)), (result . window) );
    }

    ClearRDA();
}


static
void ScanToolTypes( STRPTR *tt )
{
    STRPTR s;

    /* scan tooltypes here */

/* ipaddr */
    if( s = FindToolType( tt, "QUERY_IPADDR" ) )
      result . ipaddr . query = (long *)s;

    if( s = FindToolType( tt, "MATCH_IPADDR" ) )
      result . ipaddr . match = (APTR)s;

/* realms */
    if( s = FindToolType( tt, "QUERY_REALMS" ) )
      result . realms . query = (long *)s;

    if( s = FindToolType( tt, "MATCH_REALM" ) )
      result . realms . match = (APTR)s;

/* hostname */
    if( s = FindToolType( tt, "QUERY_HOSTNAME" ) )
      result . hostname . query = (long *)s;

    if( s = FindToolType( tt, "MATCH_HOSTNAME" ) )
      result . hostname . match = (APTR)s;

/* service */
    if( s = FindToolType( tt, "QUERY_SERVICE" ) )
      result . service . query = (long *)s;

    if( s = FindToolType( tt, "MATCH_SERVICE" ) )
      result . service . match = (APTR)s;

/* entity */
    if( s = FindToolType( tt, "QUERY_ENTITY" ) )
      result . entity . query = (long *)s;

    if( s = FindToolType( tt, "MATCH_ENTITY" ) )
      result . entity . match = (APTR)s;

/* owner */
    if( s = FindToolType( tt, "QUERY_OWNER" ) )
      result . owner . query = (long *)s;

    if( s = FindToolType( tt, "MATCH_OWNER" ) )
      result . owner . match = (APTR)s;

/* attnflags */
    if( s = FindToolType( tt, "QUERY_ATTNFLAGS" ) )
      result . attnflags . query = (long *)s;

    if( s = FindToolType( tt, "MATCH_ATTNFLAGS" ) )
      result . attnflags . match = (APTR)s;

/* chiprevbits */
    if( s = FindToolType( tt, "QUERY_CHIPREVBITS" ) )
      result . chiprevbits . query = (long *)s;

    if( s = FindToolType( tt, "MATCH_CHIPREVBITS" ) )
      result . chiprevbits . match = (APTR)s;

/* maxfastmem */
    if( s = FindToolType( tt, "QUERY_MAXFASTMEM" ) )
      result . maxfastmem . query = (long *)s;

    if( s = FindToolType( tt, "MATCH_MAXFASTMEM" ) )
      (void)StrToLong( s, (LONG *)(&(project . match_maxfastmem)) );

/* availfastmem */
    if( s = FindToolType( tt, "QUERY_AVAILFASTMEM" ) )
      result . availfastmem . query = (long *)s;

    if( s = FindToolType( tt, "MATCH_AVAILFASTMEM" ) )
      (void)StrToLong( s, (LONG *)(&(project . match_availfastmem)) );

/* maxchipmem */
    if( s = FindToolType( tt, "QUERY_MAXCHIPMEM" ) )
      result . maxchipmem . query = (long *)s;

    if( s = FindToolType( tt, "MATCH_MAXCHIPMEM" ) )
      (void)StrToLong( s, (LONG *)(&(project . match_maxchipmem)) );

/* availchipmem */
    if( s = FindToolType( tt, "QUERY_AVAILCHIPMEM" ) )
      result . availchipmem . query = (long *)s;

    if( s = FindToolType( tt, "MATCH_AVAILCHIPMEM" ) )
      (void)StrToLong( s, (LONG *)(&(project . match_availchipmem)) );

/* kickversion */
    if( s = FindToolType( tt, "QUERY_KICKVERSION" ) )
      result . kickversion . query = (long *)s;

    if( s = FindToolType( tt, "MATCH_KICKVERSION" ) )
      (void)StrToLong( s, (LONG *)(&(project . match_kickversion)) );

/* wbversion */
    if( s = FindToolType( tt, "QUERY_WBVERSION" ) )
      result . wbversion . query = (long *)s;

    if( s = FindToolType( tt, "MATCH_WBVERSION" ) )
      (void)StrToLong( s, (LONG *)(&(project . match_wbversion)) );

/* nipcversion */
    if( s = FindToolType( tt, "QUERY_NIPCVERSION" ) )
      result . nipcversion . query = (long *)s;

    if( s = FindToolType( tt, "MATCH_NIPCVERSION" ) )
      (void)StrToLong( s, (LONG *)(&(project . match_nipcversion)) );

/* maxtime */
    if( s = FindToolType( tt, "MAXTIME" ) )
      (void)StrToLong( s, (LONG *)(&(project . maxtime)) );

/* maxresponses */
    if( s = FindToolType( tt, "MAXRESPONSES" ) )
      (void)StrToLong( s, (LONG *)(&(project . maxresponses)) );

    if( s = FindToolType( tt, "NUM" ) )
      (void)StrToLong( s, (LONG *)(&(project . maxresponses)) );

/* dump mode */
    if( s = FindToolType( tt, "DUMP" ) )
      result . dump = (long *)s;

    if( s = FindToolType( tt, "ALL" ) )
      result . dump = (long *)s;

/* window */
    if( s = FindToolType( tt, "WINDOW" ) )
      result . window = s;

    if( s = FindToolType( tt, "TO" ) )
      result . window = s;

    ScanRDA();
}


static
void FreeInitProjectResult( void )
{
    FreeString( (project . match_realm) );
    FreeString( (project . match_hostname) );
    FreeString( (project . match_service) );
    FreeString( (project . match_owner) );
    FreeString( (project . window) );
}


static
BOOL CreateBasicResources( void )
{
    if( OpenLibStuff() )
    {
      if( StringPool = LibCreatePool( (MEMF_PUBLIC | MEMF_CLEAR), 256UL, 256UL ) )
      {
        return( TRUE );

#ifdef COMMENTED_OUT
        LibDeletePool( StringPool );
#endif /* COMMENTED_OUT */
      }
      else
      {
        main_retval2 = ERROR_NO_FREE_STORE;
        main_retval  = RETURN_FAIL;
      }

      CloseLibStuff();
    }
    else
    {
      main_retval2 = ERROR_OBJECT_NOT_FOUND;
      main_retval  = RETURN_FAIL;
    }

    return( FALSE );
}


static
void DeleteBasicResources( void )
{
    LibDeletePool( StringPool );
    CloseLibStuff();
}


static
BOOL OpenLibStuff( void )
{
    UtilityBase = IconBase = NIPCBase = NULL;

    if( IntuitionBase = OpenLibrary( "intuition.library", 37UL ) )
    {
      AttemptOpenLibrary( (&UtilityBase), NAME, UTILITYNAME,     37UL );
      AttemptOpenLibrary( (&IconBase),    NAME, ICONNAME,        37UL );
      AttemptOpenLibrary( (&NIPCBase),    NAME, "nipc.library",  39UL );

      if( UtilityBase && IconBase && NIPCBase )
      {
        return( TRUE );
      }

      CloseLibStuff();
    }

    return( FALSE );
}


static
void CloseLibStuff( void )
{
    CloseLibrary( NIPCBase );
    CloseLibrary( IconBase );
    CloseLibrary( UtilityBase );
    CloseLibrary( IntuitionBase );
}


void AttemptOpenLibrary( struct Library **library, STRPTR title, STRPTR libname, ULONG libversion )
{
    struct EasyStruct LibNotFoundES,
                      LibWrongVersionES;

    LibNotFoundES . es_StructSize   = sizeof( struct EasyStruct );
    LibNotFoundES . es_Flags        = 0UL;
    LibNotFoundES . es_Title        = title;
    LibNotFoundES . es_TextFormat   = "%s\nnot found.";
    LibNotFoundES . es_GadgetFormat = "Retry|Cancel";

    LibWrongVersionES . es_StructSize   = sizeof( struct EasyStruct );
    LibWrongVersionES . es_Flags        = 0UL;
    LibWrongVersionES . es_Title        = title;
    LibWrongVersionES . es_TextFormat   = "Requires at least\n%s version %lu";
    LibWrongVersionES . es_GadgetFormat = "Cancel";

    if( (*library) == NULL )
    {
      for( ;; )
      {
        /* attemp to open shared library */
        (*library) = OpenLibrary( libname, 0UL );

        if( (*library) )
        {
          /* check if we got at least "libversion" version of shared library */
          if( ((*library) -> lib_Version) < libversion )
          {
            (void)EasyRequest( NULL, (&LibWrongVersionES), 0UL, libname, libversion );

            CloseLibrary( (*library) );
            (*library) = NULL;
          }

          break;
        }

        /* prompt the user */
        if( EasyRequest( NULL, (&LibNotFoundES), 0UL, libname ) == 0L )
        {
          /* user canceled */
          break;
        }
      }
    }
}


STRPTR StringSave( STRPTR s )
{
    return( StringSavePooled( StringPool, s ) );
}


STRPTR StringSavePooled( APTR pool, STRPTR s )
{
    STRPTR saved;

    if( s )
    {
      if( (saved = AllocVecPooled( pool, (ULONG)strlen( s ) ) ) )
      {
        /* Copy string into new buffer ... */
        strcpy( saved, s );

        return( saved );
      }
    }

    return( NULL );
}


void FreeString( STRPTR s )
{
    FreeStringPooled( StringPool, s );
}


void FreeStringPooled( APTR pool, STRPTR s )
{
    if( s )
    {
      FreeVecPooled( pool, s );
    }
}


STRPTR UpdateString( STRPTR *storage, STRPTR newstring )
{
    STRPTR s;

    s = NULL;

    if( storage )
    {
      FreeString( (*storage) );

      s = (*storage) = StringSave( newstring );
    }

    return( s );
}


APTR AllocVecPooled( APTR poolheader, ULONG memsize )
{
    ULONG *memory;

    memsize += sizeof( ULONG );

    if( memory = (ULONG *)LibAllocPooled( poolheader, memsize ) )
    {
      *memory = memsize;

      memory++;
    }

    return( (APTR)memory );
}


void FreeVecPooled( APTR poolheader, APTR mem )
{
    ULONG *memory;

    if( mem )
    {
      memory = (ULONG *)mem;

      memory--;

      LibFreePooled( poolheader, memory, (*memory) );
    }
}


static
void RunTool( void )
{
    struct NIPCInquiryHook nipcdispatcherhook;

    if( nipcdispatcherhook . nih_MsgPort = CreateMsgPort() )
    {
      nipcdispatcherhook . nih_Signal = (LONG)AllocSignal( (-1) );

      if( (nipcdispatcherhook . nih_Signal) != (-1L) )
      {
        BPTR fh;

        /* setup inquiry hook */
        nipcdispatcherhook . nih_Hook . h_Entry   = (HOOKFUNC)dispatchNIPCInquiry;
#ifdef BE_RESIDENTABLE
        nipcdispatcherhook . nih_REGA4            = (void *)getreg( REG_A4 );
#endif /* BE_RESIDENTABLE */
        nipcdispatcherhook . nih_DispatcherRetval = 1UL;
        nipcdispatcherhook . nih_Error            = 0L;
        nipcdispatcherhook . nih_TrackedMessages  = NULL;
        nipcdispatcherhook . nih_DumpMessages     = (BOOL)(project . dump);

        if( project . window )
        {
          fh = Open( (project . window), MODE_READWRITE );
        }
        else
        {
          fh = Output();
        }

        if( fh )
        {
          struct TagItem NIPCInquiryTags[ 31 ];

          /* queries... */
          NIPCInquiryTags[  0 ] . ti_Tag  = XTAG( (project . query_ipaddr),       QUERY_IPADDR );         /* Get machine's IP address (Not guranteed to be supported) */
          NIPCInquiryTags[  0 ] . ti_Data = 0UL;
          NIPCInquiryTags[  1 ] . ti_Tag  = XTAG( (project . query_realms),       QUERY_REALMS );         /* Get names of all known realms */
          NIPCInquiryTags[  1 ] . ti_Data = 0UL;
          NIPCInquiryTags[  2 ] . ti_Tag  = XTAG( (project . query_hostname),     QUERY_HOSTNAME );       /* Get hostname */
          NIPCInquiryTags[  2 ] . ti_Data = 0UL;
          NIPCInquiryTags[  3 ] . ti_Tag  = XTAG( (project . query_service),      QUERY_SERVICE );        /* Get list of services */
          NIPCInquiryTags[  3 ] . ti_Data = 0UL;
          NIPCInquiryTags[  4 ] . ti_Tag  = XTAG( (project . query_entity),       QUERY_ENTITY );         /* Get list of entities */
          NIPCInquiryTags[  4 ] . ti_Data = 0UL;
          NIPCInquiryTags[  5 ] . ti_Tag  = XTAG( (project . query_owner),        QUERY_OWNER );          /* Get machines's owner */
          NIPCInquiryTags[  5 ] . ti_Data = 0UL;
#ifdef COMMENTED_OUT
          NIPCInquiryTags[  6 ] . ti_Tag  = QUERY_MACHDESC;                                               /* For future use, currently ignored */
          NIPCInquiryTags[  6 ] . ti_Data = 0UL;
#endif /* COMMENTED_OUT */
          NIPCInquiryTags[  6 ] . ti_Tag  = XTAG( (project . query_attnflags),    QUERY_ATTNFLAGS );      /* Get (ExecBase -> AttnFlags) */
          NIPCInquiryTags[  6 ] . ti_Data = 0UL;
#ifdef COMMENTED_OUT
          NIPCInquiryTags[  7 ] . ti_Tag  = QUERY_LIBVERSION;                                             /* Get a specific library version number (see nipc.library autodocs) */
          NIPCInquiryTags[  7 ] . ti_Data = 0UL;
#endif /* COMMENTED_OUT */
          NIPCInquiryTags[  7 ] . ti_Tag  = XTAG( (project . query_chiprevbits),  QUERY_CHIPREVBITS );    /* Get (GfxBase -> ChipRevBits) */
          NIPCInquiryTags[  7 ] . ti_Data = 0UL;
          NIPCInquiryTags[  8 ] . ti_Tag  = XTAG( (project . query_maxfastmem),   QUERY_MAXFASTMEM );     /* Get amount of Fast ram on a system */
          NIPCInquiryTags[  8 ] . ti_Data = 0UL;
          NIPCInquiryTags[  9 ] . ti_Tag  = XTAG( (project . query_availfastmem), QUERY_AVAILFASTMEM );   /* Get amount of available fast mem */
          NIPCInquiryTags[  9 ] . ti_Data = 0UL;
          NIPCInquiryTags[ 10 ] . ti_Tag  = XTAG( (project . query_maxchipmem),   QUERY_MAXCHIPMEM );     /* Get amount if Chip ram on a system */
          NIPCInquiryTags[ 10 ] . ti_Data = 0UL;
          NIPCInquiryTags[ 11 ] . ti_Tag  = XTAG( (project . query_availchipmem), QUERY_AVAILCHIPMEM );   /* Get amount of available Chip ram */
          NIPCInquiryTags[ 11 ] . ti_Data = 0UL;
          NIPCInquiryTags[ 12 ] . ti_Tag  = XTAG( (project . query_kickversion),  QUERY_KICKVERSION );    /* Get KickStart Version */
          NIPCInquiryTags[ 12 ] . ti_Data = 0UL;
          NIPCInquiryTags[ 13 ] . ti_Tag  = XTAG( (project . query_wbversion),    QUERY_WBVERSION );      /* Get Workbench Version */
          NIPCInquiryTags[ 13 ] . ti_Data = 0UL;
          NIPCInquiryTags[ 14 ] . ti_Tag  = XTAG( (project . query_nipcversion),  QUERY_NIPCVERSION );    /* Get nipc.library Version */
          NIPCInquiryTags[ 14 ] . ti_Data = 0UL;

            /* matches... */
          NIPCInquiryTags[ 15 ] . ti_Tag  = XTAG( (project . match_ipaddr),       MATCH_IPADDR );         /* Answer machine's IP address (Not guranteed to be supported) */
          NIPCInquiryTags[ 15 ] . ti_Data = (project . match_ipaddr);
          NIPCInquiryTags[ 16 ] . ti_Tag  = XTAG( (project . match_realm),        MATCH_REALM );          /* Answer names of all known realms */
          NIPCInquiryTags[ 16 ] . ti_Data = (ULONG)(project . match_realm);
          NIPCInquiryTags[ 17 ] . ti_Tag  = XTAG( (project . match_hostname),     MATCH_HOSTNAME );       /* Answer hostname */
          NIPCInquiryTags[ 17 ] . ti_Data = (ULONG)(project . match_hostname);
          NIPCInquiryTags[ 18 ] . ti_Tag  = XTAG( (project . match_service),      MATCH_SERVICE );        /* Answer list of services */
          NIPCInquiryTags[ 18 ] . ti_Data = (ULONG)(project . match_service);
          NIPCInquiryTags[ 19 ] . ti_Tag  = XTAG( (project . match_entity),       MATCH_ENTITY );         /* Answer list of entities */
          NIPCInquiryTags[ 19 ] . ti_Data = (ULONG)(project . match_entity);
          NIPCInquiryTags[ 20 ] . ti_Tag  = XTAG( (project . match_owner),        MATCH_OWNER );          /* Answer machines's owner */
          NIPCInquiryTags[ 20 ] . ti_Data = (ULONG)(project . match_owner);
#ifdef COMMENTED_OUT
          NIPCInquiryTags[ 21 ] . ti_Tag  = MATCH_MACHDESC;                                               /* For future use, currently ignored */
          NIPCInquiryTags[ 21 ] . ti_Data = 0UL;
#endif /* COMMENTED_OUT */
          NIPCInquiryTags[ 21 ] . ti_Tag  = XTAG( (project . match_attnflags),    MATCH_ATTNFLAGS );      /* Answer (ExecBase -> AttnFlags) */
          NIPCInquiryTags[ 21 ] . ti_Data = (project . match_attnflags);
#ifdef COMMENTED_OUT
          NIPCInquiryTags[ 22 ] . ti_Tag  = MATCH_LIBVERSION;                                             /* Answer a specific library version number (see nipc.library autodocs) */
          NIPCInquiryTags[ 22 ] . ti_Data = 0UL;
#endif /* COMMENTED_OUT */
          NIPCInquiryTags[ 22 ] . ti_Tag  = XTAG( (project . match_chiprevbits),  MATCH_CHIPREVBITS );    /* Answer (GfxBase -> ChipRevBits) */
          NIPCInquiryTags[ 22 ] . ti_Data = (project . match_chiprevbits);
          NIPCInquiryTags[ 23 ] . ti_Tag  = XTAG( (project . match_maxfastmem),   MATCH_MAXFASTMEM );     /* Answer amount of Fast ram on a system */
          NIPCInquiryTags[ 23 ] . ti_Data = (project . match_maxfastmem);
          NIPCInquiryTags[ 24 ] . ti_Tag  = XTAG( (project . match_availfastmem), MATCH_AVAILFASTMEM );   /* Answer amount of available fast mem */
          NIPCInquiryTags[ 24 ] . ti_Data = (project . match_availchipmem);
          NIPCInquiryTags[ 25 ] . ti_Tag  = XTAG( (project . match_maxchipmem),   MATCH_MAXCHIPMEM );     /* Answer amount if Chip ram on a system */
          NIPCInquiryTags[ 25 ] . ti_Data = (project . match_maxchipmem);
          NIPCInquiryTags[ 26 ] . ti_Tag  = XTAG( (project . match_availchipmem), MATCH_AVAILCHIPMEM );   /* Answer amount of available Chip ram */
          NIPCInquiryTags[ 26 ] . ti_Data = (project . match_availchipmem);
          NIPCInquiryTags[ 27 ] . ti_Tag  = XTAG( (project . match_kickversion),  MATCH_KICKVERSION );    /* Answer KickStart Version */
          NIPCInquiryTags[ 27 ] . ti_Data = (project . match_kickversion);
          NIPCInquiryTags[ 28 ] . ti_Tag  = XTAG( (project . match_wbversion),    MATCH_WBVERSION );      /* Answer Workbench Version */
          NIPCInquiryTags[ 28 ] . ti_Data = (project . match_wbversion);
          NIPCInquiryTags[ 29 ] . ti_Tag  = XTAG( (project . match_nipcversion),  MATCH_NIPCVERSION );    /* Answer nipc.library Version */
          NIPCInquiryTags[ 29 ] . ti_Data = (project . match_nipcversion);
          NIPCInquiryTags[ 30 ] . ti_Tag  = TAG_DONE;
          NIPCInquiryTags[ 30 ] . ti_Data = 0UL;

          if( NIPCInquiryA( (&(nipcdispatcherhook . nih_Hook)), (ULONG)(project . maxtime), (ULONG)(project . maxresponses), NIPCInquiryTags ) )
          {
            ULONG waitsignals,
                  signals;

            waitsignals = (1UL << (nipcdispatcherhook . nih_MsgPort -> mp_SigBit)) |
                          (1UL << (nipcdispatcherhook . nih_Signal))               |
                          SIGBREAKF_CTRL_C;

            /* now we're waiting until the hook will signal us */
            for( ;; )
            {
              signals = Wait( waitsignals );

              if( signals & (1UL << (nipcdispatcherhook . nih_Signal)) )
              {
                break;
              }

              if( signals & SIGBREAKF_CTRL_C )
              {
                /* next time the hook will use this, it will got the break code */
                nipcdispatcherhook . nih_DispatcherRetval = 0UL;

                main_retval2 = ERROR_BREAK;
                main_retval  = RETURN_WARN;
              }

              if( signals & (1UL << (nipcdispatcherhook . nih_MsgPort -> mp_SigBit)) )
              {
                ProcessQueryMsgPort( (nipcdispatcherhook . nih_MsgPort), fh );
              }
            }

            ProcessQueryMsgPort( (nipcdispatcherhook . nih_MsgPort), fh );

            DisposeQueryMessageList( (&nipcdispatcherhook) );

            /* check for errors inside hook */
            if( (main_retval == RETURN_OK) && (nipcdispatcherhook . nih_Error) )
            {
              main_retval2 = nipcdispatcherhook . nih_Error;
              main_retval  = RETURN_ERROR;
            }
          }
          else
          {
            /* NIPCInquiryA failed */
            main_retval2 = IoErr();
            main_retval  = RETURN_FAIL;
          }

          if( fh != Output() )
          {
            if( !Close( fh ) )
            {
              /* error on Close */
              main_retval2 = IoErr();
              main_retval  = RETURN_ERROR;
            }
          }
        }
        else
        {
          /* can't open file */
          main_retval2 = IoErr();
          main_retval  = RETURN_FAIL;
        }

        FreeSignal( (nipcdispatcherhook . nih_Signal) );
      }
      else
      {
        /* can't alloc signal */
        main_retval2 = ERROR_NO_FREE_STORE;
        main_retval  = RETURN_FAIL;
      }

      DeleteMsgPort( (nipcdispatcherhook . nih_MsgPort) );
    }
    else
    {
      /* can't create msgport */
      main_retval2 = ERROR_NO_FREE_STORE;
      main_retval  = RETURN_FAIL;
    }
}


void ProcessQueryMsgPort( struct MsgPort *mp, BPTR fh )
{
    struct QueryMessage *qmsg;

    while( qmsg = (struct QueryMessage *)GetMsg( mp ) )
    {
      PrintNIPCInquiryTagList( (qmsg -> qmsg_Tags), fh );
    }
}


void DisposeQueryMessageList( struct NIPCInquiryHook *nih )
{
    struct QueryMessage *worknode,
                        *nextnode;

    worknode = nih -> nih_TrackedMessages;

    while( worknode )
    {
      nextnode = (struct QueryMessage *)(worknode -> qmsg_TrackLink);

      LibDeletePool( (worknode -> qmsg_Pool) );

      worknode = nextnode;
    }
}


BOOL QueryMessageInList( struct NIPCInquiryHook *nih, struct TagItem *tags )
{
    struct QueryMessage *worknode,
                        *nextnode;

    worknode = nih -> nih_TrackedMessages;

    while( worknode )
    {
      nextnode = (struct QueryMessage *)(worknode -> qmsg_TrackLink);

      if( SameQueryMessages( (worknode -> qmsg_Tags), tags ) )
      {
        return( TRUE );
      }

      worknode = nextnode;
    }

    return( FALSE );
}


DISPATCHERFLAGS
static
ULONG dispatchNIPCInquiry( REGA0 struct NIPCInquiryHook *nih, REGA2 struct Task *o, REGA1 struct TagItem *msg )
{
    ULONG retval;

#ifdef BE_RESIDENTABLE
    putreg( REG_A4, (long)(nih -> nih_REGA4) );
#endif /* BE_RESIDENTABLE */

    retval = nih -> nih_DispatcherRetval;

    if( !SendQueryMessage( nih, msg ) )
    {
      retval = 0UL;
    }

    if( retval == 0UL )
    {
      /* send our signal */
      Signal( o, (1UL << (nih -> nih_Signal)) );
    }

    return( retval );
}


BOOL SendQueryMessage( struct NIPCInquiryHook *nih, struct TagItem *query_ti )
{
    if( nih && query_ti )
    {
      APTR pool;

      if( (nih -> nih_DumpMessages) == FALSE )
      {
        if( QueryMessageInList( nih, query_ti ) )
        {
          return( TRUE );
        }
      }

      if( pool = LibCreatePool( MEMF_PUBLIC, 512UL, 512UL ) )
      {
        struct QueryMessage *qmsg;

        if( qmsg = (struct QueryMessage *)LibAllocPooled( pool, sizeof( struct QueryMessage ) ) )
        {
          if( qmsg -> qmsg_Tags = CloneNIPCInquiryTagList( query_ti, pool ) )
          {
            struct QueryMessage *dummy;

            /* init message */
            qmsg -> qmsg_Message . mn_Node . ln_Type = NT_UNKNOWN;
            qmsg -> qmsg_Message . mn_ReplyPort      = NULL;
            qmsg -> qmsg_Message . mn_Length         = (UWORD)sizeof( struct QueryMessage );
            qmsg -> qmsg_Pool                        = pool;

            /* add msg to track list */
            dummy                      = nih -> nih_TrackedMessages;
            nih -> nih_TrackedMessages = qmsg;
            qmsg -> qmsg_TrackLink     = (char *)dummy;

            /* pass message to application process */
            PutMsg( (nih -> nih_MsgPort), (&(qmsg -> qmsg_Message)) );

            return( TRUE );
          }
        }

        LibDeletePool( pool );
      }

      /* currently only one hook error: no mem */
      nih -> nih_Error = ERROR_NO_FREE_STORE;
    }
  
    return( FALSE );
}

  
struct TagItem *CloneNIPCInquiryTagList( struct TagItem *msg, APTR pool )
{
    if( msg && pool )
    {
      struct TagItem *tags,
                     *tstate,
                     *ti;
  
      if( tags = CloneTagItemsPooled( msg, pool ) )
      {
        tstate = tags;
  
        while( ti = NextTagItem( (&tstate) ) )
        {
          switch( ti -> ti_Tag )
          {
#ifdef COMMENTED_OUT
            case QUERY_MACHDESC:               /* For future use, currently ignored */
            case QUERY_LIBVERSION:             /* Get a specific library version number (see nipc.library autodocs) */
                break;
#endif /* COMMENTED_OUT */

            case QUERY_REALMS:                 /* Get names of all known realms */
            case QUERY_HOSTNAME:               /* Get hostname */
            case QUERY_SERVICE:                /* Get list of services */
            case QUERY_ENTITY:                 /* Get list of entities */
            case QUERY_OWNER:                  /* Get machines's owner */
            case MATCH_REALM:                  /* Realm this machine belongs to */
            case MATCH_HOSTNAME:               /* Matching hostname */
            {
                /* Copy string */
                if( ti -> ti_Data )
                {
                  if( (ti -> ti_Data = (ULONG)StringSavePooled( pool, (STRPTR)(ti -> ti_Data) )) == NULL )
                  {
                    return( NULL );
                  }
                }
            }
                break;

            case QUERY_IPADDR:                 /* Get machine's IP address (Not guranteed to be supported) */
            case QUERY_ATTNFLAGS:              /* Get (ExecBase -> AttnFlags) */
            case QUERY_CHIPREVBITS:            /* Get (GfxBase -> ChipRevBits) */
            case QUERY_MAXFASTMEM:             /* Get amount of Fast ram on a system */
            case QUERY_AVAILFASTMEM:           /* Get amount of available fast mem */
            case QUERY_MAXCHIPMEM:             /* Get amount if Chip ram on a system */
            case QUERY_AVAILCHIPMEM:           /* Get amount of available Chip ram */
            case QUERY_KICKVERSION:            /* Get KickStart Version */
            case QUERY_WBVERSION:              /* Get Workbench Version */
            case QUERY_NIPCVERSION:            /* Get nipc.library Version */
            default:
                break;
          }
        }

        return( tags );
      }
    }

    return( NULL );
}


BOOL SameQueryMessages( struct TagItem *msg1, struct TagItem *msg2 )
{
    if( msg1 && msg2 )
    {
      struct TagItem *tstate1,
                     *ti1,
                     *tstate2,
                     *ti2;

      tstate1 = msg1;
      tstate2 = msg2;

      while( (ti1 = NextTagItem( (&tstate1) )), (ti2 = NextTagItem( (&tstate2) )),
             (ti1 && ti2) )
      {
        if( (ti1 -> ti_Tag) != (ti2 -> ti_Tag) )
        {
          return( FALSE );
        }

        switch( ti1 -> ti_Tag )
        {
#ifdef COMMENTED_OUT
          case QUERY_MACHDESC:               /* For future use, currently ignored */
          case QUERY_LIBVERSION:             /* Get a specific library version number (see nipc.library autodocs) */
              break;
#endif /* COMMENTED_OUT */

          case QUERY_REALMS:                 /* Get names of all known realms */
          case QUERY_HOSTNAME:               /* Get hostname */
          case QUERY_SERVICE:                /* Get list of services */
          case QUERY_ENTITY:                 /* Get list of entities */
          case QUERY_OWNER:                  /* Get machines's owner */
          case MATCH_REALM:                  /* Realm this machine belongs to */
          case MATCH_HOSTNAME:               /* Matching hostname */
          {
              /* compare strings */
              if( (ti1 -> ti_Data) || (ti2 -> ti_Data) )
              {
                if( (ti1 -> ti_Data) && (ti2 -> ti_Data) )
                {
                  if( !strcmp( (STRPTR)(ti1 -> ti_Data), (STRPTR)(ti2 -> ti_Data) ) )
                  {
                    break;
                  }
                }

                return( FALSE );
              }
          }
              break;

          case QUERY_IPADDR:                 /* Get machine's IP address (Not guranteed to be supported) */
          case QUERY_ATTNFLAGS:              /* Get (ExecBase -> AttnFlags) */
          case QUERY_CHIPREVBITS:            /* Get (GfxBase -> ChipRevBits) */
          case QUERY_MAXFASTMEM:             /* Get amount of Fast ram on a system */
          case QUERY_AVAILFASTMEM:           /* Get amount of available fast mem */
          case QUERY_MAXCHIPMEM:             /* Get amount if Chip ram on a system */
          case QUERY_AVAILCHIPMEM:           /* Get amount of available Chip ram */
          case QUERY_KICKVERSION:            /* Get KickStart Version */
          case QUERY_WBVERSION:              /* Get Workbench Version */
          case QUERY_NIPCVERSION:            /* Get nipc.library Version */
          default:
          {
              /* compare values */
              if( (ti1 -> ti_Data) != (ti2 -> ti_Data) )
              {
                return( FALSE );
              }
          }
              break;
        }
      }

      if( (ti1 == NULL) && (ti2 == NULL) )
      {
        return( TRUE );
      }
    }

    return( FALSE );
}


void PrintNIPCInquiryTagList( struct TagItem *msg, BPTR fh )
{
    if( msg && fh )
    {
      struct TagItem *tstate,
                     *ti;

      tstate = msg;

      (void)FPrintf( fh, "<start taglist>\n" );

      while( ti = NextTagItem( (&tstate) ) )
      {
        ULONG tidata;

        tidata = ti -> ti_Data;

        switch( ti -> ti_Tag )
        {
          case QUERY_IPADDR:                 /* Print machine's IP address (Not guranteed to be supported) */
          {
              (void)FPrintf( fh, "QUERY_IPADDR %lu.%lu.%lu.%lu\n", (ULONG)GETUBYTE( tidata, 3U ),
                                                                   (ULONG)GETUBYTE( tidata, 2U ),
                                                                   (ULONG)GETUBYTE( tidata, 1U ),
                                                                   (ULONG)GETUBYTE( tidata, 0U ) );
          }
              break;

          case QUERY_REALMS:                 /* Print names of all known realms */
          {
              (void)FPrintf( fh, "QUERY_REALMS \"%s\"\n", tidata );
          }
              break;

          case QUERY_HOSTNAME:               /* Print hostname */
          {
              (void)FPrintf( fh, "QUERY_HOSTNAME \"%s\"\n", tidata );
          }
              break;

          case QUERY_SERVICE:                /* Print list of services */
          {
              (void)FPrintf( fh, "QUERY_SERVICE \"%s\"\n", tidata );
          }
              break;

          case QUERY_ENTITY:                 /* Print list of entities */
          {
              (void)FPrintf( fh, "QUERY_ENTITY \"%s\"\n", tidata );
          }
              break;

          case QUERY_OWNER:                  /* Print machines's owner */
          {
              (void)FPrintf( fh, "QUERY_OWNER \"%s\"\n", tidata );
          }
              break;

#ifdef COMMENTED_OUT
          case QUERY_MACHDESC:               /* For future use, currently ignored */
          {
              (void)FPrintf( fh, "", tidata );
          }
              break;
#endif /* COMMENTED_OUT */

          case QUERY_ATTNFLAGS:              /* Print (ExecBase -> AttnFlags) */
          {
              (void)FPrintf( fh, "QUERY_ATTNFLAGS " );
              FPrintExecAttnFlags( fh, tidata );
          }
              break;

#ifdef COMMENTED_OUT
          case QUERY_LIBVERSION:             /* Print a specific library version number (see nipc.library autodocs) */
          {
              (void)FPrintf( fh, "", tidata );
          }
              break;
#endif /* COMMENTED_OUT */

          case QUERY_CHIPREVBITS:            /* Print (GfxBase -> ChipRevBits) */
          {
              (void)FPrintf( fh, "QUERY_CHIPREVBITS " );
              FPrintChipRevBitsFlags( fh, tidata );
          }
              break;

          case QUERY_MAXFASTMEM:             /* Print amount of Fast ram on a system */
          {
              (void)FPrintf( fh, "QUERY_MAXFASTMEM %lu\n", tidata );
          }
              break;

          case QUERY_AVAILFASTMEM:           /* Print amount of available fast mem */
          {
              (void)FPrintf( fh, "QUERY_AVAILFASTMEM %lu\n", tidata );
          }
              break;

          case QUERY_MAXCHIPMEM:             /* Print amount if Chip ram on a system */
          {
              (void)FPrintf( fh, "QUERY_MAXCHIPMAM %lu\n", tidata );
          }
              break;

          case QUERY_AVAILCHIPMEM:           /* Print amount of available Chip ram */
          {
              (void)FPrintf( fh, "QUERY_AVAILCHIPMEM %lu\n", tidata );
          }
              break;

          case QUERY_KICKVERSION:            /* Print KickStart Version */
          {
              (void)FPrintf( fh, "QUERY_KICKVERSION %lu.%lu\n", (ULONG)GETUWORD( tidata, 1U ),
                                                                (ULONG)GETUWORD( tidata, 0U ) );
          }
              break;

          case QUERY_WBVERSION:              /* Print Workbench Version */
          {
              (void)FPrintf( fh, "QUERY_WBVERSION %lu.%lu\n", (ULONG)GETUWORD( tidata, 1U ),
                                                              (ULONG)GETUWORD( tidata, 0U ) );
          }
              break;

          case QUERY_NIPCVERSION:            /* Print nipc.library Version */
          {
              (void)FPrintf( fh, "QUERY_NIPCVERSION %lu.%lu\n", (ULONG)GETUWORD( tidata, 1U ),
                                                                (ULONG)GETUWORD( tidata, 0U ) );
          }
              break;

          case MATCH_REALM:                  /* Print realm this machine belongs to */
          {
              (void)FPrintf( fh, "MATCH_REALM \"%s\"\n", tidata );
          }
              break;

          case MATCH_HOSTNAME:               /* Print matching hostname */
          {
              (void)FPrintf( fh, "MATCH_HOSTNAME \"%s\"\n", tidata );
          }
              break;

          default:
          {
              (void)FPrintf( fh, "<unknown tag> %lx %lx\n", (ti -> ti_Tag), tidata  );
          }
              break;
        }
      }

      (void)FPrintf( fh, "TAG_DONE\n" );
    }
}


ULONG GetExecAttnFlags( STRPTR s )
{
    ULONG retval;

    retval = 0UL;

    if( MatchToolValue( s, "ALL" ) )
      return( (ULONG)(AFF_68010 | AFF_68020 | AFF_68030 | AFF_68040 | AFF_68881 | AFF_68882 | AFF_FPU40) );

    if( MatchToolValue( s, "AFF_68010" ) )
      retval |= AFF_68010;

    if( MatchToolValue( s, "AFF_68020" ) )
      retval |= AFF_68020;

    if( MatchToolValue( s, "AFF_68030" ) )
      retval |= AFF_68030;

    if( MatchToolValue( s, "AFF_68040" ) )
      retval |= AFF_68040;

    if( MatchToolValue( s, "AFF_68881" ) )
      retval |= AFF_68881;

    if( MatchToolValue( s, "AFF_68882" ) )
      retval |= AFF_68882;

    if( MatchToolValue( s, "AFF_FPU40" ) )
      retval |= AFF_FPU40;

    return( retval );
}


void FPrintExecAttnFlags( BPTR fh, ULONG attnflags )
{
    STRPTR seperator;

    seperator = "";

    if( attnflags & AFF_68010 )
    {
      (void)FPrintf( fh, "AFF_68010" );
      seperator = "|";
    }

    if( attnflags & AFF_68020 )
    {
      (void)FPrintf( fh, "%sAFF_68020", seperator );
      seperator = "|";
    }

    if( attnflags & AFF_68030 )
    {
      (void)FPrintf( fh, "%sAFF_68030", seperator );
      seperator = "|";
    }

    if( attnflags & AFF_68040 )
    {
      (void)FPrintf( fh, "%sAFF_68040", seperator );
      seperator = "|";
    }

    if( attnflags & AFF_68881 )
    {
      (void)FPrintf( fh, "%sAFF_68881", seperator );
      seperator = "|";
    }

    if( attnflags & AFF_68882 )
    {
      (void)FPrintf( fh, "%sAFF_68882", seperator );
      seperator = "|";
    }

    if( attnflags & AFF_FPU40 )
    {
      (void)FPrintf( fh, "%sAFF_FPU40", seperator );
    }

    (void)FPrintf( fh, "\n" );
}


ULONG GetChipRevBitsFlags( STRPTR s )
{
    ULONG retval;

    retval = 0UL;

    if( MatchToolValue( s, "ALL" ) )
      return( (ULONG)(GFXF_BIG_BLITS | GFXF_HR_AGNUS | GFXF_HR_DENISE | GFXF_AA_ALICE | GFXF_AA_LISA | GFXF_AA_MLISA) );

    if( MatchToolValue( s, "GFXF_BIG_BLITS" ) )
      retval |= GFXF_BIG_BLITS;

    if( MatchToolValue( s, "GFXF_HR_AGNUS" ) )
      retval |= GFXF_HR_AGNUS;

    if( MatchToolValue( s, "GFXF_HR_DENISE" ) )
      retval |= GFXF_HR_DENISE;

    if( MatchToolValue( s, "GFXF_AA_ALICE" ) )
      retval |= GFXF_AA_ALICE;

    if( MatchToolValue( s, "GFXF_AA_LISA" ) )
      retval |= GFXF_AA_LISA;

    if( MatchToolValue( s, "GFXF_AA_MLISA" ) )
      retval |= GFXF_AA_MLISA;

    return( retval );
}


void FPrintChipRevBitsFlags( BPTR fh, ULONG chiprevbits )
{
    STRPTR seperator;

    seperator = "";

    if( chiprevbits & GFXF_BIG_BLITS )
    {
      (void)FPrintf( fh, "GFXF_BIG_BLITS" );
      seperator = "|";
    }

    if( chiprevbits & GFXF_HR_AGNUS )
    {
      (void)FPrintf( fh, "%sGFXF_HR_AGNUS", seperator );
      seperator = "|";
    }

    if( chiprevbits & GFXF_HR_DENISE )
    {
      (void)FPrintf( fh, "%sGFXF_HR_DENISE", seperator );
      seperator = "|";
    }

    if( chiprevbits & GFXF_AA_ALICE )
    {
      (void)FPrintf( fh, "%sGFXF_AA_ALICE", seperator );
      seperator = "|";
    }

    if( chiprevbits & GFXF_AA_LISA )
    {
      (void)FPrintf( fh, "%sGFXF_AA_LISA", seperator );
      seperator = "|";
    }

    if( chiprevbits & GFXF_AA_MLISA )
    {
      (void)FPrintf( fh, "%sGFXF_AA_MLISA", seperator );
    }

    (void)FPrintf( fh, "\n" );
}


BOOL IP2ULONG( STRPTR ipstr, ULONG *ipaddr )
{
    if( ipstr && ipaddr )
    {
      /* check for invalid chars */
      if( !strcspn( ipstr, "0123456789. \t" ) )
      {
        STRPTR start,      /* start of "word" */
               stop;       /* stop  of "word" */
        size_t size;       /* size of "word"  */
        TEXT   buff[ 16 ]; /* buffer to store "word" in */
        LONG   x;          /* translated "word" */
        UWORD  num;        /* current ip byte */

        *ipaddr = 0UL;

        start = ipstr;

        for( num = 0U ; num < 4U ; num++ )
        {
          if( (stop = strpbrk( start, "." )) == NULL )
          {
            stop = &ipstr[ strlen( ipstr ) ];
          }

          size = (size_t)(stop - start);

          if( size >= sizeof( buff ) )
          {
            return( FALSE );
          }

          (void)memcpy( buff, start, size );
          buff[ size ] = '\0';

          if( StrToLong( buff, (&x) ) != (-1L) )
          {
            if( (x >= 0L) && (x <= 255L) )
            {
              *ipaddr |= (((UBYTE)x) << ((3U - num) * CHAR_BIT));
            }
            else
            {
              return( FALSE );
            }
          }
          else
          {
            return( FALSE );
          }

          start = ++stop;
        }

        return( TRUE );
      }
    }

    return( FALSE );
}


/* count number of tags in taglist */
ULONG NumTagItems( struct TagItem *ti )
{
    ULONG           num;
    struct TagItem *tstate;

    num = 0UL;

    if( ti )
    {
      tstate = ti;

      while( NextTagItem( (&tstate) ) )
      {
        num++;
      }
    }

    return( num );
}


/* copy taglist from source to target and return the position of TAG_DONE */
struct TagItem *CopyTagList( struct TagItem *target, struct TagItem *source )
{
     struct TagItem *tstate,
                    *ti;

    if( target && source )
    {
      tstate = source;

      while( ti = NextTagItem( (&tstate) ) )
      {
        (*target) = (*ti);
        target++;
      }
    }

    return( target );
}


struct TagItem *CloneTagItemsPooled( struct TagItem *ti, APTR pool )
{
    if( ti && pool )
    {
      struct TagItem *newti;

      if( newti = (struct TagItem *)AllocVecPooled( pool, ((NumTagItems( ti ) + 1UL) * sizeof( struct TagItem )) ) )
      {
        struct TagItem *tag_done;

        tag_done = CopyTagList( newti, ti );
        tag_done -> ti_Tag = TAG_DONE;

        return( newti );
      }
    }

    return( NULL );
}


void FreeTagItemsPooled( struct TagItem *ti, APTR pool )
{
    if( ti && pool )
    {
      FreeVecPooled( pool, (APTR)ti );
    }
}


