home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / pascal / shdk_1.zip / SHCMDLIN.PAS < prev    next >
Pascal/Delphi Source File  |  1992-03-23  |  11KB  |  344 lines

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