home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / x / xref10.zip / XREF.PAS < prev   
Pascal/Delphi Source File  |  1992-05-06  |  27KB  |  827 lines

  1. Uses Crt;
  2. {}
  3. CONST MaxCorpus=10000;
  4.       MaxDepth=200;
  5.       MaxFiles=200; (* max insert files *)
  6.    CONST DefaultColumns=70;
  7.    CONST DefaultLines=60;
  8. TYPE
  9.      aFunctionType=(WhoKnows,aProcedure,aFunction);
  10.      pDeclaration=^aDeclaration;
  11.      pCaller=^aCaller;
  12.      pSTRING=^STRING;
  13.      aCaller=RECORD _Itself: pDeclaration;
  14.           _Next: pCaller;
  15.           _FileWhereFound: pSTRING;
  16.                   CallLine: WORD;
  17.        END;
  18.      aDeclaration=RECORD
  19.            _FileWhereFound: pSTRING;
  20.            Forwarded: BOOLEAN;
  21.            _FileWhereForwarded: pSTRING;
  22.            LineWhereForwarded: WORD;
  23.        StartLine,EndLine,Depth: WORD;
  24.        _Owner: pDeclaration;
  25.        _Caller: pCaller;
  26.        FunctionType: aFunctionType;
  27.        Body: STRING;
  28.     END;
  29.      aCharFile= TEXT;
  30.      aState=(inProgram,inQuotes,inComment,
  31.        StartCommentMaybe,EndCommentMaybe,
  32.            PerhapsInclude);
  33.  
  34.  
  35. VAR CommentDelimiter: (Curly,Parenthesis);
  36.     Dictionary: ARRAY[1..MaxCorpus] OF pDeclaration;
  37.     IncludedFileName: ARRAY[0..MaxFiles] OF pSTRING;
  38.     FileNbr: BYTE; (* index to IncludedFileName *)
  39.     WordFound: BOOLEAN; (* Result of FindStringAt *)
  40.     There: WORD; (* where found in index, or where to be inserted *)
  41.     DictionarySize,CurrentDepth,CurrentLine: WORD;
  42.     Continue,FunctionBeginsNext: BOOLEAN;
  43.     State: aState;
  44.     _Main: pDeclaration;
  45.  
  46.     WhatNext: aFunctionType;
  47.     CurrentFunctions: INTEGER;
  48.     CallerAt: ARRAY[1..MaxDepth] OF pDeclaration;
  49.     ProgramName: STRING;
  50.  
  51. CONST DeclarationSize=SizeOf(aDeclaration)-SizeOf(STRING);
  52.  
  53.  
  54. PROCEDURE MakeMain;
  55. CONST Main='Main ProgramMain Program';
  56. BEGIN
  57.   GetMem(CallerAt[1],Length(Main)+1+DeclarationSize);
  58.                (*         ^ the length byte *)
  59.   FillChar(CallerAt[1]^,Length(Main)+1+DeclarationSize,0);
  60.  
  61.   CallerAt[1]^.Body:=Main;
  62.   CallerAt[1]^.Body[0]:=#12;
  63. END;
  64.  
  65. PROCEDURE AddFunction(f: pDeclaration);
  66. BEGIN
  67.    Inc(CurrentFunctions);
  68.    CallerAt[CurrentFunctions]:=f;
  69. END;
  70.  
  71. PROCEDURE CloseFunction;
  72. BEGIN WITH CallerAt[CurrentFunctions]^ DO
  73.       BEGIN EndLine:=CurrentLine
  74.       END;
  75.       Dec(CurrentFunctions);
  76.       FunctionBeginsNext:=CurrentFunctions>0;
  77. END;
  78.  
  79. FUNCTION Capitalize(st: STRING): STRING;
  80. VAR i: BYTE;
  81. BEGIN FOR i:=1 TO Length(st) DO st[i]:=Upcase(st[i]);
  82.       Capitalize:=st;
  83. END;
  84.  
  85. PROCEDURE MakeID(fname: STRING; VAR id: STRING);
  86. (* Returns a string "id" consisting of:
  87.    function name, capitalized, starting with class identifier,
  88.    function name, as found.
  89.    the length byte is the length of the function name,
  90.    i.e. half the actual length of the string returned
  91. *)
  92. VAR n,i: BYTE;
  93.     Leader,Method: STRING;
  94.     Underscore1,Underscore2: INTEGER;
  95. BEGIN n:=Length(fname);
  96.       IF n>125 THEN BEGIN n:=125; fname[0]:=#125 END;
  97.       Leader:=fname;
  98.       FOR i:=1 TO n DO Leader[i]:=Upcase(Leader[i]);
  99.       Underscore1:=Pos('_',Leader);
  100.       IF Underscore1>1 THEN (* save method *)
  101.       BEGIN Method:=Copy(Leader,1,Underscore1-1);
  102.         Delete(Leader,1,Underscore1);
  103.         Underscore2:=Pos('_',Leader);
  104.         IF Underscore2>0 THEN
  105.         (* insert method after underscore *)
  106.         Insert(Method,Leader,Underscore2+1)
  107.         ELSE (* it wasn't a method after all, put it back *)
  108.         Leader:=Method+Leader;
  109.         Leader:='_'+Leader;
  110.       END;
  111.  
  112.       id:=Leader+fname;
  113.       id[0]:=CHR(n)
  114. END;
  115.  
  116. FUNCTION StateName(s: aState): STRING;
  117. (* for debugging purposes *)
  118. BEGIN
  119.   CASE s OF
  120.      inProgram: StateName:='in program';
  121.      inQuotes:  StateName:='in quotes';
  122.      inComment: StateName:='comment';
  123.      StartCommentMaybe: StateName:='comment next?';
  124.      EndCommentMaybe: StateName:='end comment?';
  125.   END;
  126. END;
  127.  
  128. FUNCTION WordOfRank(n: INTEGER): STRING;
  129. BEGIN
  130.   WordOfRank:=Dictionary[n]^.Body
  131. END;
  132.  
  133. PROCEDURE ReadDictionaryFrom(VAR Mainfile: aCharFile);
  134. VAR ch,Delimiter: CHAR;
  135.     CurrentWord,UCurrentWord,NewWord: STRING;
  136.     RestoreDelimiter: BOOLEAN;
  137.     Buffer: STRING;
  138.     BufPos,BufLength: BYTE;
  139.     IncludedFile: aCharFile;
  140.     CurrentFile: ^aCharFile;
  141.     IncludeFile: BOOLEAN; (* true when must include file on
  142.                              exiting {$I....} *)
  143.     InfileName: STRING;   (* the name of the file to include *)
  144.     InMainFile: BOOLEAN;  (* true on entry, false when in included file*)
  145.     _LastDeclaration: pDeclaration;
  146.     MainFileState: RECORD
  147.         Delimiter: CHAR;
  148.         CurrentWord,UCurrentWord,NewWord: STRING;
  149.         RestoreDelimiter: BOOLEAN;
  150.         Buffer: STRING;
  151.     BufPos,BufLength: BYTE;
  152.     CurrentLine: WORD;
  153.        END;
  154.  
  155.     PROCEDURE GetInfileName;
  156.     BEGIN Inc(BufPos); ch:=Buffer[BufPos];
  157.           IF ch>' ' THEN (* not an include file command *)
  158.           BEGIN InfileName:=''; IncludeFile:=FALSE;
  159.           END
  160.           ELSE
  161.           BEGIN
  162.             WHILE ch<=' ' DO BEGIN Inc(BufPos); ch:=Buffer[BufPos]; END;
  163.             InfileName:=ch;
  164.             REPEAT Inc(BufPos); ch:=Buffer[BufPos];
  165.                    InfileName:=InfileName+ch
  166.             UNTIL ch IN [' ',^I,'*','}'];
  167.             IncludeFile:=TRUE;
  168.             (* now rub out that delimiter *)
  169.             Dec(InfileName[0]);
  170.             Dec(BufPos);
  171.           END
  172.     END;
  173.  
  174.     PROCEDURE EnterIncludedFile(VAR fn: STRING);
  175.     BEGIN (* first, save Main file info *)
  176.        MainFileState.Delimiter:=Delimiter;
  177.        MainFileState.CurrentWord:=CurrentWord;
  178.        MainFileState.UCurrentWord:=UCurrentWord;
  179.        MainFileState.NewWord:=NewWord;
  180.        MainFileState.RestoreDelimiter:=RestoreDelimiter;
  181.        MainFileState.Buffer:=Buffer;
  182.        MainFileState.BufPos:=BufPos;
  183.        MainFileState.BufLength:=BufLength;
  184.        MainFileState.CurrentLine:=CurrentLine;
  185.        (* record included file name *)
  186.        Inc(FileNbr);
  187.        New(IncludedFileName[FileNbr]);
  188.        IncludedFileName[FileNbr]^:=InfileName;
  189.  
  190.        (* now enter included file *)
  191.        Assign(IncludedFile,fn); Reset(IncludedFile);
  192.        CurrentFile:=@IncludedFile;
  193.        InMainFile:=FALSE;
  194.        WhatNext:=WhoKnows;
  195.        CurrentLine:=0;
  196.        FunctionBeginsNext:=FALSE;
  197.        State:=inProgram;
  198.        IncludeFile:=FALSE;
  199.     END; {EnterIncludedFile}
  200.  
  201.     PROCEDURE ReenterMainFile;
  202.     BEGIN (* restore Main file info *)
  203.        Delimiter:=MainFileState.Delimiter;
  204.        CurrentWord:=MainFileState.CurrentWord;
  205.        UCurrentWord:=MainFileState.UCurrentWord;
  206.        NewWord:=MainFileState.NewWord;
  207.        RestoreDelimiter:=MainFileState.RestoreDelimiter;
  208.        Buffer:=MainFileState.Buffer;
  209.        BufPos:=MainFileState.BufPos;
  210.        BufLength:=MainFileState.BufLength;
  211.        CurrentLine:=MainFileState.CurrentLine;
  212.        (* exit included file, and enter main file *)
  213.        Close(IncludedFile);
  214.        CurrentFile:=@MainFile;
  215.        WhatNext:=WhoKnows;
  216.        FunctionBeginsNext:=FALSE;
  217.        State:=inProgram;
  218.        InMainFile:=TRUE;
  219.     END; {ReenterMainFile}
  220.  
  221.  
  222.     FUNCTION FunctionEnd: BOOLEAN;
  223.     BEGIN FunctionEnd:=(CurrentFunctions>0)
  224.       AND (CallerAt[CurrentFunctions]^.Depth=CurrentDepth)
  225.  
  226.     END;
  227.  
  228.     PROCEDURE Identify(VAR _Function,_Caller: pDeclaration;
  229.              VAR NewWord: STRING; CurrentDepth: WORD);
  230.     VAR lo,hi,mid: LONGINT; First,Last,Here: WORD;
  231.     _f: pDeclaration;
  232.     Found: BOOLEAN;
  233.     BEGIN Found:=FALSE; _Function:=NIL; _Caller:=NIL;
  234.       lo:=1; hi:=DictionarySize;
  235.       WHILE (lo<=hi) AND NOT Found DO
  236.       BEGIN
  237.     mid:=(lo+hi) DIV 2;
  238.     _f:=Dictionary[mid];
  239.     IF  (NewWord<_f^.Body) THEN hi:=mid-1 ELSE
  240.     IF  (NewWord>_f^.Body) THEN lo:=mid+1
  241.         ELSE Found:=TRUE;
  242.       END;
  243.       IF NOT Found THEN Exit;
  244.       IF Found THEN
  245.       BEGIN IF CurrentFunctions<1 THEN _Caller:=CallerAt[1]
  246.             ELSE
  247.         _Caller:=CallerAt[CurrentFunctions];
  248.             _Function:=_f
  249.       END;
  250.     END;
  251.  
  252.     FUNCTION InsertionPointOf(VAR s: STRING): WORD;
  253.     VAR lo,hi,mid: LONGINT;
  254.     _f: pDeclaration;
  255.     (* Found: BOOLEAN; *)
  256.     BEGIN
  257.       lo:=1; hi:=DictionarySize;
  258.       WHILE (lo<=hi) (* AND NOT Found *) DO
  259.       BEGIN
  260.     mid:=(lo+hi) DIV 2;
  261.     _f:=Dictionary[mid];
  262.     IF  (s<_f^.Body) THEN hi:=mid-1 ELSE
  263.     IF  (s>_f^.Body) THEN lo:=mid+1 ELSE
  264.         IF  (CurrentLine<_f^.StartLine) THEN hi:=mid-1
  265.         ELSE lo:=mid+1;
  266.       END;
  267.       InsertionPointOf:=hi+1;
  268.     END;
  269.  
  270.     PROCEDURE AddCaller(_f,_Function: pDeclaration; Line: WORD);
  271.     VAR  _u: pCaller;
  272.     BEGIN WITH _Function^ DO
  273.     BEGIN IF _Caller=NIL THEN
  274.       BEGIN New(_Caller);
  275.             FillChar(_Caller^,SizeOf(_Caller^),0);
  276.             _Caller^._Next:=NIL;
  277.         _Caller^._Itself:=_f;
  278.             _Caller^.CallLine:=Line;
  279.             IF NOT InMainFile THEN
  280.             _Caller^._FileWhereFound:=IncludedFileName[FileNbr];
  281.       END
  282.       ELSE
  283.       BEGIN _u:=_Caller;
  284.             WHILE _u^._Next<>NIL DO _u:=_u^._Next;
  285.             New(_u^._Next);
  286.             _u:=_u^._Next;
  287.             FillChar(_u^,SizeOf(_u^),0);
  288.         _u^._Itself:=_f;
  289.             _u^._Next:=NIL;
  290.             _u^.CallLine:=Line;
  291.             IF NOT InMainFile THEN
  292.             _u^._FileWhereFound:=IncludedFileName[FileNbr];
  293.  
  294.       END
  295.     END
  296.     END; {AddCaller}
  297.  
  298.  
  299.     PROCEDURE Load(VAR NewWord: STRING; CurrentLine: WORD);
  300.     VAR n: WORD; There: WORD;
  301.     BEGIN  There:=InsertionPointOf(NewWord);
  302.        IF There<=DictionarySize THEN (* make room there *)
  303.        Move(Dictionary[There],
  304.         Dictionary[There+1],
  305.         4*(DictionarySize-There+1));
  306.            n:=(Length(NewWord))*2+1;
  307.        GetMem(Dictionary[There],n+DeclarationSize);
  308.            _LastDeclaration:=Dictionary[There];
  309.            FillChar(Dictionary[There]^,n+DeclarationSize,0);
  310.        WITH Dictionary[There]^ DO
  311.        BEGIN
  312.                  Move(NewWord,Body[0],n);
  313.                  IF NOT InMainFile THEN
  314.                  _FileWhereFound:=IncludedFileName[FileNbr];
  315.                  StartLine:=CurrentLine;
  316.                  Depth:=CurrentDepth;
  317.                  FunctionType:=WhatNext;
  318.        END;
  319.            AddFunction(Dictionary[There]);
  320.        Inc(DictionarySize);
  321.     END;
  322.  
  323.     PROCEDURE ProcessWord(c: CHAR);
  324.     VAR functionID: WORD;
  325.         _Caller,_Function: pDeclaration;
  326.       FUNCTION IsReturnValue: BOOLEAN;
  327.       BEGIN IsReturnValue:=FALSE;
  328.             WHILE (c<=' ') AND (BufPos<BufLength)
  329.             DO BEGIN Inc(BufPos); c:=Buffer[BufPos];
  330.                RestoreDelimiter:=TRUE;
  331.             END;
  332.             IF (c=':') AND (BufPos<BufLength) THEN
  333.             BEGIN
  334.               Inc(BufPos);
  335.               Delimiter:=Buffer[BufPos];
  336.               IsReturnValue:= Delimiter='=';
  337.             END;
  338.       END; {IsReturnValue}
  339.     BEGIN {ProcessWord}
  340.       IF CurrentWord='' THEN Exit;
  341.       RestoreDelimiter:=FALSE;
  342.       UCurrentWord:=Capitalize(CurrentWord);
  343.       IF (UCurrentWord='BEGIN') THEN
  344.          BEGIN IF FunctionBeginsNext THEN
  345.             (* it is the first BEGIN after a function
  346.                declaration, for which depth has already
  347.                been incremented, so do nothing other
  348.                than ... *)
  349.             FunctionBeginsNext:=FALSE
  350.             ELSE Inc(CurrentDepth)
  351.          END
  352.          ELSE
  353.       IF (UCurrentWord='CASE')
  354.       OR (UCurrentWord='RECORD') THEN Inc(CurrentDepth)
  355.      ELSE
  356.       IF (UCurrentWord='END') THEN
  357.          BEGIN  IF FunctionEnd THEN CloseFunction;
  358.                 Dec(CurrentDepth);
  359.          END
  360.          ELSE
  361.       IF (UCurrentWord='PROCEDURE') THEN
  362.      BEGIN WhatNext:=aProcedure;
  363.                Inc(CurrentDepth);
  364.                FunctionBeginsNext:=TRUE;
  365.      END
  366.      ELSE
  367.       IF (UCurrentWord='FUNCTION') THEN
  368.      BEGIN WhatNext:=aFunction;
  369.                Inc(CurrentDepth);
  370.                FunctionBeginsNext:=TRUE;
  371.      END
  372.          ELSE
  373.       IF (UCurrentWord='FORWARD') AND FunctionBeginsNext THEN
  374.          BEGIN (* last function was only a forward declaration,
  375.                   retrieve it and fix it *)
  376.                IF _LastDeclaration<>NIL THEN
  377.                WITH _LastDeclaration^ DO
  378.                BEGIN
  379.                     Forwarded:=TRUE;
  380.                     New(_FileWhereForwarded);
  381.                     _FileWhereForwarded^:=_FileWhereFound^;
  382.                     LineWhereForwarded:=StartLine;
  383.                     FunctionBeginsNext:=FALSE;
  384.                     Dec(CurrentDepth);
  385.                     Dec(CurrentFunctions);
  386.                END;
  387.          END
  388.          ELSE
  389.       IF (WhatNext=aProcedure) OR (WhatNext=aFunction) THEN
  390.      BEGIN
  391.            MakeID(CurrentWord,NewWord);
  392.                (* check if already in dictionary *)
  393.                Identify(_Function,_Caller,NewWord,CurrentDepth);
  394.                IF _Function=NIL THEN
  395.            Load(NewWord,CurrentLine)
  396.                ELSE WITH _Function^ DO
  397.                BEGIN StartLine:=CurrentLine;
  398.                      AddFunction(_Function);
  399.                      Depth:=CurrentDepth;
  400.                      IF InMainFile THEN
  401.                      _FileWhereFound:=NIL
  402.                      ELSE _FileWhereFound:=IncludedFileName[FileNbr];
  403.                      FunctionBeginsNext:=TRUE;
  404.                END;
  405.            WhatNext:=WhoKnows;
  406.      END
  407.       ELSE BEGIN
  408.          MakeID(CurrentWord,NewWord);
  409.                  Identify(_Function,_Caller,NewWord,CurrentDepth);
  410.                  IF _Caller<>NIL THEN
  411.                  BEGIN IF (_Caller^.FunctionType=aFunction)
  412.                         AND IsReturnValue
  413.                         THEN RestoreDelimiter:=TRUE
  414.                         ELSE
  415.                         AddCaller(_Caller,_Function,CurrentLine);
  416.                  END;
  417.          END;
  418.          CurrentWord:='';
  419.     END; {ProcessWord}
  420.  
  421.     PROCEDURE ProcessBuffer;
  422.     VAR ch: CHAR;
  423.     BEGIN WHILE BufPos<BufLength DO
  424.        BEGIN
  425.          IF RestoreDelimiter THEN
  426.          BEGIN ch:=Delimiter;
  427.                RestoreDelimiter:=FALSE;
  428.          END
  429.      ELSE
  430.      BEGIN Inc(BufPos); ch:=Buffer[BufPos];
  431.      END;
  432.      IF (State=inProgram) OR (State=StartCommentMaybe)
  433.          THEN
  434.      CASE (ch) OF
  435.           'A'..'Z','a'..'z','0'..'9','_':
  436.            CurrentWord:=CurrentWord+ch;
  437.           '*': ;
  438.           ELSE
  439.                   ProcessWord(ch);
  440.                   State:=inProgram;
  441.      END;
  442.          CASE ch OF
  443.            '''': CASE State OF
  444.             inProgram,StartCommentMaybe: State:=inQuotes;
  445.             inQuotes:  State:=inProgram
  446.                  END;
  447.        '{':  IF State=inProgram
  448.                 THEN BEGIN State:=inComment;
  449.                       CommentDelimiter:=Curly;
  450.                  END;
  451.        '}':  IF (State=inComment)
  452.                  AND (CommentDelimiter=Curly) THEN
  453.                  BEGIN State:=InProgram;
  454.                        IF IncludeFile THEN
  455.                        BEGIN
  456.                            EnterIncludedFile(InfileName)
  457.                        END;
  458.                  END;
  459.        '(':  IF State=inProgram THEN State:=StartCommentMaybe;
  460.            '*':  CASE State OF
  461.             StartCommentMaybe:
  462.                        BEGIN State:=inComment;
  463.                              CommentDelimiter:=Parenthesis
  464.                        END;
  465.             inComment: IF CommentDelimiter=Parenthesis
  466.                                THEN State:=EndCommentMaybe;
  467.             inProgram: ProcessWord(ch);
  468.                  END;
  469.        ')':  IF  (State=EndCommentMaybe)
  470.                  AND (CommentDelimiter=Parenthesis)
  471.                  THEN BEGIN
  472.                       State:=InProgram;
  473.                       IF IncludeFile THEN EnterIncludedFile(InfileName)
  474.                  END;
  475.            '$':  IF State=inComment THEN State:=PerhapsInclude;
  476.            'I','i':
  477.                  IF State=PerhapsInclude
  478.                  THEN BEGIN GetInfileName;
  479.                       (* into InfileName,
  480.                          and make IncludeFile true *)
  481.                       State:=inComment
  482.                  END;
  483.        #10:  BEGIN Inc(CurrentLine);
  484.                        GotoXy(1,1); write(CurrentLine:6);
  485.                  END;
  486.  
  487.     END;
  488.       END;
  489.       IF (State=inProgram) THEN ProcessWord(ch);
  490.     END; {ProcessBuffer}
  491.  
  492. BEGIN  {ReadDictionary}
  493.     CurrentFile:=@MainFile; InMainFile:=TRUE;
  494.     Reset(CurrentFile^);
  495.     CurrentWord:=''; RestoreDelimiter:=FALSE;
  496.     IncludeFile:=FALSE;
  497.     ClrScr;
  498.     WHILE NOT Eof(Mainfile) AND Continue DO
  499.     BEGIN Readln(CurrentFile^,Buffer);
  500.           BufPos:=0; BufLength:=Length(Buffer);
  501.           Inc(CurrentLine);
  502.           Gotoxy(1,1); write(CurrentLine:6);
  503.           ProcessBuffer;
  504.           IF Eof(CurrentFile^) AND NOT Eof(Mainfile)
  505.           THEN (* end of included file, so *)
  506.           ReenterMainfile;
  507.     END;
  508. END; {ReadDictionary}
  509.  
  510. PROCEDURE WriteIndex(VAR f: TEXT; fn: STRING;
  511.           LinesPerPage,Columns: INTEGER);
  512.  
  513. VAR Declaration,
  514.     PreviousCaller,
  515.     PreviousCallerFile,
  516.     PreviousClass,ClassName,Line,
  517.     FileName: STRING;
  518.     i: WORD;
  519.     n,Column,LineNo: INTEGER;
  520.    _u: pCaller; _f: pDeclaration;
  521.     PageFed: BOOLEAN;
  522.  
  523.     PROCEDURE NewLine;
  524.     BEGIN writeln(f);
  525.           IF LineNo=LinesPerPage THEN
  526.           BEGIN writeln(f,^L); PageFed:=TRUE; LineNo:=0;
  527.           END ELSE PageFed:=FALSE;
  528.           Inc(LineNo);
  529.     END;
  530.  
  531.     FUNCTION ClassFor(i: WORD): STRING;
  532.     VAR str: STRING; p: INTEGER;
  533.     BEGIN ClassFor:='';
  534.       str:=Dictionary[i]^.Body;
  535.       IF str[1]='_' THEN
  536.       BEGIN Delete(str,1,1);
  537.         p:=Pos('_',str);
  538.         IF p>0 THEN ClassFor:=Copy(str,1,p-1)
  539.       END;
  540.     END;
  541.  
  542.     FUNCTION HeaderFor(i: WORD): STRING;
  543.     VAR n: INTEGER; Declaration: STRING; Start,Fin: STRING[5];
  544.     BEGIN WITH Dictionary[i]^ DO
  545.     BEGIN  n:=Length(Body); Move(Body[n+1],Declaration[1],n);
  546.       Declaration[0]:=CHR(n);
  547.       IF Length(Declaration)>Columns-8 THEN
  548.       BEGIN Declaration[0]:=CHR(Columns-8);
  549.             Declaration[Length(Declaration)]:='*'
  550.       END;
  551.       Str(StartLine,Start); Str(EndLine,Fin);
  552.       IF FunctionType=aProcedure
  553.       THEN Declaration:=Declaration+' PROCEDURE'
  554.       ELSE
  555.       IF FunctionType=aFunction
  556.       THEN Declaration:=Declaration+' FUNCTION';
  557.       IF _FileWhereFound<>NIL
  558.       THEN Declaration:=Declaration+' in '+_FileWhereFound^;
  559.       Declaration:=Declaration+' ('+Start+'..'+Fin+')';
  560.       IF Forwarded THEN
  561.       BEGIN Str(LineWhereForwarded,Start);
  562.           Declaration:=Declaration+' forward in '+_FileWhereForwarded^
  563.                        +' ('+Start+')';
  564.       END;
  565.     END;
  566.     HeaderFor:=Declaration;
  567.   END;
  568.  
  569.   FUNCTION FileFor(i: INTEGER): STRING;
  570.   BEGIN WITH Dictionary[i]^ DO
  571.   BEGIN IF _FileWhereFound=NIL THEN FileFor:=''
  572.         ELSE FileFor:=_FileWhereFound^
  573.   END;
  574.   END;
  575.  
  576.   PROCEDURE WriteCaller(i: INTEGER);
  577.   VAR _u: pCaller; n: INTEGER;
  578.       CallerName,FileName: STRING;
  579.       FeedLine: BOOLEAN;
  580.       PROCEDURE WriteClassNameContinued;
  581.       BEGIN
  582.         IF ClassName<>'' THEN
  583.     BEGIN write(f,'Class '+ClassName);
  584.               IF ClassName=PreviousClass THEN write(f,' continued');
  585.               NewLine;
  586.     END;
  587.       END;
  588.       PROCEDURE WriteDeclaration;
  589.       BEGIN write(f,Declaration); NewLine
  590.       END;
  591.       PROCEDURE WriteCaller;
  592.       BEGIN
  593.            write(f,'':5,CallerName); Column:=5+Length(CallerName);
  594.       END;
  595.   BEGIN
  596.     WITH Dictionary[i]^ DO
  597.     BEGIN IF _Caller=NIL THEN
  598.       BEGIN write(f,' **UNUSED**');
  599.       END ELSE
  600.       BEGIN PreviousCaller:='';
  601.             FeedLine:=FALSE;
  602.         _u:=_Caller;
  603.         WHILE _u<>NIL DO
  604.         BEGIN WITH _u^ DO
  605.        BEGIN
  606.          n:=Length(_Itself^.Body);
  607.              Move(_Itself^.Body[n+1],CallerName[1],n);
  608.          CallerName[0]:=CHR(n);
  609.              IF Length(CallerName)>Columns-10 THEN
  610.              BEGIN CallerName[0]:=CHR(Columns-10);
  611.                    CallerName[Length(CallerName)]:='*';
  612.              END;
  613.  
  614.              IF CallerName<>PreviousCaller THEN
  615.              BEGIN PreviousCaller:=CallerName;
  616.                    PreviousCallerFile:='?';
  617.                    FeedLine:=TRUE;
  618.              END;
  619.              IF _FileWhereFound=NIL THEN FileName:=''
  620.              ELSE FileName:=_FileWhereFound^;
  621.              Str(CallLine,Line);
  622.              IF _FileWhereFound=NIL THEN FileName:=''
  623.              ELSE FileName:=_FileWhereFound^;
  624.              IF FileName<>PreviousCallerFile THEN
  625.              BEGIN IF FileName<>'' THEN
  626.                    Line:='in '+FileName+': '+Line;
  627.                    PreviousCallerFile:=FileName;
  628.              END;
  629.              IF FeedLine THEN
  630.              BEGIN NewLine; FeedLine:=FALSE;
  631.            IF PageFed THEN
  632.            BEGIN WriteClassNameContinued;
  633.                          WriteDeclaration;
  634.                    END;
  635.                    WriteCaller;
  636.              END;
  637.  
  638.              IF Column+Length(Line)+1>Columns THEN
  639.              BEGIN NewLine;
  640.                    IF PageFed THEN
  641.            BEGIN WriteClassNameContinued;
  642.                  WriteDeclaration;
  643.                          WriteCaller;
  644.                    END;
  645.                    write(f,'':8); Column:=8
  646.              END;
  647.  
  648.              Write(f,' '+Line); Inc(Column,Length(Line)+1);
  649.            END;
  650.            _u:=_u^._Next;
  651.         END;
  652.       END;
  653.   END;
  654.   END;
  655.  
  656. BEGIN {WriteIndex}
  657.       writeln(f,fn); writeln(f);
  658.       LineNo:=3;
  659.       PreviousClass:='';
  660.       FOR i:=1 TO DictionarySize DO
  661.       BEGIN Declaration:=HeaderFor(i);
  662.         ClassName:=ClassFor(i);
  663.             FileName:=FileFor(i);
  664.         IF (ClassName<>'') AND (ClassName<>PreviousClass)
  665.         THEN
  666.         BEGIN NewLine;
  667.                   writeln(f,'Class '+ClassName);
  668.           PreviousClass:=ClassName;
  669.         END;
  670.         write(f,Declaration);
  671.             writeCaller(i);
  672.             NewLine;
  673.       END;
  674. END;
  675.  
  676.  
  677. PROCEDURE GetParameters(VAR p1,p2: STRING; VAR Lin,Col: INTEGER);
  678. VAR st: ARRAY[1..4] OF STRING; i,n: INTEGER;
  679.  
  680.    FUNCTION FileExists(VAR fn: STRING): BOOLEAN;
  681.    VAR f: FILE; Exists: BOOLEAN;
  682.    BEGIN Assign(f,fn); {$I-} Reset(f); {$I+};
  683.          Exists:=IOResult=0;
  684.          (* writeln;
  685.          write(fn,' reset and ');
  686.          IF Exists THEN writeln('found.') ELSE writeln(' not found.');
  687.          *)
  688.          IF Exists THEN BEGIN FileExists:=TRUE; Close(f) END
  689.          ELSE FileExists:=FALSE;
  690.    END;
  691.    FUNCTION GetInt(str: STRING): INTEGER;
  692.    VAR n: LONGINT; ErrPos: INTEGER;
  693.    BEGIN WHILE (str<>'') AND NOT (str[1] IN ['0'..'9']) DO
  694.          Delete(str,1,1);
  695.          Val(str,n,ErrPos);
  696.          IF ErrPos=0 THEN Getint:=-Maxint ELSE GetInt:=n;
  697.    END;
  698.  
  699.    PROCEDURE AppendPasTo(VAR str: STRING);
  700.    VAR DotPos: INTEGER;
  701.    BEGIN
  702.          DotPos:=Length(str)+1;
  703.          REPEAT Dec(DotPos);
  704.          UNTIL (str[Dotpos]='.')
  705.          OR (DotPos=Length(str)-3)
  706.          OR (DotPos=1);
  707.          IF str[DotPos]<>'.' THEN str:=str+'.PAS';
  708.    END;
  709.    PROCEDURE GetProgramFile;
  710.    VAR fn: STRING; Ask, Abort: BOOLEAN;
  711.    BEGIN Abort:=FALSE;
  712.       IF (st[1]='') THEN Ask:=TRUE ELSE
  713.       BEGIN p1:=st[1];  AppendPasTo(p1);
  714.          Ask:=NOT FileExists(p1);
  715.          IF Ask THEN writeln('Program file '+p1+' not found.')
  716.       END;
  717.       WHILE Ask DO
  718.       BEGIN
  719.          write('Program is in file: ');
  720.          readln(p1);
  721.          IF (p1='') THEN BEGIN Abort:=TRUE; Ask:=FALSE END ELSE
  722.          BEGIN AppendPasTo(p1);
  723.            IF FileExists(p1) THEN Ask:=FALSE
  724.            ELSE
  725.            writeln('Program file '+p1+' not found.')
  726.          END
  727.       END;
  728.       IF Abort THEN Halt;
  729.       p1:=Capitalize(p1);
  730.    END;
  731.    FUNCTION OKtoOverwrite(VAR fn: STRING): BOOLEAN;
  732.    VAR Answer: STRING; Done: BOOLEAN; i: INTEGER;
  733.    BEGIN IF NOT FileExists(fn) THEN
  734.          OKtoOverwrite:=TRUE
  735.          ELSE BEGIN  OKtoOverwrite:=FALSE;
  736.          write('Overwrite '+fn+' (y/n)? '); Done:=FALSE;
  737.          REPEAT Readln(Answer); Answer:=Capitalize(Answer);
  738.                 IF (Answer='Y') OR (Answer='YES')
  739.                 THEN BEGIN OKtoOverwrite:=TRUE; Done:=TRUE
  740.                      END ELSE
  741.                 IF (Answer='N') OR (Answer='NO')
  742.                 THEN BEGIN OKtoOverwrite:=FALSE; Done:=TRUE
  743.                 END
  744.                 ELSE
  745.                 BEGIN GotoXY(Length(fn)+19,WhereY-1);
  746.                       ClrEol;
  747.                 END;
  748.          UNTIL Done;
  749.          END;
  750.    END;
  751.    PROCEDURE GetIndexFile;
  752.    VAR Ask,Abort: BOOLEAN;
  753.    BEGIN
  754.       IF (st[2]='') THEN Ask:=TRUE ELSE
  755.       BEGIN p2:=st[2];
  756.             Ask:=NOT OKtoOverwrite(p2);
  757.       END;
  758.       Abort:=FALSE;
  759.       WHILE Ask DO
  760.       BEGIN
  761.             write('Index goes to file: ');
  762.             readln(p2);
  763.             IF p2='' THEN BEGIN Abort:=TRUE; Ask:=FALSE END
  764.             ELSE
  765.             Ask:=NOT OKtoOverwrite(p2);
  766.       END;
  767.       IF Abort THEN Halt;
  768.    END;
  769.    PROCEDURE GetLinesPerPage;
  770.    BEGIN Lin:=DefaultLines;
  771.          CASE n OF
  772.            3:    IF Upcase(st[3][1])='L' THEN Lin:=GetInt(st[3]);
  773.            4:    IF Upcase(st[3][1])='L' THEN Lin:=GetInt(st[3])
  774.                  ELSE
  775.                  IF Upcase(st[4][1])='L' THEN Lin:=GetInt(st[4]);
  776.          END;
  777.          IF ((Lin<10) OR (Lin>132)) AND (Lin<>0) THEN Lin:=DefaultLines
  778.    END;
  779.    PROCEDURE GetColumns;
  780.    BEGIN Col:=DefaultColumns;
  781.          CASE n OF
  782.            3:    IF Upcase(st[3][1])='C' THEN Col:=GetInt(st[3]);
  783.            4:    IF Upcase(st[3][1])='C' THEN Col:=GetInt(st[3])
  784.                  ELSE
  785.                  IF Upcase(st[4][1])='C' THEN Col:=GetInt(st[4]);
  786.          END;
  787.          IF (Col<50) OR (Col>132) THEN Col:=DefaultColumns
  788.    END;
  789.  
  790. BEGIN n:=ParamCount;
  791.     FOR i:=1 TO 4 DO st[i]:='';
  792.     FOR i:=1 TO n DO st[i]:=ParamStr(i);
  793.     GetProgramFile;
  794.     GetIndexFile;
  795.     GetLinesPerPage;
  796.     GetColumns;
  797. END;
  798.  
  799. VAR ProgramFile: aCharFile;
  800.     IndexFile: TEXT;
  801.     IndexName: STRING;
  802.     LinesPerPage,Columns: INTEGER;
  803. BEGIN writeln; writeln;
  804.       GetParameters(ProgramName,IndexName,LinesPerPage,Columns);
  805.       DictionarySize:=0;
  806.       Continue:=TRUE; (* unused so far, always true *)
  807.       MakeMain;
  808.       FileNbr:=0; (* index to included files: none yet *)
  809.  
  810.  
  811.       Assign(ProgramFile,ProgramName); reset(ProgramFile);
  812.       writeln;
  813.  
  814.       CurrentDepth:=1;
  815.       WhatNext:=WhoKnows;
  816.       CurrentLine:=0;
  817.       FunctionBeginsNext:=FALSE;
  818.       CurrentFunctions:=1;
  819.       State:=inProgram;
  820.  
  821.       ReadDictionaryFrom(ProgramFile);
  822.       Close(ProgramFile);
  823.       Assign(IndexFile,IndexName);
  824.       Rewrite(IndexFile);
  825.       WriteIndex(IndexFile,ProgramName,LinesPerPage,Columns);
  826.       Close(IndexFile);
  827. END.