== *************************************************************************
==
==  MSGMACS - Macro Library
==  Mark Dickinson, 2001
==
==  This library is intended for use with the MSGLOGER (MSGLGOBJ)
==  program. It allows the user to manipulate the nonstop capability
==  of the program dynamically.
==
==
==     ONLY THE FIRST SEVEN SECTIONS SHOULD EVER NEED TO BE CHANGED
==
==     ensure : is in your #uselist
==     load this file with load /keep 1/ <thisfile>
==       use MSGLOG to view log
==       use MSGCMD to ussue commands
==
==     start message logger with a macro such as
==        ?tacl macro
==        #frame
==        [#if [:msgtools:procs:start_collector]
==          |then| #set :x^ [:msgtools:procs:start_logger]
==        ]
==        #unframe
==
== *************************************************************************

?SECTION :msgtools:data:collector^name text
$0

?SECTION :msgtools:data:logger^process text
$TLOG

?SECTION :msgtools:data:logger^file^mask text
$SYSTEM.TACLLOG.T

?SECTION :msgtools:data:ems^log^subvol text
$SYSTEM.TACLLOG

?SECTION :msgtools:data:prefered^primary^cpu text
01

?SECTION :msgtools:data:prefered^backup^cpu text
00

?SECTION :msgtools:data:prefered^owner text
255,100

?SECTION :msgtools:data:program^object text
$SYSTEM.TACLLOG.MSGLGOBJ

?SECTION :msgtools:data:version^string text
MSGCMD Version 1.02 - 10 September 2001, for MSGLOGER:V01.01.03:2001/09/13

?SECTION :msgtools:data:prompter:dayname text
MON

?SECTION :msgtools:data:prompter:daynum text
0

?SECTION :msgtools:data:previous^user text
NONE

?SECTION :msgtools:data:previous^defaults text
NONE

?SECTION :msgtools:data:previous^priority text
0

?SECTION :msgtools:data:an^error text
0

?SECTION :msgtools:data:verbose^mode text
TRUE

?SECTION :msgtools:data:run^mode text
LIVE

?SECTION :msgtools:data:prompter:init routine
  == called internally when we have already got a date number so use that
  == rather than calling time again.
  [#if [#argument/value :msgtools:data:prompter:daynum/number]]
  #set :msgtools:data:prompter:dayname [:msgtools:procs:dayofweek]

?SECTION :msgtools:procs:verbose^mode routine
[#if [#match TRUE [:msgtools:data:verbose^mode]]
   |then| #result -1
   |else| #result 0
]

?SECTION :msgtools:procs:start_collector routine
  [#if [#match $0 [:msgtools:data:collector^name]] |then|
     == assume $0 is always running
     #result -1
  |else|
     [#if NOT [#processexists [:msgtools:data:collector^name]] |then|
        [#if [:msgtools:procs:verbose^mode] |then|
           #output EMS EVENT COLLECTOR [:msgtools:data:collector^name] IS NOT RUNNING, TRYING TO START.....
        ]
        [#if [#match [:msgtools:data:prefered^owner] [#processinfo/paid/]] |then|
           emsacoll /name [:msgtools:data:collector^name], nowait, cpu 5, pri 30/ &
                    logsubvol [:msgtools:data:ems^log^subvol], rotatefiles on, &
                    maxfile 0002, allocate, security ccoo, replyafterwrite on, &
                    ext 200, buffered on, poolpages 20, backup 4
           #delay 100
           [#if [#processexists [:msgtools:data:collector^name]]
              |then| [#if [:msgtools:procs:verbose^mode] |then|
                        #output STARTED OK
                     ]
                     #result -1
              |else| #output **ERROR** COULD NOT START EMS EVENT COLLECTOR
                     #result 0
           ]
        |else|
           #output *ERROR* ONLY USER [#username [:msgtools:data:prefered^owner]] CAN START THE EMS EVENT COLLECTOR
           #result 0
        ]
     |else|
        #result -1
     ]
  ]

?SECTION :msgtools:procs:start_logger routine
  [#if NOT [#processexists [:msgtools:data:logger^process]] |then|
     [#if [:msgtools:procs:verbose^mode] |then|
        #output MESSAGE LOGGER [:msgtools:data:logger^process] IS NOT RUNNING, TRYING TO START.....
     ]
     [#if [#match [:msgtools:data:prefered^owner] [#processinfo/paid/]] |then|
        delete define =logger_ems_collector
        clear all param
        clear all assign
        [#if [:msgtools:procs:start_collector] |then|
           == Note: if the below define is set to file $NONE event logging
           ==       will not be used by the program (no status messages will
           ==       be reported).
           add define =logger_ems_collector,class map, file [:msgtools:data:collector^name]
           == Number of days to retain the data files.
           param logger-data-keep 5
           == Guardian security of created data files
           param logger-data-security COOO
           == More log file details
           assign logger-data-file,[:msgtools:data:logger^file^mask],code 1003,ext (20,20),rec 132,block 4096
           [#if [#match DEBUG [:msgtools:data:run^mode]]
             |then|
                rund [:msgtools:data:program^object] /name [:msgtools:data:logger^process], &
                             cpu [:msgtools:data:prefered^primary^cpu],pri 20/
             |else|
                run [:msgtools:data:program^object] /name [:msgtools:data:logger^process], &
                            cpu [:msgtools:data:prefered^primary^cpu], &
                            nowait,pri 20/ [:msgtools:data:prefered^backup^cpu]
           ]
           #delay 200
           [#if [#processexists [:msgtools:data:logger^process]]
              |then| :msgtools:procs:write_message_to_server LOGGER PROCESS [:msgtools:data:logger^process] IS NOW USING THIS FILE
                     [#if [:msgtools:procs:verbose^mode] |then|
                        #output STARTED OK
                     ]
                     #result -1
              |else| #output **ERROR** COULD NOT START MSGLGOBJ PROCESS
                     #result 0
           ]
        |else|
           #result 0
        ]
     |else|
        #output *ERROR* ONLY USER [#username [:msgtools:data:prefered^owner]] CAN START THE MESSAGE LOGGER
     ]
  |else|
     #result -1
  ]

?SECTION :msgtools:procs:bounce_logger_process macro
  [#if [#processexists [:msgtools:data:logger^process]]
    |then| #push :x^
           [#if [:msgtools:procs:verbose^mode] |then|
              #output
              #output ***WARNING*** THIS COMMAND WILL IMPACT ANY PROGRAMS THAT ARE USING THE
              #output .             LOGGER PROCESS [:msgtools:data:logger^process] !
              #output
              #set :x^ [#input DO YOU WISH TO CONTINUE (Y/N)?]
           |else|
              #set :x^ YES
           ]
           [#if [#match Y* [:x^]] |then|
              :msgtools:procs:write_message_to_server MSGCMD RECYCLE COMMAND ON TERMINAL [#myterm]
              :msgtools:procs:write_message_to_server USER HAS REPLIED Y TO ACCEPT OTHER PROCESSES MAY BE IMPACTED
              :msgtools:procs:command_issue SHUTDOWN
              #delay 100
              [#if [#processexists [:msgtools:data:logger^process]]
                 |then| #output [:msgtools:data:logger^process] DID NOT SHUTDOWN, USING TACL STOP COMMAND
                        :msgtools:procs:stop_process [:msgtools:data:logger^process]
              ]
           ]
           #pop :x^
  ]
  #delay 100
  [#if [#processexists [:msgtools:data:logger^process]]
    |then| #output **ERROR** UNABLE TO STOP PROCESS [:msgtools:data:logger^process]
    |else| [#if [:msgtools:procs:verbose^mode] |then|
              #output STOPPED [:msgtools:data:logger^process], NOW RESTARTING
           ]
           #push :x^
           #set :x^ [:msgtools:procs:start_logger]
           #pop :x^
  ]

?SECTION :msgtools:procs:dayofweek routine
  #frame
  #push a^ b^ c^
  #setmany a^ b^ c^ _, [#contime [#timestamp]]
  #setmany a^ _, [#computejuliandayno [a^] [b^] [c^]]
  #set b^ [#compute [a^] / 7 * 7]
  [#case [#compute [a^] - [b^]]
    |    0    | #result MON
    |    1    | #result TUE
    |    2    | #result WED
    |    3    | #result THU
    |    4    | #result FRI
    |    5    | #result SAT
    |    6    | #result SUN
    |otherwise| #result *ERROR*
  ]
  #unframe

?SECTION :msgtools:_prompter routine
 [#case [#exception]
   |_call|
   |_error|
     #push :text
     #errortext /capture :text/
     == Testlevel is used to ensure we pop everything back to normal
     [#loop |while| [#variableinfo/existence/:testlevel] |do|
        #pop #out #defaults :testlevel
     ]
     #output Trapped error...
     #outputv :text
     #pop :text
     #set #prompt -1
     #output Prompt turned back on.
     #return
   |otherwise|
     #return
 ] {case}
 #filter _error
 [#if [#processexists [#mysystem].[:msgtools:data:logger^process]]
  |then| #push #out #defaults :testlevel
         system
         #setmany :msgtools:data:an^error _ _ _ , [#errornumbers]
         #set #out [#mysystem].[:msgtools:data:logger^process]
         #history /show 1/
         [#if not [#match [:msgtools:data:previous^user] [#processinfo/paid/]] |then|
          #output **USER changed from [:msgtools:data:previous^user] to [#processinfo/paid/]
          #set :msgtools:data:previous^user [#processinfo/paid/]
         ]
         [#if not [#match [:msgtools:data:previous^defaults] [#defaults]] |then|
          #output **DEFAULT changed from [:msgtools:data:previous^defaults] to [#defaults]
          #set :msgtools:data:previous^defaults [#defaults]
         ]
         [#if not [#compute ([:msgtools:data:previous^priority] = [#processinfo/pri/])] |then|
          #output **PRIORITY changed from [:msgtools:data:previous^priority] to [#processinfo/pri/]
          #set :msgtools:data:previous^priority [#processinfo/pri/]
         ]
         [#if [:msgtools:data:an^error] |then|
          [#if ([:msgtools:data:an^error]<>1048) |then|
           #output **ERROR OCCURED = [#errornumbers]
           #set #errornumbers 0 0 0 0
          ]
         ]
         [#if not [#match *OSP [#myterm]]
          |then|
           [#if ([#processinfo/pri/] > 120) |then|
            #output **ALTPRI FROM [#processinfo/pri/] TO 120
            [#if not [#alterpriority [#processinfo/processid/] 120]
             |then| #output **ALTPRI NOT SUCCESSFULL
            ]
           ]
          |else|
           [#if ([#processinfo/pri/] < 199) |then|
            #output **ALTPRI FROM [#processinfo/pri/] TO 199
            [#if not [#alterpriority [#processinfo/processid/] 199]
             |then| #output **ALTPRI NOT SUCCESSFULL
            ]
           ]
         ]
         #pop #out #defaults :testlevel
  |else| #output Warning: Tacl logger [#mysystem].[:msgtools:data:logger^process] is not active
 ]
 [#if not [#empty [#process]] |then|
  [#if [#match *,* [#process]] |then|
   #output Warning: Process [#process] ([#fileinfo/file/[#processinfo/programfile/[#process]]]) still running.
  ]
 ]
 #set #prefix [#shiftstring [#defaults]]
 [#if [#variableinfo/existence/:msgtools:^prompter] |then| :msgtools:^prompter]

?section :msgtools:^prompter macro
 #push :d^
 #setmany _ _ :d^ _, [#contime [#timestamp]]
 [#if NOT [#match [:d^] [:msgtools:data:prompter:daynum]]
    |then| :msgtools:data:prompter:init [:d^]
 ]
 #set :d^ [:msgtools:data:prompter:dayname] [:msgtools:data:prompter:daynum]
 LINE^25 [#SHIFTSTRING/UP/ *** [FORMAT_TIME] [#MYSYSTEM].[#MYTERM] UID=[#PROCESSINFO/PAID/] [:d^]]
 #pop :d^

?section :msgtools:msglog macro
   #frame
   #push y^ m^ d^ fdate^ one^day^ days^
   #set one^day^ 8640000
   #set days^ %1%
   [#if not [#emptyv days^]
     |then| #set days^ [#compute [days^] * [one^day^]]
     |else| #set days^ 0
   ]
   #setmany y^ m^ d^ _, [#contime [#compute [#timestamp] - [days^]]]
   #set fdate^ [#charget y^ 3 for 2][two_digits [m^]][two_digits [d^]]
   [#case [m^]
     | 1| #set m^ January
     | 2| #set m^ February
     | 3| #set m^ March
     | 4| #set m^ April
     | 5| #set m^ May
     | 6| #set m^ June
     | 7| #set m^ July
     | 8| #set m^ August
     | 9| #set m^ September
     |10| #set m^ October
     |11| #set m^ November
     |12| #set m^ December
   ]
   #output Browsing Marks test tacl (V) log file for [d^] [m^], [y^].
   esds [:msgtools:data:logger^file^mask][fdate^]
   #unframe

?section :_prompter routine
 [#if [#variableinfo/existence/:msgtools:_prompter] |then| :msgtools:_prompter]

?section :msglog macro
 [#if [#match HELP %1%]
    |then| MSGCMD HELP VIEWLOG
    |else| [#if [#variableinfo/existence/:msgtools:msglog]
              |then| :msgtools:msglog %*%
              |else| #output The msglog command was not found (:msgtools:msglog).
           ]
 ]

?section :vlog alias
  :msglog

?SECTION :msgtools:procs:write_message_to_server routine
  #frame
  #push :textvar
  [#if [#processexists [:msgtools:data:logger^process]] |then|
      [#case [#argument/value :textvar/text end]
         |    1    | #push #out
                     #set #out [:msgtools:data:logger^process]
                     #output [:textvar]
                     #pop #out
         |otherwise| #output **ERROR** No text supplied to send to server.
      ]
  |else|
      #output **ERROR** [:msgtools:data:logger^process] IS NOT CURRENTLY RUNNING
  ]
  #unframe

?section :msgtools:data:command_struct struct
begin
  int  cmdflag(0:1);
  char text(0:79);
end;

?section :msgtools:procs:command_issue routine
  #frame
  #push :command^string
  [#case [#argument/value :command^string/ text end]
     |    1    |
     |    2    | #set :command^string HELP
     |otherwise|
  ]
  #set :command^string [:msgtools:procs:check_command [:command^string]]
  [#if NOT [#emptyv :command^string]
     |then| [#if NOT [:msgtools:procs:internal_command [:command^string]]
               |then| :msgtools:procs:write_message_to_server MSGLOGGER-COMMAND [:command^string]
                      [#if [:msgtools:procs:verbose^mode] |then|
                          #output SENT [:command^string]
                      ]
                      ==
                      == If a shutdown give server time to stop to avoid
                      == an error 201 from the TACL prompt.
                      [#if [#match SHUTDOWN* [:command^string]]
                         |then| #delay 200
                      ]
            ]
     |else| #output **ERROR** ILLEGAL MSGCMD COMMAND.
  ]
  #pop :command^string
  #unframe

?section :msgcmd alias
  :msgtools:procs:command_issue

?section :msgtools:procs:check_command routine
  #frame
  #push :cmd^test :cpu^test :max^cpus
  [#case [#argument/value :cmd^test/keyword/wordlist INIT SHUTDOWN STATUS STATS HELP LOG VIEWLOG RECYCLE EMSDIST VERSION CHECKENV DEBUGMODE EMSCOLL VERBOSE DEVEL/ &
           keyword /wordlist BACKUPCPU PRIMARYCPU/ text end]
     |    1    | #result [:cmd^test] [#rest]
     |    2    | [#case [#argument/value :cpu^test/number keyword/wordlist STOP/ text end]
                    |    1    | [#if [:msgtools:procs:check_cpu [:cpu^test]]
                                   |then| #result [:cmd^test] [:cpu^test]
                                   |else| #result
                                ]
                    |    2    | [#if [#match BACKUPCPU [:cmd^test]]
                                   |then| #result [:cmd^test] [:cpu^test]
                                   |else| #output **ERROR** STOP CAN ONLY BE USED ON BACKUPCPU
                                          #result
                                ]
                    |otherwise| #result
                 ]
     |otherwise| #result
  ]
  #unframe

?section :msgtools:procs:check_cpu routine
  #frame
  #push :cpu^num :max^cpus :c0^ :c1^ :c2^ :c3^ :c4^ :c5^ :c6^ :c7^
  #push :c8^ :c9^ :c10^ :c11^ :c12^ :c13^ :c14^ :c15^
  [#if [#argument/value :cpu^num/number]]
  #setmany :max^cpus :c0^ :c1^ :c2^ :c3^ :c4^ :c5^ :c6^ :c7^ :c8^ &
        :c9^ :c10^ :c11^ :c12^ :c13^ :c14^ :c15^ _, [#processorstatus]
  [#if [#compute ([:cpu^num] > [:max^cpus])]
     |then| #output [:cpu^num] IS TOO HIGH, ONLY [:max^cpus] ARE CONFIGURED
            #result 0
     |else| [#if [#compute ([:cpu^num] < 0)]
               |then| #output [:cpu^num] IS NOT A LEGAL CPU NUMBER
                      #result 0
               |else| [#if [#variableinfo/existence/:c[:cpu^num]^]
                         |then| [#if [:c[:cpu^num]^]
                                   |then| #result -1
                                   |else| #output CPU [:cpu^num] IS CURRENTLY DOWN
                                ]
                         |else| #output CPU NUMBER [:cpu^num] IS NOT ALLOWED
                                #result 0
                      ]
            ]
  ]
  #unframe

?section :msgtools:help:_execute macro
== A TACL user typed in help and we are in there search path.
== we don't want to emulate a system help function here so
== return a no-help message.
#output HELP not available. Check your search path.

?section :msgtools:help:help_text_all text
TACL commands available.
  MSGLOG [daysback]  (alias for MSGLOG VIEWLOG, see below)
  MSGCMD             (front end for issuing commands to msgloger, see below)
.
MSGCMD Commands available...
 BACKUPCPU  nn|STOP (change backup process cpu or stop backup process)
 CHECKENV [QUIET]   (check that all processes are running)
 DEBUGMODE ON|OFF   (if on rund the server, default is off)
 EMSCOLL <process>  (change the event collector status messages are logged to)
 EMSDIST [setupparm](start an emsdist against the event collector)
 HELP command       (help on each individual command)
 INIT               (reset program counters)
 LOG                (log a message to the current days log)
 PRIMARYCPU nn      (change primary process cpu)
 RECYCLE            (stop and restart the msgloger process)
 SHUTDOWN           (stop program)
 STATUS             (log internal info)
 VERSION            (show the version of the macro library)
 VERBOSE ON|OFF     (turn verbose message display on or off)
 VIEWLOG [daysback] (view the current days log file)

There is detailed help for each command available through MSGCMD HELP command
ie: MSGCMD HELP VIEWLOG

?section :msgtools:help:help_text_msgcmd alias
:msgtools:help:help_text_all

?section :msgtools:help:help_text_msglog alias
:msgtools:help:help_text_viewlog

?section :msgtools:help:help_text_init text
Syntax:
   INIT
.
Function:
   The INIT command causes the program to perform a reset and self check.
.
   It will reset the takeover counter, close and reopen the log files
   (which is usefull if you have renamed out a large log file), and perform
   a process vs cpu test to ensure the primary and backup processes are in
   the cpus they are intended to be in.

?section :msgtools:help:help_text_status text
Syntax:
   STATUS
.
Function:
   The status command will log the current status of the program to both
   the current log file and to the EMS event log.
.
   It will display the program object file name and version, the event
   collector process being used, the log file prefix and current log file
   being used, the prefered primary cpu and current primary cpu, the
   prefered backup cpu and current backup cpu, the internal takeover count
   and the maximum takeovers permitted before the backup process will be
   disabled.
.
   Notes: to view the output of the status command you either need
   and event distrubuter running, or you can view the log file using
   MSGCMD VIEWLOG (shift-pagedown to go to end of file).

?section :msgtools:help:help_text_emsdist text
Syntax:
   EMSDIST [setupparms]
.
   This will start an EMSDIST process running against an event collector
   process. Unless the [setupparms] are used to override the default collector
   the collector configured for use by the msglogger program will be used.
.
   The emsdist process will run continuously until you use the
   break key to exit the command.
.
   The [setupparms] can be used to enter additional selection criteria. If
   used either the collector name to be used or the keyword ALL must be the
   first parameter. Additional parameters after that may be the emsdist
   time and stop parameters.
   If a collector process name is provided that will be the collector
   messages are displayed from. If the keyword ALL is used a list of
   collectors configured by the administrator will be used. Currently this
   is $0 and the msglogger collector.
Examples:
   MSGCMD EMSDIST                          (uses msglogger collector)
   MSGCMD EMSDIST ALL                      (uses $0 and msglogger collector)
   MSGCMD EMSDIST $0                       (uses $0 only)
   MSGCMD EMSDIST $0,TIME 09:00,STOP 09:05 (uses $0, shows only time range)

?section :msgtools:help:help_text_shutdown text
Syntax:
   SHUTDOWN
.
Function:
   This command will shutdown the program.
.
   This is the prefered method of stopping the program as it leaves an audit
   trail.

?section :msgtools:help:help_text_version text
Syntax:
   VERSION
.
Function:
   Display the current version of the MSGCMD macro library and the version
   of the MSGLOGER program the macro library has been designed to run against.
.
Notes:
   To check the version of the MSGLOGER program that is running use the
   MSGCMD STATUS option and view the results from the event log or log file.

?section :msgtools:help:help_text_backupcpu text
Syntax:
   BACKUPCPU nn | STOP
.
Function:
   BACKUPCPU nn
      If the program is currently running as a nonstop process pair this
      command will stop the existing backup process and restart it in the
      new cpu that has been requested.
.
      If the program is not running as a nonstop process pair this command
      can be used to make it a nonstop process pair by starting a backup in
      the cpu requested.
.
   BACKUPCPU STOP
      If a backup process is running this command will stop the backup
      process and the program will continue running in single process
      mode.
.
Notes:
   If the program is not running as a nonstop process pair due to the backup
   being stopped because of too many takeover errors then the backup cannot
   be started until an INIT command has been issued.
.
Caution: This command increments the takeover count maintained by the
   program. If MAX_TAKEOVERS is exceeded then backup processes will be
   disabled.

?section :msgtools:help:help_text_primarycpu text
Syntax:
   PRIMARYCPU nn
.
Function:
   If the program is running as a nonstop process pair this command will
   alter the cpu the primary process is running in. There must be a
   backup process running for this command to be accepted.
.
   This is a CPU intensive process so should not be used often. It will
   normally require two process ownership switches, one process deletion
   and one process creation.
.
   If the new primary cpu is the cpu the current backup is running in the
   program will just perform an ownership switch which has minimal overhead.
.
   If the new primary cpu is not the current backup cpu a lot of work is done.
   It must switch ownership to the backup process (make it primary), and
   stop the previous primary process. It then starts a new backup process
   in the cpu designated as the new primary.
   When the new backup is started primary ownership of the process pair is
   switched to it to make it the new primary.
.
   ie: current primary is 5, current backup is 4, command is PRIMARYCPU 3
       4 becomes primary (5 now backup)
       5 is stopped
       3 starts a backup
       3 becomes primary (4 now backup)
.
Caution: This command increments the takeover count maintained by the
   program. If MAX_TAKEOVERS is exceeded then backup processes will be
   disabled.

?section :msgtools:help:help_text_viewlog text
Syntax:
   VIEWLOG [days-offset]
.
Function:
   Displays the contents of the current days log file, or logs from a
   previous day.
.
VIEWLOG on its own will display the current days log file.
.
If the optional days-offset parameter is provided it will display the log
file for previous days counting back from the parameter provided.
For example
  VIEWLOG 1 will display the log for one day previous (yesterdays).
  VIEWLOG 2 displays two days back etc.
.
Notes: This requires the ESDS file browser installed on your system and
       located in your search path.

?section :msgtools:help:help_text_checkenv text
Syntax:
   CHECKENV [QUIET]
.
Function:
   This command is used to check that all the required processes are
   running. This is currently only the event collector and the message
   logger process itself.
.
   If the event collector is $0 it is not checked. $0 is always assumed
   to be running as the entire Tandem system will have problems if its
   not.
.
   If you are running the command logged on as the preferred owner then
   the command will start the event collector and logger process if
   they are not running.
.
   If the QUIET option is used only non-recoverable error messages will
   be displayed, all OK messages will be suppressed.

?section :msgtools:help:help_text_debugmode text
Syntax:
   DEBUGMODE ON|OFF
.
Function:
   This command is used to control whether the server starts normally
   or if it is going to be started by a RUND command.
.
   If set to ON then the next time the message collection server is started
   it will be started by a RUND command.

?section :msgtools:help:help_text_emscoll text
Syntax:
   EMSCOLL <process-name>|OFF
.
Function:
   This command is used to alter the ems event collector the message
   collection server logs its status messages to.
.
   The <process-name> must be a running process name and must be a valid
   Tandem ems collector process ($system.sysnn.emsacoll).
.
   Any errors will be reported by the server to trhe current ems collector
   if one is being used.
.
   NOTES: ***NOT IMPLEMENTED*** This will be a future enhancement.

?section :msgtools:help:help_text_log text
Syntax:
   LOG message-text
.
Function:
   Write a text message into the current days log file.

?section :msgtools:help:help_text_verbose text
Syntax:
   VERBOSE  ON | OFF
.
Function:
   Turn verbose message displays on or off.
.
   If ON then status messages will be written to the users terminal
   advising the user of things of interest. Error messages will also
   be reported on.
.
   If OFF then only non-recoverable errors that require action by the
   user will be written to the terminal. Commands that complete without
   error will produce no response.

?section :msgtools:help:help_text_about text
The MSGTOOLS macro library provides an interface to the MSGLOGER program
to manage the nonstop operation of the program.
It should only be used by system administrators.

?section :msgtools:help:help_text_msgtools alias
:msgtools:help:help_text_about

?section :msgtools:help:help_processor routine
  #frame
  #push :cmd^
  [#if [#argument /value :cmd^/ word end]]
  [#if [#emptyv :cmd^]
     |then| #outputv :msgtools:help:help_text_all
     |else| [#if [#variableinfo/existence/:msgtools:help:help_text_[:cmd^]]
               |then| #outputv :msgtools:help:help_text_[:cmd^]
               |else| #output THERE IS NO HELP DISPLAY FOR [#shiftstring/UP/[:cmd^]]
            ]
  ]
  #unframe

?section :msgtools:procs:internal_command routine
  #frame
  #push :command^ :rest^
  [#case [#argument /value :command^/ keyword/wordlist CHECKENV HELP VIEWLOG LOG RECYCLE EMSDIST VERSION DEBUGMODE VERBOSE DEVEL/ text end]
     |    1    | [#if [#argument/value :rest^/text end]]
                 [#case [:command^] |of|
                    | CHECKENV| [#if [#emptyv :rest^] |then| #set :rest^ X]
                                [#if [#match QUIET [:rest^]]
                                   |then| :msgtools:checkenv
                                   |else| :msgtools:checkenv_verbose
                                ]
                    |DEBUGMODE| [#if [#match ON [:rest^]]
                                   |then| #set :msgtools:data:run^mode DEBUG
                                   |else| #set :msgtools:data:run^mode ALIVE
                                ]
                    | EMSDIST | :msgtools:procs:msgcmd_emsdist [:rest^]
                    | HELP    | :msgtools:help:help_processor [:rest^]
                    | LOG     | :msgtools:procs:write_message_to_server [:rest^]
                    | RECYCLE | :msgtools:procs:bounce_logger_process
                    | VIEWLOG | :msgtools:msglog [:rest^]
                    | VERSION | #outputv :msgtools:data:version^string
                    | VERBOSE | [#if [#emptyv :rest^] |then| #set :rest^ ON]
                                [#case [:rest^] |of|
                                   |   OFF   | #set :msgtools:data:verbose^mode FALSE
                                               #output Informational messages will not be shown.
                                   |   ON    | #set :msgtools:data:verbose^mode TRUE
                                               #output Informational messages will be shown.
                                   |otherwise| #output **ERROR** VERBOSE SHOULD BE ON, OFF OR EMPTY TO DEFAULT TO ON.
                                ]
                    | DEVEL   | :msgtools:procs:devel:devel_command [:rest^]
                    |otherwise|
                 ]
                 #result 1
     |otherwise| #result 0
  ]
  #unframe

?section :msgtools:procs:cpu_availablity_display macro
  #frame
  [#def :walk_through_list routine |body|
     #frame
     #push :max^cpus :I^ :flag^
     [#if [#argument/value :max^cpus/number]]
     #output This system has [:max^cpus] processors configured.
     #set :I^ 0
     #output /hold, width 4/ CPU
     [#loop |do|
            #output /hold, width 5/ [:I^]
            #set :I^ [#compute ([:I^] + 1)]
            |until| [#compute ([:I^] >= [:max^cpus])]
     ]
     #output
     #set :I^ 0
     #output /hold, width 4/
     [#loop |do|
            [#if [#argument/value :flag^/number]]
            [#if [:flag^]
               |then| #output /hold, width 5/ UP
               |else| #output /hold, width 5/ DOWN
            ]
            #set :I^ [#compute ([:I^] + 1)]
            |until| [#compute ([:I^] >= [:max^cpus])]
     ]
     #output
     #unframe
  ] { end of :walk_through_list }
  :walk_through_list [#PROCESSORSTATUS]
  #unframe

?section :msgtools:checkenv_verbose routine
  #frame
  #push :x^
  #output
  #output MSGLOGER Environment status.
  #output
  :msgtools:procs:cpu_availablity_display
  #output
  #output The MSGLOGGER Environment runs under the prefered user [:msgtools:data:prefered^owner]
  #output
  #output Checking tasks...
  [#if [:msgtools:procs:start_collector]
     |then| #set :x^ [:msgtools:procs:start_logger]
  ]
  #output /hold,width 21/ EMS EVENT COLLECTOR
  #output /hold,width 7/ [:msgtools:data:collector^name]
  [#if [#processexists [:msgtools:data:collector^name]]
     |then| #output : OK (Tandem supplied EMSACOLL)
     |else| #output : ** DOWN **
  ]
  #output /hold,width 21/ LOGGER PROCESS
  #output /hold,width 7/ [:msgtools:data:logger^process]
  [#if [#processexists [:msgtools:data:logger^process]]
     |then| #output : OK ([:msgtools:data:program^object])
     |else| #output : ** DOWN **
  ]
  #output
  #output LOG FILES ARE BEING WRITTEN TO [:msgtools:data:logger^file^mask]yymmdd
  #output
  #unframe

?section :msgtools:checkenv routine
  #frame
  #push :x^
  #push :msgtools:procs:verbose^mode
  #set :msgtools:procs:verbose^mode FALSE
  [#if [:msgtools:procs:start_collector]
    |then| #set :x^ [:msgtools:procs:start_logger]
  ]
  #unframe

?section :msgtools:procs:stop_process routine
  #frame
  #push :pid^to^stop :programname :text
  [#case [#exception]
    |_call|
    |_error|
      #errortext /capture :text/
      #outputv :text
      #unframe
      #return
    |otherwise|
      #unframe
      #return
  ] {case}
  #filter _error

  [#if [#argument/value :pid^to^stop/text]]
  #set :programname [#processinfo/programfile/[:pid^to^stop]]
  [#if [#processexists [:pid^to^stop]] |then|
     STOP [:pid^to^stop]
  ]
  #delay 200
  [#if [#processexists [:pid^to^stop]] |then|
    #output UNABLE TO STOP [:pid^to^stop], STOP MANUALLY ([:programname])
  |else|
    [#if [:msgtools:procs:verbose^mode] |then|
       #output STOPPED [:pid^to^stop] ([:programname])
    ]
  ]
  #unframe

?section :msgtools:procs:monitor_eventlog routine
  #frame
  [#case [#exception]
    |_call|
    |_error|
      #push :junk
      #errortext /capture :junk/
      #output
      #outputv :junk
      #set :junk [#process]
      [#if NOT [#emptyv :junk] |then|
         #set :msgtools:procs:stop_process [:junk]
      ]
      #unframe
      #return
    |_break|
      #output
      #output STOPPING DUE TO BREAK
      #push :junk
      #set :junk [#process]
      [#if NOT [#emptyv :junk] |then|
         :msgtools:procs:stop_process [:junk]
      ]
      #unframe
      #return
    |otherwise|
      #unframe
      #return
  ] {case}
  #filter _error _break

  #push :command^string :ok^
  [#case [#argument/value :command^string/text end]
     |    1    |
     |otherwise| #output BAD CALL TO :msgtools:procs:monitor_eventlog
                 #unframe
                 #return
  ]
  #output [#shiftstring/up/[:command^string]]
  #output ...EMS DISTRIBUTOR WAITING FOR MESSAGES...
  [:command^string]
  #output EMS DISTRIBUTER ENDED.
  #unframe

?section :msgtools:procs:eventlog_last_90_seconds macro
  #frame
  #push :start^time :stop^time :h^ :mn^ :s^ :y^ :m^ :d^ :x^ :command^string
  #set :x^ [#timestamp]
  #setmany :y^ :m^ :d^ :h^ :mn^ :s^ _, [#contime [:x^]]
  [#if [#compute [:mn^] < 10] |then| #set :mn^ 0[:mn^]]
  [#if [#compute [:s^]  < 10] |then| #set :s^ 0[:s^]]
  #set :stop^time STOP [:y^] [:m^] [:d^] [:h^]:[:mn^]:[:s^]
  == Back 90 seconds
  #setmany :y^ :m^ :d^ :h^ :mn^ :s^ _, [#contime [#compute [:x^] - 9000]]
  [#if [#compute [:mn^] < 10] |then| #set :mn^ 0[:mn^]]
  [#if [#compute [:s^]  < 10] |then| #set :s^ 0[:s^]]
  #set :start^time TIME [:y^] [:m^] [:d^] [:h^]:[:mn^]:[:s^]
  :msgtools:procs:monitor_eventlog ,[:start^time],[:stop^time]
  == Run the program
  #set :command^string emsdist /name,pri 40/ type printing, &
     textout [#myterm], collector [:collector^name],[:start^time],[:stop^time]
  :msgtools:procs:monitor_eventlog [:command^string]
  #unframe

?section :msgtools:procs:msgcmd_emsdist routine
  #frame
  [#case [#exception]
    |_call|
    |_error|
      #push :junk
      #errortext /capture :junk/
      #outputv :junk
      #unframe
      #return
  ] {case}
  #filter _error

  #push :collector^name :ok^ :rest^ :command^string

  [#case [#argument/value :collector^name/word end]
     |    1    | [#case [#argument comma end]
                    |    1    | [#case [#argument/value :rest^/text end]
                                   |    1    | #set :rest^ ,[:rest^]
                                   |otherwise|
                                ]
                    |otherwise|
                 ]
     |    2    | #set :collector^name [:msgtools:data:collector^name]
     |otherwise| #output BAD CALL TO :msgtools:procs:msgcmd_emsdist
                 #unframe
                 #return
  ]

  == Use OK to see if event collector is OK as $0 is not a process
  == we can check on with processexists.
  #set :ok^ 0
  [#if [#match ALL [:collector^name]] |then|
     [#if NOT [#match $0 [:msgtools:data:collector^name]]
        |then| #set :collector^name ([:msgtools:data:collector^name],$0)
        |else| #set :collector^name $0
     ]
     #set :ok^ -1
  |else|
     [#if [#match $0 [:collector^name]]
        |then| #set :ok^ -1
        |else| [#if [#processexists [:collector^name]]
                 |then| #set :ok^ -1
               ]
     ]
  ]
  [#if [:ok^] |then|
    #set :command^string emsdist /name,pri 40/ type printing,textout [#myterm],collector [:collector^name][:rest^]
    :msgtools:procs:monitor_eventlog [:command^string]
 |else|
    #output *ERROR* THE EVENT COLLECTOR PROCESS [:collector^name] IS NOT RUNNING.
  ]
  #unframe

?SECTION :msgtools:_execute macro
== If a wally runs the directory instead of a command, show the help.
#output Use the MSGCMD to use server management tools in this library.
#output
#outputv :msgtools:help:help_text_all

?SECTION :msgtools:procs:devel:_execute routine
== **********************************************************************
==
==
==                      Development tools.
==
==
== This is normally in its own file, and loaded seperately. I have
== included it in this distribution file for completenes. You may wish
== to move it back out to a seperate file (to be loaded by developers
== in conjunction with the main file, but not loaded by sys-admins).
==
==
== **********************************************************************
#output Well you obviously arn't in this applications development group or you
#output would have known better.
#output I'm removing these devel libraries from your temptation.
#pop :msgtools:procs:devel

?SECTION :msgtools:help:devel:help_text_all text
+-----------------------------------------------------------------------------+
| The DEVEL commands are used by the developers to help test the program.     |
| >>>>>>>>>>> They should not be used by day-to-day users <<<<<<<<<<<<<<      |
+-----------------------------------------------------------------------------+
|                                                                             |
| FLOOD    Flood the message collector with messages (see DEVEL HELP FLOOD)   |
| COMPILE  Compile a new version.                                             |
|                                                                             |
+-----------------------------------------------------------------------------+

?SECTION :msgtools:help:devel:help_text_compile text
+-----------------------------------------------------------------------------+
| The DEVEL commands are used by the developers to help test the program.     |
| >>>>>>>>>>> They should not be used by day-to-day users <<<<<<<<<<<<<<      |
+-----------------------------------------------------------------------------+
|                                                                             |
| COMPILE [VERSION FIXLEVEL|MINOR|MAJOR]                                      |
|                                                                             |
|    This command will compile a new copy of the program.                     |
|    If the version option is used it will have the following effect...       |
|       FIXLEVEL - fixlevel is incremented, V1.02.05 becomes V1.02.06         |
|       MINOR    - release level is incremented, V1.02.05 becomes V1.03.01    |
|       MAJOR    - version number is incremented, V1.02.05 becomes V2.01.01   |
|                                                                             |
|    Examples: COMPILE                                                        |
|              COMPILE VERSION FIXLEVEL                                       |
|                                                                             |
| THIS COMMAND SHOULD ONLY BE USED BY DEVELOPERS IN A DEVELOPMENT ENVIRONMENT.|
|                                                                             |
+-----------------------------------------------------------------------------+

?SECTION :msgtools:help:devel:help_text_flood text
+-----------------------------------------------------------------------------+
| The DEVEL commands are used by the developers to help test the program.     |
| >>>>>>>>>>> They should not be used by day-to-day users <<<<<<<<<<<<<<      |
+-----------------------------------------------------------------------------+
|                                                                             |
| FLOOD <nnn>                                                                 |
|                                                                             |
|    This command will send <nnn> messages to the message collector process.  |
|    It is used to test the checkpoint handling (ie: while its running using  |
|    another screen to stop/start backups, switch etc.)                       |
|                                                                             |
|    Example: MSGCMD DEVEL FLOOD 100                                          |
|                                                                             |
| THIS COMMAND SHOULD ONLY BE USED BY DEVELOPERS IN A DEVELOPMENT ENVIRONMENT.|
|                                                                             |
+-----------------------------------------------------------------------------+

?SECTION :msgtools:help:devel:help_processor routine
#frame
== --------------------------------------------------------------------------
== Help request. Display the appropriate help panel, or an error if the
== help panel (variable) does not exist.
== --------------------------------------------------------------------------
#push :help^request
[#if [#argument /value :help^request/ word end]]
[#if [#emptyv :help^request] |then| #set :help^request ALL]
[#if [#variableinfo/existence/:msgtools:help:devel:help_text_[:help^request]]
   |then| #outputv :msgtools:help:devel:help_text_[:help^request]
   |else| #output There is no help available for [:help^request]
]
#unframe

?SECTION :msgtools:data:devel:source^file text
$AUDIT.MARK.MSGLOGER

?SECTION :msgtools:data:devel:report^out text
$S.#MARK.COMP

?SECTION :msgtools:procs:devel:add_leading_zeros routine
#frame
#push :number :digits
[#if [#argument/value :digits/number]]
[#case [#argument/value :number/number text end]
   |    1    |
   |otherwise| #set :number 00
]
[#loop |while| [#compute ([#charcount :number] < [:digits])] |do|
   #set :number 0[:number]
]
#result [:number]
#unframe

?SECTION :msgtools:procs:devel:two_digits routine
#frame
#push :data
[#if [#argument/value :data/number text end]]
#result [:msgtools:procs:devel:add_leading_zeros 2 [:data]]
#unframe

?SECTION :msgtools:procs:devel:four_digits routine
#frame
#push :data
[#if [#argument/value :data/number text end]]
#result [:msgtools:procs:devel:add_leading_zeros 4 [:data]]
#unframe

?SECTION :msgtools:procs:devel:get_compile_type routine
#frame
#push :userresp :saveresp
#output
#output Select compile type...
#output
#output 1) Minor fix-only release
#output 2) Minor code enhancement release
#output 3) Major functional change release
#output

[#loop |while| [#emptyv :saveresp] |do|
   #set :userresp [#input Select 1, 2 or 3:]
   [#if [#match 1 [:userresp]] |then| #set :saveresp [:userresp]]
   [#if [#match 1 [:userresp]] |then| #set :saveresp [:userresp]]
   [#if [#match 1 [:userresp]] |then| #set :saveresp [:userresp]]
   [#if [#emptyv :saveresp] |then| #output Reply must be 1, 2 or 3]
]

[#case [:saveresp] |of|
   |    1    | #result FIXLEVEL
   |    2    | #result MINOR
   |    3    | #result MAJOR
   |otherwise| #result FIXLEVEL
]
#unframe

?SECTION :msgtools:procs:devel:get_current_source_version routine
#frame
#push :outbuf :version :pos :date
edit /outv :outbuf/ [:msgtools:data:devel:source^file];lb/msgloger:v/;e
#set :version [#extract :outbuf]  == text editor msg
#set :version [#extract :outbuf]  == current file msg
#set :version [#extract :outbuf]  == the version string
#set :pos [#charfind :version 1 .]
[#if [#compute ([:pos] > 3)] |then| #set :pos [#compute [:pos] - 2]]
#set :version [#charget :version [:pos] for 30]
#set :pos [#charfind :version 1 :]
#set :pos [#compute [:pos] + 1]
#set :date [#charget :version [:pos] for 30]
[#if [#compute ([:pos] > 3)] |then| #set :pos [#compute [:pos] - 2]]
#set :version [#charget :version 1 for [:pos]]
#set :pos [#charfind :date 1 "]
[#if [#compute ([:pos] > 1)] |then| #set :pos [#compute [:pos] - 1]]
#set :date [#charget :date 1 for [:pos]]
#output Current: Source Version = [:version], Source Release Date = [:date]
#result [:version] [:date]
#unframe

?SECTION :msgtools:procs:devel:change_source_version routine
#frame
#push :current^version :current^reldate :new^version :new^reldate :vertype
#push :fieldtochange :pos :part1 :part2 :y^ :m^ :d^

[#case [#argument /value :vertype/ keyword/wordlist MAJOR MINOR FIXLEVEL/ end]
   |    1    |
   |    2    | #set :vertype FIXLEVEL
   |otherwise| #output **ERROR** ILLEGAL VERSION REQUEST, NO VERSION CHANGE PERFORMED
]
#output Changing source code version string for new [:vertype] release.

#setmany :current^version :current^reldate, [:msgtools:procs:devel:get_current_source_version]
[#if [#emptyv :current^version]
   |then| #output **ERROR UNABLE TO GET CURRENT VERSION, NO VERSION CHANGE PERFORMED
          #return
]
[#if [#emptyv :current^reldate]
   |then| #output **ERROR UNABLE TO GET CURRENT VERSION, NO VERSION CHANGE PERFORMED
          #return
]

== Build the new version number depending on what part of it we are changing.
[#case [:vertype] |of|
   | MAJOR  | #set :pos [#charfind :current^version 1 .]
              [#if [#compute ([:pos] < 2)] |then|
                 #output **ERROR VERSION CODE IN SOURCE FILE IS THE WRONG FORMAT
                 #output NO VERSION CHANGE PERFORMED
                 #return
              ]
              #set :pos [#compute [:pos] - 1]
              #set :fieldtochange [#charget :current^version 1 for [:pos]]
              #set :fieldtochange [:msgtools:procs:devel:two_digits [#compute [:fieldtochange] + 1]]
              #set :new^version [:fieldtochange].01.01
   | MINOR  | #set :pos [#charfind :current^version 1 .]
              [#if [#compute ([:pos] < 2)] |then|
                 #output **ERROR VERSION CODE IN SOURCE FILE IS THE WRONG FORMAT
                 #output NO VERSION CHANGE PERFORMED
                 #return
              ]
              #set :part1 [#charget :current^version 1 for [:pos]]
              #set :pos [#compute [:pos] + 1]
              #set :part2 [#charget :current^version [:pos] for 20]
              #set :pos [#charfind :part2 1 .]
              [#if [#compute ([:pos] < 2)] |then|
                 #output **ERROR VERSION CODE IN SOURCE FILE IS THE WRONG FORMAT
                 #output NO VERSION CHANGE PERFORMED
                 #return
              ]
              #set :pos [#compute [:pos] - 1]
              #set :fieldtochange [#charget :part2 1 for [:pos]]
              #set :fieldtochange [:msgtools:procs:devel:two_digits [#compute [:fieldtochange] + 1]]
              #set :new^version [:part1][:fieldtochange].01
   |FIXLEVEL| #set :pos [#charfind :current^version 1 .]
              [#if [#compute ([:pos] < 2)] |then|
                 #output **ERROR VERSION CODE IN SOURCE FILE IS THE WRONG FORMAT
                 #output NO VERSION CHANGE PERFORMED
                 #return
              ]
              #set :pos [#compute [:pos] + 1]
              #set :pos [#charfind :current^version [:pos] .]
              [#if [#compute ([:pos] < 2)] |then|
                 #output **ERROR VERSION CODE IN SOURCE FILE IS THE WRONG FORMAT
                 #output NO VERSION CHANGE PERFORMED
                 #return
              ]
              #set :part1 [#charget :current^version 1 for [:pos]]
              #set :pos [#compute [:pos] + 1]
              #set :fieldtochange [#charget :current^version [:pos] for 10]
              #set :new^version [:part1][:msgtools:procs:devel:two_digits [#compute [:fieldtochange] + 1]]
    |otherwise| #output GOT UNKNOWN VERSION REQUEST ([:vertype]) !!!
                #return
]  { case for new version }

== Build the new release date
#setmany :y^ :m^ :d^ _, [#contime [#timestamp]]
#set :y^ [:msgtools:procs:devel:four_digits [:y^]]
#set :m^ [:msgtools:procs:devel:two_digits [:m^]]
#set :d^ [:msgtools:procs:devel:two_digits [:d^]]
#set :new^reldate [:y^]/[:m^]/[:d^]

#push :ed^command :outbuf
#set :ed^command cba'MSGLOGER:V[:current^version]:[current^reldate]'MSGLOGER:V[new^version]:[new^reldate]'all;na;p!;p!;e
#output New Version   : Version = [:new^version], New Release Date    = [:new^reldate]
#set :outbuf [#input Change version string in source code to New Version ?]
[#if [#match Y* [:outbuf]]
   |then| EDIT /outv :outbuf/ &
              [:msgtools:data:devel:source^file];[:ed^command]
          #outputv :outbuf
          :msgtools:procs:write_message_to_server &
              [:msgtools:data:devel:source^file] VERSION STRING UPDATED TO &
              V[:new^version], dated [:new^reldate]
   |else| #output **WARNING** Compiling with an old version string.
          :msgtools:procs:write_message_to_server **WARNING** &
              [:msgtools:data:devel:source^file] VERSION UPDATE SKIPPED, &
              RETAINING V[:current^version], dated [:current^reldate]
]
#unframe

?SECTION :msgtools:procs:devel:compile macro
#frame
#push :current^owner :result^ :result^text
#set :current^owner [#username [#fileinfo /owner/ [:msgtools:data:program^object]]]
[#if [#match [#username [#processinfo/paid/]] [:current^owner]]
   |then|
      :msgtools:procs:devel:change_source_version [:msgtools:procs:devel:get_compile_type]
      #output Compiling [:msgtools:data:devel:source^file] to [:msgtools:data:program^object]
      #output Please wait...
      TAL/IN [:msgtools:data:devel:source^file],pri 20,name, &
             out [:msgtools:data:devel:report^out] / &
             [:msgtools:data:program^object]
      #set :result^ [:_completion:completioncode]
      #set :result^text [:_completion:text]
      [#if [#compute ([:result^] < 5)]
         |then| FUP SECURE [:msgtools:data:program^object],"OOCO"
                [#if [#compute ([:result^] > 0)]
                   |then| #output Warnings issued, Result code was [:result^]
                          #output Check the output listing for errors at [:msgtools:data:devel:report^out]
                ]
                #output Compile completed.
         |else| #output Errors occurred, listing output...
                peruse ;job [:msgtools:data:devel:report^out];la;exit
                #output **Errors have occurred.
                #output Result code was [:result^]
                #output [:result^text]
                #output Output listing is at [:msgtools:data:devel:report^out]
      ]
   |else|
      #output The current owner of the target object file is [:current^owner]
      #output Either log on as that user or purge [:msgtools:data:program^object] before retrying.
]
#unframe

?SECTION :msgtools:procs:devel:devel_flood routine
#frame
== --------------------------------------------------------------------------
== Flood the message collector process with <nnn> messages. The <nnn> value
== must be passed to this procedure.
== --------------------------------------------------------------------------
#push :x^ :y^ :process^
#set :process^ [:msgtools:data:logger^process]
[#if [#argument/value :x^/number text end]]
[#if [#processexists [:process^]] |then|
   #output Stress Test.
   #output Writing [:x^] test messages to process [:process^], wait !
   #set :y^ 0
   #push #out
   #set #out [:process^]
   #output Stress test starting,[:x^] test messages expected
   [#loop |while| [#compute ([:y^] < [:x^])] |do|
      #output TEST DATA MESSAGE NUMBER [:y^] FOR [:process^]
      #set :y^ [#compute [:y^] + 1]
   ]
   #output Stress test ended.
   #pop #out
   #output Completed test.
|else|
   #output Target process [:process^] is not currently running.
]
#unframe

?SECTION :msgtools:procs:devel:devel_command routine
#frame
== --------------------------------------------------------------------------
==  Check the command the user entered. If its a valid command then
==  call the proc toprocess the request.
== --------------------------------------------------------------------------
#push :command^ :command^parms
[#case [#argument /value :command^/ word end]
   |    1    | [#case [:command^]
                  | HELP    | == **** Help request ****
                       [#if [#argument /value :command^parms/ word end]]
                       :msgtools:help:devel:help_processor [:command^parms]
                  | FLOOD   | == **** Flood the message collector with messages ****
                       [#case [#argument /value :command^parms/ number text end]
                          |    1    | :msgtools:procs:devel:devel_flood [:command^parms]
                          |otherwise| #output Command is incorrect.
                       ]
                  | COMPILE | == **** Compile a new version ****
                        :msgtools:procs:devel:compile
                  |OTHERWISE| == **** Not a valid command ****
                       #output Unknown development command...
               ]
   |otherwise| #output Unknown development command...
]
#unframe
