/*
   Date: 23 Oct 2015 16:01:05
 Update: 31 Oct 2015 03:52:53 - Handle filespec and var substitution
 Update: 11 Jul 2016 03:35:20 - Improvee Help prose. Thanks to Walter Pachl
 Update: 11 Jul 2016 22:02:50 - Fix for whole file and start to eof
 Update: 12 Jul 2016 09:49:54 - Catch not enough args. Remove 'interpret'
                                leftover from attempt at Regina compat.
 Update: 12 Jul 2016 17:49:14 - Cleanup some dead code
 Update: 14 Jul 2016 00:36:10 - Document double-quote "Do not substitute"
                                Display HELP subroutine as part of Help
 Update: 14 Jul 2016 15:35:35 - Add options trigger ':' for NOSHOW, QUIET
 Update: 15 Jul 2016 17:08:05 - Add NOQUIET to 'valids' Thanks Walter!
 Update: 15 Jul 2016 23:33:07 - Allow NOSHOW and QUIET from cmdline
 Update: 16 Jul 2016 00:25:19 - Add NOTEPAD option for cmdline testing
 Update: 16 Jul 2016 06:54:02 - Show abbrev of options. Walter
 Update: 16 Jul 2016 16:48:52 - Allow Help keywords from cmdline
 Update: 17 Jul 2016 15:30:09 - Show that the Help keywords are optional
 Update: 18 Jul 2016 14:46:26 - Use Seek in GetFile subroutine (Walter)
 Update: 25 Jul 2016 16:34:34 - Comment code for testers (Rony Flatcher)
 Update: 26 Jul 2016 14:00:12 - Use the modern lower case name
 Update:  5 Aug 2016 06:08:54 - Use .context~name Fix example Add comments
 Update: 12 Aug 2016 09:29:11 - Use single-quotes in first example
*/ beghelp=thisline()+1  /*
showit.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: Display the data items passed. Items can be a stem, a file 
         (or a piece of one), a literal string or the definition of 
         a variable that will be used for substituting its occurrences 
         in lines from stems, literal strings, and/or files. For example:
              '&varname=txt' or '&varname='value
         As many items as needed can be used.
         A string that starts with ':' triggers Options processing.
         Valid options to change the defaults are: 
           NOShow  - Don't display the output file
           Quiet   - Skip the Exit message when NOSHOW is used
           NOTepad - Allow testers to use NotePad
         Any number of Options lines can be used and any option can be
         negated by starting it with 'NO' (or not) as needed.
 
 Syntax: Call &lcsme .context~name , [output fileid] , item [, item ...]

The output fileid defaults to the caller appended with '_help.txt'

Example: Call &lcsme 'C:\MyRexxStuff\test.rex' , -- Caller 
           , 'C:\Reports\Symposium.txt' ,        -- Output fle (optional)
           , 'Financial Data Report' ,           -- Literal string
           , stema. , stemb. ,                   -- Stems
           , '1 5 C:\Sigs\Lesk.txt' ,            -- Lines 1-5 of a file   
           , '1 * C:\temp\junk.txt' ,            -- The whole file
           , '7 eof C:\temp\test.txt' ,          -- Line 7 to end-of-file
           , ':quiet noshow' ,                   -- Options
           , '&lcme='lcme                        -- Variable definition

   Help: &lcsme [&helps]

Here's a real example, in the file &fullme:
*/
endhelp=thisline()-2
Call Parse_source
Parse Arg args
Call Parse_args args
If Arg()<3 Then Call Exit 12 'At least 3 args are required'
--call dump
argarray = Arg(1,'a')
--say argarray~last 'items to process'
gothim?=0
gotfile?=0
outarray=.array~new           /* We'll collect the lines to write, here */
outfile=''
ampersands.0=0
--trace r
Do i = 1 To argArray~last
  Arg = argArray[i]
  Select
    When arg~isA(.stem) Then Do
--      Say 'Arg' i 'is a stem'
      Call handlestem(Arg)
    End
    When arg~isA(.string) Then Do
--      say 'Arg' i 'is a string:' Arg
      Call Handlestring Arg
    End
    When Arg=.nil Then Call Handledefaultfile
    Otherwise Say 'Arg' i 'is' Arg '(not handled)'
  End                                                         /* select */
End                                                               /* DO */

If outarray~last=.nil Then Do
  Call Exit 12 'There is no data to do variable substitution against.'
End
If ampersands.0>0 Then Do                  /* Look for var substitution */
  Do l=1 To outarray~last
    line=outarray[l]
    If Pos('&',line)>0 Then Do             /* Need one here? */
      Do a=1 To ampersands.0               /* Yes. Walk down the list */
        Parse Var ampersands.a ampername'='val
        hit=Pos('&',line)
        If hit>1 Then Do               /* Room to test for double-quote? */
          If Substr(line,hit-1,1)='"' Then Iterate a    /* Skip if there */
        End
        If Pos(ampername,line)>0 Then Do          /* Substitute if there */
          line=Changestr(ampername,line,val)
        End
      End
      outarray[l]=line                          /* Replace the line */
    End
  End
End
Call Sysfiledelete outfile
outobject = .stream~new(outfile)
--outObject~open("WRITE")
--If outobject~open('WRITE REPLACE') \= 'READY:'
If outobject~open('WRITE') \= 'READY:'
Then Call Exit 12 'Can''t open file:' outfile '('mystream~description')'

outobject~arrayout(outarray,"LINES")
outobject~close()
--call dump
If show? Then Do
  Parse Upper Source os .
  If Left(os,7)='WINDOWS' Then Do
    uid=Word(Userid(),1)
    Select                        /* Give testers their favorite editot */
      When uid='Les' & \notepad? Then ,
       'cmd.exe /c start /max c:\the\thec.exe "'outfile'"'
      When uid='Walter' & \notepad? Then ,
       'cmd.exe /c start /max ked "'outfile'"'
      Otherwise                           /* Everyone else gets NotePad */
        'cmd.exe /c start /max notepad.exe "'outfile'"'
    End
  End
  Else 'cat "'outfile'" | more'                   /* Must be Linux type */
End
Else If \quiet? Then Call Exit 0 'created:' outfile
Call Exit
Exit

/* ============================================= */
HANDLESTEM: Procedure Expose outarray
  use Arg stem.
  If stem.0~datatype('w') Then Do i = 1 To stem.0
    outarray~append(stem.i)
  End
  Else Do ind over stem.~allIndexes
    outarray~append(stem.ind)
  End

  Return
/* ============================================= */
HANDLESTRING: Procedure Expose gothim? fullhim i outfile ,
 gotfile? ampersands. outarray (exposes) ucvalids abbrev ,
 keyword_parms? flags unknown? unknowns quiet? notepad? help?
--Say arg(1) 
  If \gothim? & i=1 Then Do
    fullhim=Arg(1)
    gothim?=1
  End
  If \gotfile? & i=2 Then Do
    If Arg(1)\='' Then Do
      If Arg(1)\=fullhim Then Do
        outfile=Arg(1)
        gotfile?=1
      End
    End
  End
  If i>2 Then Do
    Select
      When Datatype(Word(Arg(1),1),'W') Then Do
        Call Getfile Arg(1)
      End
      When Left(Arg(1),1)=':' Then Call Validate Substr(Arg(1),2)
      When Left(Arg(1),1)='&' Then Call Next 'ampersands',Arg(1)
      Otherwise outarray~append(Arg(1))
    End
  End
  Return
HANDLEDEFAULTFILE: Procedure Expose fullhim i outfile gotfile?
  If \gotfile? & i=2 Then Do
--    say 'Arg 2 is .nil setting the default:'
    outfile=fullhim||'_help.txt'
--    say '  'outfile
    gotfile?=1
  End
  Return
GETFILE: Procedure Expose (exposes) outarray i         /* Thanks to Jon Wolfers! */
  Parse Arg start last filename
  mystream = .stream~new(filename)
  If myStream~open('read') \= 'READY:'
    Then Call Exit 12 'Can''t open file:' filename '('mystream~description')'
  
  myStream~seek(start 'LINE') /* Position READ pointer to start point */
  myarray = myStream~arrayIn /* Read in the file, from 'start' to eof */
  If myStream~close \= 'READY:'
    Then Call Exit 12 'Can''t close file:' filename '('myStream~description')'
  
  If Datatype(last,'W') Then , /* Pick out our piece of MyArray */
   mysection = myArray~section(1, last+1 - start)
  Else ,
   mysection = myArray~section(1) /* The whole thing! */
  outarray = outArray~union(mysection)
Return
GETFILE: Procedure Expose outarray          /* Fails for multiple calls */
  Parse Arg start last filename
  outArray~appendAll(.stream~new(filename)~arrayIn~section(start, last+1 - start))
  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)
  pad=Copies(' ',Length(sme))
  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 Exit 99 'Sorry, Regina is not supported. ooRexx only.'
  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 | how='COMMAND' Then Call Help
  Else Return
VALIDATE:
--trace r --call dump --trace r
  wds=Words(Arg(1))
  ucargs=Translate(Arg(1))
  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
      ucwd=Word(ucvalids,v)
      z=Word(flags,v)                           /* Set flags indirectly */
      If z='notepad?' Then Do
        If Left(ucwd,2)\='NONO' Then Do
          Call Value z'.'w,1                     /* Set positional flag */
          Call Value z,1                                /* Set arg flag */
        End
        Else Do
          Call Value z'.'w,0                     /* Set positional flag */
          Call Value z,0                                /* Set arg flag */
        End
      End
      Else Do
        If Left(ucwd,2)\='NO' Then Do
          Call Value z'.'w,1                     /* Set positional flag */
          Call Value z,1                                /* Set arg flag */
        End
        Else Do
          Call Value z'.'w,0                     /* Set positional flag */
          Call Value z,0                                /* Set arg flag */
        End
      End
      argix.wd=ucwd
    End
    Else Do
      Call Value 'unknown?.'w,1
      unknown?=1
      unknowns=unknowns wd
    End
  End
--all dump
  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 'SHOW  NOSHOW QUIET NOQUIET NOTEPAD' --< your keywords
  abbrev=abbrev '1     3      1      3      3' --< your abbreviations
  flags=flags   'show? show?  quiet? quiet? notepad?' --< 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
  show?=1
  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?=0
--  keyword_parms='TO FILE PATH'
--  Parse Value '' With file path To
--  msg.0=0
  exposes='sme lcsme me msg. c cn m myrc pad quiet? notepad?' ,
   'help? mypath log? the? rex? logfile oorexx? regina? show? fullme',
   'beghelp endhelp helps'
  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
DUMP:
  the?=0
  If the? Then Do
--  Interpret dumpvars('K')
--  Interpret dumpvars('K','zz')
    Interpret oodumpvars(,'zz')
    Interpret zz
  End
  Else Do
--  Interpret oodumpvars('K')
--  Interpret oodumpvars('K','zz')
    Interpret oodumpvars(,'zz')
    Interpret zz
  End
  Exit
VALUEOF:
  Arg !_!label
  Signal Value !_!label
  Return
HELP:
  xmpstart=thisline()-1
  xmpend=thisline()+9
  more=''
  if symbol('ARGS')='VAR' then more=':'args /* Allow options */
  outvar=beghelp endhelp fullme /* startline endline & file to extract from */
  Call Showit fullme ,, outvar ,              /* Caller , data to show  */
   ,more ,       /* Allow options to be tested from a command window */
   ,"&lcsme="lcsme , '&sme='sme , '&this file.='fullme , /* Substitutions */
   ,"&helps="helps , '&fullme='fullme , xmpstart xmpend fullme
-- Note that the above 2 lines are NOT substituted when &sme displays them.
-- That's because of the double-quote before the first '&'.
  Call Exit
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
  Else logline=logline '@' mysigl
logfile=mypath||Lower(sme)'.log'
If oorexx? Then Do
  .stream~new(logfile)~~lineout(logline)~close
--  writeline='.stream~new("'logfile'")~~lineout("'logline'")~close'
--  Interpret writeline /* Bypass Regina prescan */
End
Else Do
  Call Stream logfile, 'C', 'OPEN WRITE APPEND'
  Call Lineout logfile,logline
  Call Stream logfile,'C', 'CLOSE'
End
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
*/