KLIST

From Pickwiki
Revision as of 23:48, 26 February 2015 by Conversion script (talk) (link fix)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

This utility attempts to combine the functionality of the "LIST.ITEM", "SEARCH", "COUNT" command and "SELECT" commans. It has been developed and enhanced for a 7 years now and is an invaluable aid to day to day development and system administration tasks in a Universe environment. Any bugs can be reported to me at [email protected]. There is a preamble that gives users an idea of the functions available, as well as a '-HELP' option. Compile and catalog the code and at TCL enter: KLIST -H to see what options are available. The lastest (5th December, 2014) version is the last before I retire. It has the following extra functions added to it: 1. Use of '...' in file, record and string masks. 2. Use of PERL regular expressions (requires REGEXP UVBasic function) in string searches. 3. Expansion of values in fields and subvalues in values on a global and individual field/value basis. 4. Conversion of internal dates to external format on a global and individual field/value/subvalue basis. 5. Display of specific fields only 6. Prompts for missing command line parameters (UVAccount, File, Record masks)

The code:

* -*- mode:unibasic -*-
********************************************************************************
* Programme - KLIST
*
* Version 4.0
*
* Author    - Ken Ford                                Date - 7th March, 2008
*
* Description -
*        This programme lists selected records or optionally fields withing records
*   optionally expanding selected values and subvalues and optionally converting
*   potential internal dates.  Records can be selected by record mask within file
*   mask, optionally within Universe account.  A string mask can be used to narrow
*   down which records to list, and matching fields only within a record can be
*   listed.  Universe Retrieve phrases can be used in place of record masks as well.
*   Also, a record count or keys only can be listed.
*       This programme optionally combines the functionality of the following
*  Universe commands:
*    SELECT
*    COUNT
*    LIST.ITEM
*    SEARCH
*    ED
*    COMO
*    GET.LIST
*
* Modifications -
* 16/3/08    Added '-V' option.
* 26/3/08    Added '-M' option
* 27/3/08    Changed '-Q' option to consider Q-pointer references only if
*            F-pointer reference returns no record VOC item file ID.
* 28/3/08    Use '-M' option with string-mask to print
*            only lines containing a match.  Enable placing of options either
*            before or after masking arguments.
* 17/4/08    Default file selection to F-pointer references, considering Q-pointer
*            references only if no F-pointer match/es; force consideration of
*            pointer references if '-Q' present; limit to F-pointer references
*            if '-F' present.
*  3/7/08    Allow REC.RESPONSE to be the name of a &SAVEDLIST& record of keys.
* 12/12/08   Added '-Dn[,n]' option to convert dates to external format.
* 24/12/08   Enhanced '-V' option so that if ',S' is appended, sub-values are
*            also expanded.
* 20/5/09    Enabled running on active select list if file is in current account
* 8/12/09    Introduced '-COMO' command line option to capture output in a COMO
*            file 'KLIST.COMO'
* 25/5/11    Restructured code; fixed bug in string matching; new argument '-nn'
*            to limit no. of records to list.
* 2/6/14     New option '-NO.ERR' to supress error messages.
* 27/11/14   New option '-F' or '-A' to print specific fields/attributes;
*            Revised option '-V[,S]' to enable expanding values in specific
*            fields and subvalues in specific values;
*            Revised option '-D' to enable specifying fields, values and subvalues
*            for potential date conversion;
*            Prevented listing of empty values and subvalues where appropriate;
*            New option '-RE' to enable search string in PERL RE form
*
********************************************************************************
*
      PROMPT ''
      PRINT '[KLIST v4.1 5/12/2014]'
      DEFFUN REGEXP(STRING.OBJ,RE) ;* Define UVBasic function
      IF INDEX(@SENTENCE,'-DEBUG',1) THEN DEBUG.ON = @TRUE ELSE DEBUG.ON = @FALSE
*      
      GOSUB INITIALISATION
*
      GOSUB GET.OPTIONS
*      
      ACCOUNT.CNT = DCOUNT(ACCOUNT,@FM)
      FOR I = 1 TO ACCOUNT.CNT
         GOSUB PROCESS.UV.ACCOUNT   ; * Process Universe Account
      NEXT I
*
      GOSUB FINALISATION
*
      STOP
*
*================================================================================
*
PROCESS.UV.ACCOUNT:
*
* Processing of all (or default) Universe account/s instances.
*
      CMD = ''
      EXPR = ''
      IF PAUSE THEN
         PRINTLINE = 'Account: ':ACCOUNT<I>'30L':' <Enter>(=Continue)[[/END]](=Stop) : '
         GOSUB PRINT.LINE
         INPUT RESPONSE
         IF RESPONSE = 'END' THEN STOP
      END
*---- Check that Universe account exists.
      UV.ACCOUNT = RAISE(TRANS('UV.ACCOUNT',ACCOUNT<I>,-1,'X'))
      EXEC 'SH -c "ls -d ':UV.ACCOUNT<11>:'"' CAPTURING OUTPUT
      IF INDEX(OUTPUT,'does not exist',1) THEN
         PRINT 'Universe Account ':ACCOUNT<I>:' does not exist'
         RETURN
      END
      HUSH ON
      EXEC 'SET.FILE ':ACCOUNT<I>:' VOC ':VOC.PTR CAPTURING OUTPUT
      HUSH OFF
      IF DICT.FILE THEN
         FILE.RESPONSE = FIELD(FILE.RESPONSE,' ',2)
         IF FILE.RESPONSE = '_' THEN FILE.RESPONSE = ''    ; *@@@@#KF-081006
      END
      DICT = ''
*---- Determine which files to consider within the Universe account.
      BEGIN CASE
       CASE FILE.RESPONSE = 'A' OR FILE.RESPONSE = 'ALL' OR FILE.RESPONSE = ''
         IF FPTR.ONLY THEN EXPR = ' WITH F1 LIKE F...' ELSE
            IF QPTR.ONLY THEN EXPR = ' WITH F1 LIKE Q...' ELSE
               EXPR = ' WITH F1 LIKE F... OR WITH F1 LIKE Q...'
            END
         END
         CMD = 'SELECT ':VOC.PTR:EXPR
         HUSH ON
         EXEC CMD CAPTURING OUTPUT
         HUSH OFF
       CASE TRANS(VOC.PTR,FILE.RESPONSE,0,'X')
         PTR.TYPE = TRANS(VOC.PTR,FILE.RESPONSE,1,'X')[1,1]
         IF PTR.TYPE MATCHES 'F':@VM:'Q' THEN
            IF QPTR.ONLY THEN
               IF (PTR.TYPE = 'Q') THEN
                  FILE = FILE.RESPONSE
               END ELSE FILE = ''
            END
            IF FPTR.ONLY THEN
               IF (PTR.TYPE = 'F') THEN
                  FILE = FILE.RESPONSE
               END ELSE FILE = ''
            END
            FILE = FILE.RESPONSE
         END ELSE
            PRINT "'":FILE.RESPONSE:"' is not a file or file pointer"
            RETURN
         END
       CASE 1
         IF INDEX(FILE.RESPONSE,'...',1) THEN
            CMD = 'SELECT ':VOC.PTR:' LIKE ':FILE.RESPONSE
         END ELSE
            CMD = 'SELECT ':VOC.PTR:' = "':FILE.RESPONSE:'"'
         END
         IF FPTR.ONLY THEN EXPR = 'SELECT ':VOC.PTR:' WITH F1 LIKE F...' ELSE
            IF QPTR.ONLY THEN EXPR = 'SELECT ':VOC.PTR:' WITH F1 LIKE Q...' ELSE
               EXPR = 'SELECT ':VOC.PTR:' WITH F1 LIKE F... OR WITH F1 LIKE Q...'
            END
         END
         CMD = EXPR:@FM:CMD
         HUSH ON
         EXEC CMD CAPTURING OUTPUT
         HUSH OFF
      END CASE
      IF CMD THEN
         FILE = ''
         LOOP WHILE READNEXT ID DO
            FILE<-1> = ID
         REPEAT
      END
      FILE.CNT = DCOUNT(FILE,@FM)
      IF FILE.CNT = 0 THEN
         PRINTLINE = 'No File matches file-mask "':FILE.RESPONSE:'" in Account ':ACCOUNT<I>
         GOSUB PRINT.LINE
      END
      SAVE.LIST = 1
*
      FOR J = 1 TO FILE.CNT
         GOSUB PROCESS.FILE.IN.ACCOUNT   ; * Process File within Account
      NEXT J
*
      RETURN
*
*--------------------------------------------------------------------------------
*
PROCESS.FILE.IN.ACCOUNT:
*
* Processing of all selected file instances within a Universe account
*
*---- Filter out object code files.
      IF FIELD(FILE<J>,'.',DCOUNT(FILE<J>,'.')) = 'O' THEN RETURN
      EXEC 'SET.FILE ':ACCOUNT<I>:' ':FILE<J>:' ':FILE.PTR CAPTURING OUTPUT
      IF DICT.FILE THEN
         DICT = 'DICT '
      END ELSE
         DICT = ''
      END
      FILE.HDR.PRINTED = 0
      OPEN FILE.PTR TO FILE.FV ELSE
         IF NOT(NO.ERROR) THEN PRINT 'Could not access file ':FILE<J>:' in Account ':ACCOUNT<I>
         HUSH ON
         EXEC 'DELETE VOC ':FILE.PTR
         HUSH OFF
         RETURN
      END
      IF FILEINFO(FILE.FV,3) = 4 THEN
         IF LEN(DICT) THEN RETURN
         DIR.FILE = 1
      END ELSE
         DIR.FILE = 0
      END
      CLOSE FILE.FV
      IF TRANS(VOC.PTR,FILE<J>,1,'X')[1,1] = 'Q' THEN QPTR = 1 ELSE QPTR = 0
      RECORD = ''
      RECORD.SAVED = ''
      CMD = ''
* --- Determine which records to consider within a file in a Universe account.
      IF SAMPLE THEN SAMPLE = 'SAMPLE ':SAMPLE ELSE SAMPLE = ''
      BEGIN CASE
       CASE REC.RESPONSE = 'A' OR REC.RESPONSE = 'ALL' OR REC.RESPONSE = '' OR REC.RESPONSE = '...'
         IF SORT.KEYS THEN CMD = 'SSELECT ':DICT:FILE.PTR
         ELSE CMD = 'SELECT ':DICT:FILE.PTR
       CASE NOT(INDEX(REC.RESPONSE,'[',1)) AND NOT(INDEX(REC.RESPONSE,']',1)) AND NOT(INDEX(REC.RESPONSE,'...',1)) AND TRANS(DICT:FILE.PTR,REC.RESPONSE,0,'X')
         CMD = 'SELECT ':DICT:FILE.PTR:' "':REC.RESPONSE:'"'
       CASE FIELD(REC.RESPONSE,' ',1) MATCHES 'WITH':@VM:'LIKE'
         STRING.RESPONSE = REC.RESPONSE
         REC.RESPONSE = '...'
         CMD = 'SELECT ':DICT:FILE.PTR:' LIKE "':REC.RESPONSE:'"'
       CASE 1
         IF NOT(RECORD.LIST) THEN
            IF NOT(TRANS('&SAVEDLISTS&',REC.RESPONSE,0,'X')) THEN
               IF SORT.KEYS THEN CMD = 'SSELECT ':DICT:FILE.PTR:' LIKE "':REC.RESPONSE:'"'
               ELSE CMD = 'SELECT ':DICT:FILE.PTR:' LIKE "':REC.RESPONSE:'"'
            END ELSE
               RECORD.LIST = 1
               CMD = 'GET.LIST ':REC.RESPONSE
            END
         END ELSE
            CMD = 'GET.LIST ':LIST.NAME
         END
      END CASE
      IF STRING.RESPONSE AND NOT(RE.MATCHING) THEN
         BEGIN CASE
          CASE FIELD(STRING.RESPONSE,' ',1) = 'WITH'
            CMD := ' AND ':STRING.RESPONSE
          CASE FIELD(STRING.RESPONSE,' ',1) = 'LIKE'
            CMD := ' AND WITH EVAL "@RECORD" ':STRING.RESPONSE
          CASE NOT(INDEX(STRING.RESPONSE,'...',1))
            IF STRING.RESPONSE MATCHES '...[...':@VM:'...]...' THEN
               CMD := ' WITH EVAL "@RECORD" = "':STRING.RESPONSE:'"'
               STRING.RESPONSE = TRIM(TRIM(STRING.RESPONSE,'[','L'),']','T')
            END ELSE
               CMD := ' WITH EVAL "@RECORD" = "[':STRING.RESPONSE:']"'
            END
          CASE INDEX(STRING.RESPONSE,'...',1)
            CMD := ' WITH EVAL "@RECORD" LIKE ':STRING.RESPONSE
         END CASE
      END
      CMD := ' ':SAMPLE
      EXEC CMD CAPTURING OUTPUT
      IF NOT(OUTPUT MATCHES "...record(s)...selected...to...") AND NOT(NO.ERROR) THEN
         PRINT 'Error selecting records from file ':FILE<J>:' : '
         FOR OUTPUT.INX = 1 TO DCOUNT(OUTPUT,@FM)
            IF OUTPUT<OUTPUT.INX> ELSE CONTINUE
            PRINT OUTPUT<OUTPUT.INX>
         NEXT OUTPUT.INX
      END
      K.MAX = @SELECTED
      IF COUNT.RECS THEN
         PRINTLINE = "Records matching record-mask and/or string-mask in file ":DICT:FILE<J>:" : ":K.MAX
         GOSUB PRINT.LINE
         RETURN
      END
      IF @SELECTED > 0 THEN
         RECS.PRINTED = 0
         IF K.MAX > 10 THEN WARN.USER = 1 ELSE WARN.USER = 0
*---------- Process Records in File within Account
         FOR K = 1 TO K.MAX
            READNEXT ID ELSE EXIT
            RECORD<-1> = ID
*------------- Process records in groups of 10 to save waiting time.
            IF NOT(MOD(K,10)) THEN
               GOSUB LIST.RECORD.GROUP   ; * List records within file within account.
               RECORD = ''
            END
         NEXT K
         IF LEN(RECORD) THEN
            GOSUB LIST.RECORD.GROUP
         END
         PRINT ''
         PRINTLINE = 'Records listed in file ':DICT:FILE<J>:' : ':K.MAX
         GOSUB PRINT.LINE
         IF LIST.NAME THEN EXEC 'DELETE.LIST ':LIST.NAME
      END
*
      RETURN
*
*--------------------------------------------------------------------------------
*
LIST.RECORD.GROUP:
*
* Processing a group of 10 or fewer records within a file within a Universe account
*
      RECORD.CNT = DCOUNT(RECORD,@FM)
      IF RECORD.CNT = 0 THEN
         PRINTLINE = "No Record matches record-mask and/or string-mask in a/c ":ACCOUNT<I>
         GOSUB PRINT.LINE
      END
      LAST.SAVEDLIST = ''
      IF KEYS.ONLY THEN
         OUTPUT = RECORD
         RECORD.CNT = 1
      END
      FOR L = 1 TO RECORD.CNT
         GOSUB LIST.RECORD
      NEXT L
      RETURN
*
*--------------------------------------------------------------------------------
*
LIST.RECORD:
*
* Listing a record within a file within a Universe account.
*
      IF NOT(FILE.HDR.PRINTED) THEN
         IF QPTR THEN QPTR = '(Q-pointer)' ELSE QPTR = ''
         PRINTLINE = ''
         GOSUB PRINT.LINE
         PRINTLINE = 'Account: ':ACCOUNT<I>:' - File: ':DICT:FILE<J>:' ':QPTR
         GOSUB PRINT.LINE
         IF KEYS.ONLY THEN
            PRINTLINE = ''
            GOSUB PRINT.LINE
         END
         LAST.SAVEDLIST = ACCOUNT<I>:@FM:DICT:FILE<J>
         FILE.HDR.PRINTED = 1
      END
      REC.HDR.PRINTED = @FALSE
      IF NOT(KEYS.ONLY) THEN
         RECORD.PTR = RECORD<L>
         CMD = 'LIST.ITEM ':DICT:FILE.PTR:' "':RECORD.PTR:'" NOPAGE COL.HDR.SUPP'
         IF COMO AND COMO.ON THEN
            COMO.ON = @FALSE
            HUSH ON
            EXEC 'COMO OFF'
            EXEC 'SH -c "cat \&COMO\&[[/KLIST]].PART.COMO >> \&COMO\&[[/KLIST]].COMO"'
            EXEC 'DELETE &COMO& KLIST.PART.COMO'
            HUSH OFF
         END
         EXEC CMD CAPTURING OUTPUT
         IF COMO THEN
            COMO.ON = @TRUE
            HUSH ON
            EXEC 'COMO ON KLIST.PART.COMO'
            HUSH OFF
         END
         IF (STRING.RESPONSE = '') THEN
            OUTPUT = OUTPUT[INDEX(OUTPUT,DICT:FILE.PTR,1),99999]
            OUTPUT = CHANGE(OUTPUT,DICT:FILE.PTR,DICT:FILE<J>,-1)
         END ELSE
            IF RE.MATCHING THEN
*------------- Check for a match on a PERL regular expression within the record.
               STRING.OBJ = CHANGE(FIELD(OUTPUT,@FM,2,999),@FM,'')
               IF LEN(STRING.OBJ) < 257 THEN
                  IF RE[1,1] MATCHES "'":@VM:'"' THEN
                     RE = RE[2,LEN(RE)-2]
                  END
                  RESPONSE = REGEXP(STRING.OBJ,RE)
                  IF TEST THEN
                     PRINT RESPONSE<1>
                     PRINT RESPONSE<2>
                  END
                  IF NOT(RESPONSE<1>) THEN RETURN
               END
            END
         END
         RECORD.SAVED := @FM:RECORD<L>
      END
      LINE.CNT = DCOUNT(OUTPUT,@FM)
      IF DICT.FILE AND OUTPUT<5> = '001 I' THEN I.TYPE = @TRUE ELSE I.TYPE = @FALSE
      FOR M = 1 TO LINE.CNT
         IF I.TYPE AND M > 13 THEN RETURN
         GOSUB PROCESS.LINE ;* Process each line in the record listing output.
      NEXT M
*
      RETURN
*
*--------------------------------------------------------------------------------
*
PROCESS.LINE:
*
* Processing a line within listing of a record within a file within a Universe account.
*
      IF KEYS.ONLY THEN
         RECORD.PTR = OUTPUT<M>
         PRINTLINE = OUTPUT<M>
         GOSUB PRINT.LINE
      END ELSE
         IF OUTPUT<M> = '' THEN RETURN
         LINE.IN.SCREEN.OUTPUT = MOD(M,@CRTHIGH)
         IF LINE.IN.SCREEN.OUTPUT = 1 THEN RETURN
         IF INDEX(OUTPUT<M>,'LIST.ITEM ',1) THEN RETURN
         IF INDEX(OUTPUT<M-1>:OUTPUT<M>,' PAGE ',1) THEN RETURN
         IF TRIM(OUTPUT<M>) = RECORD.PTR THEN
            REC.HDR = 'Record: ':OUTPUT<M>
            RETURN
         END
*------- Check for a match within the current output line on either a specified string,
*           or if supplied, a PERL regulare expression.
         IF NOT(FULL.ITEM) THEN
            IF RE.MATCHING THEN
               IF NOT(REGEXP(OUTPUT<M>,RE)<1>) THEN RETURN
            END ELSE
               IF LEN(STRING.RESPONSE) THEN
                  IF NOT(OUTPUT<M> MATCHES '...':STRING.RESPONSE:'...') THEN RETURN
               END
            END
         END
*------- Combine split lines in output from LIST.ITEM
         N = 1
         LOOP WHILE NOT((M+N) > LINE.CNT) DO
            IF OUTPUT<M+N>[1,4] = '    ' THEN
               OUTPUT<M> := TRIM(OUTPUT<M+N>)
               N += 1
            END ELSE EXIT
         REPEAT
         LINE = OUTPUT<M>
         M += (N-1)
         OUTPUT.FIELD.NO = TRIM(FIELD(LINE,' ',1),'0','L')
         LINE = LINE[5,999]
*------- Check whether field is to be printed or not.
         PRINT.FIELD = @FALSE
         IF LEN(FIELDS) THEN
            RECORD.POS = 0
            LOCATE(OUTPUT.FIELD.NO, FIELDS; RECORD.POS) THEN
               PRINT.FIELD = @TRUE
            END
         END ELSE
            PRINT.FIELD = @TRUE
         END
         IF PRINT.FIELD THEN
            IF NOT(REC.HDR.PRINTED) THEN
               REC.HDR.PRINTED = @TRUE
               PRINTLINE = ''
               GOSUB PRINT.LINE
               PRINTLINE = REC.HDR
               GOSUB PRINT.LINE
               RECS.PRINTED += 1
            END
            IF DATE.CONV THEN
*------------- Check each subvalue within each value within the current output line.
               CONVERSIONS = @FALSE
               MV.MAX = DCOUNT(LINE<1>,@VM)
               IF MV.MAX = 0 THEN
                  TRY.DATE = LINE<1>
                  IF LEN(TRY.DATE) THEN GOSUB TRY.CONVERTING
               END ELSE
                  FOR MV.INX = 1 TO MV.MAX
                     THIS.VAL = LINE<1,MV.INX>
                     SV.MAX = DCOUNT(THIS.VAL<1,1>,@SM)
                     IF SV.MAX = 0 THEN
                        TRY.DATE = THIS.VAL
                        IF LEN(TRY.DATE) THEN GOSUB TRY.CONVERTING
                     END ELSE
                        FOR SV.INX = 1 TO SV.MAX
                           TRY.DATE = THIS.VAL<1,1,SV.INX>
                           IF LEN(TRY.DATE) THEN GOSUB TRY.CONVERTING
                        NEXT SV.INX
                     END
                  NEXT MV.INX
               END
               IF CONVERSIONS THEN OUTPUT<M> = OUTPUT<M>[1,4]:LINE
            END
*
            IF LEN(EXP.MVS) AND (INDEX(LINE,@VM,1) OR INDEX(LINE,@SM,1)) THEN
*------------- Field is multivalued, so check whether expansion of values is required or not.
               EXPAND.MV = @FALSE
               MV.FIELD.POS = 0
               LOCATE(OUTPUT.FIELD.NO, EXP.MVS; MV.FIELD.POS) THEN
                  EXPAND.MV = @TRUE
               END ELSE
                  IF EXP.MVS<1> = 0 THEN
                     EXPAND.MV = @TRUE
                     MV.FIELD.POS = 1
                  END
               END
               IF EXPAND.MV THEN
                  GOSUB EXPAND.MVS
               END ELSE
                  IF LEN(LINE) > 0 THEN
                     PRINTLINE = OUTPUT.FIELD.NO"R%3":' ':LINE
                     GOSUB PRINT.LINE
                  END
               END
            END ELSE
               IF LEN(LINE) > 0 THEN
                  PRINTLINE = OUTPUT.FIELD.NO"R%3":' ':LINE
                  GOSUB PRINT.LINE
               END
            END
         END ELSE PRINT.FIELD = @FALSE
      END
*
      RETURN
*
*--------------------------------------------------------------------------------
*
TRY.CONVERTING:
*
* If the value in the field looks like an internal date and if it is, convert it.
*
      IF NUM(TRY.DATE) THEN
         IF INT(TRY.DATE) AND (TRY.DATE > 9999) AND (TRY.DATE < 20000) THEN
*---------- See if this date is to be date-converted
            FOR CONV.INX = 1 TO CONV.SPECIFIERS
               IF (CONV.FIELDS<CONV.INX> = 0) OR (CONV.FIELDS<CONV.INX> = OUTPUT.FIELD.NO) THEN
                  IF (CONV.MVS<CONV.INX> = 0) OR (CONV.MVS<CONV.INX> = MV.INX) THEN
                     IF (CONV.SVS<CONV.INX> = 0) OR (CONV.SVS<CONV.INX> = SV.INX) THEN
                        EXT.DATE = OCONV(TRY.DATE,'D4')
                        IF ICONV(EXT.DATE,'D4') = TRY.DATE THEN
                           LINE<1,MV.INX,SV.INX> = EXT.DATE
                           CONVERSIONS = @TRUE
                        END
                     END
                  END
               END
            NEXT CONV.INX
         END
      END
*
      RETURN
*
*--------------------------------------------------------------------------------
*
EXPAND.MVS:
*
* Expand multivalues in a field in a record in a file within a Universe account
*
      MV.MIN = 1
      MV.MAX = DCOUNT(LINE,@VM)
      FOR MV.INX = MV.MIN TO MV.MAX
         IF (MV.MAX > 1 OR INDEX(LINE,@SM,1)) AND MV.INX = 1 THEN   ; * Allow for sub-valued single multivalue field.
            PRINTLINE = OUTPUT.FIELD.NO"R%3":' Multivalues:'
            GOSUB PRINT.LINE
         END
         CURRENT.MV = LINE<1,MV.INX>
         IF LEN(EXP.SVS) AND INDEX(CURRENT.MV,@SM,1) THEN
*---------- Value contains subvalues, so check whether expansion of subvalues is required or not.
            EXPAND.SV = @FALSE
            SV.VALUE.POS = 0
            LOCATE(MV.INX, EXP.SVS<MV.FIELD.POS>; SV.VALUE.POS) THEN
               EXPAND.SV = @TRUE
            END ELSE
               IF EXP.SVS<MV.FIELD.POS> = 0 THEN
                  EXPAND.SV = @TRUE
                  SV.VALUE.POS = 1
               END
            END
            IF EXPAND.SV THEN
               SV.MIN = 1
               SV.MAX = DCOUNT(CURRENT.MV,@SM)
               FOR SV.INX = SV.MIN TO SV.MAX
                  IF SV.MAX > 1 AND SV.INX = 1 THEN
                     PRINTLINE = SPACES(4):MV.INX"3R%3":' Subvalues:'
                     GOSUB PRINT.LINE
                  END
                  CURRENT.SV = CURRENT.MV<1,1,SV.INX>
                  IF LEN(CURRENT.SV) THEN
                     PRINTLINE = SPACES(8):SV.INX"3R%3":' ':CURRENT.SV
                     GOSUB PRINT.LINE
                  END
               NEXT SV.INX
            END ELSE
               IF LEN(CURRENT.MV) THEN
                  PRINTLINE = SPACES(4):MV.INX"3R%3":' ':CURRENT.MV
                  GOSUB PRINT.LINE
               END
            END
         END ELSE
            IF LEN(CURRENT.MV) THEN
               PRINTLINE = SPACES(4):MV.INX"3R%3":' ':CURRENT.MV
               GOSUB PRINT.LINE
            END
         END
      NEXT MV.INX
      RETURN
*
*--------------------------------------------------------------------------------
*
PRINT.LINE:
*
* Print a line of output
*
      IF COMO AND NOT(COMO.ON) THEN
         COMO.ON = @TRUE
         HUSH ON
         EXEC 'COMO ON KLIST.PART.COMO'
         HUSH OFF
      END
      IF (LINES.PRINTED > (@CRTHIGH-3)) AND NOT(NOSTOP) THEN
         PRINT
         PRINT 'Continue? Y(es)=default[[/N]](oStop)[[/Q]](uit)[[/END]] ... : ':
         INPUT RESPONSE
         BEGIN CASE
          CASE UPCASE(RESPONSE) = 'END' OR UPCASE(RESPONSE) = 'Q'
            PRINT
            STOP
          CASE UPCASE(RESPONSE) = 'N'
            NOSTOP = 1
            IF WARN.USER THEN
               PRINT 'There are ':(K.MAX-RECS.PRINTED):' more records to go.  List them ALL? (Y[[/N]]=default) : ':
               INPUT RESPONSE
               IF UPCASE(RESPONSE)[1,1] # 'Y' THEN NOSTOP = 0
            END
          CASE 1
            NULL
         END CASE
         PRINT 'Account: ':ACCOUNT<I>:' - File: ':DICT:FILE<J>:' ':QPTR:' - Record: ':RECORD.PTR:' (continued ... )'
         PRINT
         LINES.PRINTED = 2
      END
      PRINT PRINTLINE
      LINES.PRINTED += 1
      RETURN
*
*--------------------------------------------------------------------------------
*
INITIALISATION:
*
      IF INDEX(@SENTENCE,'-H',1) THEN
         PRINT 'KLIST [<A/c-mask>] [<File-mask> <Record-mask>] [<String-mask>][<Options>]'
         PRINT 'Options - '
         PRINT " -An or -Fn   List Attribute[[/Field]] 'n' only.  Note: Multiple fields can be"
         PRINT '              specified delimited by ";"'
         PRINT ' -C[OUNT]     Count of records in a file'
         PRINT " -D[f][,[v][,[s]]]"
         PRINT "              Convert date/s in attributes/fields, values and subvalues."
         PRINT '              Note: A optional field no., optional value no., and an'
         PRINT '              optional subvalue no. can be included with a comma delimiter.'
         PRINT "              Multiple 'D,,' instances are permitted, delimited by ';'"
         PRINT ' -E[D]        Execute Line Editor using items selected from last used file'
         PRINT '              Note: Last selection is in saved list KLIST.<Unix-login-id>'
         PRINT ' -K[EYS]      Keys to records only are listed.'
         PRINT ' -M[ATCHES]   Matching lines only in records selected are listed.'
         PRINT ' -N[OSTOP]    Eliminate Header and End-of-Screen pause.'
         PRINT ' -RE          List only lines in records matching a PERL Regular Expression.'
         PRINT ' -S[ORT]      Sort by record ID'
         PRINT ' -V[f][,S[v]] Expand Values in attributes/fields (and optionally Subvalues'
         PRINT '              in values)'
         PRINT "              Note: A Field[[/Attribute]] no. can follow 'V' to limit expansions."
         PRINT "              A Value no. can follow 'S' to limit expansions."
         PRINT "              Multiple 'V,S' instances are permitted delimited by ';'."
         PRINT ''
         PRINT 'Press <Enter> to continue ...':
         INPUT RESPONSE
         PRINT " -n           List a sample of 'n' records only."
         PRINT ''
         PRINT 'Less commonly used Options -'
         PRINT ' -COMO        Capture output to KLIST.COMO command output file'
         PRINT ' -F[PTRS]     Include only files referenced by F-pointers'
         PRINT ' -NO.ERR      Eliminate messages about file access restrictions'
         PRINT ' -P[AUSE]     Pause at each A/c header'
         PRINT ' -Q[PTRS]     Include only files referenced by Q-pointers'
         PRINT ''
         PRINT 'Note: A command line mask item containing one or more spaces requires'
         PRINT "       the whole mask item to be quoted.  "
         PRINT "       E.g. 'WITH EVAL ":'"@RECORD<48,1>"':' # ""':"'"
         PRINT '       Dictionary item names can be used in field listing expressions.'
         PRINT "       Either '[' and/or ']' masks OR '...' masks can be used."
         PRINT ''
         PRINT " A/c-mask     'A'|'' = All Accounts / Default = Current Account"
         PRINT " File-mask    '' = All files in an Account / '[',']' and '...' allowed"
         PRINT " Record-mask  ''|null = All records in file in Account / 'WITH' clause allowed"
         PRINT " String-mask  '[',']' and '...' allowed / 'WITH' clause allowed / PERL Regular"
         PRINT "               Expression[[/Record]]-selection-phrase based on field content"
         PRINT ''
         PRINT 'Press <Enter> to continue ...':
         INPUT RESPONSE
         PRINT ' Examples :'
         PRINT " KLIST FMC.CON... ...FTRIG... -N -V,S -COMO"
         PRINT "    Captures in KLIST.COMO a list of all records with keys containing 'FTRIG'"
         PRINT "    in all files with names containing 'FMC.CON' in the current Account, with"
         PRINT "    expansion of all values and subvalues in multivalued fields"
         PRINT " KLIST FMC.TEST DICT APP.INDEX -N -K -S"
         PRINT "    List keys only in sorted order in the dictionary of file APP.INDEX in the"
         PRINT "    Account FMC.TEST."
         PRINT " KLIST TRAN ...S9... -A17 -V,S -N -5"
         PRINT "    List 5 samples of records with keys containing 'S9' in the TRAN file, "
         PRINT "    displaying Attribute 17 only, expanding all values and subvalues"
         PRINT " KLIST VOC LOGIN '(kford)' -RE"
         PRINT "    List lines in the 'LOGIN' record in the 'VOC' file in the current Account"
         PRINT "    that match the PERL regular expression string mask."
         PRINT " KLIST TRAN ...S9... -V,S -N -2 -D17,2,1"
         PRINT "    List 2 samples of records with keys containing 'S9' in the TRAN file, "
         PRINT "    displaying all fields, expanding all values and subvalues, and converting"
         PRINT "    the date (if it is one) in field/attribute 17, value 2, subvalue 1."
         STOP
      END
      IF TRANS('VOC','UV.ACCOUNT',0,'X') = '' THEN
         HUSH ON
         DATA 'Q'
         DATA 'UV'
         DATA 'UV.ACCOUNT'
         DATA ''
         DATA 'FI'
         EXEC 'ED VOC UV.ACCOUNT'
         HUSH OFF
      END
      AC.RESPONSE      = ''
      ACCOUNT          = ''
      ARG              = ''
      ARGS             = ''
      CMD.LINE         = CHANGE(@SENTENCE,' ',@FM)
      COMO             = @FALSE
      COMO.ON          = @FALSE
      COUNT.RECS       = 0
      DATE.CONV        = 0
      DICT.FILE        = 0
      EDIT.LIST        = 0
      ELT.CNT          = 0
      EXP.MVS          = ''
      EXP.SVS          = ''
      EXPECT.RE        = @FALSE
      FIELDS           = ''
      FILE             = ''
      FILE.PTR         = 'KLIST.FILE'
      FILE.RESPONSE    = ''
      FPTR.ONLY        = 0
      FULL.ITEM        = 1
      ITEM             = 1
      KEYS.ONLY        = 0
      LAST.SAVEDLIST   = ''
      LEFTOVERS        = ''
      LINE             = ''
      LINES.PER.PAGE   = @CRTHIGH
      LINES.PRINTED    = 0
      LIST.NAME        = ''
      NO.ERROR         = @FALSE
      NOSTOP           = 0
      PAUSE            = 0
      QPTR.ONLY        = 0
      RE               = ''
      RE.MATCHING      = @FALSE
      REC.HDR          = ''
      REC.HDR.PRINTED  = @FALSE
      REC.RESPONSE     = ''
      RECORD           = ''
      RECORD.LIST      = 0
      RECORD.SAVED     = ''
      RECS.PRINTED     = 0
      SAMPLE           = 0
      SAVE.LIST        = 0
      SORT.KEYS        = 0
      SPECIFIC.FIELDS  = ''
      STRING.RESPONSE  = ''
      SVAL.POS         = 0
      TEST             = 0
      VAL.POS          = 0
      VOC.PTR          = 'KLIST.VOC'
      WARN.USER        = 0
      RETURN
*      
*--------------------------------------------------------------------------------
*
GET.OPTIONS:
*
      LOOP
         REMOVE ELT FROM CMD.LINE SETTING DELIM
         ELT.CNT += 1
         IF ELT = 'RUN' OR ELT = 'RAID' THEN
            REMOVE ELT FROM CMD.LINE SETTING DELIM
            ELT.CNT += 1
            CONTINUE
         END
         IF ELT[1,5] = 'KLIST' THEN CONTINUE
         IF NOT(ELT) THEN
            IF NOT(DELIM) THEN EXIT
            ELSE CONTINUE
         END
*------- Parse command line options.
         BEGIN CASE
          CASE ELT[1,2] = '-A'                    ;* List specific attribute(s)/field(s) only
            FIELDS = CHANGE(ELT[3,99],';',@FM)
          CASE ELT[1,5] = '-COMO'                 ;* Capture to a command output file
            COMO = @TRUE
            HUSH ON
            EXEC 'DELETE &COMO& KLIST.COMO'
            HUSH OFF
          CASE ELT[1,2] = '-C'                    ;* Count of records in a file
            COUNT.RECS = 1
          CASE ELT[1,2] = '-D'                    ;* Convert dates to external format
            DATE.CONV = @TRUE
*---------- Create arrays of field, value and subvalue positions for data conversion
*           Note: '0' means convert all fields/values/subvalues; null means no conversion
*
* Examples:
* '-D1;2,1;2,2,;3;4'
*   This produces 3 arrays internally, thus:
*       Field array -     1:@FM:2:@FM:2:@FM:3:@FM:4
*       Value array -     0:@FM:1:@FM:2:@FM:0:@FM:0
*       [[SubValue]] array -  0:@FM:0:@FM:0;@FM:0:@FM:0
*   This is interpreted to mean:
*      Date convert internal dates in field 1 (all values and subvalues, if any)
*                   internal dates in field 2, value 1
*                   field 2, value 2 (all subvalues, if any)
*                   field 3 (all values and subvalues, if any)
*                   field 4 (all values and subvalues, if any)
            CONV.FIELDS = ''
            CONV.MVS    = ''
            CONV.SVS    = ''
            SETS = CHANGE(ELT[3,99],';',@FM)
            LOOP REMOVE SUBELT FROM SETS SETTING ELT.DELIM
               IF LEN(SUBELT) THEN
                  FIELD.POS = FIELD(SUBELT,',',1)
                  IF LEN(FIELD.POS) THEN
                     CONV.FIELDS<-1> = FIELD.POS
                  END ELSE
                     CONV.FIELDS<-1> = 0
                  END
                  VAL.POS = FIELD(SUBELT,',',2)
                  IF LEN(VAL.POS) THEN
                     CONV.MVS<-1> = VAL.POS
                  END ELSE
                     CONV.MVS<-1> = 0
                  END
                  SVAL.POS = FIELD(SUBELT,',',3)
                  IF LEN(SVAL.POS) THEN
                     CONV.SVS<-1> = SVAL.POS
                  END ELSE
                     CONV.SVS<-1> = 0
                  END
               END
            WHILE ELT.DELIM DO
            REPEAT
            IF NOT(LEN(CONV.FIELDS)) THEN
               CONV.FIELDS = 0
               CONV.MVS    = 0
               CONV.SVS    = 0
            END
            CONV.SPECIFIERS = DCOUNT(CONV.FIELDS,@FM)
          CASE ELT[1,2] = '-E'                    ;* Edit the listed records 
            EDIT.LIST = 1
          CASE ELT[1,5] = '-FPTR'                 ;* List only files with 'F' pointers
            FPTR.ONLY = 1
          CASE ELT[1,2] = '-F'                    ;* List specific field(s)/attribute(s) only
            FIELDS = CHANGE(ELT[3,99],';',@FM)
          CASE ELT[1,2] = '-K'                    ;* List only keys in a file
            KEYS.ONLY = 1
          CASE ELT[1,2] = '-M'                    ;* List only lines in record that  match string
            FULL.ITEM = 0
          CASE ELT[1,7] = '-NO.ERR'               ;* No errors listed
            NO.ERROR = @TRUE
          CASE ELT[1,2] = '-N'                    ;* No stopping after a screen of output
            NOSTOP = 1
            EXEC 'TERM ,9999' CAPTURING OUTPUT
          CASE ELT[1,2] = '-P'                    ;* Pause on change of Universe account
            PAUSE = 1
          CASE ELT[1,5] = '-QPTR'                 ;* List only files with 'Q' pointers
            QPTR.ONLY = 1
          CASE ELT[1,3] = '-RE'                   ;* Search records using a PERL regular expression
            RE.MATCHING = @TRUE
            FULL.ITEM = @FALSE
          CASE ELT[1,2] = '-S'                    ;* Sort listed output by record key
            SORT.KEYS = 1
          CASE ELT[1,2] = '-T'                    ;* Testing (debug) mode
            TEST = 1
            DEBUG
          CASE ELT[1,2] = '-V'                    ;* Expand values withing fields
*
*           '-V' specifys which field or fields are to have values and optionally
*                              subvalues displayed'
*           Valid variants of the '-V' option:
*              '-V'            means expand all multivalued fields 
*              '-Vn'           means expand multivalued field "n" only 
*              '-V,S'          means expand all multivalued fields and all subvalued
*                                  values in each field
*              '-Vn,S'         means expand multivalued field "n" only and all subvalued 
*                                  values in that field   
*              '-Vn,Sp'        means expand multivalued field "n" only and subvalues 
*                                  in value "p" of the field
*              '-V,Sp'         means expand all multivalued fields and subvalues in
*                                  value "p" of each field
*              '-Vn[,S[p]];q[,S[r]] ... ;[x[,S[y]]'
*                              represents a semi-colon delimited list of specific fields
*                                  with optional value and subvalue expansion 
*           Examples:
*              '-V'            means expand all values in all fields.
*              '-V5'           means expand all values in multivalued field 5 only.
*              '-V,S'          means expand all values in all multivalued fields and
*                                  all subvalued values in each such field.
*              '-V5,S'         means expand all values in multivalued field 5 only and
*                                  all subvalued values in that field.
*              '-V5,S5'        means expand multivalued field 5 only and subvalues in
*                                  value 5 in that field.
*              '-V5,S;7;9,S;12,S4'
*                              means expand multivalued fields 5, 7, 9 & 12, and  
*                                  all subvalues in all values in fields 5 & 9, and subvalues  
*                                  in value 4 if field 12.
*
*           Note: It is valid to have multiple "-Vn" options, so the last example could be
*                     expressed as '-V5,S -V7 -V9,S -V12,S4'
*
*---------- Create arrays of value and subvalue positions for expansion
*           Note: '0' indicates expand all values/subvalues; null indicates no expansion
            SETS = CHANGE(ELT[3,99],';',@FM)
            LOOP REMOVE SUBELT FROM SETS SETTING ELT.DELIM
               IF LEN(SUBELT) THEN
                  VAL.POS = FIELD(SUBELT,',',1)
                  IF LEN(VAL.POS) THEN
                     EXP.MVS<-1> = VAL.POS
                  END ELSE
                     EXP.MVS<-1> = 0
                  END
                  SVAL.POS = FIELD(SUBELT,',',2)
                  IF LEN(SVAL.POS) THEN
                     SVAL.POS = FIELD(SVAL.POS,'S',2)
                     IF LEN(SVAL.POS) THEN
                        EXP.SVS<-1> = SVAL.POS
                     END ELSE
                        EXP.SVS<-1> = 0
                     END
                  END ELSE
                     EXP.SVS<-1> = @FM
                  END
               END ELSE
                  EXP.MVS<-1> = 0
                  EXP.SVS<-1> = @FM
               END
            WHILE ELT.DELIM DO
            REPEAT
          CASE ELT[1,1] = '-' AND ELT[2,99] MATCHES "0N" ;* List a sample of 'n' records
            SAMPLE = ELT[2,99]
          CASE 1
            ARGS := ELT:' '
         END CASE
      WHILE DELIM DO
      REPEAT
      CHAR.CNT = LEN(ARGS)
*---- If there is an active select list of record IDs, save it for use later.
      IF SYSTEM(11) THEN
         RECORDS = ''
         EOI = @FALSE
         LOOP
            READNEXT ID ELSE EOI = @TRUE
         WHILE NOT(EOI) DO
            IF NOT(LEN(ID)) THEN EXIT
            RECORDS<-1> = ID
         REPEAT
         IF LEN(RECORDS) THEN
            LIST.NAME = 'KLIST.LIST.':DATE():'.':FIELD(TIME(),'.',1)
            OPEN '&SAVEDLISTS&' TO SAVEDLISTS.FV THEN
               WRITE RECORDS ON SAVEDLISTS.FV, LIST.NAME
            END
            RECORD.LIST = 1
         END
      END
*---- Process positional arguments from command line.
      SELECT.EXPR = ''
      REGEXPR = ''
      ARG.CNT = 0
      LOOP
         ARG.CNT += 1
         ARG = FIELD(ARGS,' ',ARG.CNT)
      WHILE LEN(ARG) DO
         IF ARG[1,1] = '"' OR ARG[1,1] = "'" THEN
            ARG.DELIM = ARG[1,1]
            ARG = FIELD(ARGS,ARG.DELIM,2)
            ARG.CNT += COUNT(ARG,' ')
         END
         BEGIN CASE
          CASE AC.RESPONSE = ''
            BEGIN CASE
*----------- Are all Universe Accounts to be checked?
             CASE ARG MATCHES 'ALL':@VM:'[]' OR ARG = '...' OR ARG = '""' OR ARG = "''"
               AC.RESPONSE = 'ALL'
*----------- Is there a Universe A/c name mask in ARG?
             CASE INDEX(ARG,'...',1) OR  INDEX(ARG,'[',1) OR INDEX(ARG,']',1)
               AC.RESPONSE = ARG
* ---------- Is there a Universe A/c with its name in ARG?
             CASE TRANS('UV.ACCOUNT',ARG,0,'X') = ARG
               AC.RESPONSE = ARG
*----------- Is the Universe A/c not provided and there a file dictionary specified?
             CASE ARG = 'DICT'
               AC.RESPONSE = @WHO
               DICT.FILE = @TRUE
*----------- Is the Universe A/c not provided and there a file with its name in ARG?
             CASE TRANS('VOC',ARG,0,'X') = ARG
               AC.RESPONSE = @WHO
               FILE.RESPONSE = ARG
             CASE 1
               AC.RESPONSE = '_'
            END CASE
          CASE FILE.RESPONSE = ''
            BEGIN CASE
*----------- Are all files in the current Universe A/c to be checked?
             CASE ARG MATCHES 'ALL':@VM:'[]'OR ARG = '...' OR ARG = '""' OR ARG = "''"
               FILE.RESPONSE = 'ALL'
*----------- Is there a file dictionary specified in ARG?
             CASE ARG = 'DICT'
               DICT.FILE = @TRUE
*----------- Is there a file in the current Universe A/c with its name in ARG?
             CASE TRANS('VOC',ARG,0,'X') = ARG
               FILE.RESPONSE = ARG
*----------- Is there a name mask for the file/s to be check in the current Universe A/c?
             CASE INDEX(ARG,'...',1) OR INDEX(ARG,'[',1) OR INDEX(ARG,']',1)
               FILE.RESPONSE = ARG
             CASE 1
               FILE.RESPONSE = '_'
            END CASE
          CASE REC.RESPONSE = ''
            BEGIN CASE
             CASE INDEX(ARG,'...',1) OR INDEX(ARG,'[',1) OR INDEX(ARG,']',1)
               REC.RESPONSE = ARG
             CASE ARG MATCHES "'WITH":@VM:'"WITH'
               SELECT.EXPR = ARG
             CASE LEN(SELECT.EXPR)
               SELECT.EXPR := ARG
               IF ARG[LEN(ARG),1] MATCHES "'":@VM:'"' THEN
                  REC.RESPONSE = SELECT.EXPR
               END
             CASE 1
               REC.RESPONSE = ARG
            END CASE
          CASE STRING.RESPONSE = ''
            BEGIN CASE
             CASE RE.MATCHING
               IF LEN(REGEXPR) THEN
                  REGEXPR := ' ':ARG
                  IF ARG[LEN(ARG),1] MATCHES "'":@VM:'"' THEN
                     STRING.RESPONSE = REGEXPR
                  END
               END ELSE
                  REGEXPR = ARG
               END
             CASE INDEX(ARG,'...',1) OR INDEX(ARG,'[',1) OR INDEX(ARG,']',1)
               STRING.RESPONSE = ARG
             CASE ARG MATCHES "'WITH":@VM:'"WITH'
               SELECT.EXPR = ARG
             CASE LEN(SELECT.EXPR)
               SELECT.EXPR := ' ':ARG
               IF ARG[LEN(ARG),1] MATCHES "'":@VM:'"' THEN
                  STRING.RESPONSE = SELECT.EXPR
               END
             CASE 1
               REC.RESPONSE = ARG
            END CASE
          CASE 1
            LEFTOVERS = ARG
         END CASE
      REPEAT
      IF AC.RESPONSE = '' THEN AC.RESPONSE = '_'
      IF DICT.FILE THEN
         FILE.RESPONSE = 'DICT ':FILE.RESPONSE
      END
      IF AC.RESPONSE = '_' THEN
         IF NOT(LEN(FILE.RESPONSE)) THEN
            VALID.AC = @FALSE
            LOOP WHILE NOT(VALID.AC) DO
               PRINT 'Account(s)? A(ll)|Name-mask|A/c-name|""(=All)|':"''(=All)|Null(=Current)[[/END]] : ":
               INPUT AC.RESPONSE
               VALID.AC = @TRUE
               BEGIN CASE
                CASE AC.RESPONSE = ''
                  AC.RESPONSE = @WHO
                CASE AC.RESPONSE MATCHES 'ALL':@VM:'A':@VM:'[]' OR AC.RESPONSE = '""' OR AC.RESPONSE = "''" OR AC.RESPONSE = '...'
                  AC.RESPONSE = 'ALL'
                CASE TRANS('UV.ACCOUNT',AC.RESPONSE,0,'X') = AC.RESPONSE
                  AC.RESPONSE = AC.RESPONSE
                CASE AC.RESPONSE = 'END'
                  PRINT 'KLIST stopped.'
                  STOP
                CASE 1
                  PRINT 'Invalid Account response.'
                  VALID.AC = @FALSE
               END CASE
            REPEAT
         END
      END
      BEGIN CASE
       CASE AC.RESPONSE = 'ALL'
         CMD = 'SELECT UV.ACCOUNT'
         * HUSH ON
         EXEC CMD CAPTURING OUTPUT
         * HUSH OFF
       CASE INDEX(AC.RESPONSE,'...',1) OR INDEX(AC.RESPONSE,'[',1) OR INDEX(AC.RESPONSE,']',1)
         CMD = 'SELECT UV.ACCOUNT = "':AC.RESPONSE:'"'
         EXEC CMD CAPTURING OUTPUT
         IF INDEX(OUTPUT,@FM:'0 record',1) THEN
            ACCOUNT = @WHO
            IF TEST THEN
               PRINT "First argument (":AC.RESPONSE:") is not a Universe A/c - defaulting to Current A/c - ":ACCOUNT
               PRINT
            END
            IF AC.RESPONSE = 'DICT' THEN
               AC.RESPONSE := ' ':FILE.RESPONSE
               FILE.RESPONSE = REC.RESPONSE
               REC.RESPONSE = STRING.RESPONSE
               STRING.RESPONSE = ''
            END
            STRING.RESPONSE = REC.RESPONSE
            REC.RESPONSE = FILE.RESPONSE
            FILE.RESPONSE = AC.RESPONSE
            AC.RESPONSE = ''
         END
       CASE 1
         ACCOUNT = AC.RESPONSE
         @SELECTED = 0
      END CASE
      IF SYSTEM(11) THEN
         FOR I = 1 TO @SELECTED
            READNEXT ID ELSE EXIT
            ACCOUNT<-1> = ID
         NEXT I
      END
      IF ACCOUNT = '' THEN
         PRINT 'No Universe A/c matches account-mask (':AC.RESPONSE:')'
         STOP
      END
      IF FILE.RESPONSE # '' THEN
         IF FILE.RESPONSE = '_' THEN
            FILE.RESPONSE = ''
         END
      END ELSE
         PRINT 'File(s)?    (A)ll(=default)/<string-expression>/<filename>[[/END]]            : ':
         INPUT FILE.RESPONSE
         FILE.RESPONSE = UPCASE(FILE.RESPONSE)
         IF FILE.RESPONSE = 'END' THEN
            PRINT
            STOP
         END
      END
      IF REC.RESPONSE # '' THEN
         IF REC.RESPONSE = '_' THEN
            REC.RESPONSE = ''
         END
      END ELSE
         IF RECORD.LIST THEN
            REC.RESPONSE = LIST.NAME
         END ELSE
            IF ARG = '' AND NOT(KEYS.ONLY) THEN
               PRINT 'Record ID(s)?  (A)ll(=default)/<string-expression>/<record-ID[[/END]]         : ':
               INPUT REC.RESPONSE
               REC.RESPONSE = UPCASE(REC.RESPONSE)
               IF REC.RESPONSE = 'END' THEN
                  PRINT
                  STOP
               END
            END ELSE
               REC.RESPONSE = 'A'
            END
         END
      END
      IF STRING.RESPONSE # '' THEN
         IF STRING.RESPONSE = '_' THEN
            STRING.RESPONSE = ''
         END
      END
      RETURN
*
*--------------------------------------------------------------------------------
*
FINALISATION:
*
      PRINT
      IF TEST THEN PRINT 'Cleaning up temporary VOC items ...'
      HUSH ON
      EXEC 'DELETE VOC ':VOC.PTR
      HUSH OFF
      IF SAVE.LIST THEN
         K.MAX = DCOUNT(RECORD.SAVED,@FM)
         IF K.MAX > 1 THEN
            PRINT 'Saved ':K.MAX-1:' (selected) keys from last selected file (':LAST.SAVEDLIST<2>:' in a/c ':LAST.SAVEDLIST<1>:') in KLIST.':@ACCOUNT:' ...'
            HUSH ON
            EXEC 'DELETE &SAVEDLISTS& KLIST.':@ACCOUNT
            DATA ''
            FOR K = 2 TO K.MAX
               DATA 'I ':RECORD.SAVED<K>
            NEXT K
            DATA 'FI'
            EXEC 'ED &SAVEDLISTS& KLIST.':@ACCOUNT
            HUSH OFF
            IF EDIT.LIST THEN
               DATA 'ED ':DICT:FILE.PTR
               EXEC 'GET.LIST KLIST.':@ACCOUNT
            END
         END
      END
      HUSH ON
      EXEC 'DELETE VOC ':FILE.PTR
      EXEC 'TERM ,':LINES.PER.PAGE
      HUSH OFF
      CLEARSELECT
      IF COMO AND COMO.ON THEN
         COMO.ON = @FALSE
         HUSH ON
         EXEC 'COMO OFF'
         EXEC 'SH -c "cat \&COMO\&[[/KLIST]].PART.COMO >> \&COMO\&[[/KLIST]].COMO"'
         EXEC 'DELETE &COMO& KLIST.PART.COMO'
         HUSH OFF
      END
*
      RETURN
*
*--------------------------------------------------------------------------------
*
   END
*
*================================================================================