home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / MADTRB13.ZIP / CROSSREF.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1985-07-14  |  17.2 KB  |  602 lines

  1.  
  2.  
  3. program TXREF;
  4.  
  5. {$V-}
  6. {$R+}
  7.  
  8. {  Program TXREF - Produce a Listing and Cross Reference for a Turbo Pascal
  9.    source file.
  10.  
  11.    You must have Turbo Toolbox from Borland International, Inc. in order to
  12.    compile this program.
  13.  
  14.    As written, this program assumes that you have an Epson FX-80 printer. It
  15.    may work on other printers if they are compatible enough.
  16.  
  17.    By Michael Quinlan
  18.       Version 1.0.0
  19.       12/1/84
  20.  
  21.   Known bugs:
  22.  
  23.     1. This program doesn't correctly handle certain types of constants;
  24.        the 'E' in a floating point constant will be considered a name as
  25.        will some hex constants. The procedure CopyTillAlpha needs to be
  26.        re-written to handle these things better.
  27.  
  28.     2. Numeric labels are not included in the cross reference.
  29.  
  30.     3. Names longer than 79 bytes may mess up the page alignment while
  31.        printing the cross reference.
  32.  
  33. }
  34.  
  35. const
  36.   LinesPerPage = 60;
  37.  
  38. Type
  39.   Str = String[127];
  40.   XrefRec = record
  41.               Name : Str;
  42.               Page : Integer;
  43.               Line : Integer;
  44.             end;
  45.  
  46. var
  47.   XrefVar       : XrefRec;
  48.   NumOnLine     : Integer;
  49.   CurLine       : Integer;
  50.   CurPage       : Integer;
  51.   SortResult    : Integer;
  52.   InFileName    : Str;
  53.   InFile        : Text;
  54.   Line          : Str;
  55.   CurPosn       : Integer;
  56.   CommentStatus : (NoComment, CurlyBracket, ParenStar);
  57.   InsideString  : Boolean;
  58.   Summary       : Char;
  59.  
  60. const
  61.  NumReservedWords = 44;
  62.  BiggestReservedWord = 9;
  63.  ReservedWordList : array [1..NumReservedWords] of String[BiggestReservedWord]
  64.    = (
  65.   'ABSOLUTE', 'AND'     , 'ARRAY' , 'BEGIN', 'CASE'    , 'CONST' , 'DIV',
  66.   'DO'      , 'DOWNTO'  , 'ELSE'  , 'END'  , 'EXTERNAL', 'FILE'  , 'FOR',
  67.   'FORWARD' , 'FUNCTION', 'GOTO'  , 'IF'   , 'IN'      , 'INLINE', 'LABEL',
  68.   'MOD'     , 'NIL'     , 'NOT'   , 'OF'   , 'OR'      , 'PACKED', 'PROCEDURE',
  69.   'PROGRAM' , 'RECORD'  , 'REPEAT', 'SET'  , 'SHL'     , 'SHR'   , 'STRING',
  70.   'THEN'    , 'TO'      , 'TYPE'  , 'UNTIL', 'VAR'     , 'WHILE' , 'WITH',
  71.   'XOR'     , 'OVERLAY');
  72.  
  73. VAR  TDate : String[8];  {Global variable for DOSDate}
  74.  
  75. PROCEDURE DOSDate;
  76.   TYPE
  77.     regpack = record
  78.                 ax,bx,cx,dx,bp,si,ds,es,flags: integer;
  79.               end;
  80.   VAR
  81.     recpack:       regpack;                {record for MsDos call}
  82.     month,day:     string[2];
  83.     year:          string[4];
  84.     dx,cx:         integer;
  85.   begin
  86.     with recpack do
  87.     begin
  88.       ax := $2a shl 8;
  89.     end;
  90.     MsDos(recpack);                        { call function }
  91.     with recpack do
  92.     begin
  93.       str(cx,year);                        {convert to string}
  94.       str(dx mod 256,day);                     { " }
  95.       str(dx shr 8,month);                     { " }
  96.     end;
  97.     Year:=Copy(Year,3,2);
  98.     If Length(Month) = 1 then Month:='0'+Month;
  99.     If Length(Day) = 1 then Day:='0'+Day;
  100.     Tdate := month + '/' + day + '/' + year;
  101.   end;
  102.  
  103. var
  104.   ReservedWordHashTable : array [1..NumReservedWords] of
  105.                             record
  106.                               WordPtr : Integer;
  107.                               NextPtr : Integer
  108.                             end;
  109.  
  110.  {$I A:SORT.BOX}  { Include the sort routines from Turbo ToolBox }
  111.  
  112. {=======================================================================}
  113. {  Printer Routines                                                     }
  114. {=======================================================================}
  115.  
  116. procedure Printer_Init;
  117. { Init the printer to 132 column mode }
  118. begin
  119.   Write(Lst,#27'Q'#27'L020')
  120. end;
  121.  
  122. procedure Printer_Reset;
  123. { reset printer back to 80 column mode }
  124. begin
  125.   Write(Lst,#27'Q'#27'L020')  { turn compressed mode off }
  126. end;
  127.  
  128. procedure Printer_Underscore;
  129. { Turn on underlines }
  130. begin
  131.   Write(Lst, #27'X')  { turn on underlines }
  132. end;
  133.  
  134. procedure Printer_NoUnderscore;
  135. { Turn off underlines }
  136. begin
  137.   Write(Lst, #27'Y')  { turn off underlines }
  138. end;
  139.  
  140. procedure Printer_Eject;
  141. { Eject to a new page }
  142. Var I:Integer;
  143. begin
  144.   If CurLine <60 then For I:=CurLine to 60 do WriteLn(Lst);
  145.   WriteLn(Lst);
  146.   Writeln(Lst,'Listing of ':90, InFileName, ', Page ':5, CurPage);
  147.   Write(Lst, #12)
  148. end;
  149.  
  150. {======================================================================}
  151. { Procedures for handling the hash table; this is used to speed up     }
  152. { checking for reserved words.                                         }
  153. {======================================================================}
  154.  
  155. function ReservedWordHash(var w : Str) : Integer;
  156. var
  157.   c : char;
  158.   h : integer;
  159.   i : integer;
  160.   n : integer;
  161. begin
  162.   h := 0;
  163.   n := 1;
  164.   for i := 1 to (length(w) div 2) do
  165.     begin
  166.       h := h xor ((Ord(w[n]) shl 8) or Ord(w[n+1]));
  167.       n := n + 2
  168.     end;
  169.   if n = length(w) then
  170.     h := h xor Ord(w[n]);
  171.   ReservedWordHash := ((h and $7FFF) mod NumReservedWords) + 1
  172. end;
  173.  
  174. procedure SetUpReservedWordHashTable;
  175. var
  176.   h : integer;
  177.   i : integer;
  178.   NewH : integer;
  179.   MinProbes, MaxProbes, NumProbes, TotProbes : integer;  { for debugging only }
  180.   AvgProbes : Real;  { for debugging only }
  181.  
  182.   function FindFreeEntry(h : integer) : integer;
  183.   begin
  184.     repeat
  185.       if h >= NumReservedWords then h := 1
  186.       else h := h + 1
  187.     until ReservedWordHashTable[h].WordPtr = 0;
  188.     FindFreeEntry := h
  189.   end;
  190.  
  191. begin
  192.   for i := 1 to NumReservedWords do
  193.     begin
  194.       ReservedWordHashTable[i].WordPtr := 0;
  195.       ReservedWordHashTable[i].NextPtr := 0
  196.     end;
  197.   for i := 1 to NumReservedWords do
  198.     begin
  199.       h := ReservedWordHash(ReservedWordList[i]);
  200.       if ReservedWordHashTable[h].WordPtr = 0 then
  201.         ReservedWordHashTable[h].WordPtr := i
  202.       else
  203.         begin { handle collisions }
  204.           { first find the end of the chain }
  205.           while ReservedWordHashTable[h].NextPtr <> 0 do
  206.             h := ReservedWordHashTable[h].NextPtr;
  207.           { now attach the new entry onto the end of the chain }
  208.           NewH := FindFreeEntry(h);
  209.           ReservedWordHashTable[h].NextPtr := Newh;
  210.           ReservedWordHashTable[NewH].WordPtr := i
  211.         end
  212.     end;
  213.  
  214. { the following is for debugging only }
  215.   (***********************************************************************
  216.  
  217.      D E B U G G I N G   C O D E   C O M M E N T E D   O U T
  218.  
  219.    ***********************************************************************
  220.  
  221.   { calculate the min, max, and average number of probes required into the
  222.     hash table }
  223.   TotProbes := 0;
  224.   MinProbes := MaxInt;
  225.   MaxProbes := 0;
  226.   for i := 1 to NumReservedWords do
  227.     begin
  228.       h := ReservedWordHash(ReservedWordList[i]);
  229.       NumProbes := 1;
  230.       while ReservedWordHashTable[h].WordPtr <> i do
  231.         begin
  232.           NumProbes := NumProbes + 1;
  233.           h := ReservedWordHashTable[h].NextPtr
  234.         end;
  235.       TotProbes := TotProbes + NumProbes;
  236.       if NumProbes > MaxProbes then MaxProbes := NumProbes;
  237.       if NumProbes < MinProbes then MinProbes := NumProbes
  238.     end;
  239.   AvgProbes := TotProbes / NumReservedWords;
  240.   writeln('RESERVED WORD HASH TABLE STATISTICS');
  241.   writeln(' Max Probes = ', MaxProbes);
  242.   writeln(' Min Probes = ', MinProbes);
  243.   writeln(' Avg Probes = ', AvgProbes:8:2)
  244.  
  245. *************************************************************************)
  246.  
  247. end;
  248.  
  249. {======================================================================}
  250. {  Procedures to set up the input file.                                }
  251. {======================================================================}
  252.  
  253. procedure UpStr(var s : Str);
  254. var
  255.   i : integer;
  256. begin
  257.   for i := 1 to length(s) do s[i] := UpCase(s[i])
  258. end;
  259.  
  260. function GetParm : Str;
  261. var
  262.   Parm : Str absolute CSeg:$80;
  263. begin
  264.   GetParm := Parm
  265. end;
  266.  
  267. function AskFileName : Str;
  268. var
  269.   f : Str;
  270. begin
  271.   Write('Name of file to cross reference: ');
  272.   Readln(f);
  273.   if f = '' then halt;  { provide an exit for the user }
  274.   AskFileName := f
  275. end;
  276.  
  277. function OpenInFile : boolean;
  278. begin
  279.   UpStr(InFileName);  { convert file name to upper case }
  280.   if Pos('.', InFileName) = 0 then InFileName := InFileName + '.PAS';
  281.   Assign(InFile, InFileName);
  282.   {$I-} Reset(InFile); {$I+}
  283.   OpenInFile := (IOResult = 0)
  284. end;
  285.  
  286. procedure GetInFile;
  287. begin
  288. { on entry, InFileName may already have the file name }
  289.   if InFileName = '' then InFileName := AskFileName;
  290.   while not OpenInFile do
  291.     begin
  292.       Writeln('Cannot open ', InFileName);
  293.       InFileName := AskFileName
  294.     end
  295. end;
  296.  
  297. procedure NewPage;
  298. begin
  299.   if CurPage = 0 then
  300.     begin
  301.       Writeln('Make sure printer is lined up at the top of the page and powered on.');
  302.       Write('Press Enter when ready... ');
  303.       readln;
  304.       Printer_Init  { set printer in 132 column mode }
  305.     end
  306.   else
  307.     Printer_Eject;
  308.   CurPage := CurPage + 1;
  309.   CurLine := 1;
  310.   Writeln(Lst,'Listing of ':80, InFileName, ' on ', TDate, ', Page ':5, CurPage);
  311.   Writeln(Lst)
  312. end;
  313.  
  314. procedure ReadLine;
  315. begin
  316.   Readln(InFile, Line);
  317.   if CurLine >= LinesPerPage then NewPage
  318.   else CurLine := CurLine + 1;
  319.   CurPosn := 1;
  320.   InsideString := FALSE;
  321.   Write(Lst, CurLine:2, ':   ')
  322. end;
  323.  
  324. {======================================================================}
  325. {  Procedures to process the input file.                               }
  326. {======================================================================}
  327.  
  328. procedure CopyTillAlpha;
  329. { copy chars from Line to the printer until the start of a name is found }
  330. begin
  331.   while (CurPosn <= length(Line)) and
  332.          (not (Line[CurPosn] in ['A'..'Z','a'..'z','_']) or InsideString or
  333.           (CommentStatus <> NoComment)) do
  334.     begin
  335.       if CommentStatus = NoComment then
  336.         begin
  337.           if Line[CurPosn] = '''' then InsideString := not InsideString
  338.         end;
  339.       if not InsideString then
  340.         case CommentStatus of
  341.           NoComment : begin
  342.                         if Line[CurPosn] = '{' then CommentStatus := CurlyBracket
  343.                         else if CurPosn < length(Line) then
  344.                                begin
  345.                                  if Copy(Line, CurPosn, 2) = '(*' then
  346.                                    CommentStatus := ParenStar
  347.                                end
  348.                       end;
  349.           CurlyBracket : if Line[CurPosn] = '}' then CommentStatus := NoComment;
  350.           ParenStar    : if CurPosn < length(Line) then
  351.                            begin
  352.                              if Copy(Line, CurPosn, 2) = '*)' then
  353.                                CommentStatus := NoComment
  354.                            end
  355.         end; { Case }
  356.       Write(Lst, Line[CurPosn]);
  357.       CurPosn := CurPosn + 1
  358.     end
  359. end;
  360.  
  361. function Reserved(var w : Str) : boolean;
  362. var
  363.   h : integer;
  364.   r : (DontKnow, IsReserved, NotReserved);
  365. begin
  366.   h := ReservedWordHash(w);
  367.   r := DontKnow;
  368.   repeat
  369.     if w = ReservedWordList[ReservedWordHashTable[h].WordPtr] then
  370.       r := IsReserved
  371.     else if ReservedWordHashTable[h].NextPtr = 0 then
  372.       r := NotReserved
  373.     else h := ReservedWordHashTable[h].NextPtr
  374.   until r <> DontKnow;
  375.   Reserved := (r = IsReserved)
  376. end;
  377.  
  378. procedure WriteReserved(var w : Str);
  379. begin
  380.   Printer_Underscore;  { turn on underscores }
  381.   write(Lst, w);
  382.   Printer_NoUnderscore { turn off underscores }
  383. end;
  384.  
  385. procedure WriteWord(var Word, CapWord : Str);
  386. begin
  387.   XrefVar.Name := CapWord;
  388.   XrefVar.Page := CurPage;
  389.   XrefVar.Line := CurLine;
  390.   SortRelease(XrefVar);
  391.   write(Lst, Word)
  392. end;
  393.  
  394. procedure DoWord;
  395. var
  396.   wstart  : integer;
  397.   Word    : Str;
  398.   CapWord : Str;
  399.   ResWord : Str;
  400. begin
  401.   wstart := CurPosn;
  402.   repeat
  403.     CurPosn := CurPosn + 1
  404.   until (CurPosn > length(Line)) or not (Line[CurPosn] in ['A'..'Z','a'..'z','_','0'..'9']);
  405.   Word := Copy(Line, wstart, CurPosn - wstart);
  406.   CapWord := Word;
  407.   UpStr(CapWord);  { Upper case version of the word }
  408.   if Reserved(CapWord) then begin
  409.     ResWord := CapWord;
  410.     WriteReserved(Word);
  411.   end else begin
  412.     WriteWord(Word, CapWord);
  413.     If (ResWord='PROCEDURE') or (ResWord='FUNCTION') then begin
  414.       CapWord:=ResWord + ' ' + Word;
  415.       Word := '';
  416.       WriteWord(Word, CapWord);
  417.     End;
  418.   End;
  419. end;
  420.  
  421. procedure Inp;
  422. begin
  423.   GetInFile;
  424.   CurLine := 1000;  { force page break on first line }
  425.   CurPage := 0;
  426.   CommentStatus := NoComment;
  427.   while not EOF(InFile) do
  428.     begin
  429.       ReadLine;
  430.       while CurPosn <= length(Line) do
  431.         begin
  432.           CopyTillAlpha;
  433.           if CurPosn <= length(Line) then DoWord
  434.         end;
  435.       Writeln(Lst)
  436.     end
  437. end;
  438.  
  439. {======================================================================}
  440. {  Procedure called by TurboSort to order the cross reference entries  }
  441. {======================================================================}
  442.  
  443. function Less;
  444. var
  445.   FirstRec  : XrefRec absolute X;
  446.   SecondRec : XrefRec absolute Y;
  447. begin
  448.   if FirstRec.Name = SecondRec.Name then
  449.     begin
  450.       if FirstRec.Page = SecondRec.Page then
  451.         Less := FirstRec.Line < SecondRec.Line
  452.       else
  453.         Less := FirstRec.Page < SecondRec.Page
  454.     end
  455.   else
  456.     Less := FirstRec.Name < SecondRec.Name
  457. end;
  458.  
  459. {======================================================================}
  460. {  Procedures to print the sorted cross reference                      }
  461. {======================================================================}
  462.  
  463. procedure Xref_NewPage;
  464. begin
  465.   Printer_Eject;
  466.   Writeln(Lst);
  467.   Writeln(Lst);
  468.   Writeln(Lst, 'C R O S S   R E F E R E N C E':54);
  469.   Writeln(Lst, 'Entries are PAGE:LINE':50);
  470.   Writeln(Lst);
  471.   CurLine := 5;
  472.   CurPage := CurPage+1;
  473. end;
  474.  
  475. procedure Xref_NewLine;
  476. begin
  477.   Writeln(Lst);
  478.   if CurLine >= LinesPerPage then Xref_NewPage
  479.   else CurLine := CurLine + 1;
  480.   NumOnLine := 0
  481. end;
  482.  
  483. procedure Xref_Write_Number(n, count : integer);
  484. { write "n" to Lst with "count" digits (add leading zeros) }
  485. var
  486.   s : Str;
  487.   i : integer;
  488. begin
  489.   for i := count downto 1 do
  490.     begin
  491.       s[i] := Chr((n mod 10) + Ord('0'));
  492.       n := n div 10
  493.     end;
  494.   s[0] := Chr(count);  { set correct string length }
  495.   write(Lst, s)
  496. end;
  497.  
  498. procedure Xref_Write;
  499. begin
  500.   if NumOnLine >= 8 then Xref_NewLine;
  501.   if NumOnLine = 0 then Write(Lst, '   ');
  502.   Write(Lst, ' ');
  503.   Xref_Write_Number(XrefVar.Page, 5);
  504.   Write(Lst, ':');
  505.   Xref_Write_Number(XrefVar.Line, 2);
  506.   NumOnLine := NumOnLine + 1
  507. end;
  508.  
  509. procedure Xref_NewName;
  510. begin
  511.   if (CurLine + 2) >= LinesPerPage then Xref_NewPage;
  512.   Printer_Underscore;  { turn on underscores }
  513.   Write(Lst, XrefVar.Name);
  514.   Printer_NoUnderscore; { turn off underscores }
  515.   Xref_NewLine
  516. end;
  517.  
  518. procedure Outp;
  519. var
  520.   CurName : Str;
  521. begin
  522.   Printer_Reset;  { put printer back into 80 column mode }
  523.   Xref_NewPage;
  524.   SortReturn(XrefVar);
  525.   CurName := XrefVar.Name;
  526.   If (Summary = 'B') or
  527.          (Summary = 'C') and NOT (Copy(XrefVar.Name,1,9) = 'PROCEDURE') or
  528.          (Summary = 'C') and NOT (Copy(XrefVar.Name,1,8) = 'FUNCTION') or
  529.          (Summary = 'L') and (Copy(XrefVar.Name,1,9) = 'PROCEDURE') or
  530.          (Summary = 'L') and (Copy(XrefVar.Name,1,8) = 'FUNCTION') then begin
  531.     Xref_NewName;
  532.     Xref_Write;
  533.   End;
  534.   while not SortEOS do
  535.     begin
  536.       SortReturn(XrefVar);
  537.       If (Summary = 'B') or
  538.          (Summary = 'C') and NOT (Copy(XrefVar.Name,1,9) = 'PROCEDURE') or
  539.          (Summary = 'C') and NOT (Copy(XrefVar.Name,1,8) = 'FUNCTION') or
  540.          (Summary = 'L') and (Copy(XrefVar.Name,1,9) = 'PROCEDURE') or
  541.          (Summary = 'L') and (Copy(XrefVar.Name,1,8) = 'FUNCTION') then begin
  542.            if CurName <> XrefVar.Name then
  543.              begin
  544.                Xref_NewLine;
  545.                CurName := XrefVar.Name;
  546.                Xref_NewName
  547.              end;
  548.            Xref_Write
  549.       End;
  550.     end;
  551.   Writeln(Lst);
  552.   CurLine := CurLine +2;
  553.   Printer_Eject
  554. end;
  555.  
  556. {======================================================================}
  557. {  Main Program                                                        }
  558. {======================================================================}
  559.  
  560. begin
  561.   ClrScr;
  562.   Printer_Init;
  563.   DOSDate;
  564.   Write('Pascal Source Listing and Cross Reference Program V1.0.0');
  565.   Writeln('  By Michael Quinlan');
  566.   Writeln;
  567.   SetUpReservedWordHashTable;
  568.   InFileName := GetParm;
  569.   while (length(InFileName)>0) and (InFileName[1] = ' ') do
  570.     delete(InFileName, 1, 1);
  571.   WriteLn;
  572.   WriteLn('[C]omplete Summary WITHOUT Procedures and Functions or...');
  573.   WriteLn('[L]ist Procedures and Functions ONLY or...');
  574.   Write('[B]oth ');
  575.   Repeat
  576.     GotoXY(WhereX,WhereY);
  577.     Repeat until KeyPressed;
  578.     Read(Kbd,Summary);
  579.     Summary := Upcase(Summary);
  580.     Write(Summary);
  581.     If NOT (Summary in ['C','L','B']) then Write(^G);
  582.   Until Summary in ['C','L','B'];
  583.   WriteLn;
  584.   WriteLn;
  585.   SortResult := TurboSort(SizeOf(XrefRec));
  586.   writeln;
  587.   case SortResult of
  588.      0 : Writeln('Program Completed OK');
  589.      3 : Writeln('Insufficient Memory for Sort');
  590.      8 : Writeln('Illegal Item Length for Sort (Program Logic Error)');
  591.      9 : Writeln('More Than ', MaxInt, ' Items to be Sorted');
  592.     10 : Writeln('Sort Error, Disk Error or Disk Full?');
  593.     11 : Writeln('Write Error During Sort, Bad Disk?');
  594.     12 : Writeln('File Creation Error During Sort')
  595.   else
  596.     Writeln('Unknown Error ', SortResult, ' From Sort')
  597.   end; { Case }
  598.   if SortResult <> 0 then
  599.     Writeln('*** Sort Failed; Cross Reference Invalid or Incomplete')
  600. end.
  601.  
  602.