home *** CD-ROM | disk | FTP | other *** search
/ Stars of Shareware: Programmierung / SOURCE.mdf / programm / msdos / pascal / pxl214a / pxllist.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1993-02-14  |  52.5 KB  |  1,367 lines

  1. {$R-}    {Range checking off}                                         {.CP14}
  2. {$B-}    {Boolean complete evaluation on}
  3. {$S+}    {Stack checking on}
  4. {$I+}    {I/O checking on}
  5. {$N-}    {No numeric coprocessor}
  6.  
  7. Unit PXLLIST;
  8.  
  9. Interface
  10.  
  11. Uses
  12.   Crt,
  13.   Dos,
  14.   PXLINIT;
  15.  
  16. procedure ListIt;
  17.  
  18. {===========================================================================}
  19.  
  20. Implementation
  21.  
  22. procedure ListIt;                                                     {.CP15}
  23. const
  24.    TableSize               = 2521;
  25.    Digits                  = 5;
  26.    TabChr                  = #0;
  27.    DummyHdrSeg             = #255;
  28.    AtStart:    set of char = ['A'..'Z'];
  29.    MiddleSet:  set of char = ['A'..'Z','0'..'9','_'];
  30.    HexNumbers: set of char = ['A'..'F','0'..'9'];
  31.    NumZ9:      set of char = ['0'..'9'];
  32.    Num19:      set of char = ['1'..'9'];
  33.    PlusMinus:  set of char = ['+','-'];
  34.    TabSize: byte = 8;
  35.    MaxHeader = 5;
  36. type                                                                  {.CP20}
  37.    Ref          =   ^Item;
  38.    WPt          =   ^WordType;
  39.    TableNum     =   0..TableSize;
  40.    WordType     =   record
  41.                        Key:    Str20;
  42.                        Name:   Str20;
  43.                        First:  Ref;
  44.                     end;
  45.    Item         =   record
  46.                        LinNum: 0..MaxInt;
  47.                        Next:   Ref;
  48.                     end;
  49.    Incs        =    (CantFind,TooDeep,Started,Ended,OK);
  50.    HdSegType   =    (Left,Center,Right);
  51.    HdPgType    =    (First,Other);
  52.    HdLineType  =    array[Left..Right] of string;
  53.    HdType      =    array[1..MaxHeader] of HdLineType;
  54.    HeaderType  =    array[First..Other] of HdType;
  55.    BLine       =    string[1];
  56.    ProcPtr     =  ^ProcWord;
  57.    ProcWord    =  record
  58.                      Name:   Str20;
  59.                      Key:    Str20;
  60.                      LinNum: 0..MaxInt;
  61.                      Next:   ProcPtr;
  62.                   end;
  63. var                                                                   {.CP26}
  64.    FirstProc:     ProcPtr;
  65.    Header:         HeaderType;
  66.    NumOfWords:     TableNum;
  67.    T:              array[TableNum] of WPt;
  68.    Tp:             WPt;
  69.    InRec,
  70.    MaxLess,
  71.    Max,Longest,
  72.    ScanCount,K,
  73.    Occur, PCount,
  74.    Pager,Depth:    integer;
  75.    HeaderMark:     Str3;
  76.    Cut,Uncut:      Str2;
  77.    Cuts,Uncuts:    array[1..3] of Str2;
  78.    OpLen,ClLen,
  79.    B:              byte;
  80.    RecDepth,
  81.    CaseDepth:      array[1..20] of integer {byte} ;
  82.    IncLine,
  83.    UndLn,
  84.    LineEnd,UC:     string;
  85.    IncMark:        string[8];
  86.    CountingProc,
  87.    NextIsProc,
  88.    AltHeaders,
  89.    LongOne,NoLine: boolean;
  90.    IncState:       Incs;
  91.  
  92.    procedure BlankHeaderLines(Content: BLine);                        {.CP10}
  93.    var
  94.       LNo:    integer;
  95.       HS:     HdSegType;
  96.    begin
  97.       for LNo := 1 to MaxHeader do
  98.          for HS := Left to Right do
  99.             Header[First][LNo,HS] := Content;
  100.       Header[Other] := Header[First]
  101.    end; {BlankHeaderLines}
  102.  
  103.    function IsBlank(HL: HdLineType): boolean;                          {.CP8}
  104.    var
  105.       Sg: HdSegType;
  106.    begin
  107.       IsBlank := True;
  108.       for Sg := Left to Right do
  109.          if (HL[Sg]<>'') and (HL[Sg]<>DummyHdrSeg) then IsBlank := False;
  110.    end; {IsBlank}
  111.  
  112.    function HeaderLineNo(var H: HdType):integer;                       {.CP8}
  113.    var
  114.       Nr: integer;
  115.    begin
  116.       Nr := MaxHeader;
  117.       while (Nr>0) and IsBlank(H[Nr]) do dec(Nr);
  118.       HeaderLineNo := Nr
  119.    end; {HeaderLineNo}
  120.  
  121.    procedure GetHeaderInstruction(Line: string);                      {.CP25}
  122.    var
  123.       IStrg: string;
  124.       Cue:   Str3;
  125.       Col:   integer;
  126.  
  127.    (* What this is supposed to do:
  128.          "{" + ".H" triggers header function.  Possibilities are
  129.          .HN  = no header at all
  130.          .HnL = Left side of Header line #n
  131.          .HnC = Center of Header line #n
  132.          .HnR = Right side of Header line #n
  133.          .HnN = No Header line #n
  134.          .HA  = reverse Alternate page headers (for b-to-b printing)
  135.          .HTn = Tab size (default is 8)
  136.          .HPLnn = nn lines per page incl header (default: 66 - BottomMargin)
  137.       Text for header line segment begins 1 col AFTER end of symbol
  138.       Within header line text:
  139.          .Fn = file name
  140.          .Fd = file date (style: July 4,1776)
  141.          .Ft = file time (style: 2:00 pm)
  142.          .Pd = present (or printout) date (style: 7/4/76)
  143.          .Pd = present (or printout) time (style: 14:00 )
  144.          .Id = ID (from PXL.ID)
  145.           #  = page number   *)
  146.  
  147.       procedure ResetMaxLin(S: string);                             {.CP24}
  148.       {This is activated by an .HPLnn command in the text or in PXL.HDR.}
  149.       {Be careful.  It sets the number of lines printed, not the length }
  150.       {of the paper.  It will override the BottomMargin set in PXL.PAS. }
  151.       {If your printer is set up to put fewer than the number set here, }
  152.       {you get a mess.  Ordinarily, strange paper sizes can be set with }
  153.       {PXLINST.  Of course, you have to give up FF's do to that.        }
  154.       var
  155.          NumStr: Str20;
  156.          K,E:    integer;
  157.       begin
  158.          if S[1]='L' then begin
  159.             K := 2;
  160.             NumStr := '';
  161.             while (S[K] in NumZ9) and (K<=length(S)) do begin
  162.                NumStr := NumStr + S[K];
  163.                inc(K)
  164.             end; {while 0..9}
  165.             if length(NumStr)>0 then val(NumStr,K,E);
  166.             if (K>0) and (E=0) then MaxLin := K        {if error, do nothing}
  167.          end {if L}
  168.       end; {ResetMaxLin}
  169.  
  170.       procedure SetTabSize(S: string);                              {.CP14}
  171.       var
  172.          K,C: integer;
  173.          NumeralStr: string;
  174.       begin
  175.          NumeralStr := '';
  176.          B := 1;
  177.          while (S[B] in NumZ9) and (B<=length(S)) do begin
  178.             NumeralStr := NumeralStr + S[B];
  179.             inc(B);
  180.          end; {while NumZ9}
  181.          val(NumeralStr,K,C);
  182.          if C=0 then TabSize := K     {Leave at default unless ABSOLUTELY Ok}
  183.       end; {SetTabSize}
  184.  
  185.       function FixedUpHeaderLine(L: string): string;                {.CP10}
  186.       begin
  187.          while pos('.Fn',L)>0 do Replace('.Fn',FileName,L);
  188.          while pos('.Fd',L)>0 do Replace('.Fd',FileDate,L);
  189.          while pos('.Pd',L)>0 do Replace('.Pd',PrintDate,L);
  190.          while pos('.Ft',L)>0 do Replace('.Ft',FileTime,L);
  191.          while pos('.Pt',L)>0 do Replace('.Pt',PrintTime,L);
  192.          while pos('.Id',L)>0 do Replace('.Id',UserID,L);
  193.          If L='' then L := DummyHdrSeg;
  194.          FixedUpHeaderLine := L
  195.       end; {FixedUpHeaderLine}
  196.  
  197.       procedure InterpretInstruction(Strg: string);                 {.CP17}
  198.       const
  199.          Symbols:  set of char = ['C','L','N','R'];
  200.       var
  201.          HNo:  byte;
  202.          HSg:  HdSegType;
  203.          C:    char;
  204.          Pg:   HdPgType;
  205.  
  206.       begin {InterpretInstruction}
  207.         C := Strg[1];
  208.         delete(Strg,1,1);
  209.         if C='N' then
  210.            BlankHeaderLines(DummyHdrSeg)
  211.         else if C='P' then
  212.            ResetMaxLin(Strg)
  213.         else if C = 'T' then
  214.            SetTabSize(Strg)
  215.         else if C = 'A' then
  216.            AltHeaders := True {Can be turned on but not off}
  217.         else if C in Num19 then begin                            {.CP28}
  218.            HNo := ord(C) - $30;
  219.            if HNo<1 then HNo := 1;
  220.            if HNo>MaxHeader then HNo := MaxHeader;
  221.            C := Strg[1];
  222.            delete(Strg,1,2);        {eat both this char and delimiting space}
  223.            if C in Symbols then begin
  224.               if C='N' then begin
  225.                  if (Page<2) and IsBlank(Header[Other][HNo])
  226.                     then Pg := First
  227.                     else Pg := Other;
  228.                  for HSg := Left to Right do
  229.                     Header[Pg][HNo,HSg] := DummyHdrSeg
  230.               end {if N}
  231.               else begin
  232.                  case C of
  233.                     'L':  HSg := Left;
  234.                     'C':  HSg := Center;
  235.                     'R':  HSg := Right;
  236.                  end; {case}
  237.                  Strg := FixedUpHeaderLine(Strg);
  238.                  if (Page>1) or (Header[First][HNo,HSg]<>'')
  239.                       then Pg := Other
  240.                     else Pg := First;
  241.                  for Pg := Pg to Other do Header[Pg][HNo,HSg] := Strg;
  242.               end {else not N}
  243.            end {if Symbol}
  244.         end {else if 1..9}
  245.       end; {InterpretInstruction}
  246.  
  247.    begin {GetHeaderInstruction}                                     {.CP13}
  248.       Cue := HeaderMark;
  249.       while pos(Cue,Line)>0 do begin
  250.          Col := pos(Cue,Line) + 3;
  251.          IStrg := '';
  252.          while (Line[Col]<>'}') and (Col<=length(Line)) do begin
  253.             IStrg := IStrg + Line[Col];
  254.             inc(Col)
  255.          end; {while}
  256.          Line := Copy(Line,succ(Col),255);
  257.          InterpretInstruction(IStrg)
  258.       end {while}
  259.    end; {GetHeaderInstruction}
  260.  
  261.    function HeaderLine(H: HdLineType): string;                        {.CP21}
  262.    var
  263.       Spaces,
  264.       Wid,K:    integer;
  265.       Temp:     string;
  266.       Pg:       HdPgType;
  267.       Sg:       HdSegType;   C: char;
  268.    begin
  269.       Temp := '';
  270.       Wid := pred(Inst.Bt[LW]);
  271.       if Page<2
  272.          then Pg := First
  273.          else Pg := Other;
  274.       if AltHeaders and not Odd(Page) then begin
  275.          Temp := H[Left];
  276.          H[Left] := H[Right];
  277.          H[Right] := Temp;
  278.       end; {if Alternate}
  279.       for Sg := Left to Right do begin
  280.          while pos('#',H[Sg])>0  do      {Must update page number every page}
  281.             Replace('#',StrgI(Page,1),H[Sg]);
  282.          if H[Sg]=DummyHdrSeg then H[Sg] := '';
  283.       end; {for Sg}
  284.       repeat               {Splice left & right segs --chopping if necessary}
  285.          Spaces := length(H[Left]) + length(H[Right]);
  286.          if Spaces>Wid then begin
  287.              if length(H[Right])>0 then delete(H[Right],1,1)
  288.              else if length(H[Left])>0 then dec(H[Left,0])
  289.          end {if Spaces}
  290.       until Spaces<=Wid;
  291.       Temp := H[Left];           {Overprint line with Center segment} {.CP10}
  292.       for K := 1 to (Wid - Spaces) do Temp := Temp + #32;
  293.       Temp := Temp + H[Right];
  294.       if H[Center]<>'' then begin
  295.          Spaces :=  (Wid - length(H[Center])) div 2;
  296.          for K := 1 to length(H[Center]) do
  297.             Temp[K+Spaces] := H[Center,K]
  298.       end; {if Center}
  299.       HeaderLine := Temp;
  300.    end; {HeaderLine}
  301.  
  302.    procedure MakeFirstHeader(var Fil: text);                          {.CP25}
  303.    var
  304.       Lin:    string;
  305.  
  306.       function GotDefaultHeaderFromFile: boolean;
  307.       const
  308.          FName: string = 'PXL.HDR';
  309.       var
  310.          F:      text;
  311.  
  312.          function ZeroSize: boolean;
  313.          var
  314.             Fb:     file of byte;
  315.          begin
  316.             assign(Fb,FName);
  317.             reset(Fb);
  318.             ZeroSize := FileSize(Fb)=0;
  319.             close(Fb)
  320.          end; {ZeroSize}
  321.  
  322.       begin {GotDefaultHeaderFromFile}
  323.          if FindFile(FName) then begin
  324.             GotDefaultHeaderFromFile := True;    {Even if nothing in PXL.PRN}
  325.             if not ZeroSize then begin     {Can't use FileSize on text files}
  326.                assign(F,FName);
  327.                reset(F);
  328.                while not Eof(F) do begin
  329.                   readln(F,Lin);
  330.                   if pos(HeaderMark,Lin)<>0 then begin
  331.                      GetHeaderInstruction(Lin)
  332.                   end {if Cue}
  333.                end; {while not Eof}
  334.                close(F)
  335.             end {if >0}
  336.          end {if FindFile}
  337.          else
  338.             GotDefaultHeaderFromFile := False
  339.       end; {GotDefaultHeaderFromFile}
  340.  
  341.       procedure MakeStandardDefaultHeader;                            {.CP14}
  342.       begin
  343.          Header[First][1,Right] := FileTime + ', ' + FileDate;
  344.          if XRefOnly
  345.             then Header[First][1,Left] :='Cross-Reference of: '
  346.             else Header[First][1,Left] := 'File: ';
  347.          Header[First][1,Left] := Header[First][1,Left] + FileName;
  348.          if length(UserID)>0 then
  349.             Header[First][1,Left] := Header[First][1,Left]
  350.                                      + '  [' + UserID + ']';
  351.          Header[Other][1] := Header[First][1];
  352.          Header[Other][1,Right] := 'Page #'
  353.       end; {MakeStandardDefaultHeader}
  354.  
  355.       procedure LoadFirstHeader(var F: text);                         {.CP16}
  356.       var
  357.          L:      string;
  358.          B,Col:  byte;
  359.       begin
  360.          reset(Fil);
  361.          repeat
  362.             readln(Fil,L);
  363.             B := pos(HeaderMark,L);
  364.             if B>0 then begin
  365.                GetHeaderInstruction(L);
  366.                delete(L,1,B);
  367.                while (L[1]<>'}') and (L<>'')  do delete(L,1,1)
  368.             end {if >0}
  369.          until B=0;
  370.       end; {LoadFirstHeader}
  371.  
  372.    begin {MakeFirstHeader}                                             {.CP9}
  373.       BlankHeaderLines('');
  374.       if not GotDefaultHeaderFromFile then MakeStandardDefaultHeader;
  375.       reset(Fil);                                        {Fille arrives open}
  376.       readln(Fil,Lin);
  377.       if pos(HeaderMark,Lin)<>0 then LoadFirstHeader(F);  {Check top of file}
  378.       reset(Fil);                                {Return file open but reset}
  379.       PageLineNumber := HeaderLineNo(Header[First]) + 2;
  380.    end; {MakeFirstHeader}
  381.  
  382.    procedure PrintHeader(var PLine: integer); {Print header line(s)}  {.CP20}
  383.    var
  384.       K,Nr:   integer;
  385.       Pg:     HdPgType;
  386.    begin
  387.       {$I-}
  388.       writeln(Lst,'');
  389.       {$I+}
  390.       if not (IOresult=0) then
  391.          CantCont('','Printer''s out');
  392.       if GotPrnData then write(Lst,Istring[SetLg]);         {Set normal pica}
  393.       if Page<2
  394.          then Pg := First
  395.          else Pg := Other;
  396.       Nr := HeaderLineNo(Header[Pg]);
  397.       for K := 1 to Nr do                                             {.CP13}
  398.          writeln(Lst,HeaderLine(Header[Pg][K]));
  399.       if GotPrnData then
  400.          if NumberLines then write(Lst,Istring[SetSm]);             {or Elite}
  401.       writeln(Lst);
  402.       inc(Page);
  403.       PLine := 2 + Nr;
  404.    end; {PrintHeader}
  405.  
  406.    procedure PrintControl(var PageLineNumber: integer);               {.CP21}
  407.    var
  408.       Sym: string[8];
  409.       I, J, Err: integer;
  410.    begin
  411.       if pos(concat('{.','PA}'),Line)<>0 then
  412.          PageLineNumber := succ(MaxLin)
  413.       else if pos(concat('{.','CP'),Line) <>0 then begin
  414.          I := pos(concat('{.','CP'),Line) + 4;
  415.          Sym := '';
  416.          while Line[I] in NumZ9 do begin
  417.             Sym := concat(Sym,Line[I]);
  418.             I := succ(I);
  419.          end {while};
  420.          val(Sym,I,Err);
  421.          if Err<>0 then I := 0;  {in case print control symbol is bungled}
  422.          if PageLineNumber > (MaxLin-I) then PageLineNumber := succ(MaxLin);
  423.       end {if}
  424.    end; {PrintControl}
  425.  
  426.    procedure ReadingMatterI;                                          {.CP16}
  427.    begin
  428.       if XRefOnly then begin
  429.          Blank(9,12);
  430.          CenterCRT('Scanning ' +  FileName,10,Bright,0)
  431.       end {if XrefOnly}
  432.       else begin
  433.          Blank(8,12);
  434.          if not Xref then
  435.             CenterCRT('Sending ' + FileName + ' to ' + OutputDevice,
  436.                       10,Bright,0)
  437.          else
  438.             CenterCRT('Scanning ' + FileName + ' and sending to '
  439.                       + OutputDevice + '.', 10,Bright,0)
  440.       end {else not XRO}
  441.    end; {ReadingMatterI}
  442.  
  443.    procedure ReadingMatterII;                                          {.CP5}
  444.    begin
  445.       Blank(8,8);
  446.       CenterCRT('Sending cross-reference to ' + OutputDevice,
  447.                 10,Bright,Inside)
  448.    end; {ReadingMatterII}
  449.  
  450.    procedure NewPage(Pager: integer);                                 {.CP18}
  451.    var
  452.       I:           integer;
  453.    begin
  454.       if Inst.Bt[FF]=12 then begin
  455.          {$I-}
  456.          write(Lst,#12);
  457.          {$I-}
  458.          if IOresult<>0 then CantCont('','Printer''s out.');
  459.       end {if FF}
  460.       else begin
  461.          {$I-}
  462.          writeln(Lst);
  463.          {$I-}
  464.          if IOresult<>0 then CantCont('','Printer''s out.');
  465.          for I := succ(Pager) to Inst.Bt[FF] do writeln(Lst);
  466.       end {no FF}
  467.    end; {NewPage}
  468.  
  469.    procedure PrintTable;                                              {.CP17}
  470.    var
  471.       I:           TableNum;
  472.       Lin:         integer;
  473.       NumPerLine:  byte;
  474.  
  475.       procedure Compress(var N: TableNum);                            {.CP11}
  476.       var
  477.          I: TableNum;
  478.       begin
  479.          N := 0;
  480.          for I := 0 to TableSize do
  481.             if T[I] <> Nil then begin
  482.                T[N] := T[I];
  483.                inc(N)
  484.             end; {if T[I]}
  485.       end; {Compress}
  486.  
  487.       procedure Sort(Lo, Hi: integer); {Quicksort}                    {.CP31}
  488.       var
  489.          Low,High: TableNum;
  490.          Mid,Temp: WPt;
  491.       begin
  492.          repeat                                 {Pick split points}
  493.             Mid := T[(Lo+Hi) div 2];
  494.             Low := Lo;
  495.             High := Hi;
  496.             repeat                                 {partitions}
  497.                while T[Low]^.Key<Mid^.Key do Inc(Low);
  498.                while T[High]^.Key>Mid^.Key do dec(High);
  499.                if Low<=High then begin
  500.                   Temp := T[Low];
  501.                   T[Low] := T[High];
  502.                   T[High] := Temp;
  503.                   if Low<TableSize then inc(Low);
  504.                   if High>0 then dec(High)
  505.                end {if Low<=}
  506.             until Low > High;
  507.             {recursively sort shorter sub-segment}
  508.             if (High-lo) < (Hi-Low) then begin
  509.                if Lo < High then Sort(Lo,High);
  510.                Lo := Low
  511.             end {if (High}
  512.             else begin
  513.                if Low < Hi then Sort(Low,Hi);
  514.                Hi := High;
  515.             end {else}
  516.          until Hi <= Lo
  517.       end; {Sort}
  518.  
  519.       procedure PageOut;                                               {.CP7}
  520.       begin
  521.          NewPage(Lin);
  522.          PrintHeader(Lin);
  523.          writeln(Lst);
  524.          inc(Lin)
  525.       end; {PageOut}
  526.  
  527.       procedure PrintWord(W: WordType);                               {.CP20}
  528.       var
  529.          X,Y,Z:      Ref;
  530.          Num:        integer;
  531.          B:          byte;
  532.  
  533.       begin {PrintWord}                                               {.CP10}
  534.          if Lin>MaxLin then PageOut;
  535.          X := W.First; Y := X^.Next; X^.Next := Nil;
  536.          while Y<>Nil do begin         {inky pinky pider, reversing pointers}
  537.             Z := Y^.Next; Y^.Next := X; X := Y; Y := Z;
  538.          end; {while Y<>Nil}
  539.          Num := 0;
  540.          Write(Lst,#32,W.Name);
  541.          for B := 1 to Longest-length(W.Name) do write(Lst,#32);
  542.          repeat                                  {write line numbers} {.CP21}
  543.             if Num=NumPerLine then begin              {new line if necessary}
  544.                Num := 0;
  545.                writeln(Lst);
  546.                inc(Lin);
  547.                if Lin>MaxLin then begin
  548.                   PageOut;
  549.                   Write(Lst,#32,W.Name);
  550.                   for B := 1 to Longest-length(W.Name) do
  551.                      write(Lst,#32)
  552.                end {if Lin}
  553.                else
  554.                   Write(Lst,#32:(succ(Longest)))
  555.             end; {if Num}
  556.             inc(Num);
  557.             write(Lst,X^.LinNum:Digits);
  558.             X := X^.Next
  559.          until X=Nil;
  560.          writeln(Lst);
  561.          inc(Lin)
  562.       end; {PrintWord}
  563.  
  564.       procedure PrintPL;  {Print list of procedures & functions}      {.CP15}
  565.       var
  566.          B:      byte;
  567.  
  568.          procedure PrintAProc;             {print one line in proc/func list}
  569.          var
  570.             B: byte;
  571.          begin
  572.             write(Lst,#32,FirstProc^.Name);
  573.             for B := 1 to Longest-length(FirstProc^.Name) do write(Lst,#32);
  574.             writeln(Lst,FirstProc^.LinNum:Digits);
  575.             inc(I);
  576.             GotoXY(30,16);
  577.             Write(I:5);
  578.             FirstProc := FirstProc^.Next;
  579.          end; {PrintAProc}
  580.  
  581.       begin {PrintPL}                                                 {.CP19}
  582.          if (Lin+PCount+5) > MaxLin then
  583.             PageOut
  584.          else begin
  585.             writeln(Lst);
  586.             inc(Lin)
  587.          end; {else}
  588.          writeln(Lst,'Procedures and Functions:');
  589.          writeln(Lst);
  590.          if FirstProc^.Next=nil then               {Just one proc/func in list}
  591.             PrintAProc
  592.          else
  593.             while (FirstProc<>Nil) and not enough do begin
  594.                inc(Lin);
  595.                if Lin > MaxLin then PageOut;
  596.                PrintAProc;
  597.                Enough := Escape
  598.             end {while}
  599.       end; {PrintPL}
  600.  
  601.    begin {PrintTable}                                                 {.CP15}
  602.       if NumberLines then
  603.          if Mrk then Max := Max+10 {take account of space for beg/end count}
  604.          else Max := Max + 6;
  605.       NumPerLine := (Max-Longest) div Digits;
  606.       Compress(NumOfWords);
  607.       Sort(0,pred(NumOfWords));
  608.       PrintHeader(Lin);
  609.       writeln(Lst);
  610.       writeln(Lst,'Crosslisting of Identifiers:');
  611.       writeln(Lst);
  612.       WriteCRT('X-Ref Lines:   ',16,15,Bright);
  613.       Lin := Lin + 3;
  614.       I := 0;
  615.       while (I<NumOfWords) and not Enough do begin {print XRef lines} {.CP15}
  616.          PrintWord(T[I]^);
  617.          inc(I);
  618.          GotoXY(30,16); write(I:5);                   {keep user entertained}
  619.          Enough := Escape
  620.       end; {while}
  621.       if (FirstProc<>Nil) and not Enough then PrintPL;
  622.       writeln(Lst);
  623.       write(Lst,'Lines: ',LineNumber,'    Identifiers: ',ScanCount,
  624.          '    Occurrences: ',Occur);
  625.       if PCount>0 then
  626.          writeln(Lst,'    Procedures: ',PCount)
  627.       else
  628.          writeln(Lst)
  629.    end; {PrintTable}
  630.  
  631.    procedure ScanAndHash(var UC,Line: string; LinNo: integer);        {.CP18}
  632.    var
  633.       Ident:      WordType;
  634.       Len,I:      byte;
  635.       Col:        integer;
  636.  
  637.       procedure Calamity;
  638.       begin
  639.          ClrScr;
  640.          PXLRectangle;
  641.          CenterCRT('CALAMITY',11,Bright,0);
  642.          WriteCRT('Too many @$#%'+#237+'@! identifiers',13,25,Bright);
  643.          WriteCRT('    I can''t handle that.',14,25,Bright);
  644.          CloseCarefully(F);
  645.          RestoreScreen;
  646.          Halt
  647.       end; {Calamity}
  648.  
  649.       procedure Hash(Ident: WordType);                                 {.CP17}
  650.       var
  651.          Found:     boolean;
  652.          ID:        record
  653.                        case byte of
  654.                           1: (Key: str20);
  655.                           2: (O:   word);                   {integer);}
  656.                           3: (Arr: array[0..20] of byte);
  657.                     end;
  658.          X:         Ref;
  659.          H:         longint; {avoid trouble during re-hash}
  660.          D,Start:   TableNum;
  661.       begin
  662.          ID.Key := Ident.Key;
  663.          inc(Occur);
  664.          H := ID.O mod TableSize;             {hash using 1st 2 bytes of key}
  665.          Start := H;
  666.          new(X);X^.LinNum := LinNo; Start := H; D := 1;
  667.          repeat                                                       {.CP26}
  668.             if T[H]^.Key = ID.Key then begin          {found the Key        }
  669.                Found := True;
  670.                X^.Next := T[H]^.First;                   {add line # to list}
  671.                T[H]^.First := X;
  672.             end {if found key}
  673.             else if T[H] = Nil then begin             {empty place --new key}
  674.                Found := True;
  675.                inc(ScanCount);                            {count it         }
  676.                if length(ID.Key)>Longest then             {update Longest   }
  677.                    Longest := length(ID.Key);
  678.                New(Tp);
  679.                Tp^.Key := ID.Key;                         {set up new key   }
  680.                Tp^.Name := Ident.Name;                    {and name         }
  681.                Tp^.First := X;                            {and first line # }
  682.                T[H] := Tp;                                {& put in hash tbl}
  683.                X^.Next := Nil
  684.             end {else if new}
  685.             else begin                                {place occupied       }
  686.                Found := False;
  687.                H := H + ID.Arr[ID.Arr[0]];    {re-hash using last byte of key}
  688.                if H>=TableSize then H := H - TableSize;
  689.                if H=Start then Calamity
  690.             end {else --place otherwise occupied}
  691.          until Found
  692.       end; {Hash}
  693.  
  694.    begin  {ScanAndHash}                                               {.CP16}
  695.       GotoXY(30,14); write(LinNo:5);                  {keep user entertained}
  696.       Col := 1;
  697.       Len := length(UC);
  698.       while Col<=Len do begin                                {creep along UC}
  699.          if UC[Col]<>#32 then begin                  {looking for non-blanks}
  700.             Ident.Key := ''; Ident.Name := '';
  701.             I := Col + 20;                    {20 chars is max key length}
  702.             while (UC[Col]<>#32) and (Col<=Len) do begin {read non-blanks}
  703.                if Col<I then begin
  704.                   Ident.Key := Ident.Key + UC[Col];
  705.                   Ident.Name := Ident.Name + Line[Col]
  706.                end; {if Col}
  707.                inc(Col);
  708.             end; {while}
  709.             Hash(Ident)                          {put into the hash table}
  710.          end {if not blank}
  711.          else
  712.             inc(Col);
  713.       end {while}
  714.    end; {ScanAndHash}
  715.  
  716.    procedure Underline (var Line: string);                             {.CP6}
  717.    var
  718.       K,J:         integer;
  719.       B:           byte;
  720.       InMiddle,
  721.       InHex:    Boolean;
  722.  
  723.       procedure ProcProc(Name: string); {PROCess PROCedure}         {.CP15}
  724.       var                                 {ie, add new proc name to list}
  725.          Temp,PLptr:  ProcPtr;
  726.          B:           byte;
  727.       begin
  728.          New(PLptr);
  729.          PLptr^.Name := '';
  730.          PLptr^.Key := '';
  731.          if length(Name)>20 then Name[0] := #20;
  732.          for B := 1 to length(Name) do begin
  733.             PLptr^.Name := PLptr^.Name + Name[B];
  734.             PLptr^.Key := PLptr^.Key + UpCase(Name[B]);
  735.          end; {for B}
  736.          PLptr^.LinNum := LineNumber;
  737.          PLptr^.Next := Nil;
  738.          if FirstProc = Nil then begin   {.CP19}           {if list is empty}
  739.             FirstProc := PLptr;
  740.          end {if first procedure}
  741.          else if FirstProc^.Key <= PLptr^.Key then begin  {if >= 1st in list}
  742.             Temp := FirstProc;
  743.             while (Temp^.Next<>Nil) and (Temp^.Next^.Key<PLptr^.Key) do
  744.                Temp := Temp^.Next;
  745.             if Temp^.Next=Nil then          {if > end of list, append}
  746.                Temp^.Next := PLptr
  747.             else if (Temp^.Next^.Key<>PLptr^.Key) then begin
  748.                PLptr^.Next := Temp^.Next;  {if between, insert}
  749.                Temp^.Next := PLptr;
  750.             end; {if not duplicate}        {Note: if =, do nothing}
  751.          end {else if after first}
  752.          else begin                                        {if < 1st in list}
  753.             PLptr^.Next := FirstProc;
  754.             FirstProc := PLptr;
  755.          end {else put before the first}
  756.       end; {ProcProc}
  757.  
  758.       procedure Ins (var Line,UC :string; Op,Cl:InsType);              {.CP5}
  759.       var
  760.          Z,Len,B:     byte;
  761.          K,Col:       integer;
  762.          Obj:         ResWType;
  763.  
  764.          function NextResWd: ResWType;                                {.CP17}
  765.          {Returns next res word wd from line or '' if EOL found first.      }
  766.          {                                                                  }
  767.          {Archaeological note: This function belongs to the 1989 stratum.   }
  768.          {It replaced a clumsy one dating from the earliest, 1984 ELIST era.}
  769.          {ELIST kept the reserved words in a simple array, and went through }
  770.          {it once per line, using TP's pos() function to search for all oc- }
  771.          {currences of each reserved word.  In April, 1989, W. L. Peavy sent}
  772.          {me a lovely bug about record-end troubles.  Fixing it required the}
  773.          {identifiers to be peeled out & examined in order.  The slowness of}
  774.          {that process forced me to rethink the search pattern.  The upshot }
  775.          {is a new method (here and in PXLMENU, LoadReserv) which makes the }
  776.          {overall process about 40% faster.                                 }
  777.          var
  778.             GotOne:     boolean;
  779.             PossObj:    string;
  780.             P:          ResWPtrType;
  781.          begin                                                        {.CP18}
  782.             GotOne := False;
  783.             repeat
  784.                repeat
  785.                   inc(K)
  786.                until (UC[K]<>#32) or (K>length(UC));
  787.                if K<=length(UC) then begin
  788.                   if NextIsProc then begin
  789.                      NextIsProc := False;
  790.                      PossObj := '';
  791.                      while UC[K]<>#32 do begin    {get it;   Note; last char}
  792.                         PossObj := PossObj + Line[K];      {on line is blank}
  793.                         inc(K)
  794.                      end; {while not blank}
  795.                      ProcProc(PossObj);            {Put it in Proc/func list}
  796.                   end {if NextIsProc}                      {in UC is a blank}
  797.                   else if Rsv[UC[K]]=nil then {if no res wd has this initial}
  798.                      while UC[K]<>#32 do inc(K)                     {pass it}
  799.                   else if K<length(UC) then begin {if poss initial}   {.CP20}
  800.                      Col := K;                 {mark beginning of identifier}
  801.                      PossObj := '';
  802.                      while UC[K]<>#32 do begin
  803.                         PossObj := PossObj + UC[K];
  804.                         inc(K);
  805.                      end; {while not blank}         {if PossObj not too long}
  806.                      if length(PossObj)<=MaxResLen then begin
  807.                         P := Rsv[PossObj[1]];    {cmp res wds w this initial}
  808.                         while (P^.R<>PossObj) and (P^.Next<>nil) do
  809.                            P := P^.Next;
  810.                         if P<>nil then GotOne := PossObj=P^.R;
  811.                      end {if not too long for a res wd}
  812.                   end {else starts with possible char}
  813.                end {if not EoL}
  814.             until GotOne or (K>=length(UC));
  815.             if GotOne
  816.                then NextResWd := PossObj
  817.                else NextResWd := '';
  818.          end; {NextResWd}
  819.  
  820.       begin {Ins}                                                     {.CP16}
  821.          Col := 1;
  822.          repeat
  823.             K := Col;
  824.             Obj := NextResWd;
  825.             if Obj<>'' then begin                     {We have a Res Wd}
  826.                Len := length(Obj);
  827.                if MarkWCaps then
  828.                   for B := Col to Col+pred(Len) do            {Capitalize It}
  829.                      Line[B] := upcase(Line[B])
  830.                else if not MarkWCR then begin
  831.                   insert(Cl,Line,Col+Len);                 {Insert Closing  }
  832.                   insert(Op,Line,Col);                     {Insert Opening  }
  833.                end {if not CR}
  834.                else begin                               {Make overprint line}
  835.                   while length(UndLn)<pred(Col) do              {with blanks}
  836.                      UndLn := UndLn + #32;
  837.                   for B := Col to Col+pred(Len) do          {and underscores}
  838.                      UndLn := UndLn + '_';
  839.                end; {else MarkWCR}
  840.                for B := Col to Col+pred(Len) do            {blank Obj in UC }
  841.                   UC[B] := #32;
  842.                if Xref then begin                                     {.CP22}
  843.  
  844.     {The procedure list will show the first occurance of the procedure and }
  845.     {function names (presumaby their declarations) in the IMPLEMENTATION   }
  846.     {section, not in the interface.  (ALL occurrances are shown in the reg-}
  847.     {ular identifier list, of course.)  If you want it to show the inter-  }
  848.     {face declarations instead, you can brace out the 5 lines marked below.}
  849.  
  850.               if      { <----- Leave the "if" }
  851.     {====  beginning of brace-out section for interface declarations  ====}
  852.                      Obj='UNIT' then
  853.                      CountingProc := False
  854.                   else if Obj='IMPLEMENTATION' then
  855.                      CountingProc := True
  856.                   else if CountingProc and                 {Mark Proc & Func}
  857.    {=======  end of brace-out section for interface declarations   =========}
  858.  
  859.                   ((Obj='PROCEDURE') or (Obj='FUNCTION')) then begin
  860.                      inc(PCount);
  861.                      NextIsProc := True
  862.                   end; {if Counting}
  863.                end; {if XRef}
  864.  
  865.                for B := 1 to OpLen+ClLen do                            {.CP3}
  866.                   insert(#32,UC,Col);                 {Blanks to match up UC}
  867.                Col := Col + Len + OpLen + ClLen;         {move to end of Obj}
  868.  
  869.                if NumberLines then begin                              {.CP24}
  870.                   if (Obj='BEGIN') or
  871.                   (Obj='REPEAT') or (Obj='CASE') then {count begin/end}
  872.                      inc(Depth)
  873.                   else if (Obj='END')  then begin
  874.                      if InRec=0 then begin
  875.                         if Line[Col]<>'.' then dec(Depth)
  876.                      end {if not InRec}
  877.                      else begin
  878.                         Depth := RecDepth[InRec];
  879.                         dec(InRec)
  880.                      end {else if InRec}
  881.                   end {else if END}
  882.                   else if (Obj='UNTIL') then
  883.                      dec(Depth)
  884.                   else if Obj='RECORD' then begin
  885.                      inc(InRec);
  886.                      RecDepth[InRec] := Depth;
  887.                      inc(Depth)
  888.                   end {else if RECORD}
  889.                end; {if NumberLines}
  890.             end {if Obj<>''}
  891.          until Obj = '';
  892.          if MarkWCR and (UndLn<>'') then
  893.             while length(UndLn)<pred(length(Line)) do
  894.               UndLn := UndLn + #32;
  895.       end; {procedure Ins}
  896.  
  897.       procedure BlankBrackets(var UC: string);                        {.CP18}
  898.       var
  899.          I,J,PosCut,
  900.          PosUnCut:       byte;
  901.       begin
  902.          if Cut <> '' then begin     {already in a bracket --check for close}
  903.             PosUnCut := pos(UnCut,UC);
  904.             if PosUnCut=0 then                  {no close}
  905.                for I := 1 to length(UC) do      {blank all of UC}
  906.                   UC[I] := #32
  907.             else begin                          {has closer}
  908.                if UnCut = '*)' then
  909.                   inc(PosUnCut);
  910.                for I := 1 to PosUnCut do        {blank UC to closer}
  911.                   UC[I] := #32;
  912.                Cut := ''; UnCut := ''
  913.             end {else}
  914.          end; {if Cut}
  915.          while (pos(Cuts[1],UC)<>0) or                                {.CP29}
  916.                (pos(Cuts[2],UC)<>0) or
  917.                (pos(Cuts[3],UC)<>0) do begin   {UC contains openers}
  918.             J := length(UC);
  919.             for I := 1 to 3 do begin               {find first opener}
  920.                PosCut := pos(Cuts[I],UC);
  921.                if (PosCut>0) and
  922.                   (PosCut<J) then begin
  923.                      Cut := Cuts[I];
  924.                      UnCut := UnCuts[I];
  925.                      J := PosCut
  926.                end {if}
  927.             end; {for I}
  928.             PosCut := J;
  929.             PosUncut := pos(UnCut,copy(UC,succ(pos(Cut,UC)),255));
  930.             if PosUnCut<>0 then begin  {If there's a closer, find its posit}
  931.                PosUnCut := PosUnCut + PosCut;
  932.                if UnCut = '*)' then
  933.                   inc(PosUnCut);
  934.                for I := PosCut to PosUnCut do         {blank UC in brackets}
  935.                   UC[I] := #32;
  936.                Cut := '';                             {reset Cut & UnCut}
  937.                UnCut := ''
  938.             end {there's a closer}
  939.             else                                {if no closer}
  940.                for I := PosCut to length(UC) do      {blank rest of UC}
  941.                   UC[I] := #32;
  942.          end {while openers in UC}
  943.       end; {BlankBrackets}
  944.  
  945.       procedure ClearIdentifiers (var UC: string);                    {.CP10}
  946.       var
  947.          I:           byte;
  948.       begin
  949.          InMiddle := False; InHex := False;
  950.          for I := 1 to length(UC) do
  951.             if UC[I] = #32 then begin                               {a blank}
  952.                InMiddle := False;
  953.                InHex := False
  954.             end {if blank}
  955.             else if UC[I] = '$' then begin        {start of hex number}{.CP5}
  956.                InHex := True;
  957.                InMiddle := False;
  958.                UC[I] := #32
  959.             end {else $}
  960.             else
  961.                if InMiddle then begin                {in an identifier}{.CP6}
  962.                   if not (UC[I] in MiddleSet) then begin
  963.                      UC[I] := #32;
  964.                      InMiddle := False
  965.                   end {if not UC}
  966.                end {if InMiddle}
  967.                else if InHex then begin               {in a hex number}{.CP8}
  968.                   if not (UC[I] in HexNumbers) then InHex := False;
  969.                   if InHex or not (UC[I] in AtStart) then UC[I] := #32
  970.                end {else Hex number}
  971.                else if (UC[I] in AtStart) then
  972.                   InMiddle := True                           {start an ident}
  973.                else
  974.                   UC[I] := #32
  975.       end; {ClearIdentifiers}
  976.  
  977.    begin {Underline}                                                   {.CP7}
  978.       UC := Line;                                    {Prepare guide template}
  979.       UndLn := '';
  980.       for B := 1 to length(UC) do UC[B] := UpCase(UC[B]);      {All capitals}
  981.       BlankBrackets(UC);                   {Remove all comments & quotations}
  982.       ClearIdentifiers(UC);             {Remove everything not an identifier}
  983.       Ins(Line,UC,Opening,Closing);   {Insert printer chars around Key words}
  984.    end; {Underline}
  985.  
  986.    procedure PrintLine;               {Print one line}                {.CP26}
  987.    var
  988.       B,
  989.       RealLength:  byte;
  990.       Opener:      string;
  991.    begin
  992.       RealLength := length(Line) - 2;       {Length w/o pad or print symbols}
  993.       Opener := '';
  994.       if Mrk or XRef then Underline(Line);
  995.       if (NumberLines) then begin            {write line number or spaces}
  996.          if NoLine or (RealLength=0) then begin    {if a continuation    }
  997.             Opener := Opener + '     ';
  998.             if Mrk then
  999.                Opener := Opener + '       '            {spaces only      }
  1000.             else
  1001.                Opener := Opener + '  '
  1002.          end {if NoLine}
  1003.          else begin                                {if beginning new line}
  1004.             Opener := Opener + StrgI(LineNumber,5);      {write line numb}
  1005.             if Mrk then
  1006.                Opener := Opener + ' ' + StrgI(Depth,2) + '    '  {& depth}
  1007.             else
  1008.                Opener := Opener + '  ';                         {no depth}
  1009.             NoLine := False
  1010.          end {else --not NoLine}
  1011.       end; {if Numberlines}
  1012.       if XRef then                                                {.CP22}
  1013.          ScanAndHash(UC,Line,LineNumber)                 {Scan for X-ref}
  1014.       else begin
  1015.          GotoXY(46,16);                           {Keep user entertained}
  1016.          write(LineNumber:5)
  1017.       end; {else not XRef}
  1018.       Line := copy(Line,2,length(Line)-2);               {remove padding}
  1019.       if MarkWCR and (UndLn<>'') then begin
  1020.          delete(UndLn,1,1);
  1021.          while length(UndLn)<length(Line) do
  1022.             UndLn := #32 + Undln;
  1023.       end; {if UndLn}
  1024.       if (length(IncMark)>0) or (length(IncLine)>0) then begin
  1025.          for B := RealLength to pred(MaxLess) do
  1026.              Line := Line + #32;
  1027.          Line := Line + IncLine + IncMark;
  1028.          IncLine := '';
  1029.          IncState := OK;
  1030.       end; {if IncMark}
  1031.       if not XRefOnly then begin
  1032.          if MarkWCR and (UndLn<>'') then begin
  1033.             while UndLn[length(UndLn)]=#32 do
  1034.                dec(UndLn[0]);
  1035.             for B := 1 to length(Opener) do
  1036.                UndLn := #32 + UndLn;
  1037.             writeln(Lst,Opener,Line,^M,UndLn);        {Enfin! WRITE here}
  1038.          end {if UndLn}
  1039.          else
  1040.             writeln(Lst,Opener,Line);                 {or here}
  1041.       end; {if not XRefOnly}
  1042.       if LongOne then
  1043.          NoLine := True
  1044.       else begin
  1045.          NoLine := False;
  1046.          inc(LineNumber)
  1047.       end {else if not NoLine}
  1048.    end; {PrintLine}
  1049.  
  1050.    procedure TabSpace; {make room for tabs (every TabSize chars)}     {.CP15}
  1051.    var
  1052.       B,Col,Nchrs: byte;
  1053.  
  1054.       procedure StartLineEnd;
  1055.       begin
  1056.          LineEnd := '';
  1057.          LongOne := True
  1058.       end; {StartLineEnd}
  1059.  
  1060.    begin {TabSpace}
  1061.       if Line[1]=TabChr then begin    {turn ldg TabChr to Tab & strip others}
  1062.          Line[1] := #9;
  1063.          while Line[2]=TabChr do delete(Line,2,1)
  1064.       end; {if Line[1]}
  1065.       Col := 1;                                                       {.CP26}
  1066.       while Col<= length(Line) do begin
  1067.          if Line[Col]=#9 then begin                   {if Tab in that column}
  1068.             Delete(Line,Col,1);                             {remove Tab char}
  1069.             Nchrs := Col mod TabSize;
  1070.             if Nchrs=0 then Nchrs := TabSize;
  1071.             Nchrs := 9 - Nchrs;                  {number of blanks to insert}
  1072.             for B := 1 to Nchrs do begin
  1073.                insert(TabChr,Line,Col);                      {insert TabChrs}
  1074.                if not LongOne then                      {Check if overlength}
  1075.                   if length(Line)>Max then StartLineEnd;
  1076.             end; {for B}
  1077.             Col := Col + pred(Nchrs);                {move Col to end of Tab}
  1078.             if LongOne then begin                   {re-cut Line and LineEnd}
  1079.                B := length(Line) - Nchrs;
  1080.                while not (Line[B] in [#32,TabChr]) do dec(B);    {find blank}
  1081.                Nchrs := length(Line) - B;
  1082.                for B := 1 to Nchrs do begin                     {shift chars}
  1083.                   LineEnd := Line[length(line)] + LineEnd;
  1084.                   delete(Line,length(line),1)
  1085.                end {for B}
  1086.             end {if LongOne}
  1087.          end; {if Line[Col] is Tab}
  1088.          inc(Col)                                             {increment Col}
  1089.       end {while Col}
  1090.    end; {TabSpace}
  1091.  
  1092.    procedure FixRemainder;                                            {.CP17}
  1093.    var
  1094.       B:           byte;
  1095.    begin
  1096.       while (LineEnd[1]=#32) and (LineEnd<>'') do       {Strip leading}
  1097.          delete(LineEnd,1,1);                           {blanks from LineEnd}
  1098.       B := 1;
  1099.       while (LineEnd[B]=TabChr) and (B<=length(LineEnd)) do        {get past}
  1100.          inc(B);                                                    {TabChrs}
  1101.       while (LineEnd[B]=#32) and (length(LineEnd)>=B) do      {strip further}
  1102.          delete(LineEnd,B,1);                                        {blanks}
  1103.       B := 1;
  1104.       while (B<length(Line)) and (Line[B]=' ') do begin      {Pad LineEnd to}
  1105.          inc(B);                                                 {line it up}
  1106.          LineEnd := ' ' + LineEnd
  1107.       end {while (B<}
  1108.    end; {FixRemainder}
  1109.  
  1110.    procedure DeTab; {turn initial Tab chars into blanks}              {.CP10}
  1111.    var
  1112.       B:           byte;
  1113.    begin
  1114.       for B := 1 to length(Line)do
  1115.          if Line[B]=TabChr then Line[B] := #32;
  1116.    end; {DeTab}
  1117.  
  1118.    procedure CutIt(Mx: integer); {Cut line at last}                   {.CP16}
  1119.    var                            {possible blank}
  1120.       B,Col:       byte;
  1121.       Temp:        string;
  1122.    begin {CutIt}
  1123.       B := Mx;
  1124.       while (B>0) and (Line[B]<>' ') do dec(B); {Find last blank space}
  1125.       Col := 1;
  1126.       while (Col<=B) and (Line[Col]=' ') do inc(Col);       {find 1st non-sp}
  1127.       if (Col>=B) then B := Mx;
  1128.       Temp := copy(Line,1,pred(B));
  1129.       delete(Line,1,pred(B));                                     {Chop line}
  1130.       LineEnd := Line + LineEnd;                     {Remainder into LineEnd}
  1131.       Line := Temp;
  1132.       LongOne := True;                                             {Set flag}
  1133.    end; {CutIt}
  1134.  
  1135.    procedure SetMax;                                                  {.CP15}
  1136.    begin
  1137.       if NumberLines
  1138.          then Max := 68
  1139.          else Max := 79;
  1140.       if GotPrnData then begin
  1141.          if NumberLines then
  1142.             if Mrk then
  1143.                Max := Inst.Bt[SW] - 12
  1144.             else
  1145.                Max := Inst.Bt[SW] - 8
  1146.          else
  1147.             Max := pred(Inst.Bt[LW]);
  1148.       end; {else GotPrnData}
  1149.    end; {SetMax}
  1150.  
  1151.    procedure XRBillboard;                                              {.CP9}
  1152.    begin
  1153.       if XRef then
  1154.          WriteCRT('Program lines:',14,15,Bright)
  1155.       else begin
  1156.          WriteCRT('--- Not Cross-Referencing ---',14,26,Bright);
  1157.          WriteCRT('    Printing Line: ',16,26,Bright)
  1158.       end {else}
  1159.    end; {XRBillboard}
  1160.  
  1161.    procedure TotItUp;                                                  {.CP6}
  1162.    begin
  1163.       GotoXY(49,14); write('Identifiers: ',ScanCount:5);
  1164.       GotoXY(49,15); write('Procedures:  ',Pcount:5);
  1165.       GotoXY(49,16); write('Occurrences: ',Occur:5)
  1166.    end; {TotItUp}
  1167.  
  1168.    procedure MarkInc;  {insert INC marker in Line}                    {.CP15}
  1169.    var
  1170.       B,Indent:    byte;
  1171.    begin
  1172.       IncMark := '';
  1173.       for B := 2 to IFN do IncMark := IncMark + '*';
  1174.       case IncState of
  1175.          Started:  IncLine := '<=== Including '
  1176.                               + IFileName[IFN] + ' ';
  1177.          Ended:    IncLine := '<=== Finished '
  1178.                               + IFileName[succ(IFN)] + ' *';
  1179.          TooDeep:  IncLine := '<=== Too many includes.  Can''t include it.';
  1180.          CantFind: Incline := '<=== Couldn''t find it.';
  1181.       end; {case}
  1182.    end; {MarkInc}
  1183.  
  1184.    procedure Include;                                                 {.CP10}
  1185.    var
  1186.       B,E:         byte;
  1187.       ComString:   CMD;
  1188.       IncFile:     boolean;
  1189.  
  1190.       function DepthOK: boolean;
  1191.       begin
  1192.          DepthOK := IFN < NoIncFiles
  1193.       end; {DepthOK}
  1194.  
  1195.       procedure TryToOpen(FName: string; var F: text);                 {.CP10}
  1196.       begin
  1197.          assign(F,FName);
  1198.          {$I-}
  1199.          reset(F);
  1200.          {$I+}
  1201.          if IOresult=0
  1202.             then IncState := Started
  1203.             else IncState := CantFind
  1204.       end; {TryToOpen}
  1205.  
  1206.    begin  {Include}                                                   {.CP16}
  1207.       B := Pos('{$'+'I',Line) + 3;
  1208.       E := Pos('}',Line);
  1209.       if (E<>0) and (E>B) then begin
  1210.          ComString := Copy(Line,B,E-B);   {Peel out string}
  1211.          if ComString[1] in PlusMinus then
  1212.             IncFile := False              {It's an IO check}
  1213.          else if not Turbo3 then
  1214.             IncFile := ComString[1]=#32   {T4 needs blank for INClude}
  1215.          else
  1216.             IncFile := True;              {T3 doesn't & has no IFDEF}
  1217.       end {if E...}
  1218.       else begin
  1219.          ComString := '';
  1220.          IncFile := False
  1221.       end; {else}
  1222.       if IncFile then begin                        {if an INCLUDE}    {.CP14}
  1223.          ComString := InCapitals(Strip(ComString,[#32]));
  1224.          inc(IFN);
  1225.          IFileName[IFN] := ComString;
  1226.          if DepthOK then begin                     {if Inc depth left}
  1227.             FixUpFileName(IFileName[IFN]);
  1228.             TryToOpen(IFileName[IFN],IFil[IFN]);          {try name as found}
  1229.             if IncState=CantFind then begin
  1230.                while (pos(':',IFileName[IFN])<>0)         {if no go as found}
  1231.                      or (pos('\',IFileName[IFN])<>0) do
  1232.                         delete(IFileName[IFN],1,1);   {try same path as main}
  1233.                IFileName[IFN] := PathSign + IFileName[IFN];
  1234.                TryToOpen(IFileName[IFN],IFil[IFN]);
  1235.             end; {if couldn't find}
  1236.             if IncState=CantFind then    {if still no go, search path} {.CP9}
  1237.                if FindFile(IFileName[IFN]) then begin    {if found}
  1238.                   Assign(IFil[IFN],IFileName[IFN]);         {set up new file}
  1239.                   Reset(IFil[IFN]);
  1240.                   IncState := Started
  1241.                end; {if file found}
  1242.             if IncState=Started then             {if file found (somewhere)}
  1243.                CenterCRT('Including ' + IFileName[IFN],
  1244.                          12,Bright,Inside)    {showing where found}
  1245.             else begin                        {If file not found  }   {.CP11}
  1246.                Blank(12,12);                                 {report failure}
  1247.                FixUpFileName(IFileName[IFN]);
  1248.                CenterCRT('Can''t find '+IFileName[IFN],
  1249.                           12,Bright,Inside);
  1250.                dec(IFN);
  1251.             end; {if can't find it}
  1252.             while (pos(':',IFileName[IFN])<>0)           {strip pathmarks}
  1253.                or (pos('\',IFileName[IFN])<>0) do        {for printout}
  1254.                   delete(IFileName[IFN],1,1);
  1255.          end {if inc depth left}
  1256.          else begin                         {report no inc depth left} {.CP8}
  1257.             CenterCRT('Too many Include files',12,Bright,Inside);
  1258.             dec(IFN);
  1259.             IncState := TooDeep
  1260.          end; {else --no inc depth left}
  1261.          MarkInc;
  1262.       end {if IncFile}
  1263.    end; {Include}
  1264.  
  1265.    procedure CutAndPrint;                                             {.CP26}
  1266.    begin
  1267.       if LongOne then begin
  1268.          Line := LineEnd;
  1269.          LongOne := False
  1270.       end {if LongOne}
  1271.       else begin
  1272.          readln(IFil[IFN],Line);
  1273.          if EOF(IFil[IFN]) and (IFN>1) then begin
  1274.             CloseCarefully(IFil[IFN]);
  1275.             dec(IFN);
  1276.             IncState := Ended;
  1277.             MarkInc
  1278.          end; {if Eof}
  1279.          if not Vanilla then begin
  1280.             if pos('{',Line)<>0 then begin
  1281.                if pos('{.',Line)<>0 then begin
  1282.                   if pos(HeaderMark,Line)<>0 then GetHeaderInstruction(Line);
  1283.                   if (pos('{'+'.C',Line)<>0) or (pos('{'+'.P',Line)<>0) then
  1284.                      PrintControl(PageLineNumber);
  1285.                end; {if '{.'}
  1286.                if Pos('{'+'$I',Line)=1 then Include
  1287.             end; {if '{'}
  1288.          end; {if not Vanilla}
  1289.          if PageLineNumber=-1 then PrintHeader(PageLineNumber);
  1290.       end; {else --read next line}
  1291.       LineEnd := '';                                                  {.CP15}
  1292.       MaxLess := Max - length(IncMark) - length(IncLine);
  1293.       if length(Line)>MaxLess then CutIt(MaxLess);{CutIt sets LongOne = True}
  1294.       if pos(#9,Line)<>0 then TabSpace;
  1295.       if LineEnd<>'' then FixRemainder; {pad LineEnd w matching blanks}
  1296.       if Pos(TabChr,Line)<>0 then DeTab;
  1297.       Line := ' ' + Line + ' ';                   {Pad line w blanks at ends}
  1298.       inc(PageLineNumber);
  1299.       Pager := PageLineNumber;
  1300.       if (PageLineNumber>MaxLin) and not XRefOnly then begin
  1301.          NewPage(Pager);
  1302.          PrintHeader(PageLineNumber);
  1303.       end; {if (PageLine.. }
  1304.       PrintLine;
  1305.    end; {CutAndPrint}
  1306.  
  1307.    procedure Initialize;                                              {.CP18}
  1308.    var
  1309.       HS: HdSegType;
  1310.       K: integer;
  1311.    begin
  1312.       assign(Lst,OutputDevice); rewrite(Lst);
  1313.       if GotPrnData then begin
  1314.          write(Lst,Istring[PreP]);
  1315.          PrePSent := True
  1316.       end {if GotPrnData}
  1317.       else
  1318.          PrePSent := False;
  1319.       CursorOff;
  1320.       for K := 1 to NoIncFiles do IFileName[K] := '';
  1321.       for K := 1 to 20 do begin
  1322.          RecDepth[K] := 0;
  1323.          CaseDepth[K] := 0
  1324.       end; {for K}
  1325.       HeaderMark := '{' + '.H'; AltHeaders := False;                  {.CP14}
  1326.       Occur := 0; ScanCount := 0; PCount := 0;
  1327.       for K := 0 to TableSize do T[K] := Nil; Longest := 0;
  1328.       OpLen := length(Opening); ClLen := length(Closing);
  1329.       Cut := ''; UnCut := ''; Depth := 0; InRec := 0;
  1330.       LongOne := False; NoLine := False; Enough := False;
  1331.       Cuts[1] := '(*'; Cuts[2] := '{'; Cuts[3] := #39;
  1332.       UnCuts[1] := '*)'; UnCuts[2] := '}'; UnCuts[3] := #39;
  1333.       LineNumber := 1; Page := 1; IncState := OK;
  1334.       IFN := 1; assign(IFil[1],FileName); FileName := Shortened(FileName);
  1335.       MakeFirstHeader(IFil[1]);
  1336.       IncMark := ''; IncLine := '';  NextIsProc := False;
  1337.       QuitStrg := Istring[SetLg];
  1338.       FirstProc := Nil;
  1339.       SetMax;
  1340.    end; {Initialize}
  1341.  
  1342. begin {ListIt}                                                        {.CP23}
  1343.    ReadingMatterI;
  1344.    Enough := Escape;
  1345.    if not Enough then begin
  1346.       Initialize;
  1347.       if FFeed then NewPage(1);
  1348.       if not XRefOnly then PageLineNumber := -1;
  1349.       XRBillboard;
  1350.       while (LongOne or not EOF(IFil[IFN])) and not Enough do begin
  1351.          CutAndPrint;
  1352.          Enough := Escape
  1353.       end; {while}
  1354.       for B := IFN to 1 do CloseCarefully(IFil[IFN]);    {Close source files}
  1355.       if not XRefOnly then NewPage(Pager);
  1356.       if XRef and not Enough then begin
  1357.          XRefOnly := True;          {used as a flag  --more clever than good}
  1358.          ReadingMatterII;             {Hmm.  Isn't that what "kludge" means?}
  1359.          PrintTable;
  1360.          TotItUp;
  1361.          NewPage(Pager)
  1362.       end; {if XRef and not Enough}
  1363.    end {if not Enough}
  1364. end; {ListIt}
  1365.  
  1366. End. {Unit PXLLIST}
  1367.