Lyster wrote:

>I'm still not comfortable with the conditions
>evaluated in the When statements. I think that the conditions need
>more-descriptive names than they currently have.

>[After reviewing the code whilst constructing v0.9, I'm especially unclear
>on the usage of the stem variable ?Key in the When statements.]

Ok, the When statements have a general comment right before them as to
what the code does. Specifically, maybe a better description of what I'm
doing with the compound symbol concept would help.

 ?Key = 'filekey' <-code default pattern for identifying an .iq's filekey
                    or to clairify, the map key, contentkey/iqkey
                    (see above)

 ?Key.1 = 'word:' <-.iq filekey definable (i.e. ':') - default is 'word:'
                    This is the "wordkey" to test for.

 ?Key.2 = 'sub:'  <-.iq filekey definable (i.e. '::')- default is 'sub:'
                    This is the "subkey" to test for.

 ?Key.3 = 'file:' <-.iq filekey definable ("" ':::') - "" is 'file:'
                    This is the "filekey" to test for. To clairify, the
                    linkkey (see above)

 ?Key.4 = 1       <- switch for identifying current line potential for
                     being output. It's set to '1' by a positive test
                     result from matching 'word' or 'sub'. If 'word' or
                     'sub' doesn't match then set to '0'

 ?Key.1.!Result   <- holds result of line test for wordkey existance
 ?Key.2.!Result   <- holds result of line test for subkey existance
 ?Key.3.!Result   <- holds result of line test for file/linkkey existance
 ?Key.4.!Level    <- holds depth value. 0 for outside word. 1 for inside
                     word. 2 for inside of sub.

So as you can see, these symbols are all related to each other in what
they are used for. We could make nine independant simple symbols, but
since they are related, a compound symbol fits. Besides if we made simple
symbols then we have to figure out a way to increment what is now 1 to 3.
True '4' could be a non-number but I'm being consistant "enough" to tie it
to the compound symbol (human wise). Hmmm, maybe '?Key' should be '?Key.'
(making it a stem symbol proper) but I'm not sure how the use of this and
initialization would effect the compound symbol. Probably wouldn't hurt
since we do initialize each anyway.
----

------------------------------>8----------------------------
/* $VER: IQ 0.9 (12.11.98)
** IQ command SYNTAX: IQ.rexx TO FROM WORD SUBS...
** Copyright 1998 Timothy Rue - VIC IQ command: version 0.31-arexx
**
** V0.3 Edited 9809.02-9809.03 by Lyster E. Wick Jr.
** Changes Copyright (C) 1998 by Lyster E. Wick Jr.
**
** to make script more readable, and to remove a couple VERY MINOR bugs.
** Larger changes defered until next edit session.
**
** V0.4 Edited 9809.10 by Lyster E. Wick Jr.
** Changes Copyright (C) 1998 by Lyster E. Wick Jr.
**
** Reworked the options parsing routine --heck, replaced it with 99% new
** code! New version uses Abbrev() to allow variable-length abbreviations
** of the command-line switches, instead of the all-or-nothing approach
** used before, and to allow MULTIPLE switches to be specified when needed.
**
** A new switch was added, -WARNINGS, as a synonym for -S, whose presumed
** full-length alias was somewhat less than mnemonic.
**
** Added a version string.
**
** Changed the main loop to use Abbrev() to check for the EXISTANCE of
** special lines, which always have their ID bytes at the beginning of the
** line. rexxtricks.library's RxTr_MatchPattern() is still used to match
** the pattern itself, once the line is identified.
**
** Uses rexxtricks.library (available on Aminet) for ADOS style pattern
** matching
**
** V0.5 Edited 9809.17 by Lyster E. Wick Jr.
** Changes Copyright (C) 1998 by Lyster E. Wick Jr.
**
** Fixed a bug introduced by me whereby the script would loop at the first
** non-filekey line of the data file.
**
** V0.6 Edited 9809.23 by Lyster E. Wick Jr.
** Changes Copyright (C) 1998 by Lyster E. Wick Jr.
**
** Fixed a bug introduced by me whereby the line-counter wasn't getting
** incremented when a new line was read.
**
** V0.7 Created by Timothy Rue from his v0.31 with features from my v0.4 added.
**
** V0.8 Created 9811.12 from v0.6 with new features from v0.7 folded in.
** Changes Copyright (C) 1998 by Lyster E. Wick Jr.
**
** V0.9 Edited 9811.26 [Happy Thanksgiving Day!] by Lyster E. Wick Jr.
**
** Simplified some logical tests by removing redundant explicit tests of
** boolian variables. As comparison expressions generate only 0 and 1
** values, there is no point in explicitly comparing a variable to 0 or 1
** when these are the only values that the variable in question CAN have.
**
** Fixed a bug --I forgot to copy one occurance of the new variable
** FlipFlop into v0.8
*/

/*
** Variables and their uses
**
** ?CP          Character Position within file
** ?FlipFlop    Unknown
** ?From        Name of file being processed
** ?FromT       Name of file to process NEXT, before we know that we want to
** ?I           Loop counter
** ?InLine      Text of current line
** ?IQSC        IQ "stack" count
** ?IQ_Stack.   Stack of files currently being processed
** ?IQ_StackT   Temporary storage of entry from stack of current files
** ?IQ_StackU.  List of files already processed
** ?IQSU        IQ used-list count
** ?J           Temporary loop variable, one larger than the loop's control variable
** ?Key         String constant containing the Magic Cookie that identifies non-standard IQ files
** ?Key.        Keywords for file parsing
** ?Key_Count.  ? Unclear. "Key array base and counter" doesn't amplify enough, Timothy.
** ?LineNumbers Switch to enable debugging output
** ?LineTest    ? Result of matching the current line against the wildcards [only compared to zero]
** ?LN          Number of current line
** ?Ports       List of system ports, used when -P switch is used
** ?Position    Position of an entry in a list or stack
** ?Subn        Number of words in second-level pattern
** ?SubPattern  Individual word within second-level pattern, only used in one place
** ?Subs        Second-level pattern
** ?T           Loop counter
** ?TN          "Tell Number"? Used several places to count something
** ?To_Port     Name of port to send output to
** ?Word        First-level pattern
*/

/*
** Constants and their uses
**
** !IQFile      I/O handle for current file
** !Level       Stem variable qualifier
** !Result      Stem variable qualifier
*/

/*
** Exit codes
**
**  0 OK, nothing wrong
** 10 Fatal error, resource not available
*/

/*
** Variables added:
**
** 9809.10 ?ArgLine  Used to hold the command-line arguments for processing
** 9809.10 ?Switch   Used to hold the command-line switch
** 9811.26 ?ShowKeys Internal form of -K switch, formerly ?Tell
**
** Variables removed:
**
** 9809.10 ?Key      Uppercase copy of ?To_Port, tested against the -K switch
** 9809.10 ?Subl     Catch second-level pattern when -S switch is used
** 9811.26 ?Tell     Internal form of -K switch, changed to ?ShowKeys
**
** Variables renamed:
**
**         Old      New
** 9809.10 ?TellNot ?Warnings Internal form of -S switch
**                            Enable/disable warnings;
**                            Default: Enabled
** 9811.12 ?SN      ?T        Loop counter, only used in one place,
** 9811.12 ?Test    ?T        Loop counter, only used in two places
**                            [both within the debugging output block]
**                            [?SN and ?Test both changed to ?T
**                            to match Timothy's latest version]
** 9811.26 ?Tell ?ShowKeys    Internal form of -K switch
*/

 /*
 ** set ?LineNumbers to 1 to show line numbers & IQ stacks.
 ** set to 0 otherwise
 */

 ?LineNumbers = 0

 /*
 ** Set parameter defaults.
 */

 ?Warnings = 1
 ?ShowKeys = 0

 /*
 ** check for and AddLib rexxtricks.library
 */
 If ~Show('L', "rexxtricks.library")
  Then If ~AddLib("rexxtricks.library", 0, -30, 0)
   Then Do
     Echo 'rexxtricks.library not available, exiting'
     Exit 10
    End

 /*
 ** Parse command line options.
 */

 ?ArgLine=Arg(1)
 If Abbrev('HELP', ?ArgLine) | ?ArgLine='?' | ?ArgLine='-?' Then Signal Help

 Do Forever
  ?Switch=Upper(Word(?ArgLine, 1))
  Select
   When Abbrev('-HELP',  ?Switch, 2)    Then Signal Help
   When Abbrev('-PORTS', ?Switch, 2)
    Then Do
      /*
      ** show available ports
      */
      Echo 'Ports Available:'
      Echo '----------------'
      ?Ports = Show('P')
      Do ?I = 1 To Words(?Ports)
       Echo Word(?Ports, ?I)
      End ?I
      Echo
      Exit 0
     End /* When Ports */
   When '-S' == ?Switch                 Then ?Warnings = 0 /* Suppress
   warnings */ When Abbrev('-WARNINGS', ?Switch, 2) Then ?Warnings = 0 /*
   Suppress warnings */ When Abbrev('-KEYS',     ?Switch, 2) Then ?ShowKeys =
   1 Otherwise Leave /* Forever */
  End /* Select */
  ?ArgLine = DelWord(?ArgLine, 1, 1)
 End /* Forever */

 /*
 ** parse command line arguements
 */
 Parse Var ?ArgLine ?To_Port ?From ?Word ?Subs

 /*
 ** Check to see if specified putput port exists
 */
 If ~Show('p', ?To_Port)
  Then Do
    If ~?Warnings
     Then Echo "WARNING: Cannot find port "?To_Port" - Defaulting to
     STDOUT"
   End
  Else Shell Value ?To_Port

 If ?ShowKeys
  Then Echo 'Listing Unique KEYS (first time found - filekeys and w/word and
  sub pattern matching):'

 /*
 ** Set default file keywords
 */
 ?Key = 'filekey' /* Should be something like "!IQ:" or "!VIC.IQ:" */
 ?Key.1 = 'word:'
 ?Key.2 = 'sub:'
 ?Key.3 = 'file:'
 ?Key.4 = 1
 ?IQSU = 0
 ?IQSC = 0
 ?TN = 1

 /*
 ** test for valid file arguement
 */
 If ~Open(!IQFile, ?From, 'R')
  Then Do
    Echo 'ERROR: IQ file '?From' not found!'
    Exit 10
   End

 /*
 ** File opened OK; check for signature and read first data line
 */
 ?InLine = ReadLn(!IQFile)
 ?LN = 1
 ?Key_Count.0 = Strip(?InLine)
 If Abbrev(?InLine, ?Key)
  Then Do
    Do ?I = 1 To 3
     ?Key.?I = Word(?InLine, ?I+1)
    End ?I
    /*
    ** We have a filekey; read next line
    */
    ?InLine = ReadLn(!IQFile)
    ?LN = 2
   End /* If */

 ?Subn=Words(?Subs) /* Count "sub" words */

 /* DO IT */
 Do Forever

  ?FlipFlop = 0 /* What's this for? */

  /*
  ** Test line for key.
  */
  Do ?I = 1 To 3
   ?Key.?I.!Result = Abbrev(?InLine, ?Key.?I)
  End ?I
  ?LineTest = ?Key.1.!Result + ?Key.2.!Result + ?Key.3.!Result

  Select
   /*
   ** Skip unselected data lines.
   */
   When ?LineTest == 0 & ?Key.4 == 0 Then Nop

   /*
   ** Output selected data lines.
   */
   When ?LineTest == 0 & ?Key.4 == 1 & ~?ShowKeys
    Then If Address() == ?To_Port
     Then ''?InLine
     Else Echo ?InLine

   /*
   ** When file.
   */
   When ?Key.3.!Result & ?Key.4 == 1
    Then Do
      ?FromT = Strip(?InLine, 'L', ?Key.3)

      If ?From == ?FromT /* Is file self-recursive? */
       Then Break /* from Select */

      /* set/reset IQ_stacks position and break variable */
      ?Position = 0

      If ?IQSU > 0 /* Do we have any previous files to check for? */
       Then Do ?I = 1 To ?IQSU
         If ?IQ_StackU.?I == ?FromT /* Is the new file already on the list?
         */
          Then ?Position = ?I
        End ?I
      If ?Position > 0
       Then Break /* from Select */

      If ~Exists(?FromT) /* Does the file exist? */
       Then Do
         If ~?Warnings
          Then Echo 'WARNING: File '?From' Line' ?LN': File '?FromT' not
          found.'
         ?Position = -1
        End
      If ?Position == -1
       Then Break /* from Select */

      /*
      ** check if in current stack - if so save current settings,
      ** read in previous settings, sort stack, seek to cp
      */
      If ?IQSU > 0 /* Do we have any previous files to check for? */
       Then Do ?I = 1 To ?IQSC /* Look for it. */
         If Word(?IQ_Stack.?I, 1) == ?FromT /* Is this it? */
          Then ?Position = ?I /* Remember it! */
        End ?I
      If ?Position > 0 /* Did we find it? */
       Then Do
         ?CP = Seek(!IQFile, 0)
         ?IQ_StackT = ?From ?Key.1 ?Key.2 ?Key.3 ?Key.4 ?Key.4.!Level ?LN ?CP
         Parse Var ?IQ_Stack.?Position ?From ?Key.1 ?Key.2 ?Key.3 ?Key.4
         ?Key.4.!Level ?LN ?CP
         /*
         ** Ripple stack downward one place to remove file from list.
         */
         Do ?I = ?Position To ?IQSC
          ?J = ?I + 1
          ?IQ_Stack.?I = ?IQ_Stack.?J
         End ?I
         ?IQ_Stack.?IQSC = ?IQ_StackT
         Call Close(!IQFile)
         Call Open(!IQFile, ?From, 'R')
         Call Seek(!IQFile, ?CP, 'BEGIN')
         ?Position = -1
        End /* If */
      If ?Position == -1
       Then Break /* from Select */
      /*
      ** if not in either stack - save current settings and
      ** read in key of new file if exist
      */
      If ?Position == 0
       Then Do
         ?CP = Seek(!IQFile, 0)
         ?IQSC = ?IQSC + 1
         ?IQ_Stack.?IQSC = ?From ?Key.1 ?Key.2 ?Key.3 ?Key.4 ?Key.4.!Level
         ?LN ?CP
         ?From = ?FromT
         Call Close(!IQFile)

         If ?ShowKeys & ?Key.4 == 1
          Then Do
            /*
            ** Check and build array of words, subs and files
            */
            ?InLine = Strip(?InLine)
            Do ?T = 0 To ?Tn -1
             If Compare(?Key_Count.?T, ?InLine) ~= 0
              Then Iterate ?T
              Else Leave ?T
            End ?T
            If ?T = ?TN
             Then Do
               ?Key_Count.?T = ?InLine
               ?TN = ?TN + 1
              End
           End /* If */

         Call Open(!IQFile, ?From, 'R')
         /*
         ** File opened OK; check for signature and read first data line
         */
         ?InLine = ReadLn(!IQFile)
         ?LN = 1
         ?Key.4.!Level = 0
         If Abbrev(?InLine, ?Key)
          Then Do
            Do ?I = 1 To 3
             ?Key.?I = Word(?InLine, ?I+1)
            End ?I
            /*
            ** We have a filekey; read next line
            */
            ?InLine = ReadLn(!IQFile)
            ?LN = 2
           End /* If */
          Else ?FlipFlop = 1 /* Why? */
        End /* If */
     End /* When File */

   /* When sub from sub or word level*/
   When ?Key.2.!Result & ?Key.4.!Level > 0
    Then Do
      ?Key.4 = 0
      ?Key.4.!Level = 1
      /* Test arg sub match (within matching word) */
      Do ?T = 1 To ?Subn
       ?SubPattern = ?Key.2 || Word(?Subs, ?T)
       If RxTr_MatchPattern(?InLine, ?SubPattern)
        Then Do
          ?Key.4.!Level = 2
          ?Key.4 = 1
          Break /* Select */
         End
      End ?T
      /* if w/blank sub */
      If ?InLine == ?Key.2 & ?Key.4.!Level == 1
       Then ?Key.4 = 1
     End /* When */

   /* When word */
   When ?Key.1.!Result
    Then Do
      ?Key.4 = 0
      ?Key.4.!Level = 0
      /* If arg word match */
      If RxTr_MatchPattern(?InLine, ?Key.1 || ?Word)
       Then Do
         ?Key.4.!Level = 1
         ?Key.4 = 1
        End
      /* If exiting word w/blank word */
      If ?InLine == ?Key.1
       Then ?Key.4 = 1
     End

   When ?ShowKeys Then Nop /* What's this doing? */

   /* ??? can this ever happen ??? */
   Otherwise Echo 'INTERNAL ERROR: File '?From' Line' ?LN': How did this
   happen?'
  End /* Select */

/*
**** begin multi-file test/IQ_stacks output.
**** Set '?LineNumbers' ~= 0, to use
*/

  If ?LineNumbers
   Then Do
     If ?IQSC > 0
      Then Do
        ?Result = Word(?IQ_Stack.1, 1)'@Line#'Word(?IQ_Stack.1, 7)
        Do ?T = 2 To ?IQSC
         ?Result = ?Result Word(?IQ_Stack.?T, 1)'@Line#'Word(?IQ_Stack.?T, 7)
        End ?T
        Echo 'IQ Stack>'?Result ?From'@Line#'?LN /* This is the PK file's IQ
        stack. */
       End /* If */
      Else Echo 'IQ Stack>'?From'@Line#'?LN /* This is the PK file's IQ
      stack*/

     If ?IQSU > 0
      Then Do
        ?Result = ?IQ_StackU.1
        Do ?T = 2 To ?IQSU
         ?Result = ?Result ?IQ_StackU.?T
        End ?T
        Echo 'IQ completely scanned file list> '?Result
       End /* If ?IQSU */
    End /* If ?LineNumbers */
/*
**** end of multi-file test output
*/

  /*
  ** Make list of unique Keys if -k option selected.
  */
  If ?LineTest ~= 0 & ?ShowKeys & ?Key.4 = 1 & ~?FlipFlop
   Then Do
     /* Check and build array of words, subs and files */
     ?InLine = Strip(?InLine)
     Do ?T = 0 To ?TN -1
      If Compare(?Key_Count.?T, ?InLine) ~= 0
       Then Iterate ?T
       Else Leave ?T
     End ?T
     If ?T = ?TN
      Then Do
        ?Key_Count.?T = ?InLine
        ?TN = ?TN + 1
       End /* If */
    End /* If */

  /* handle EOF and IQ stacks*/
  If Eof(!IQFile)
   Then If ?IQSC > 0
    Then Do
      Call Close(!IQFile)
      ?IQSU = ?IQSU + 1
      ?IQ_StackU.?IQSU = ?From
      Parse Var ?IQ_Stack.?IQSC ?From ?Key.1 ?Key.2 ?Key.3 ?Key.4
      ?Key.4.!Level ?LN ?CP
      ?IQSC = ?IQSC -1
      Call Open(!IQFile, ?From, 'R')
      Call Seek(!IQFile, ?CP, 'BEGIN')
     End /* If */
    Else Break /* from do forever */
  ?InLine = ReadLn(!IQFile)
  ?LN = ?LN + 1
 End /* Do Forever */

 /*
 ** List unique keys if option -k selected
 */

 If ?ShowKeys
  Then Do ?T = 0 To ?TN
    Echo ?Key_Count.?T
   End ?T
 Exit 0

 /*
 ** help options
 */
Help:
 Echo "FOR HELP:"
 Echo "rx IQ.rexx -OR- rx IQ.rexx [ ? | -? | -h | help ]"
 Echo
 Echo "COMMAND LINE:"
 Echo "rx IQ.rexx   [word|pattern] {sub|pattern}"
 Echo
 Echo "OPTIONAL REDIRECTION COMMAND LINE (may be used with any '-' option):"
 Echo "rx IQ.rexx [>dev:path/filename | pipe:name]   [word|pattern] {sub|pattern}"
 Echo
 Echo "TO SILENCE WARNINGS:"
 Echo "rx IQ.rexx < -s | -S >   [word|pattern] {sub|pattern}"
 Echo Echo "FOR UNIQUE KEYS LIST (filekeys and w/word and sub pattern matching):"
 Echo "rx IQ.rexx < -k | -K | -Keys | -KEYS >  [word|pattern] {sub|pattern}"
 Echo
 Echo "FOR AVAILABLE PORTS LIST:"
 Echo "rx IQ.rexx [ -p | -P | -Ports | -PORTS ]"
 Echo
 Exit 0
----------------------------8<-----------------------------------