PARSE.CMDLINE

From Pickwiki
Jump to navigationJump to search

HomePage>>SourceCode>>BasicSource>>SVN>>PARSE.CMDLINE


             subroutine PARSE.CMDLINE(SENTENCE, MAT CMDLINE, OPTIONS) 
* =======================================================================================
* @LIB: Parse the [[UniVerse]] Command line using a standardized format
* =======================================================================================
* 24.Mar.2009 james: Add '\' as a valid Quote character for command line options.
* 30.Oct.2007 jim: NOPARAM option to allow no-parameter flags before Arguments.
*                  DEBUG.FLAG now uses new LOCATE method: DEBUG CMDLINE to debug.
* 04.Sep.2007 jim: Handle CMD.VERB = 'RUN' so callers don't have to.
* 02.Jul.2007 jim: SINGLEPARAM option to allow Flags before Arguments, 'Unix-Style'; i.e.:
*                  -<flag> <param> ARG ARG ...
*                  (was for DISPLAY.DIFFS but now not used anymore ?)
* 30-Aug-2005 jra: Add OPTIONS.
* 14-Nov-2003 New syntax for 'COPY.TO.REV'; to be used for all future commands.
* =======================================================================================
*
*   SENTENCE : Pass in @SENTENCE or really anything you want to parse.
*
*   CMDLINE  : Pass this array (so we don't have to put it in Global Common)
*
*   OPTIONS  : 'LONGFLAGS'   : -TEST will be treated as one flag instead of '-T EST'.
*              'SINGLEPARAM' : -x will use only ONE following parameter.
*              'NOPARAM'     : -x will be the flag with NO following parameter.
*
* =======================================================================================
*
*  Note that this does a COMPLETE CHARACTER BY CHARACTER PARSE:
*  this allows it to handle strings in quotes, strange file names, etc.
*
* Usage:
*
* %< -----------------------
*   $include RMS.BP EQU.CMDLINE 
*   call PARSE.CMDLINE(@SENTENCE, MAT CMDLINE, Options) 
*   if CMD.HELP then
*     print "<your help here>...
*     stop
*   end
*   <etc...>
* %< -----------------------
*
*    DSI 'Standardized' Syntax:
*
*  <Verb> <Arg1> <Arg2> <-Flag1> <Param1> <-Flag2Param2> ( <Option1> <Option2>
*
* Examples:
*    COPY.TO.REV FILE ITEM -T "TCL COMMAND" -F A (O P
* 
* Guidelines:
*     - Use ARGS for 'fixed position' parameters which are always required
*       ALL ARGS are to be used BEFORE OPTIONS AND FLAGS ! (unlike Unix...)
*     - Options and Flags are single characters unless 'LONGFLAGS' is used
*     - Use '(' OPTIONS for options with no parameters (but '-' is always recommended)
*     - Options do NOT need spaces or subsequent '('s, but they won't hurt.
*     - Use '-' FLAGS for things with another value; i.e. the 'parameter'
*     - Flags do NOT need spaces from their params, but they won't hurt.
*     - Programs should check the 'CMD.HELP' flag after calling this and
*       immediately display a help msg and exit if it's set.
*
* See 'RMS.BP COPY.TO.REV' for an example.
*
* Use: 'CMDLINE.TEST' to check your parameters and as an aid to writing your code.
*
* Note that since this program does a 'pure parse' of the command line, it only
* acknowledges '-', '(', and quotes when they START a string; therefore they
* can be embedded in Item-ID's without conflicting with the option parsing.
*
* =======================================================================================

  $include RMS.BP RMS.COMMON  ;*  DEBUG.FLAG to check for 'c' flag.
  
  * We need this to get at the EQU'ed names.
  * (and although we don't need the 'dim'; it won't hurt):
  $include RMS.BP EQU.CMDLINE 

  mat CMDLINE = ''  ;*  Clear out old COMMON's

  PROCLINE = SENTENCE  ;*  Now passed in !
  
  convert @VM to @FM in OPTIONS  ;*  Just in case caller used <1,-1> instead of <-1>
  locate('LONGFLAGS',   OPTIONS; LOC) then LONG.FLAGS  = @TRUE else LONG.FLAGS  = @FALSE
  locate('NOPARAM',     OPTIONS; LOC) then NOPARAM     = @TRUE else NOPARAM     = @FALSE
  locate('SINGLEPARAM', OPTIONS; LOC) then SINGLEPARAM = @TRUE else SINGLEPARAM = @FALSE
  CMD.HELP = @FALSE   ;*  Flag for '-H'
  OPTIONS  = @FALSE   ;*  After a '(' ALL are options
  FLAGS    = @FALSE   ;*  After a '-' are parameters or another Flag
  POS      = 1        ;*  Position in PROCLINE
  FLAG.PTR = 0        ;*  Which FLAG we are on: keeps incrementing.
  loop
    gosub [[GetNextVal]]
   while P # '' DO
*    locate( "CMDLINE", DEBUG.FLAG; dummy) then
*      print "P:":P: ; input j ; if j = 'D' then debug
*    end
    begin case
      case CMD.VERB = '' ; CMD.VERB = P
      case P[1,1] = '-'  ; gosub [[StartNewFlag]]
      case P[1,1] = '('  ; gosub [[StartOptions]]
      case OPTIONS       ; gosub [[AddOptions]]
      case FLAGS
        if P[1,1] = '-' then
          gosub [[StartNewFlag]]
        end else
          FLAG.PARAM = P ; gosub [[AddFlagParameter]]
        end
        
      case 1             ; CMD.ARG.S<1,-1> = P
    end case
  repeat

  *  Handle 'RUN <file.BP> <program>' here so callers don't have to:
  
  if CMD.VERB = 'RUN' and CMD.ARG.S<1, 1>'R#3' = '.BP' then
    CMD.VERB = CMD.ARG.S<1, 2>
    CMD.ARG.S = delete( CMD.ARG.S, 1, 1 )  ;*  Delete the '.BP'
    CMD.ARG.S = delete( CMD.ARG.S, 1, 1 )  ;*  Delete the VERB (now in CMD.VERB)
  end
  
  *  Finally set these convenience variables:
  CMD.NUM.ARGS    = dcount(CMD.ARG.S, @VM)
  CMD.NUM.OPTIONS = dcount(CMD.OPTION.S, @VM)
  CMD.NUM.FLAGS   = dcount(CMD.FLAG.S, @VM)
return

[[StartOptions]]:
  OPTIONS = 1
  P = P[2,9999]
  gosub [[AddOptions]]
return

[[AddOptions]]:
  if P = 'H' or P = '?' then
    CMD.HELP = 1
  end
  CMD.OPTION.S<1,-1> = P
return

[[StartNewFlag]]:
  OPTIONS = @FALSE  ;*  Switch modes
  FLAGS   = @TRUE
  if LONG.FLAGS then
    FLAG = P
    EXTRA = ''
  end else
    FLAG  = P[2,1]
    EXTRA = P[3,9999]
  end
  if FLAG[1,1] = 'H' or FLAG[1,1] = '?' then
    CMD.HELP = @TRUE
  end
  
  *  Now re-use the same position if the flag is the same:
  locate FLAG in CMD.FLAG.S<1> setting OLD.PTR then
    FLAG.PTR = OLD.PTR
  end else
    FLAG.PTR += 1
  end

  CMD.FLAG.S<1,FLAG.PTR> = FLAG
  if EXTRA # '' then
    FLAG.PARAM = EXTRA ; gosub [[AddFlagParameter]]
  end
  if NOPARAM then FLAGS = @FALSE
return

[[AddFlagParameter]]:
  CMD.PARAM.S<1,FLAG.PTR,-1> = FLAG.PARAM
  if SINGLEPARAM then  ;*  We've got one: so NO MORE for this flag:
    FLAGS = @FALSE
  end
return

*  Everything within Quotes (single or double ?) is a single value too:
*  old: VAL = field(PROCLINE,' ',I)

[[GetNextVal]]:
  P = ''  ;*  This gets returned
  loop 
    C = PROCLINE[POS, 1]
   while POS <= len(PROCLINE) and C # " " do
    if C = '"' or C = "'"  or C = "\" then
      gosub [[GetQuotedVal]]
    end else
      P := C
    end
    POS += 1
  repeat
  POS += 1  ;*  Past the space
  * Now get past MORE spaces for over-spaces lines (like a 'trim'):
  loop
  while PROCLINE[POS, 1] = ' ' do
    POS += 1
  repeat
return

[[GetQuotedVal]]:
  Q = C    ;*  Look for matching 'Q'uote character
  POS += 1 ;*  Past the quote
  loop
    CHAR = PROCLINE[POS, 1]
   while POS <= len(PROCLINE) and CHAR # Q do
    P := CHAR
    POS += 1 ;*  Past the quote
  repeat
return