esrf

Beamline Instrument Software Support
SPEC Macro documentation: [ Macro Index | BCU Home ]

"""
#%TITLE%STLOCAL.MAC
#%NAME%
#Standard macros which can be used independ. of hardware
#%DESCRIPTION%
#In stlocal.mac you will find a collection of utilities to :
#%UL%
#%LI%edit macros
#%LI%for writting and reading local help
#%LI%print output to printer
#%LI%save and recall a configuration
#%LI%include the scan results in the data file
#%LI%save automatically the state file
#%LI%create and change the working directory
#%LI%define a new data group, using free number
#%LI%print tango error form the TANGO_ERR_STACK
#%XUL%
#%EXAMPLE%
#%PRE%
#emac mymacro (Calls your editor to modify mymacro)
#pon
#wa
#poff   (Prints all the motor positions to the printer)
#putconf mysetup2 (saves a configuration)
#getconf mysetup2 (recalls mysetup2)
#help local (shows local help)
#groupinit (globalvar,width,elemno) (inits the group and returns its number)
#ctu ( counts in a loop )
#%PRE%
#%SETUP%
#No setup is necessary but you can change the editor with
#      EDITOR="vi" if you do not want to use emacs
#%ATTENTION%
#This version of stlocal.mac is to be used with SPEC version 6.00.07 and later!
#Many ESRF specific macros like mv, ct have been eliminated by improvements
#made to the standard macros in standard.mac.
#The effort to keep all ESRF functionality was quite big, but we need to be
#vigilant to eliminate errors. All old code has been kept in this file as
#comments, which should allow tracking errors.
#%END%
"""

global STLOCALMACLOADED

global EDITOR
if (EDITOR=="") EDITOR="emacs"

#%UU%
#%MDESC%
#To clear an terminal window. Instead of tty_cntl("cl") use clscreen ...
#it will not loose the lift.
#
def clscreen() '{
    local ii
    for(ii = 0; ii < ROWS; ii ++) {
        print
    } # don`t do a clear screen as often visual information is lost.
}
'

#%UU%
#%MDESC%
#like wa but without dial
#def wu '{
#        local s[]
#        waitmove
#        get_angles
#        onp
#        printf("\nCurrent Positions  (user)\n")
#        s[0] = "mne"
#        s[1] = "user"
#        show_motor_info(s, 2, MOTORS)
#        offp;
#
#}
#'

#%UU%<macrosname>
#%MDESC%
#Edit a file
#
#def emacs '
#u emacs $1
#'

#%UU%<macroname>
#%MDESC%
#Edit a macro with emacs or another editor if the variable EDITOR is asigned

def emac '{
   global EDITOR
   local file

   if ((whatis("$1") & 2) == 0) {
      print "Usage:  emac macroname"
      exit
   }
   # give the file a suffix, so that editors can pick up the type.
   file = "/tmp/$1.mac"
   if (file_info(file, "-r") == 1) {
      unix(sprintf("rm %s", file))
   }
   on(file); offt
   prdef $1
   ont; close(file)

   unix(sprintf("%s %s", EDITOR, file))
   if (file_info(file, "-r") == 1) {
      fprintf(file,"\nu rm %s\n", file)
      close(file)
      qdofile(file)
   }
}
'

#%UU%<macroname(s)>
#%MDESC%
#Like prdef but uses a pager like more to display the macro
#definition. You can define PAGER to change the page to less or
#something else.

def moredef '
def cleanup \'undef cleanup;
   { tmpfile = "/tmp/tmp_" SPEC USER
          close(tmpfile)
     unix(sprintf("/bin/rm -f %s",tmpfile)) } \'
    {
    global PAGER
    tmpfile = "/tmp/tmp_" SPEC USER
    unix(sprintf("/bin/rm -f %s",tmpfile))
    on(tmpfile); offt
    prdef $*
    ont; close(tmpfile)
    unix(sprintf("%s %s",PAGER == 0 ? "more" :PAGER,tmpfile))
    cleanup
}
'


#%UU%
#%MDESC%
#Redefines the help macro. You can get local help with help
#local
def help '
        if ($#) {
          if ("$1" == "local") {
            if ($# > 1) { localhelp "$2" } else { localhelp }
            }
          else {
            gethelp("$1");
            }
          }
        else {
                local t
                for (t="help";;)
                        if (gethelp(t) || (t = input("\nSubject?  ")) == "")
                                break
        }
'

#%IU%
#%MDESC%[subject]
#used by help, Calls a shell script machelp (must be in your path)

def localhelp '
if ($#) {
  unix("machelp $*")
} else {
  unix("machelp")
}
'


#%UU%
#%MDESC%
#switch the printer on, every thing on the screen will
#be sent to the printer until you type poff
def pon '{
   local file
   file = sprintf("/tmp/pp%s%s.log", USER, SPEC)
   if (file_info(file, "-f") == 1)
      unix(sprintf("rm %s", file))
   on(file)
   printf("PRINTER LOGFILE at %s from %s",date(),USER)
}
'

#%UU%
#%MDESC%
#switch the printer off and print the screen output since
#the last pon command.
#    3/9/98 - MP - option o12 to reduce sligthly the output so
#                  that it fits in a page
def poff '{
   local file
   file = sprintf("/tmp/pp%s%s.log", USER, SPEC)
   close(file)
   unix(sprintf("lp -o12 %s", file))
}
'

#%UU%
#%MDESC%
#dump a window to the printer - works on HP
def dump '
    unix(sprintf("/usr/bin/X11/xwd -out /tmp/%s%sdump",USER,SPEC))
    unix(sprintf("/usr/bin/X11/xpr -device ps -out /tmp/%s%sdump.ps -gray 4 /tmp/%s%sdump",USER,SPEC,USER,SPEC))
    unix(sprintf("lp /tmp/%s%sdump.ps",USER,SPEC))
'

#%UU%[name]
#%MDESC%
#saves the current configuration file under the
#name [name]. If you do not give a name you will be asked for it
def putconf '
{
    if ($# == 0) {
        cname = getval("Save current configuration under which name","")
    }
    else {
        cname = "$1"
    }

    unix(sprintf("cp %s/%s/config %s/local/spec/userconf/%s", \
                    SPECD,SPEC,BLISSADM,cname))
    unix(sprintf("chmod 777 %s/local/spec/userconf/%s 2>/dev/null", \
                    BLISSADM,cname))
}
'

#%UU%[name]
#%MDESC%
#retrieves a saved configuration. The current
#configuration will be lost! If you do not give a name a list
#of all the saved configurations will be displayed and a name will
#be asked
def getconf '
{
    if ($# == 0) {
        unix(sprintf("ls -C %s/local/spec/userconf",BLISSADM))
        printf("Use which configuration ? ")
        cname = getval("")
    }
    else {
        cname = "$1"
    }

    wait(-1)
    user_waitall
    sync
    unix(sprintf("cp %s/local/spec/userconf/%s %s/%s/config", \
                 BLISSADM,cname,SPECD,SPEC))
}
reconfig
user_config
_assign
_assign_mono
'

#%UU%<macro>
#%MDESC%
#loads a macro from $(BLISSADM)/spec/macros
#
def jdo '{
    if (file_info(sprintf("%s/../../macros/$1",SPECD), "-r") == 1)
        qdofile(sprintf("%s/../../macros/$1",SPECD))
    else
        qdofile(sprintf("%s/../../macros/$1.mac",SPECD))
}
'

#%UU%<macro>
#%MDESC%
#loads a macro from $BLISSADM/local/spec/macros
#
def udo '{
    if (file_info(sprintf("%s/local/spec/macros/$1",BLISSADM), "-r") == 1)
        qdofile(sprintf("%s/local/spec/macros/$1",BLISSADM))
    else
        qdofile(sprintf("%s/local/spec/macros/$1.mac",BLISSADM))
}
'

#%UU%
#%MDESC%
#Saves the motor positions in a file called recover.mac and defines a
#macro %B%recover%B%to recover them later.
#
def usersave '{
   local macfile
   wait(-1); user_waitall; sync; get_angles
   macfile=sprintf("%s/%s/userfiles/recover.mac",SPECD,SPEC)
   unix(sprintf("/bin/rm -f %s;umask 000;touch %s",macfile, macfile))
   if (open(macfile)) exit
   fprintf(macfile,"# positions saved by %s (%s) on %s.\n", USER, SPEC, date())
   fprintf(macfile,"def setback \'chg_dial(\$1,dial(\$1,\$2))\'\n")
   for (i=0;i<MOTORS;i++) {
      fprintf(macfile,"setback %s %g\n",motor_mne(i),A[i])
   }
   close (macfile)
   rdef recover \'qdofile(sprintf("%s/%s/userfiles/recover.mac",SPECD,SPEC))\'
   comment "Motor positions saved by %s (%s)" USER,SPEC
   print "Use `recover\' to recover."
}
'


#%UU%command
#%MDESC%
#Tells you the unix time the macro execution took
def bench '{
    local oldtime tunits tlabel
    oldtime=time()
    $*
    oldtime = time()-oldtime
    if (oldtime > 60) {
        tunits = 60; tlabel="min."
    } else if (oldtime > 1) {
        tunits = 1; tlabel="sec."
    } else {
        tunits = 0.001; tlabel="msec."
    }
    printf("Execution time: %10.2f %s\n",oldtime/tunits, tlabel)
}
'

#%UU%[problem description]
#%MDESC%
#Sends a mail to the spec administrator and asks for help

def cryforhelp '
{
    local descr
    if ((time()/60-CRYLAST)<CRYMAXFREQ) {
        printf("A little patience, please - You cried only %d Minutes ago\n",\
          time()/60-CRYLAST)
        printf("Retry in %d minutes\n",1+CRYMAXFREQ-(time()/60-CRYLAST))
        exit
    }

    if (!$#) {
        print "This macro mails a cry for help to your spec administrator"
        descr=getval("Tell me why","")
    } else {
        descr ="$*"
    }
    if (descr == "") {
        if (!yesno("Really send cry for help with no description",0)) {
            exit
        }
    }
    unix("echo CRY FOR HELP >/tmp/,xx")
    fprintf("/tmp/,xx","\nfrom %s (%s,%s) on %s at %s\n%s\n",\
       USER,SPEC,TERM,CRYBL,date(),descr)
    if ((CRYTEL != "0")&&(CRYTEL != "")) \
        fprintf ("/tmp/,xx","Tel: %s\n\nSysinfo:\n",CRYTEL)
    else fprintf ("/tmp/,xx","\n\nSysinfo:\n",CRYTEL)
    unix("echo hostname: `hostname` >>/tmp/,xx")
    unix("echo id: `id` >>/tmp/,xx")
    close("/tmp/,xx")
    if (unix(sprintf("cat /tmp/,xx | rmail %s",\
        ((CRYREC=="0")||(CRYREC==""))?"icntl@esrf.fr":CRYREC))) {
        print "Something went wrong, could not send mail"
    } else {
        print "I will try to get some help for you"
        CRYLAST=time()/60
    }
}
'

#%UU%[beamline [mail-address [tel-number]]]
#%MDESC%
#Sets some global variables for the cryforhelp macro. The [beamline]
#is for information only. I mail-address is not given, the mail is
#sent to icntl@esrf.fr. The tel-number is given for information only

def crysetup '
global CRYBL CRYREC CRYTEL CRYMAXFREQ CRYLAST
CRYMAXFREQ=15
if ($#) {
   CRYBL="$1"
   CRYREC="$2"
   CRYTEL="$3"
} else {
   CRYBL=getval("Enter the name of your beamline",CRYBL)
   CRYREC=getval("Mail address of spec administrator",CRYREC)
   CRYTEL=getval("Enter the telephon number you can be called back",CRYTEL)
}
'

### BEGIN ### Macros inserted by P.Fajardo (11/4/95)

#%IU%
#%MDESC%
#Prints the scan results in the data file
#
def resultline  'printf("#R %d %g %g %g %g %g %g\n", \
                SCAN_N, pl_xMAX, pl_MAX, pl_FWHM, pl_CWHM, pl_COM, pl_SUM);'

#%UU%
#%MDESC%
#Activates the saving of the scan results in the data file (`#R' line).
#
def resultlinesetup '{
   cdef("Ftail", "resultline\n", "resultline")
   setup_tail("resultline")
}
'

#%IU%
#%MDESC%
#Used to deactivate the result line saving set by %B%resultlinesetup%B%.
#
def resultlineunsetup '{
   cdef("", "", "resultline", "delete")
}
'

#%UU%[dummy-parameter]
#%MDESC%
#Saves the state file. The optional dummy parameter is internally used by
#the automatic saving feature activated by %B%saveallsetup%B%.
#
def saveall '{
   local _now

   _now=time()
   if ($# == 0 || _now - SAVEDTIME > SAVINGTIME || _now < SAVEDTIME) {
      savstate
      printf("\n%s\'s state is stored\n", USER)
      SAVEDTIME = _now
   }
}
'

#%UU%[saving-time]
#%MDESC%
#Activates the automatic storage of the state file. The state file is saved
#to disk if the elapsed time since the last storage is longer than
#`saving-time' in seconds (defaulted to 5 minutes).
#
def saveallsetup '
   global   SAVEDTIME

   if ($# > 0)
      constant SAVINGTIME $1
   else
      constant SAVINGTIME 300

   SAVEDTIME = time()

   cdef("prompt_mac","saveall SAVINGTIME\n","saveall")
   cdef("Ftail","offd;ont;saveall SAVINGTIME;ond;offt\n","saveall")
   setup_tail("saveall")
'

#%IU%
#%MDESC%
#Used to cancel the `saveall' feature by the unsetup procedure.
#
def saveallunsetup '
   unglobal SAVEDTIME
   cdef("", "delete", "saveall")
'

#%UU%
#%MDESC%
#Changes current directory to the SPEC working directory. The SPEC working
#directory is defaulted to $HOME/data and can be modified by %B%newdir%B%.
#
def chgwd '
   if (DATA_DIR == 0 || DATA_DIR == "./data") {
      DATA_DIR = sprintf("%s/data", HOME)
   }
   if (!chdir(DATA_DIR)) {
      printf("WARNING: Can\'t change directory to \`%s\'.\n", DATA_DIR)
      chdir(".")
   }
'

#%UU%[directory]
#%MDESC%
#Sets a new SPEC working directory. If the directory doesn't exist, the
#macro prompts the user before create it.
#
def newdir '{
   local n names dir

   if (DATA_DIR == 0 || DATA_DIR == "") DATA_DIR = CWD
   _2 = DATA_DIR

   if ($# == 0) {
      _1 = getval("\nWorking directory", _2)
   } else {
      _1 = "$1"
   }
   if (_1 == "")
      exit

   n = split(_1, names, "/")
   if (names[0] == "~") names[0] = HOME
   dir = ""
   for (i=0; i<n; i++) {
      dir = sprintf("%s%s/", dir, names[i])
      if (file_info(dir, "-d") != 1) {
         if(yesno(sprintf("\"%s\" is not an existing directory, create", dir) \
                                                                          ,1)) {
            if (unix(sprintf("mkdir %s", dir)) != 0)
               exit
         } else {
            chdir(DATA_DIR)
            exit
         }
      }
   }
   if (chdir(dir))
      DATA_DIR = CWD
}
'

#%UU%
#%MDESC%
#Lets the user change the SPEC working directory and select a new file.
#This macro should be used instead of %B%newfile%B%if %B%chgwd%B%is
#executed by the startup macros.
#
def newsession '
   newdir
   print "\nCurrent files in directory:"
   print   "--------------------------"
   unix("ls -F")
   newfile
'

if (!(whatis("preunsetup")&2)) rdef preunsetup ""
rdef unsetup ""

cdef("config_mac", "resetup\n", "_setup_")

global SEDITOR
if (!SEDITOR) SEDITOR = "nedit -xrm 'nedit.saveOldVersion: False' -lm Spec"

#%UU%["g"]
#%MDESC%
#Runs the setup editor and executes %B%reconfig%B%and the setup files if
#they exist.%BR%
#Two files contain the local setup: a global setup that is common to all
#SPEC versions in the same installation and a particular setup file that
#is independent for each version of SPEC.
#By default the editor loads the particular setup file. Whith the parameter
#`g' the global setup file is loaded.
#
def setup '{
   local file editor

   wait(-1)
   user_waitall
   sync

   editor = SEDITOR

   if (GUI_RUN)
      editor = sprintf("xterm -tn vt100 -e %s", editor)

   if (SEDITOR == "vuepad")
      editor = "/usr/vue/bin/vuepad"

   # Check if setup files exist and run editor to modify
   if ("$1" == "g") {
      file = sprintf("%s/setup", SPECD)
   } else {
      file = sprintf("%s/%s/setup", SPECD, SPEC)
   }

   if (file_info(file) != 1)
      unix(sprintf("(umask 0; touch %s)", file))


   if (file_info(file, "-w") != 1) {
      if (file_info(".", "-f") == 1)
         printf("You do not have the right to change %s.\n", file)
      else
         printf("File %s does not exist.\n", file)
      print "Press any key."
      input(1)
   } else if (!file_info(file, "size")) {
      fprintf(file,"#\n")
      fprintf(file,"# Add or modify setup lines.\n")
      fprintf(file,"# Comment out the lines you want to cancel temporarily.\n")
      fprintf(file,"#\n")
      close(file)
   }
   if (GUI_RUN)
      print "Starting setup editor. " \
            "Close editor window to go back to the SPEC prompt."
   unix(sprintf("%s %s", editor, file))

   reconfig
}
'

#%UU%
#%MDESC%
#Executes the setup files if they exist.%BR%
#
def resetup '{
   global SETUP SETUP_N
   local gfile lfile gsetup lsetup

   cdef("config_mac", "resetup\n", "_setup_")
   preunsetup; rdef preunsetup ""; SETUP=1; SETUP_N++
   cdef("prompt_mac", "resetup_end\n", "_setup_")
   rdef cleanup "SETUP=0"
   lsetup = (file_info(lfile = sprintf("%s/%s/setup",SPECD,SPEC), "-r") == 1)
   gsetup = (file_info(gfile = sprintf("%s/setup",SPECD), "-r") == 1)
   if (gsetup || lsetup) {
      printf ("\nDoing SETUP.\n")
      if (lsetup) qdofile(lfile)
      if (gsetup) qdofile(gfile)
   }
}
'

#%IU%
#%MDESC%
#Used by resetup.
#
def resetup_end '
{
   if (!SETUP) {
      print "\nAn error happened during SETUP. The current setup may be not ok."
      print "Fix the problem and run \`resetup\'."
   } else {
      unsetup
      rdef unsetup ""
   }
   SETUP=0
   cdef("prompt_mac","","_setup_","delete")
}
'

#%IU%(<name>, [<parameter_string>])
#%MDESC%
#Macro function to be included in `xxxsetup' macros to implement the
#unsetup functionalities.%BR%
#When a `xxxsetup' macro is removed from one of the setup files, the macro
#`<name>unsetup' is invoked with the parameters contained
#in <parameter_string>. If <parameter_string> is omitted, the string "<name>"
#is user as only parameter for `<name>unsetup'.
#
def setup_tail(name, pars) '{
   local _key unsetmacro

   unsetmacro = sprintf("%sunsetup", name)
   _key[0] = name
   if (whatis("name") & 0x08000000) {
      print "ERROR: wrong use of `setup_tail\'"
      print "Usage: setup_tail(name, [parameters_as_a_string])"
   } else if (SETUP) {
      if (!(whatis("pars") & 0x08000000)) {
         unsetmacro = sprintf("%s %s", unsetmacro, pars)
         split(pars, _key)
      }
      cdef("preunsetup", sprintf("cdef(\'unsetup\', \'%s\n\', \'%s\')\n", \
                                                        unsetmacro, _key[0]))
      cdef("unsetup","",_key[0],"delete")
   }
}
'

#%IU%<macro> <first_word>
#%MDESC%
#Checks if a macro called <macro> that begins with <first_word> is already
#defined. If it is, the local variable `error' is set.
#
def checkifmacro '
   local error __type
   error = 1
   __type = whatis("$1")
   if (!__type || __type & 0x08000004) {
      error = 0
   } else if (__type & 2) {
      local file line word
      file = sprintf("/tmp/checkifmacro_%s", USER)
      unix(sprintf("rm -f %s", file))
      on(file); offt; prdef $1; ont ; close(file)
      getline(file, 0)
      while ((line = getline(file)) != -1) {
         if (substr(line, 1, 3) == "def") {
            sscanf(line, "def $1 \'%s", word)
            if (word == "$2")
               error = 0
            break
         }
      }
      getline(file, "close")
   }
'

#%UU%<macro_name> <motor> [<motor> ...]
#%MDESC%
#Lets the user define new `where' macros in an easy way. A macro called
#<macro_name> is internally defined that displays the positions of the
#specified motors. If <macro_name> is invoked with a parameter more
#detailed information is presented.
#
def newwh '{
   if ($# < 2) {
      if (SETUP) print "Error in line: $0 $*"
      print "Usage:  newwh macro_name motor [motor ...]"
   } else {
      checkifmacro $1 _new_wh
      if (!error) {
         rdef $1 "_new_wh \$# $*"
         setup_tail("newwh", "newwh$1 $1")
      } else {
         print "Macro \`$1\' is already defined. Ignoring \`newwh\'."
      }
   }
}
'

#%IU%"newwh<macro_name>" <macro_name>
#%MDESC%
#Undefines the macros created by %B%newwh%B%.
#
def newwhunsetup '{
   checkifmacro $2 _new_wh
   if (!error) undef $2
}
'

#%IU%<motor> [<motor> ...]
#%MDESC%
#Used by %B%newwh%B%to display the motor positions.
#
def _new_wh '{
    local mlist newmlist n ngood nbad i j

   n = split("$*", mlist) - 2
   ngood = 0
   for (i=0; i<n; i++) {
       newmlist[ngood] = motor_num(mlist[i+2])
      if (newmlist[ngood] >= 0) {
          if (!is_using_motor(newmlist[ngood])) {
         continue;
          }
          ngood++
      } else {
          nbad ++
         printf("\`%s\' ", mlist[i+2])
      }
   }
    if (nbad > 1)
   print "are not valid motor mnemonics."
    else if (nbad == 1)
   print "is not a valid motor mnemonic."

    if (ngood > 0) {
        local row ncol col m
        get_angles
        i = 0
        for (row = 0; row <= int((ngood-1)/6); row++) {
            ncols = ngood - 6*row > 6? 6 : ngood - 6*row
            for (col=0; col<ncols; col++) {
                while(newmlist[i] < 0) i++;
                m[col] = newmlist[i++]
         }
         printf("%13s", "")
            for (col=0; col<ncols; col++)
                printf("%10.9s ", motor_name(m[col]))
            printf("\n%13s", "")
            for (col=0; col<ncols; col++)
                printf("%10.9s ", motor_mne(m[col]))
            if ($1) {
                printf("\n%13s", "(high)")
                for (col=0; col<ncols; col++)
                    printf("%10.4f ", user(m[col], get_lim(m[col], 1)))
            }
            printf("\n%13s", "User      ")
            for (col=0; col<ncols; col++)
                printf("%10.4f ", A[m[col]])
            if ($1) {
                printf("\n%13s", "(low)")
                for (col=0; col<ncols; col++)
                    printf("%10.4f ", user(m[col], get_lim(m[col], -1)))
                print
                printf("\n%13s", "(high)")
                for (col=0; col<ncols; col++)
                    printf("%10.4f ", get_lim(m[col], 1))
         }
            printf("\n%13s", "Dial      ")
            for (col=0; col<ncols; col++)
                printf("%10.4f ", dial(m[col], A[m[col]]))
            if ($1) {
                printf("\n%13s", "(low)")
                for (col=0; col<ncols; col++)
                    printf("%10.4f ", get_lim(m[col], -1))
         }
            print "\n"
      }
   }
}
'

#%UU%<macro_name> <motor> [<motor> ...]
#%MDESC%
#Lets the user define new `where' macros in an easy way. A macro called
#<macro_name> is internally defined that displays the user positions of the
#specified motors. If <macro_name> is invoked with a parameter more
#detailed information is presented.
#
def newwu '{
   if ($# < 2) {
      if (SETUP) print "Error in line: $0 $*"
      print "Usage:  newwu macro_name motor [motor ...]"
   } else {
      checkifmacro $1 _new_wu
      if (!error) {
         rdef $1 "_new_wu \$# $*"
         setup_tail("newwu", "newwu$1 $1")
      } else {
         print "Macro \`$1\' is already defined. Ignoring \`newwu\'."
      }
   }
}
'

#%IU%"newwu<macro_name>" <macro_name>
#%MDESC%
#Undefines the macros created by %B%newwu%B%.
#
def newwuunsetup '{
   checkifmacro $2 _new_wu
   if (!error) undef $2
}
'

#%IU%<limits_flag> <dummy> <motor> [<motor> ...]
#%MDESC%
#Used by %B%newwu%B%to display only the motor user positions (not dial ones).
#
def _new_wu '{
   local mlist n ngood i

   n = split("$*", mlist) - 2
   ngood = 0
   for (i=0; i<n; i++) {
      if ((mlist[i] = motor_num(mlist[i+2])) >= 0)
         ngood++
      else
         printf("\`%s\' ", mlist[i+2])
   }
   if (ngood < n-1)
      print "are not valid motor mnemonics."
   else if (ngood == n-1)
      print "is not a valid motor mnemonic."

   if (ngood > 0) {
      local row ncol col m
      get_angles
      i = 0
      for (row = 0; row <= int((ngood-1)/6); row++) {
         ncols = ngood - 6*row > 6? 6 : ngood - 6*row
         for (col=0; col<ncols; col++) {
            while(mlist[i] < 0) i++;
            m[col] = mlist[i++]
         }
         printf("%13s", "")
         for (col=0; col<ncols; col++)
            printf("%10.9s ", motor_name(m[col]))
         printf("\n%13s", "")
         for (col=0; col<ncols; col++)
            printf("%10.9s ", motor_mne(m[col]))
         if ($1) {
            printf("\n%13s", "(high)")
            for (col=0; col<ncols; col++)
               printf("%10.4f ", user(m[col], get_lim(m[col], 1)))
         }
         printf("\n%13s", "User      ")
         for (col=0; col<ncols; col++)
            printf("%10.4f ", A[m[col]])
         if ($1) {
            printf("\n%13s", "(low)")
            for (col=0; col<ncols; col++)
               printf("%10.4f ", user(m[col], get_lim(m[col], -1)))
            print
         }
         print "\n"
      }
   }
}
'

if (!(whatis("warningprompt") & 2)) rdef warningprompt ""

#%UU%<warningmacro> [<warningmacro> ...]
#%MDESC%
#Includes warning macros in %B%prompt_mac%B%. Warning macros display
#warning messages on the screen immediately before the prompt.%BR%
#For instance if a line %B%warningsetup datawarning%B%is included
#in your setup file, you will get amessage whenever no data file
#is selected.%BR%
#If you want to include your own warning macros, see %B%simulwarning%B%
#and %B%datawarning%B%as examples
#
def warningsetup '{
   local n maclist mac

   for (mac="", n = split("$*", maclist); n>0;)
      mac = sprintf("%s; %s", maclist[--n], mac)

   rdef warningprompt mac
   cdef("prompt_mac","warningprompt\n","warning",0x20)

   setup_tail("warning")
}
'

#%IU%
#%MDESC%
#Used to deactivate the warning macros by the automatic unsetup procedure.
#
def warningunsetup '
   rdef warningprompt ""
   cdef("prompt_mac", "delete", "warning")
'

#%IU%
#%MDESC%
#Warning macro that reminds the user that SPEC is in simulation mode.
#The macro name must be used as a parameter to warningsetup.
#
def simulwarning '
   if (set_sim(-1)) { tty_cntl("md");printf("\n- SIMULATION");tty_cntl("me") }
'

#%IU%
#%MDESC%
#Warning macro that reminds the user that there is no data file selected
#in SPEC. The macro name must be used as a parameter to warningsetup.%BR%
#This macro also displays a warning message if the data file is
#called %B%dummy%B%.
#
def datawarning '
   if (DATAFILE=="/dev/null" || DATAFILE=="") {
      tty_cntl("md");printf("\n- NO DATA FILE");tty_cntl("me")
   } else if (substr(sprintf("/%s",DATAFILE),length(DATAFILE)-4,6) == "/dummy") {
      tty_cntl("md");printf("\n- DUMMY DATA FILE");tty_cntl("me")
      DUMMYFILE["dummyscan"] = SCAN_N
   }
'

#%UU%
#%MDESC%
#Changes the data file to a file called `dummy' in the current working
#directory. The name and the scan number of the previous data file is kept
#in memory and can be restored with %B%offdummy%B%.%BR%
#The macros %B%ondummy%B%and %B%offdummy%B%are provided as a fast way of
#switching files when one wants to run some scans (alignment checking,
#tests of command or macro files, ...) and don't want to fill up the data
#file with garbage.
#
def ondummy '{
   global DUMMYFILE
   local scan

   if (substr(sprintf("/%s",DATAFILE),length(DATAFILE)-4,6) != "/dummy") {
      DUMMYFILE["oldfile"] = DATAFILE
      DUMMYFILE["oldscan"] = SCAN_N
      if (file_info("dummy","-r") != 1)
         DUMMYFILE["dummyscan"] = 0
      if ((scan = DUMMYFILE["dummyscan"]) > 0)
         scan = 10*(int((scan+1)/10)+1)-1

      qcomment "Switching to data file `dummy\' scan %d" scan+1
      newfile dummy scan
      DUMMYFILE["dummyscan"] = SCAN_N
   }
}
'


#%UU%
#%MDESC%
#Restores the previous data file name if %B%ondummy%B%was used.
#
def offdummy '{
   local oldfile scan
   if (substr(sprintf("/%s",DATAFILE),length(DATAFILE)-4,6) == "/dummy") {
      oldfile = DUMMYFILE["oldfile"]
      scan = 10*(int((DUMMYFILE["oldscan"]+1)/10)+1)-1
      if (oldfile != "") {
         if (file_info(oldfile, "-r") != 1) {
            printf("I don\'t find the previous datafile: %s\n", oldfile)
            oldfile = "/dev/null"
            scan = 0
         }
      } else {
         print "No previous datafile."
         oldfile = "/dev/null"
         scan = 0
      }
      DUMMYFILE["dummyscan"] = SCAN_N
      qcomment "Switching back to data file `%s\' scan %d" "oldfile,scan"
      newfile \'"oldfile"\' scan
   }
}
'

#%UU%
#%MDESC%
#It is exactly like the one in standard.mac but keeping the file name even
#if the scan number is zero.
#def newfile '
#  _3 = DATAFILE
#  if ($# == 0) {
#     _1 = getval("\nData file", DATAFILE)
#     _2 = 0
#  } else {
#     _1 = "$1"; _2 = $2
#  }
#  if (_1 == "")
#     exit
#  if (_1 == "null")
#     _1 = "/dev/null"
#  if (!index(_1, "/") && file_info(DATA_DIR, "-d"))
#     _1 = sprintf("%s/%s",DATA_DIR,_1)
#
#  if (!file_info(_1, "-r")) {
#     if (_1 == DATAFILE) {
#        printf(\
#  "Warning:  Same name as before, but file isn\'t in %s directory.\n",\
#                   index(_1, "/")? "same":"current")
#        printf("A new version of the file will be created.\n")
#     }
#     DATAFILE="/dev/null"
#  } else if (_1 != "/dev/null") {
#     local s
#     if (file_info(s = SPECD "/chk_file", "-x"))
#        if (unix(sprintf("%s %s",s,_1)))
#           print "Note:  file already exists."
#  }
#  #
#  if (open(_1)) {
#     DATAFILE="/dev/null"
#     exit
#  }
#  if (_1 != "tty" && _1 != "/dev/tty")
#     close(_1)
#  if (_1 == DATAFILE) {
#     if (open(_1)) {
#        DATAFILE="/dev/null"
#        exit
#     }
#     if ($# == 2 && SCAN_N != $2)
#        SCAN_N = $2
#  } else {
#     if ($# == 0)
#        _2 = getval("Last scan number",_2)
#     else if ($# == 1)
#        _2 = 0
#     if (_3 != "" && _3 != "0" && _3 != "tty" && _3 != "/dev/tty")
#        close(_3)
#     if (open(_1)) {
#        DATAFILE="/dev/null"
#        exit
#     }
#     DATAFILE = _1
#     SCAN_N = _2
#     ond; offt
#     printf("#F %s\n", DATAFILE)
#     printf("#E %d\n",EPOCH)
#     printf("#D %s\n",date())
#     printf("#C %s  User = %s\n",TITLE,USER)
#     user_filehead
#     local i,j,s
#     for (i=0; i<MOTORS; i+=8) {
#        s = sprintf("#O%d ", i/8)
#        for (j=i; j<i+8 && j<MOTORS;) {
#           if (motor_name(mA[j]) != "unused")
#              s = s sprintf("%8s", motor_name(mA[j]))
#           if (j%8 == 7)
#              break
#           s = s "  "
#           j++
#        }
#        print s
#     }
#     offd; ont
#  }
#  printf("Using \"%s\".  Next scan is number %d.\n",DATAFILE,SCAN_N+1)
#'

#%UU%[<parameter>]
#%MDESC%
#  Activates wizard mode for 10 minutes. If a positive parameter is provided
#  the  effect is permanent until %B%offwiz%B%is used. If <parameter> is
#  null or negative its absolute value becames the time out in seconds
#  instead of the 10 minutes default.
#def onwiz '
#   if (!spec_par("specwiz")) {
#      if (spec_par("specwiz",1)) {
#         local timeout
#         timeout = 600
#         if ($#) {
#            if (($1) == "$1" && ($1) <= 0)
#               timeout = -($1)
#            else
#               timeout  = -1
#         }
#         if (timeout >= 0) {
#            global WIZ_TIME
#            local wiz_timer
#            wiz_timer = \
#               \'if(time()>WIZ_TIME) {cdef("","","onwiz","delete");offwiz}\n\'
#            WIZ_TIME = time() + timeout
#            cdef("prompt_mac",wiz_timer,"onwiz")
#         } else
#            cdef("","","onwiz","delete")
#      }
#   }
#'
#
cdef("user_offwiz")

#%UU%
#%MDESC%
#Deactivates wizard mode set by %B%onwiz%B%. In replacement of the macro in
#standard.mac, as we use a user_offwiz macro. onwiz in standard.mac!
def offwiz 'spec_par("specwiz",0);user_offwiz;unglobal WIZ_TIME'

### END ### Macros inserted by P.Fajardo (11/4/95)

#%IU%[<motorgroup>]
#%MDESC%
#Handling of motor groups for the redefined %B%config%B%macro.
#<BR>
#This macro is used by the ESRF specific config macro, as we still need
#to support so-called motor groups. They use a feature of SPEC where you can
#define geometries with linked config files. This is a misuse of this
#geometry feature, but it is still used on some beamlines :-(
def config_geo '
   unix(sprintf("umask 0;grep GEO %s/%s/config|cut -f3 -d\' \'>/tmp/geos", \
                 SPECD, SPEC))
   if ("$1" != "all") {
      local geo ngeo geolist goodgeo geoindx
      goodgeo = 0
      ngeo = 0
      sscanf(getline("/tmp/geos", 0), "%s", geo)
      if (geo == "all") while(1) {
         sscanf(getline("/tmp/geos"), "%s", geo)
         if (geo != -1) {
            geolist[ngeo++] = geo
            if (geo == "$1") goodgeo = ngeo
         } else
            break
      }
      getline("/tmp/geos", "close")
      if (ngeo > 0 && goodgeo == 0) {
         printf("\nMotor groups currently defined are:\n\n\t")
         for (gindx = 0; gindx < ngeo; gindx++) printf("%s ", geolist[gindx])
         geo = getval("\n\nSelect motor group", "all")
         for (gindx = 0; gindx < ngeo; gindx++)
            if (geo == geolist[gindx])
               goodgeo=gindx+1
         if (goodgeo == 0 && geo != "all") {
            print "Not a valid motor group."
            exit
         }
      }
      if (goodgeo > 0)
         $2 = sprintf("-g %s", geolist[goodgeo-1])
   }
'

#%UU%[<motorgroup>]
#%MDESC%
#Redefinition of the standard %B%config%B%macro to include motor groups and
#specGUI configuration.
#<BR>
#This macro uses the config_geo macro, as we still need
#to support so-called motor groups. They use a feature of SPEC where you can
#define geometries with linked config files. This is a misuse of this
#geometry feature, but it is still used on some beamlines :-(
def config '{
   local comm simswitch geoswitch

   geoswitch = ""
   config_geo $1 geoswitch
   simswitch = set_sim(-1)? "-s":""

   comm = sprintf("%s/edconf %s %s %s/%s",SPECD,simswitch,geoswitch,SPECD,SPEC)
   wait(-1)
   user_waitall
   sync
   if (GUI_RUN) {
      print "\nStarting configuration editor. "   \
            "Use \`^c\' to close and go back to this window."
      unix(sprintf("xterm -T \"Spec configuration (%s)\" -e %s", SPEC, comm))
   } else
      unix(comm)
   reconfig
}
   user_config
   _assign
   _assign_mono
   if (GUI_RUN) {
      X_config
   }
'

#%UU%(group , points , width)
#%MDESC%
def groupinit (g, pts, w) '{

  if (g==0) {
    for ( i=0 ; (i<256)&&data_info(i,"elem") ; i++) {}
    g=i
  }
  if (g<256) {
    data_grp(g,pts,w)
    return g
  }
  else return -1
}
'

#%UU%
#%MDESC%
#Redefinition of the standard %B%newmac%B%macro to make it compatible
#with the %B%jtdo()%B%function used in startup files.
def newmac '{
        local t
        unglobal TIMEMARK

        t = sprintf("%s/site.mac", SPECD)
        if (unix(sprintf("test ! -f %s", t))) {
                qcomment "do %s" t
                qdofile(t)
        }
        t = sprintf("%s/site_f.mac", SPECD)
        if (unix(sprintf("test ! -f %s", t))) {
                qcomment "do %s" t
                qdofile(t)
        }
        t = sprintf("%s/%.4s.mac", SPECD, SPEC)
        qcomment "do %s" t
        qdofile(t)
        t = sprintf("%s/standard.mac", SPECD)
        qcomment "do %s" t
        qdofile(t)
}
'


#%IU%([<machine>, [<stderr>]])
#%MDESC%
#This macro function returns a string describing the operating system of
#the computer called <machine>. The user must have permission to remote
#execution on <machine>. If the user has no permission or an error occurs
#the function returns -1. If <machine> is omitted or null, the function
#checks the local host.%BR%
#Current OS descriptions are: %B%hp700%B%, %B%sun4%B%, %B%solaris%B%and%B%linux%B%.%BR%.
#It is possible to redirect the standard error stream by specifying a file
#or device in <stderr>.
def ostype(machine, stderr) '{
   global HT
   local remuname file os vers

   oscomm = "case `uname -s` in
                SunOS ) if expr `uname -r` : 4* >/dev/null; then
                        echo sun4   ; exit 1
                   else
                        echo solaris; exit 2
                   fi;;
                HP-UX ) echo hp700  ; exit 3;;
                Linux ) echo linux  ; exit 4;;
            esac;       echo unknown; exit 0"
   HT = unix(sprintf("(%s) >/dev/null", oscomm))
   HT = HT==1?"sun4":HT==2?"solaris":HT==3?"hp700":HT==4?"linux":"unknown"

   if (machine == "")
      return(HT)
   else {
      file = "/tmp/ostypespec"
      if(remcom(machine, "uname -rs", "", file, stderr)) return(-1)
      remuname = getline(file, 0)
      getline(file, "close")
      os = substr(remuname, 1, 5)
      vers = substr(remuname, 7)
      return(os=="HP-UX"?"hp700":os=="Linux"?"linux":os!="SunOS"?"unknown":vers<5?"sun4":"solaris")
   }
}
'

#%IU%(<machine>, <command>, [<username>, [<stdout>, [<stderr>]]])
#%MDESC%
#This macro function tries to execute remotely <command> in the computer
#called <machine>. The user must have permission to remote
#execution on <machine>. If the <machine> is accessible and the user has
#the right permission the function returns 0, otherwise it returns -1.%BR%
#If <machine> is null, the function runs <command> on the local host.%BR%
#If <username> is not null the command is run as a different user in the
#remote computer.%BR%
#The standard output and standard error streams may be redirected by
#especifying not null values for <stdout> and/or  <stderr> respectively.
def remcom(machine, command, username, stdout, stderr) '{
   local remsh rcom get_environment

   if (command == "") return(0)

   if (stdout != "")   stdout = sprintf(">%s", stdout);        else stdout=""
   if (stderr != "")   stderr = sprintf("2>%s", stderr);       else stderr=""
   if (username != "") username = sprintf("-l %s", username);  else username=""
   if (machine == "") machine = "`hostname`"
   get_environment = ". /etc/profile >/dev/null 2>&1"
   rcom = sprintf("umask 0;%s %s %s \"%s ; %s\" %s %s",  \
                    ostype()=="hp700"?"remsh":"rsh", \
                    machine, username, get_environment, command, stdout, stderr)
   return(unix(rcom))
}
'

#%UU%[count-time [sleep-time]]
#%MDESC%
#It performs a counting loop and updates the value of the counters
#on the screen.
def ctu '{
     local ctu_time ctu_sleep

     if ($# > 0) ctu_time = $1 ; else ctu_time = 1
     if ($# > 1) ctu_sleep = $2  ; else ctu_sleep = 0

     rdef cleanup \'
           undef cleanup
           onp; show_cnts;offp
     \'
     waitmove
     for (;;) {
        local iter
        count_em ctu_time
        waitcount;get_counts
        clscreen()
        iter++
        printf("%s %s. - Loop count: %4d\n","$0","$*",iter)
        printf("------------------------------------------\n")
        for (i=0;i<COUNTERS;i++)
                if (cnt_name(i) != "unused")
                        printf("%12s = %g%s\n", cnt_name(i), S[i], \
                   i != sec && S[sec]? sprintf(" (%g/s)", S[i] / S[sec]):"")
        sleep(ctu_sleep)
     }
}
'

#%UU%(arr, x, y, nb, val)
#%MDESC%This macro will use calculate an approximate y value for val. This
#approximation will simply be done by a linear interpolation of the
#table given with the first 3 parameters. Nb is the number of elements in
#the array. ( You can use array_read (file,arr) to read these values
#from a file)
def interpol(arr, x, y, nb, val) '{
  local i xgrows

  xgrows = (arr[nb-1][x] >= arr[0][x])
  for (i=0; i<nb; i++) {
    if (val == arr[i][x])
      return (arr[i][y])
    if (xgrows && val < arr[i][x] || !xgrows && val > arr[i][x])
      break;
  }
  if (i == 0)
    return(arr[0][y])
  else if (i == nb)
    return (arr[nb-1][y])
  else
    return (arr[i-1][y] + (arr[i][y] - arr[i-1][y]) * \
           (val - arr[i-1][x])/(arr[i][x] - arr[i-1][x]) )
}
'


#%UU% speccmd pipeop unixcmd
#%MDESC%executes speccmd and uses the pipeoperator to connect its output
#to a shell command. Ex.: prun prdef wa | sed -n -e '/loop/p' | wc or
#already more useful things like : prun prdef | grep user_
def prun '
    cmd =   \'$*\'
    idx =   index(cmd, "|")
    idx2=   index(cmd, ">")
    if (idx2 && (!idx || idx2 < idx)) idx=idx2
    if (!idx) {
        unix(cmd)
        exit
    }

    rnum = rand()
    pfile="/tmp/pipe-" SPEC "." rnum
    rdef speccmd substr(cmd, 1, idx-1)
    unix(sprintf("/bin/rm -f %s", pfile)) ; on(pfile) ; offt
    cdef("cleanup_once", \
         "close(pfile); unix(sprintf(\"/bin/rm -f %s\", pfile)); ont; \
            unglobal pfile rnum;\n", "wdx")
    speccmd
    close(pfile) ; ont
    unix(sprintf("cat %s %s", pfile, substr(cmd,idx)))
    #unix(sprintf("/bin/rm -f %s", pfile) # done by cleanup_once
    unglobal pfile rnum
'

#%UU%[<file>]
#%MDESC%
#Display <file> or the datafile using `less'.
#
def more '{
   local _1
   if ($# == 0)
      if (DATAFILE=="/dev/null" || DATAFILE=="") {
         print "No data file."
         exit
      } else {
         _1 = DATAFILE
      }
   else{
      if ((whatis("$1") & 0x00200000) != 0)
         _1 = $1
      else
         _1 = "$*"
   }
   unix(sprintf("less -P\"press \'q\' to exit, \'h\' for help\" %s", _1))
}
'

#%UU%[<scan_no>]
#%MDESC%
#Display the current scan ot <scan_no> using `less' with the current
#data file.
#
def shs '{
   local str

   if (DATAFILE=="/dev/null" || DATAFILE=="") {
      print "No data file."
      exit
   }

   if ($# == 0)
      _1 = SCAN_N
   else
      _1 = $1

   str = sprintf("\'+/#S %d\' %s", _1, DATAFILE)
   more str
}
'

#%UU%
#%MDESC%
#Allows to pause in loops during macros or user macro files
#If defined (with cdef) in user_scan_loop it will allow users to stop
#temporarily execution of any scan.
#The key
def pause_query '{
    local po
    po=input(-1)
    if ( po == "p" ) {
      print "Execution suspended. Type \"r\" to resume"
      while (input(-1) != "r" ) {
      }
      print "Execution resumed."
    }
    while (input(-1) != "" ) {}
}
'

#%UU%<mot1> <mot2> <lower limit Pos1-Pos2> <upper limit Pos1-Pos2>
#%MDESC%Define limits on the relative position of two motors. Pos1 - Pos2
#must be in between the lower and upper limit.

def set_rel_lm '{
if ($# != 4) {
  p "Usage: $0 <mot1> <mot2> <lower limit Pos1-Pos2> <upper limit Pos1-Pos2>"
  exit
}

_check0 "$1"; _check0 "$2"

cdef("user_checkall",sprintf("if (motor_num(\"$1\") != -1 && motor_num(\"$2\") != -1 && (A[$1] - A[$2] > %g || A[$1] - A[$2] < %g)) { print \"Relative limits for $1-$2 hit (Allowed range is [%g %g])\" ; exit }\n", ($4), ($3), ($3) , ($4)), "$1_$2")
}
'

#%UU%<mot1> <mot2>
#%MDESC%Deletes limits for relative position Mot1 - Mot2 defined with
#set_rel_lm
def del_rel_lm '{
if ($# != 4) {
  p "Usage: $0 <mot1> <mot2>"
  exit
}
_check0 "$1"; _check0 "$2"
cdef("user_checkall", "", "$1_$2", "delete")
}
'


# 2012/07/25 rh increased from 50 to 100
PLOT_CNTRS_MAX=100

#%UU%(command, macrofile)
#%MDESC%If the command is not defined, loads the macrofile
def mayneed(command, macro) '{
    local a
    a = macro
    if ((whatis(command) == 0)) {
        printf("macro set %s needed\nLoading it ....\n",macro)
        if (file_info(sprintf("%s/local/spec/macros/%s",BLISSADM, a), "-r") == 1) {
            qdofile(sprintf("%s/local/spec/macros/%s",BLISSADM, a))
        } else {
            if (file_info(sprintf("%s/local/spec/macros/%s.mac",BLISSADM, a), "-r") == 1) {
                qdofile(sprintf("%s/local/spec/macros/%s.mac",BLISSADM, a))
            } else {
                if (file_info(sprintf("%s/../../macros/%s",SPECD, a), "-r") == 1) {
                   qdofile(sprintf("%s/../../macros/%s",SPECD, a))
                } else {
                    if (file_info(sprintf("%s/../../macros/%s.mac",SPECD, a), "-r") == 1) {
                       qdofile(sprintf("%s/../../macros/%s.mac",SPECD, a))
                    } else {
                        if (file_info(sprintf("%s", a),"-r") == 1) {
                            qdofile(sprintf("%s", a))
                        } else {
                            if (file_info(sprintf("%s.mac", a),"-r") == 1) {
                                qdofile(sprintf("%s.mac", a))
                            } else {
                                printf("************* File not found **************\n")
                                printf("********** Unsuccessfull loading **********\n")
                                return 1
                            }
                        }
                    }
                }
            }
        }
    }
    return 0
}
'

#%UU%(keyword)
#%MDESC%This macro will print all the existing errors from the built-in
#associative array containing the complete error stack for a tango_io,
#tango_put and tango_get commands, generating an error - TANGO_ERR_STACK,
#sorted by keyword. %BR%
#Valid keywords are: "desc","origin", "reason" or "severity".
#If no keyword, "desc" will be printed.
def print_tango_err(keyword) '{
local i ii

  if (keyword == "")
    keyword = "desc"
  for (i in TANGO_ERR_STACK) {
    split(i,ii,"\034")
    if (ii[1] == keyword)
      printf ("%s\n", TANGO_ERR_STACK[ii[0]][keyword])
  }
}
'

#%UU%
#%MDESC%Macro to show arbitrary values, e.g. used with savemot
#and savecnt(saveload.mac)
#Make sure the calling macro uses variable "i" as the looper
#and "k" as the limit.
def _mo_loop '{
    local s
    for (j = i, s = ""; j < i + 8 && j < k; j++) {
        if (is_using_motor(mA[j])) {
            s = s sprintf("%$1", $2)
            if (j < i + 7)
                s = s " "
        }
    }
    print s
}
'

#%UU%
#%MDESC%print one value as hexadecimal
def px 'printf("%#x\n", $*)'



#%MACROS%
#%IMACROS%
#%AUTHOR%
#STLOCAL.MAC JK, with heavy modifications by HW for Spec release 6.00.07 with a
#revised standard.mac.
#%TOC%
#$Revision: 4.5 $, $Date: 2020/10/12 11:34:00 $