home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 15 / CD_ASCQ_15_070894.iso / vrac / sk210f.zip / SHCMDLIN.PAS < prev    next >
Pascal/Delphi Source File  |  1994-05-15  |  12KB  |  358 lines

  1. {$I SHDEFINE.INC}
  2.  
  3. {$I SHUNITSW.INC}
  4.  
  5. {$D-,L-}
  6. {$A-}
  7. unit ShCmdLin;
  8. {
  9.                                 ShCmdLin
  10.  
  11.                       A Command Line Parsing Unit
  12.  
  13.                                    by
  14.  
  15.                               Bill Madison
  16.  
  17.                    W. G. Madison and Associates, Ltd.
  18.                           13819 Shavano Downs
  19.                             P.O. Box 780956
  20.                        San Antonio, TX 78278-0956
  21.                              (512)492-2777
  22.                              CIS 73240,342
  23.                 Internet bill.madison@lchance.sat.tx.us
  24.  
  25.                 Copyright 1990, '94 Madison & Associates
  26.                           All Rights Reserved
  27.  
  28.         This file may  be used and distributed  only in accord-
  29.         ance with the provisions described on the title page of
  30.                   the accompanying documentation file
  31.                               SKYHAWK.DOC
  32. }
  33.  
  34. Interface
  35. {------------}
  36.  
  37. Uses
  38.   ShList,
  39.   ShUtilPk,
  40.   Dos;
  41.  
  42. const
  43.   Copyr = 'Copyright 1990, 1994 by W.G. Madison';
  44.  
  45. type
  46. {$IFNDEF Gen87}
  47.   extended  = real;
  48.   Float           = real;
  49. {$ELSE}
  50.   Float           = extended;
  51. {$ENDIF}
  52.  
  53.   ValueType = (VtStr, VtReal, VtInt);
  54.   SwRec     = record
  55.                 Name  : string;
  56.                 case SwVal  : ValueType of
  57.                   VtStr : (StrVal : string);
  58.                   VtReal: (RealVal: Float);
  59.                   VtInt : (IntVal : integer);
  60.                 end; {SwRec}
  61.   CharSet = Set of Char;
  62.  
  63. procedure ClInit;
  64. {Initializes the command line switch list}
  65.  
  66. procedure ClClose;
  67. {Closes and frees the space associated with the command line switch list}
  68.  
  69. function GetSwitch(var Y : SwRec) : boolean;
  70. {Retrieves the next switch record. Returns FALSE if no more.}
  71.  
  72. function PopSwitch(var Y : SwRec) : boolean;
  73. {Retrieves the next switch record and frees its heap space.
  74.  Returns FALSE if no more.}
  75.  
  76. function ReadSwCh : char;
  77. {Reads and returns the current switch lead-in character}
  78.  
  79. procedure SetSwCh(C : char);
  80. {Sets the switch lead-in character to C}
  81.  
  82. Procedure ClParse(StrPtr : Pointer; StrOnly : Boolean;
  83.               LeadIn, ValDelim : CharSet; var Err : Integer);
  84. {USAGE: Parsing is accomplished by invoking the procedure ClParse with
  85.   five parameters:
  86.  
  87.   StrPtr of type Pointer is used to point to the string to be parsed. If
  88.   StrPtr is NIL, the command tail will be parsed.
  89.  
  90.   StrOnly of type Boolean is used to determine if switch values of type
  91.   String are to be forced, regardless of the form of the value. StrOnly
  92.   = True forces String values.
  93.  
  94.   LeadIn of type CharSet is used to identify the set of characters used
  95.   to mark the beginning of a switch. It is suggested that LeadIn be set
  96.   to [ ReadSwCh ]. The weakest condition used should be that the
  97.   expression ( ReadSwCh in LeadIn ) be TRUE.
  98.  
  99.   ValDelim of type CharSet is used to specify the set of characters
  100.   which may be used to separate the switch name from the switch value.
  101.  
  102.   X of type ClType (i.e., a doubly linked list as defined in unit
  103.   ShList) is used to return the names and values (if any) of any
  104.   switches included in the string being parsed. The ClType must be
  105.   initialized by a call to ClInit prior to the call to ClParse.
  106.  
  107.   Err of type Integer is used to return error conditions.
  108.  
  109.   The procedure returns a doubly linked list (as defined in unit ShList)
  110.   of records, each record containing the name and value of one command
  111.   line switch.
  112.  
  113.   All switches (with the optional exception of the first) are preceeded
  114.   with the normal DOS switch lead-in character with which your DOS is
  115.   configured (normally '/', but in pseudo-UNIX environments probably
  116.   '-').
  117.  
  118.   Switches may take values of type Real, LongInt, or String. In each
  119.   case, the switch value is separated from the switch name by one of the
  120.   characters specified in the parameter ValDelim. Switches which do not
  121.   take on any explicit value will be returned as type String, with a
  122.   value length of zero.
  123.  
  124.   Switches whose VALUE is intended to be of type String, but with a FORM
  125.   qualifying as a numeric must be enclosed in either single or double
  126.   quotation marks. Otherwise, it will be returned as a Real or LongInt,
  127.   as determined by its specific syntax (unless StrOnly = True in the
  128.   call).
  129.  
  130.   Additionally, any blanks included in String values will be packed out
  131.   unless the value is included in quotation marks. Further, if single
  132.   quote marks are to be included as part of a string value, then double
  133.   quotes must be used to define the value; and vice versa.
  134.  
  135. ERROR RETURNS:
  136.   The error parameter returns one of three values:
  137.             0 --> No error encountered.
  138.             1 --> Unbalanced single quotes encountered.
  139.             2 --> Unbalanced double quotes encountered.
  140.             3 --> Insufficient heap space to store the switch list.
  141. }
  142.  
  143.  
  144. Implementation
  145. {------------}
  146.  
  147. var
  148.   IsFirst : boolean;
  149.   X       : dlList;
  150.  
  151. procedure ClInit;
  152. {Initializes the command line switch list}
  153.   begin
  154.     dlListInit(X, SizeOf(SwRec));
  155.     IsFirst := true;
  156.     end; {ClInit}
  157.  
  158. procedure ClClose;
  159. {Closes and frees the space associated with the command line switch list}
  160.   begin
  161.     dlFree(X);
  162.     end; {ClClose}
  163.  
  164. function GetSwitch(var Y : SwRec) : boolean;
  165. {Retrieves the next switch record. Returns FALSE if no more.}
  166.   var
  167.     B1  : boolean;
  168.   begin
  169.     if IsFirst then begin
  170.       B1 := dlGetFirst(X, Y);
  171.       GetSwitch := B1;
  172.       IsFirst := false;
  173.       end
  174.     else begin
  175.       B1 := dlGetNext(X, Y);
  176.       GetSwitch := B1;
  177.       end;
  178.     end; {GetSwitch}
  179.  
  180. function PopSwitch(var Y : SwRec) : boolean;
  181. {Retrieves the next switch record and frees its heap space.
  182.  Returns FALSE if no more.}
  183.   var
  184.     B1  : boolean;
  185.   begin
  186.     B1 := dlPop(X, Y);
  187.     PopSwitch := B1;
  188.     end; {PopSwitch}
  189.  
  190. function ReadSwCh : char;
  191. {Reads the current switch lead-in character}
  192.   var
  193.     X     : Registers;
  194.   begin {Read the current character}
  195.     X.AH := $37;
  196.     X.AL := 0;
  197.     Intr($21, X);
  198.     ReadSwCh := char(X.DL);
  199.     end;
  200.  
  201. procedure SetSwCh(C : char);
  202. {Sets the switch lead-in character to C}
  203.   var
  204.     X     : Registers;
  205.   begin {Set the current character}
  206.     X.AH := $37;
  207.     X.AL := 1;
  208.     char(X.DL) := C;
  209.     Intr($21, X);
  210.     end;
  211.  
  212. Procedure ClParse(StrPtr : Pointer; StrOnly : Boolean;
  213.               LeadIn, ValDelim : CharSet; var Err : Integer);
  214.   const
  215.     MQT   = ^C;   {Master quote mark}
  216.     MVD   = ^M;   {Master value delimiter}
  217.     MLI   = ^[;   {Master lead-in mark}
  218.   var
  219.     CmdLine    : ^String;
  220.     CLine      : String;
  221.     QuoteState : (Qoff, Quote1, Quote2);
  222.     ValueState : (Voff, Von);
  223.     T1         : Integer;
  224.   Procedure PackCommandLine( var Err : Integer );
  225.   {Packs out all blanks not enclosed between balanced single or double
  226.    quotes, and replaces all such quote marks with Master Quotes. Replaces
  227.    all lead-in characters with Master Lead-In characters. Replaces all
  228.    value delimiters with Master Value Delimiters.}
  229.     const
  230.       PM       : CharSet = ['+','-'];
  231.     var
  232.       T1       : Integer;
  233.     begin
  234.       CLine := '';
  235.       QuoteState := Qoff;
  236.       ValueState := Voff;
  237.       For T1 := 1 to Length(CmdLine^) do
  238.         Case QuoteState of
  239.           Qoff   : Case CmdLine^[T1] of
  240.                      ' '  : ;
  241.                      '''' : begin
  242.                               QuoteState := Quote1;
  243.                               CLine := CLine + MQT;
  244.                               end;
  245.                      '"'  : begin
  246.                               QuoteState := Quote2;
  247.                               CLine := CLine + MQT;
  248.                               end;
  249.                      else begin
  250.                             if (T1 > 1) and
  251.                                (CLine[Length(CLine)] = MVD) and
  252.                                (CmdLine^[T1] in PM) then begin
  253.                               CLine := CLine + CmdLine^[T1];
  254.                               end
  255.                             else
  256.                               if (CmdLine^[T1] in LeadIn) then begin
  257.                                 CLine := CLine + MLI;
  258.                                 ValueState := Voff;
  259.                                 end
  260.                               else
  261.                                 if (CmdLine^[T1] in ValDelim) and
  262.                                    (ValueState = Voff) then begin
  263.                                   CLine := CLine + MVD;
  264.                                   ValueState := Von;
  265.                                   end
  266.                                 else begin
  267.                                   CLine := CLine + CmdLine^[T1];
  268.                                   end;
  269.                             end;
  270.                      end;
  271.           Quote1 : Case CmdLine^[T1] of
  272.                      '''' : begin
  273.                               QuoteState := Qoff;
  274.                               CLine := CLine + MQT;
  275.                               end;
  276.                      else   CLine := CLine + CmdLine^[T1];
  277.                      end;
  278.           Quote2 : Case CmdLine^[T1] of
  279.                      '"'  : begin
  280.                               QuoteState := Qoff;
  281.                               CLine := CLine + MQT;
  282.                               end;
  283.                      else   CLine := CLine + CmdLine^[T1];
  284.                      end;
  285.           end;
  286.       If (Length(CLine) > 0) and (CLine[1] <> MLI) then
  287.         CLine := MLI + CLine;
  288.       Err := ord(QuoteState);
  289.       end; {PackCommandLine}
  290.   function MakeSwitchRecord : boolean;
  291.     var
  292.       WorkSpace : String;
  293.       Err       : Integer;
  294.       T1        : Integer;
  295.       SwitchRec : SwRec;
  296.     begin
  297.       Delete(CLine, 1, 1); {Strip leading MLI}
  298.       WorkSpace := CLine;
  299.       If Pos(MLI, WorkSpace) <> 0 then begin
  300.         WorkSpace[0] := chr(Pos(MLI, WorkSpace) - 1);
  301.         Delete(CLine, 1, Pos(MLI, CLine)-1);
  302.         end
  303.       else
  304.         CLine := '';
  305.       With SwitchRec do begin
  306.         If Pos(MVD, WorkSpace) <> 0 then begin
  307.           Name := Copy(WorkSpace, 1, Pos(MVD, WorkSpace)-1);
  308.           Delete(WorkSpace, 1, Pos(MVD, WorkSpace));
  309.           end
  310.         else begin
  311.           Name := WorkSpace;
  312.           WorkSpace := '';
  313.           end;
  314.     {Name has been set. Now get type and value}
  315.         If not StrOnly then begin
  316.           If Length(WorkSpace) = 0 then begin
  317.             SwVal   := VtStr;
  318.             StrVal  := '';
  319.             MakeSwitchRecord := dlPut(X, SwitchRec);
  320.             exit
  321.             end;
  322.           Val(WorkSpace, IntVal, Err);
  323.           If Err = 0 then begin
  324.             SwVal := VtInt;
  325.             MakeSwitchRecord := dlPut(X, SwitchRec);
  326.             exit
  327.             end;
  328.           Val(WorkSpace, RealVal, Err);
  329.           If Err = 0 then begin
  330.             SwVal := VtReal;
  331.             MakeSwitchRecord := dlPut(X, SwitchRec);
  332.             exit
  333.             end;
  334.           end; {If not StrOnly}
  335.         SwVal   := VtStr;
  336.         StrVal  := WorkSpace;
  337.         DelAll(StrVal, MQT, StrVal);
  338.         MakeSwitchRecord := dlPut(X, SwitchRec);
  339.         end; {With SwitchRec}
  340.       end; {MakeSwitchRecord}
  341.   begin {ClParse}
  342.     If StrPtr = nil then
  343.       CmdLine := Ptr(PrefixSeg, $0080)
  344.     else
  345.       CmdLine := StrPtr;
  346.     PackCommandLine(Err);
  347.     If (Length(CLine) = 0) or (Err <> 0) then exit;
  348.     While Pos(MLI, CLine) <> 0 do begin
  349.       if MakeSwitchRecord then
  350.         Err := 0
  351.       else begin
  352.         Err := 3;
  353.         exit;
  354.         end;
  355.       end;
  356.     end; {ClParse}
  357.   end.
  358.