/*
 Author: Les Koehler
   Date: 17 Oct 2015 01:40:00
 Update: 31 Oct 2015 04:03:27 - Use showit.rex for Help
 Update:  7 Nov 2015 20:13:18 - Add WhoCalledMe & ::requires for it
*/ beghelp=thisline()+1  /*
&lcsme.rex:     See "Purpose", below
Copyright (C) 2015 Leslie L. Koehler
This is free software. See "Notice:" at the bottom of 
 &this file.

 Author: Les Koehler vmrexx@tampabay.rr.com

Purpose: Return a string for the caller to INTERPRET that will dump his
         vars to a file and allow adding the file to the THE ring. For
         .REX programs THE can be invoked, if installed, or the code can
         be tailored to replace the default Notepad.

         The file is named the same as the file being edited,
         with a prefix of the upper case drive letter followed by an
         underscore (e.g. C_), an optional identifier, a suffix of: .N
         and an extension of .SDV. Initially, N=0 but If KEEP is
         specified then N is incremented by 1 until a non-existing file
         is found.

         If not invoked from a THE edit session, and a version of
         ooRexx earlier than 4.2 is installed, then the 5th argument
         is required to identify the full path and file of the caller.
         Version 4.2 introduced .stackframes, and it is used if
         available.

 Syntax: INTERPRET &sme(-->
Arg#:    1           2                    3             4       5
 >-- ['Keep'] [,edit command varname][,'identifier'] [,'stem.'] [,filename] -->
 or:
 >-- ['Delete' , '*' | first         [,'*' |  last]] [,'stem.',['searchid']]-->
 >--                       ) --><--

Help: &lcsme [&helps]

Notes:
 1-For a Delete, note that in order to pass a searchid, you must provide
   a stem name and Interpret the result. This is a safety measure so that
   you can verify the results using SAY the first time and then allow the
   delete the second time. The searchid must start with an upper case
   drive letter followed by "_". See below.
 2-If a stemname is passed when dumping variables, the individual
   commands are returned in the stem for you to Interpret or SAY.
                                                                     */ /*
Note: This code was written for THE and ooRexx.

         If the file being edited is DIR.DIR, then date.time is used
         as the filename.
                                                                  */ /*
         With KEEP you have the possible result of:
           X_something.the.0.SDV
           X_something.the.1.SDV
         etc.

         If you use the optional identifier, then you might get
         something like:
           X_something.the.ident.0.SDV
           X_something.the.ident.1.SDV

         The optional identifier allows you to more easily
         tie the &sme invocation in your code to the file it produces.
         You might, for instance, choose to use a line number or a
         label as an identifier and call &sme multiple times during a
         debugging session.

         In order to provide maximum flexibility, when doing a Delete for
         such files, the SearchId is allowed to end with either a
         period or an asterisk. Thus the following set of files can be
         selectively deleted, as needed:

           X_something.the.ident1.0.SDV
           X_something.the.ident1.1.SDV
           X_something.the.ident1.2.SDV
           X_something.the.ident2.0.SDV
           X_something.the.ident2.1.SDV
           X_something.the.ident2.2.SDV

         See the example near the end of this prolog.

         Of course the usual simple forms of Delete (without SearchId)
         continue to work as expected for THE macros.

         The Drive Letter prefix is to accomodate the use of different
         thumb drive letters on different machines, as they were for me
         when I wrote the code!
                                                                     */ /*
 Examples for THE:
  Interpret &lcsme()        -- Dumps the variables to a file
                              -- replacing the previous file.
  Interpret &lcsme(,'edit')
  Interpret edit              -- Dumps the vars and edits the file
                              -- replacing the previous file.
                                                                     */ /*
  Interpret &lcsme(,,'identifier')  -- Dumps the vars and adds
                                      -- '.identifier' after the original
                                      -- extension, replacing the
                                      -- previous file.
                                                                     */ /*
  Interpret &lcsme('K')     -- Dumps the vars to a new file,
                              -- keeping the previous file.

  Interpret &lcsme('K','edit')     -- Dumps the vars to a new file,
  Interpret edit                     -- keeping the previous file and
                                     -- edits the new file.
                                                                     */ /*
  Interpret &lcsme(,,,'mystem.')  -- Sets the elements of MYSTEM.
                                    -- to the commands to Interpret:
  do m=1 to mystem.0
    interpret mystem.m
  end

  Interpret &lcsme('K','edit',,'mystem.') -- Similar to above, except
  do m=1 to mystem.0                        -- a new file is created
    interpret mystem.m                      -- and we can edit it
  end                                       -- later.
  Interpret edit
                                                                     */ /*
  Interpret &lcsme('D','*')  -- Deletes all the program's SDV files

  Interpret &lcsme('D',3,6)  -- Deletes generation 3 to 6

  Interpret &lcsme('D',3,'*') -- Deletes generation 3 to last

  Interpret &lcsme('D','*',,'mystem.') -- Sets the elements of MYSTEM.
                                         -- to the commands to Interpret,
                                         -- perhaps like this:

  do m=1 to mystem.0
    interpret mystem.m
  end
                                                                     */ /*
  Interpret &lcsme('D','*',,'mystem.',,
   'N:\MyTHEstuff\F_testt.the.')
                      -- Similar to above, but searches
                      -- in a specific folder for SDV files
                      -- that belong to a specific program executed from
                      -- the F drive.
                      -- SysFileTree is used to do the search.
                      -- The searchid must end with a period
                      -- or an asterisk. Asterisk should only
                      -- be used when it is necessary to match
                      -- the trailing part of a subset of
                      -- identifiers. Normally an asterisk
                      -- is appended to the searchid anyway.
                      -- Special allowance is made to handle an asterisk
                      -- for the original filename and/or filetype. This
                      -- makes cleanup of a cluttered DIR easier.
                      -- The elements of MYSTEM. are set to
                      -- the commands to Interpret, just like
                      -- the previous example.
                                                                    */ /*
 -- Using Identifiers
 -- If you code:

  /* Some code */
  Interpret &lcsme('K',,'Les')
  /* More code */
  Interpret &lcsme('K',,'Lee')

 -- and run it several times, you might wind up with these files:

   C:\MyRexxStuff\testprogram.rex.Les.0.SDV
   C:\MyRexxStuff\testprogram.rex.Les.1.SDV
   C:\MyRexxStuff\testprogram.rex.Les.2.SDV
   C:\MyRexxStuff\testprogram.rex.Les.3.SDV
   C:\MyRexxStuff\testprogram.rex.Lee.0.SDV
   C:\MyRexxStuff\testprogram.rex.Lee.1.SDV
   C:\MyRexxStuff\testprogram.rex.Lee.2.SDV
   C:\MyRexxStuff\testprogram.rex.Lee.3.SDV

 -- If you then wanted to delete the generation 1 and 2 files you might
 -- code:

    interpret &lcsme('D',1,2,'mystem.', ,
    'C:\MyRexxStuff\C_testprogram.rex.')

 -- but you would NOT get the result you want! Instead, just the first 2
 -- files found would be deleted, because the code would detect that you
 -- wanted to delete just TWO files, i.e. 2-1+1. Instead, you should code
 -- something like this:

    interpret &lcsme('D',1,2,'mystem.', ,
    'C:\MyRexxStuff\C_testprogram.rex.Le*')
    do m=1 to mystem.0
      interpret mystem.m
    end

 -- and the shorcut code will be avoided.

 -- See testoodumpx.rex and testoodump.the to 
 -- play around, or create new test cases for yourself. 

 -- The settings for keywords, minimum abbreviations and flags are
 -- just below the 'flags=' statement. Just define your
 -- non-conflicting keyword and add the matching label and code near
 -- the DUMP: label! Finally, update the Help within the prolog and
 -- you're done. This is also an easy way to create a simple test case
 -- that demonstrates a bug to send to me.

 -- NOTE: If invoked from a .REX program, you will have to tailor
 -- EDIT_CMD, START_EDITOR, EDITOR and SAYIT below
 -- to suit your editor. If you want THE to display output from .REX
 -- programs, take a look at THESTACK.REX and the logic after the
 --
         lesk?=left(who_am_i,4)='LESK'
 --
 -- statement. You might find that the easiest way to go.

 -- Notice that there are 3 different ways an editor may have
 -- to be invoked. That's why there are 3 variables. If you
 -- change them, be sure to test your changes! Getting the quotes
 -- right under various circumstances can be tricky!

 -- I am deeply thankfull to Walter Pachl for his relentless
 -- testing that allowed me to adapt this code to his work habits.
 -- He also contributed the original EXPANDED_DUMPVARS.REX code that
 -- tries to fit the Name column in to a fixed width, And it
 -- handles hex! See WIDTH= below to change the column width.
 -- Additional thanks to Gil Barmater for the pieces of Object code
 -- that replace SysDumpVariables for handling CR & LF. All I did
 -- was piece it all together to suit what I needed!
*/
endhelp=thisline()-2
--trace r
width=21 /* Width of Name= column for expanded_oodumpvars. 0=No action */
walter?=Userid()='Walter'
the_installed?=Value('THE_MACRO_PATH',,'ENVIRONMENT')\=''
--the_installed?=0 /* Uncomment this for testing w/o THE */
If walter? Then the_installed?=0
who_am_i=Value('COMPUTERNAME',,'ENVIRONMENT')
lesk?=Left(who_am_i,4)='LESK'
--lesk?=0 /* For my testing */
editor='"start /max notepad"'
If walter? Then editor='"start /max ked"'
If the_installed? Then Do
--race ?r
  edit_cmd=' Address THE "THE"'                    /* When running from THE */
  If lesk? Then
    start_editor= ,
 '"cmd.exe /c start /max c:\the\thec.exe "'
--    start_editor=Word(edit_cmd,2)'stack'                        /* When not */
--  if lesk? then start_editor='"start THEC"'
  Else start_editor='start "c:\the\THE.exe"'
  sayit='"emsg"'          /* The command for the editor to use for messages */
  editor='THE'
End
Else Do
  If walter? Then Do
    edit_cmd='"start /max ked"'        /* Primary cmd to return as edit var */
    start_editor='"start /max ked"'          /* Secondary, if primary fails */
  End
  Else Do
    edit_cmd='"start /max notepad"'    /* Primary cmd to return as edit var */
    start_editor='"start /max notepad"'      /* Secondary, if primary fails */
  End
End
ayit='say'
--exit
--call trace r
Parse Source os how fullme
Parse version whatrexx lang_level build_date
--call trace o
oorexx?=Pos('ooRexx',whatrexx)>0
regina?=Pos('Regina',whatrexx)>0

if regina? then do
  msg=fullme 'cannot run with Regina.'
  say msg
  Return 'say' msg
End

If \oorexx? Then Do
  msg=fullme 'requires ooRexx'
  Say msg
  Return 'say' msg

End
If Pos('WIN',Translate(os))=0 Then Do
  msg=fullme 'cannot run under' os'. Sorry!'
  Say msg
  Return 'say' msg

End
Parse version '-ooRexx_'version'.'release'.'mod'(' .
version_release=version'.'release
stackframes?=version_release>=4.2
Parse Value Reverse(fullme) With tf'.'em '\' path
fn=Reverse(em)
me=fn':'
ft=Reverse(tf)
path=Reverse(path)'\'
fm=Left(path,1)
If Arg()=1 | how='COMMAND' Then Call check_for_help Arg(1)
Parse Value '0 0 0 0 0 0 0' With del? keep? edit? stem? search? the? rex? .
error?=0
Call on Error name Error        /* Hide Win errmsg Called from Rexx or THE? */
--trace o
--address THE  'extract' '/fpath/fmode/filename/'
if \walter? then 'extract' '/fpath/fmode/filename/'
from_the?=(error?=0)
from_the?=(rc=0)
from_rex=\from_the?
Signal Off ERROR
--trace o
If Arg()>5 Then Do
  err?=1
  msg='Too many arguments ('Arg()'). Only five are allowed.'
  Signal err
End
/*trace r*/
del?=Translate(Left(Arg(1),1))='D'
dump?=\del?
keep?=Translate(Left(Arg(1),1))='K'
a5=''
If dump? Then Do
  If Arg(5,'O') Then Do           /* Build default left portion of fileid */
    Call resolve_caller
  End
  Else Do                          /* Build left portion from args passed */
    msg=''
    If Arg(5,'E') Then a5=Strip(Arg(5))
    Parse Var a5 letter +1 colon +1 backslash +1 .
    If Left(a5,2)\='\\' Then Do
      letter?=Datatype(letter,'Mixed')
      If \letter? Then msg=msg 'First character must be alphabetic.'
      colon?=colon=':'
      If \colon? Then msg=msg 'Second character must be a colon (:).'
      backslash?=backslash='\'
      If \backslash? Then msg=msg 'Third  character must be a backslash (\).'
    End
    Else Do                                        /* Check network drive */
      If \Datatype(Substr(a5,3,1),'Alphanumeric') Then Do
        msg='The name of a networked drive should start with an' ,
         'Alphanumeric.'
      End
      msg=msg 'Network drives are not supported. Sorry!'
    End
    If msg\='' Then Do
      msg=msg 'arg(5) filespec =' a5
      Signal err
    End
    If \sysisfiledirectory(Left(a5,3)) Then Do
      err?=1
      msg='Drive letter "'Translate(Left(a5,1))'" not mounted.',
       'arg(5) filespec =' a5
      Signal err
    End
    If \sysisfile(Arg(5)) Then Do
      err?=1
      msg='The fileid, arg(5), must include the full path for an existing',
       '.rexx or .rex file.'
      Signal err
    End
    left=path||fm'_'fn'.'
  /*
  left=fpath.1()fmode.1()'_'filename.1()'.'
  */
    him=a5
    Parse Value Reverse(him) With hisft '.' hisfn '\' hispath
    hisfn=Reverse(hisfn)
    hisft=Reverse(hisft)
    hisfnft=hisfn'.'hisft
    If Wordpos(Translate(hisft),'REX REXX')=0 Then Do
      err?=1
      msg='The fileid, arg(5), must have an extension (filetype) of',
       '.rexx or .rex'
      Signal err
    End
    If Pos('_DIR.DIR.',left)>0 Then Do               /* Can't use DIR.DIR */
      left=dirfileid.1()fmode.1()'_'hisfnft'.'
    End
  /*
  Else left=fpath.1()fmode.1()'_'hisfnft'.'
  */
    Else left=path||upper(fm)'_'hisfnft'.'
  End
  If Arg(3,'E') Then left=left||Arg(3)'.'               /* Add identifier */
End
Else Do                                                         /* Delete */
  If del? & Arg(4,'O') & Arg(5,'E') Then Do
    err?=1
    msg='A stemname [Arg(4)] must precede "'Arg(5)'"'
    Signal err
  End
End
--trace r
If Arg(4,'E') Then Do                                       /* Check stem */
  stem?=Left(Reverse(Arg(4)),1)='.'
  If \stem? Then Do
    err?=1
    msg='Stemname"'Arg(4)'" must end with "."'
    Signal err
  End
  stemname=Arg(4)
  asterisk?=0
  If del? Then Do                               /* Might have a searchid! */
    If Arg(5,'E') Then Do                  /* Looks like we do. Verify it */
      searchid=Arg(5)
      Parse Var searchid drive'\' rest
      If \sysisfiledirectory(drive'\') Then Do
        err?=1
        msg='"'searchid'" must start with a directory.'
        Signal err
      End
      Parse Upper Value Reverse(searchid) With '\' -3 '_'mode '\' .
      hit=Pos(mode,'ABCDEFGHIJKLMNOPQRSTUVWXYZ')
      If mode='' | hit=0 Then Do
        err?=1
        msg='"'searchid'" must contain "fmode_" as a prefix to the fileid.'
        Signal err
      End
      lastchar=Left(Reverse(searchid),1)
      asterisk?=lastchar='*'
      If Pos(lastchar,'.*')=0 Then Do
        err?=1
        msg='"'searchid'" must end with "." or "*"'
        Signal err
      End
      If asterisk? Then searchid=Left(searchid,Length(searchid)-1)
/*
    If Left(Reverse(searchid),1)\='.' Then Do
      err?=1
      msg='"'searchid'" must end with "."'
      Signal err
    End
*/
      z=sysfiletree( searchid ||'*','test.','FL')
      If z\=0 Then Do
        err?=1
        msg='SysFileTree yielded Result='z 'for "'searchid'*"'
        Signal err
      End
      If test.0=0 Then Do
        err?=1
        msg='No files found by SysFileTree("'searchid'*")'
        Signal err
      End
      search?=1
      left=searchid
    End
  End
End
right='.SDV'
exists?=1
str=''
middle=0
If dump? Then Do
  If \keep? Then Do
    str='Call SysFileDelete "'left||middle||right'";'
  End
  Else Do                                                /* Find unused N */
--    call trace o
    Do n=0 While exists?
      exists?=sysisfile(left||n||right)
    End
--    call trace r
    middle=n-1
  End
  If Arg(2,'E') Then Do
    varname=Arg(2)
    If Symbol(varname)='BAD' Then Do
      err?=1
      msg='"'Arg(2)'" is not a valid variable name'
      Signal err
    End
    edit?=1
  End
  edit_cmd=edit_cmd '"'left||middle||right'"'                      /* !!! */
  str=str ,
   || 'Call oosysdumpvariables .context~variables ,' , 
   '"'left||middle||right'" ,' width
  If edit? Then Do
    If the_installed? Then Do
      If \stem? Then Do
        if from_the? then ,
        str=str";rc=0;"||varname"='"edit_cmd,
         ||";" "if rc\=0 then" start_editor '"'left||middle||right'"'''
        else str=str";"||varname"='" start_editor '"'left||middle||right'"'''
      End
      Else Do
        str=str";rc=0;"||varname"=''"edit_cmd"'';", --llk          /* two single quotes */
        "if rc\=0 then" editor '"'left||middle||right'"'
      End
    End
    Else Do                  /* THE not installed! Use something else :-( */
      If \stem? Then Do
        str=str||';'varname"='"start_editor '"'left||middle||right'"'''
      End
      Else Do
        str=str||";rc=0;"varname"=''"edit_cmd"'';",  /* two single quotes */
         "if rc\=0 then" editor '"'left||middle||right'"'
      End
/*
    msg='Sorry, but I only understand how to start the THE editor'
    signal err
*/
    End
  End
End
Else Do                                                 /* Must be Delete */
  If Arg(2,'O') Then Do
    err?=1
    msg='You must specify a second argument with "Delete"'
    Signal err
  End
  first=Arg(2)
  first_is_num?=Datatype(first,'Whole')
  first_is_all?=first='*'
  If \first_is_num? & \first_is_all? Then Do
    err?=1
    msg='Argument"'first'"must be a number or "*".'
    Signal err
  End
  last?=Arg(3,'E')
  If last? Then Do
    last=Arg(3)
    last_is_num?=Datatype(last,'Whole')
    last_is_all?=last='*'
    If \last_is_num? & \last_is_all? Then Do
      err?=1
      msg='Argument"'last'"must be a number or "*".'
      Signal err
    End
    If first_is_num? & last_is_num? Then Do
      If last<first Then Do
        err?=1
        msg='"'first'" must be <= "'last'".'
        Signal err
      End
    End
  End
  Else Do                                           /* No third arg, LAST */
    If \first_is_all? Then Do
      err?=1
      msg='"'Arg(1)','Arg(2)'" requires a third argument.'
      Signal err
    End
  End
--trace r
  del.0=0
  If first_is_all? Then first=0                  /* I.e '*' starts with 0 */
  If Arg(4,'O') & Arg(5,'O') Then ,                      /* We're in THE! */
   left=fpath.1()fmode.1()'_'filename.1()'.'
  Else left=Arg(5)
  ucleft=Translate(left)
  x=left ||'*'
  x=sysfiletree( left ||'*','files.','FL')            /* Add '*' to the end */
  If x=0 Then Do                                           /* All went well */
--trace r
    Do f=1 To files.0                    /* Filter possible files to delete */
      Parse Upper Value Translate(Reverse(files.f)) ,
       With ext'.'num'.'                       /* Our files end with .n.SDV */
      num=Reverse(num)
      ext=Reverse(ext)
      id=''
      If Pos(left||num'.'ext,files.f)=0 Then Do              /* Look for id */
        Parse Value files.f With (left) id '.' (num) rest
        rest=rest
      End
      If id\='' Then newleft=left||id'.'
      Else Do
        hit=Pos('*',left)
        If hit>0 Then Do
          test=Left(left,hit-1)
          trail=num'.SDV'
          Parse Value files.f With (test) fid (trail) .
          If fid\='' Then newleft=test||fid
          Else newleft=left
        End
        Else newleft=left
      End
      If Datatype(num,'Whole') & ext='SDV' Then Do
        If num>=first Then Do                       /* We have a candidate! */
          If last? Then Do
            If last_is_all? Then Do                /* Save fileid to delete */
              Parse Value del.0+1 newleft||num||right ,
               With ix del.ix 1 del.0 .
            End
            Else Do
              If num<=last Then Do
                Parse Value del.0+1 newleft||num||right With ix del.ix 1 del.0 .
                If first_is_num? & last_is_num? Then Do
                  If ix=last-first+1 ,
                   & \asterisk? Then Leave f                   /* All done! */
                End
              End
            End
          End
          Else Do
            Parse Value del.0+1 newleft||num||right ,
             With ix del.ix 1 del.0 .
          End
        End
      End
    End
  End
  str=''
  If del.0\=0 Then Do                            /* Build the delete string */
    Do d=1 To del.0
      str=str||'Call SysFileDelete "'del.d'";'
    End
  End
End
--return exit
If stem? Then Do                                         /* Stem desired? */
  stem=''
  ctr=0
  Do While str\=''            /* Pick apart the string and put it in stem */
    Parse Var str m';'str
    ctr=ctr+1
    stem=stem||stemname||ctr"='"m"';"
  End
  stem=stem||stemname'0='ctr                            /* Set stemname.0 */
  str=stem
End
Return str
RESOLVE_CALLER:
  If from_the? Then Do
    'extract /fpath/fmode/filename/'
    the?=rc=0
    If rc=0 Then Do
      left=fpath.1||fmode.1'_'filename.1'.'          /* C:\folder\X_fileid. */
    End
    Else Do
      msg='The command "extract" failed with rc='rc'. Arg(5) is required'
      Signal err
    End
    /*
      left=fpath.1()fmode.1()'_'filename.1()'.'    /* C:\folder\X_fileid. */
      left=path||fm'_'fn'.'
    */
    If Pos('_DIR.DIR.',left)>0 Then Do                 /* Can't use DIR.DIR */
      fn=Date('S')
      tm=Space(Translate(Time('L'),' _',':.'),0)
      left=dirfileid.1()fmode.1()'_'fn'.'tm'.'
    End
  End
  Else Do
    If stackframes? Then Do
 /*
      cmd=,
       "caller=.context~stackFrames[.context~stackFrames~items-1]",
       "~executable~package~name"
      interpret cmd
*/ 
      him=whocalledme()
      Parse Value Reverse(him) With hisft '.' hisfn '\' hispath
      hisfn=Reverse(hisfn)
      hisft=Reverse(hisft)
      hisfnft=hisfn'.'hisft
      hispath=Reverse(hispath)'\'
      hisfm=upper(Left(hispath,1))
      left=hispath||hisfm'_'hisfnft'.'
    End
    Else Do
      If oorexx? Then Do
        msg='arg(5) is required with ooRexx version' ,
         version_release'. Please upgrade to 4.2 or later.'
        Signal err
      End
      Else Do
        msg='arg(5) is required.'
        Signal err
      End
    End
  End
  Return
ERR:
  Select
    When del? Then Do
      msg?=Pos('.THE',Translate(Arg(5)))>0
    End
    When \del? & \keep? Then Do
      msg?=0
    End
    Otherwise msg?=0
  End

  If \msg? Then sayit='say'
  Else sayit='msg'
Return sayit "'" me msg "'"
Exit
SYSISFILE:
  If oorexx? Then Return 'sysisfile'(Arg(1))
  Return Word(Stream(Arg(1),'C','FSTAT'),8)='RegularFile'
SYSISFILEDIRECTORY:
  If oorexx? Then Return 'sysisfiledirectory'(Arg(1))
  Return Word(Stream(Arg(1),'C','FSTAT'),8)='Directory'
CHECK_FOR_HELP:
  Call parse_source
  Parse Arg args
  If how='COMMAND' Then args='?'
  Call parse_args
  Return
PARSE_SOURCE:
  Parse Source whatos how fullme
  Parse Value Reverse(fullme) With ext'.' em '\' mypath
  me=Translate(Reverse(em))':'
  sme=Substr(me,1,Length(me)-1)
  lcsme=Lower(sme)
  mypath=Reverse(mypath)'\'
  logfile=mypath||Lower(sme)'.log'
  ext=Translate(Reverse(ext))
  the?=ext='THE'
  rex?=\the?
  Parse version whatrexx rexxlevel rexx_release_date
  oorexx?=Pos('ooRexx',whatrexx)>0
  regina?=Pos('REGINA',Translate(whatrexx))>0
  If regina? Then Do
    Call rxfuncadd 'sysloadfuncs', 'rexxutil', 'sysloadfuncs'
    Call sysloadfuncs
  End
  args=''
  opts=''
  If the? Then Do
    c='command'
    cn='command nomsg'
    m='macro'
  End
  Return
PARSE_ARGS:
  Call init_vars
--  If Words(args)=0 Then call help
  wds=Words(args)
  ucargs=Translate(args)
  Do w=1 To wds
    wd=Word(ucargs,w)
    ok?=0
    Do v=1 To Words(ucvalids)
      If Abbrev(Word(ucvalids,v),wd,Word(abbrev,v)) Then Do
        ok?=1
        Leave v
      End
    End
    If ok? Then Do
      z=Word(flags,v)                               /* Set flags indirectly */
      Call value z'.'w,1                             /* Set positional flag */
      Call value z,1                                        /* Set arg flag */
      ucwd=Word(ucvalids,v)
      argix.wd=ucwd
    End
    Else Do
      Call value 'unknown?.'w,1
      unknown?=1
      unknowns=unknowns wd
    End
  End
  If help? Then Call help
  If unknown? & keyword_parms? Then Do        /* Allow parms after keywords */
    kwdptrs=''
    kwds=''
    Do u=1 To wds                    /* Get the kwds in left to right order */
      wd=Word(ucargs,u)
      If \unknown?.u Then  Do                            /* Found a keyword */
        kwdptrs=kwdptrs u
        kwds=kwds wd
      End
    End
    kwdctr=Words(kwdptrs)
    Do p=1 To kwdctr                          /* Get the parms for each kwd */
      pix=Word(kwdptrs,p)                                /* Index into args */
      If pix+1<wds & p<kwdctr Then Do                  /* Another kwd later */
        piy=Word(kwdptrs,p+1)                            /* Ptr to next kwd */
        If piy\='' Then Do
          piy=piy-1                                 /* Back up to prev word */
          pwords=piy-pix                    /* Number of words between kwds */
        End
        Else Do
          Iterate
        End
      End
      Else Do                               /* TAILOR TO SUIT! Last keyword */
        If pix<wds Then Do                          /* Something follows it */
--          If Word(ucargs,p)='FILE' Then pwords=wds-pix /* Get all of it */
          If Wordpos(Word(ucargs,p),keyword_parms)>0 Then ,
           pwords=wds-pix                                  /* Get all of it */
          Else pwords=1                                    /* Just one word */
        End
        Else pwords=0
      End
      Do u=pix+1 To pix+pwords                     /* Reset unknown?. flags */
        Call value 'unknown?.'u,0            /* For parms that go with kwds */
      End
      vname=Word(kwds,p)                                 /* Name of the var */
      vname=argix.vname
      vval=Subword(args,pix+1,pwords)                   /* Value of the var */
      Call value vname,vval                                       /* Set it */
    End
    unknowns=''                                                    /* Reset */
    unknown?=0
    Do u=1 To wds             /* Accumulate any args that are still unknown */
      If unknown?.u Then unknowns=unknowns Word(args,u)
    End
  End
  unknown?=unknowns\=''
 --   Call dump
  If unknown? Then Call exit 8 'Unknown option(s):' unknowns
  Return
INIT_VARS:
  valids='?  /? -? Help /Help -Help --Help'      /* Keywords                */
  abbrev='1  2  2  1    2     2     3     '      /* Minimum abbreviation    */
  flags=Copies('Help? ',Words(valids))           /* Flag to set for keyword */
  helps=valids
  valids=valids 'Keep Delete' --< your keywords
  abbrev=abbrev '1    1' --< your abbreviations
  flags=flags   'keep? del?' --< your flagnames
  flags=flags 'Unknown? Keyword_parms?'             /* Always the last ones */
  Do f=1 To Words(flags)
    v=Word(flags,f)
    Call value v'.'f,0                        /* Initialize positional flag */
    Call value v,0                                   /* Initialize arg flag */
  End
  last=Words(helps)
  hhelp=''
  Do h=1 To last                           /* Build the Helps line variable */
    If h\=last Then hhelp=hhelp || Word(helps,h) '| '
    Else hhelp=hhelp||Word(helps,h)
  End
  helps=hhelp
  unknowns=''
  unknown?.=0
  ucvalids=Translate(valids)
--  msg.0=0
--  keyword_parms?=1
--  keyword_parms='TO FILE PATH'
--  Parse Value '' With file path To
--  msg.0=0
  exposes='sme lcsme me msg. c cn m myrc how' ,
   'help? mypath log? the? rex? logfile oorexx? regina? fullme'
  Return
MSG: Procedure Expose sme me rex? the?
  If rex? Then Say me Arg(1)
  Else 'msg' me Arg(1)
Return
EMSG: Procedure Expose sme me emsg rex? the?
  If rex? Then Say me Arg(1)
  Else 'emsg' me Arg(1)
Return
NEXT:
  Parse Arg !stem,!val
  If \Datatype(Value(!stem'.0'),'W') Then Call value !stem'.0',0
  !ix=Value(!stem'.0')+1
  Call value !stem'.0',!ix
  Call value !stem'.'||!ix ,!val
  Return
THISLINE:
  Return sigl
HELP:
--/*
  outvar=beghelp endhelp fullme
  call showit fullme ,, outvar , 
   ,'&lcsme='lcsme , '&sme='sme , '&this file.='fullme ,
   ,'&helps='helps
  Call exit
--*/
  alph='abcdefghigklmnopqrstuvwxyz'
  search4this_file?=1
  search4sayit_below?=0
  Do h=beghelp To endhelp
    data=Sourceline(h)
    If search4this_file? Then Do
      If Right(data,10)='this file.' Then Do
        data=Changestr('this file',data,'&fullme')
        search4this_file?=0
        search4sayit_below?=1
      End
    End
    If search4sayit_below? Then Do
      If Pos('SAYIT below',data)>0 Then Do
        data=Changestr('SAYIT below',data,'SAYIT in &fullme')
        search4sayit_below?=0
      End
    End
    hit=Pos('&',data)
    line=''
    If hit>0 Then Do
      left=Substr(data,1,hit-1)
      data=Substr(data,hit)
      vline=''
      Do While data\='' & Pos('&',data)>0
--        Parse Var data lead '&' +1 wd data
        Parse Var data lead '&' +1 data
        vline=vline||lead
        lead=''
        nonalph=Verify(data,alph,'Nomatch')
        If nonalph>0 Then Do
          Parse Var data wd =(nonalph) data
        End
        Else Do
          Parse Var data wd data
        End
        If Symbol(wd)\='BAD' Then Do
          If Symbol(wd)='VAR' Then Do
/*
            If Left(data,1)=' ' Then ,
             vline=vline Value(wd)
            Else vline=vline || Value(wd)
*/
            vline=vline || Value(wd)
          End
          Else Do                               /* Might end in punctuation */
            Parse Value Reverse(wd) With punct 2 rest
            rest=Reverse(rest)
            If Datatype(rest,'S') & Symbol(rest)='VAR' Then Do
              vline=vline lead||Value(rest)||punct
            End
--            Else vline=vline lead'&'wd data
            Else vline=vline lead'&'rest||punct
          End
        End
--/*
        Else Do                   /* It might be bad because of punctuation */
          Parse Value Reverse(wd) With punct 2 rest
          rest=Reverse(rest)
          If Datatype(rest,'S') & Symbol(rest)='VAR' Then Do
            vline=vline lead||Value(rest)||punct
          End
          Else Do
            vline=vline lead'&'data
            data=' '
          End
        End
--*/
      End
      If data\='' Then vline=vline||lead||data
      line=left||Space(vline)
    End
    Else line=data                                      /* No & in the line */
  Call next 'help',line
  End
  helpfile=fullme'_help.txt'
  Call stream helpfile, 'C', 'OPEN WRITE REPLACE'
  Do l=1 To help.0
    Call lineout helpfile,help.l
  End
  Call stream helpfile, 'C', 'CLOSE'
--  ADDRESS CMD 'start /max NOTEPAD "'helpfile'"'
  Address CMD strip(start_editor,,'"') '"'helpfile'"'
  Call exit
  Return
EXIT: Procedure Expose sme me sigl msg emsg rex? the?
  Parse Arg myrc mymsg
  mysigl=sigl
  If myrc='' Then myrc=0
  If myrc\=0 & mymsg\='' Then Do
    Call emsg mymsg
    Call msg 'Enter' sme 'HELP for help'
    Call emsg 'Rc='myrc
  End
  Else If myrc=0 & mymsg\='' Then Call msg mymsg
  If myrc\=0 Then Call msg 'Exit called from line' mysigl
  Exit myrc
LOGIT: Procedure Expose (exposes) sigl
--trace r
  mysigl=sigl
  Parse Arg logargs
  If logargs='' Then logargs=Sourceline(mysigl+1)
  Parse Value Right(Space(Date(),0),9,0) Time('L') With ds ts
  logline=ds ts logargs
  If Arg(2,'E') & Arg(2)\='' Then Do
    Parse Value Arg(2) With his_sigl him
    logline=logline '>' him '@' his_sigl
  End
  logfile=mypath||Lower(sme)'.log'
  If oorexx? Then Do
   -- .stream~new(logfile)~~lineout(logline)~close
    writeline='.stream~new("'logfile'")~~lineout("'logline'")~close'
    Interpret writeline
  End
  Else Do
    Call stream logfile, 'C', 'OPEN WRITE APPEND'
    Call lineout logfile,logline
    Call stream logfile,'C', 'CLOSE'
  End
  Return
ERROR:
--trace r
/*
  If rc\=69 Then Do
    Say me 'Oops! It appears that EXTRACT.REX wasn''t intalled along'
    Say me 'with' fullme'. Please do so and retry.'
  End
*/
--say trapped error 'RC='rc
error?=1
  Return
/* --- End of skeleton code --- Put subroutines below: */

/* Notice:
    This program is free software: you can redistribute it and/or modify
    it under the terms of the EPL (Eclipse Public License) as published by
    the Open Source Initiative, either version 1.0 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    EPL for more details.

    You should have received a copy of the EPL along with this program.
     If not, see:
    http://www.opensource.org/licenses/eclipse-1.0.php
*/
::requires whocalledme.cls