PROGRAM INDEXIT;
{-------------------------------------------------------}
{Purpose  : Find procedure and function declarations and}
{           output them in alphabetical order to a file }
{Inputs   : File to be indexed.                         }
{Outputs  : File of procedures and functions            }
{CREATED  : Jan 31, 1981 NJL                            } 
{MODIFIED : Jul 26, 1982 Steve Clamage                  }
{	    Recognizes lower case, and outputs declar-	}
{	    ations extending over more than one line.	}
{NOTE	  : Keywords must be all upper or all lower case}
{-------------------------------------------------------}

TYPE
  LSTRINGP = ^LSTRING;
  FILENAME = STRING;
  RWFILE = (RESETT,REWRITTE);
  SUM_TYPE = (SHORT,LONG);
  LSTRING = STRING[132];
  SSTRING = STRING[10];

VAR
  FIN : TEXT;
  FOUT : TEXT;
  NAME : FILENAME;
  STR : LSTRING;
  STRP : LSTRINGP;
  I : INTEGER;
  LINES : INTEGER;
  PROGFLG : BOOLEAN;
  SUMMARY : SUM_TYPE;
  LOC : INTEGER;

EXTERNAL FUNCTION @BDOS(FUNC, PARM: INTEGER): INTEGER;
EXTERNAL FUNCTION @CMD: LSTRINGP;
EXTERNAL PROCEDURE @HLT;


FUNCTION KEYPRESSED: BOOLEAN;
BEGIN
  KEYPRESSED := (@BDOS(11,0) <> 0)
END;


PROCEDURE ABORT;
BEGIN
  WRITELN;
  WRITELN('Pascal/MT+ Program Index utility aborted from console');
  @HLT
END;


FUNCTION DOFILE(VAR F: TEXT; RW: RWFILE; NAME: FILENAME): BOOLEAN;
{---------------------------------------------------------------}
{ Purpose: Attempt to reset or rewrite the given file. Check    }
{          IORESULT.                                            }
{ Inputs:  File, whether to reset or rewrite, and name of file.	}
{ Outputs: File open for reading or writing. True if successful,}
{          false if not successful.                             }
{ Last Mod:                                                     }
{---------------------------------------------------------------}
BEGIN
  ASSIGN(F,NAME);
  IF RW = RESETT THEN
    RESET(F)
  ELSE
    REWRITE(F);
  IF IORESULT = 255 THEN
    DOFILE := FALSE
  ELSE
    DOFILE := TRUE;
END;

PROCEDURE GETNAME(VAR OUTSTR : FILENAME);
{---------------------------------------------------------------}
{ Purpose: Read a name from the keyboard, return in STR.        }
{ Inputs:  CPMCMDBUF.                                           }
{ Outputs: STR contains name of file if it was given to start.  }
{ Last Mod: 11/23/80                                            }
{---------------------------------------------------------------}
BEGIN
  WHILE (LENGTH(STR) <> 0) AND (STR[1] = ' ') DO
    DELETE(STR,1,1);            (* DELETE LEADING BLANKS *)
  OUTSTR := '';
  WHILE (LENGTH(STR) <> 0) AND (STR[1] <> ' ') DO
    BEGIN
      OUTSTR := CONCAT(OUTSTR,STR[1]);
      DELETE(STR,1,1)
    END;
END;


FUNCTION STRIPBLNKS(S: LSTRING):INTEGER;
VAR
  I : INTEGER;
BEGIN
  STRIPBLNKS := 0;
  I := 1;
  WHILE (S[I] = ' ') AND (I <= LENGTH(S)) DO
    I := I + 1;
  IF I <= LENGTH(S) THEN
    STRIPBLNKS := I;
END;

FUNCTION PRESENT(KEYWORD: SSTRING; STR: LSTRING): BOOLEAN; 
{---------------------------------------------------------------}
{ Purpose: Return true if the string KEYWORD                    }
{          the first string on the input line STR.  If it is    }
{          not first or is  not present return false.           }
{ Inputs:  KEYWORD,STR.                                         }
{ Outputs: Function return value of true or false.              }
{ Last Mod: 26 July 82, accept lowercase                        }
{---------------------------------------------------------------}
  PROCEDURE LOWER(VAR STR: SSTRING);
  VAR
    I: INTEGER;
  BEGIN {convert uppercase alphabetic string to lowercase}
    FOR I:=1 TO LENGTH(STR) DO
      STR[I] := CHR( ORD(STR[I]) + (ORD('a') - ORD('A')) )
  END;

BEGIN
  PRESENT := FALSE;
  LOC := STRIPBLNKS(STR);	(* LOCATION OF KEYWORD *)
  IF LOC <> 0 THEN
    IF POS(KEYWORD,STR) = LOC THEN
      PRESENT := TRUE
    ELSE
      BEGIN
        LOWER(KEYWORD);		(* NOTE: ALL UPPER OR ALL LOWERCASE ONLY! *)
        IF POS(KEYWORD,STR) = LOC THEN
          PRESENT := TRUE;
      END
END;


PROCEDURE PROGRESS;
{ Mark progress every 16 lines on the screen }
BEGIN
  LINES := LINES + 1;
  IF (LINES & $0F) = 0 THEN WRITE('.')
END;


PROCEDURE BALPAR(STR: LSTRING);
{Copy procedure header through any balanced parens,	}
{  including succeeding lines as necessary.		}
VAR
  I : INTEGER;
  LEVEL: BYTE;
  CH: CHAR;

  FUNCTION NEXTCHAR: CHAR;
  {Return next char from string, get new line if needed}
  BEGIN
    I := I + 1;
    IF I > LENGTH(STR) THEN
    BEGIN
      READLN(FIN,STR);
      WRITELN(FOUT,STR);
      PROGRESS;
      I := 1
    END;
    NEXTCHAR := STR[I]
  END;

BEGIN {BALPAR}
  I := LOC;		(* START AT KEYWORD *)
  REPEAT
    CH := NEXTCHAR;
  UNTIL (CH = '(') OR (CH = ';');
  IF CH = '(' THEN	(* NEED TO BALANCE OUT PARENS *)
    BEGIN
      LEVEL := 1;	(* NESTING DEPTH *)
      REPEAT
	CH := NEXTCHAR;
	CASE CH OF
	  '(' : LEVEL := LEVEL + 1;
	  ')' : LEVEL := LEVEL - 1;
	END
      UNTIL ((LEVEL = 0) AND (CH = ';')) OR EOF(FIN)
    END
END;


PROCEDURE TRANSFER(VAR STR : LSTRING);
{---------------------------------------------------------------}
{ Purpose: Transfer lines from FIN to FOUT until the next proc/ }
{          func or begin is encountered.                        }
{ Inputs:  STR contains the line with the PROC, FUNC or PROG def}
{          FIN provides the text.                               }
{ Outputs: STR contains the line containing a PROC, FUNC def or }
{          a begin. FOUT contains new text.                     }
{ Last Mod: 26 July 1982
{---------------------------------------------------------------}
VAR 
  DONE : BOOLEAN;
  TSTRING : LSTRING;

BEGIN
  DONE := FALSE;
  WRITELN(FOUT,STR);
  IF SUMMARY = LONG THEN
  REPEAT
    READLN(FIN,TSTRING);
    PROGRESS;
    IF KEYPRESSED THEN
      ABORT;
    IF (PRESENT('BEGIN',TSTRING)) THEN
         DONE := TRUE
    ELSE
      BEGIN
        IF PRESENT('FUNCTION',TSTRING)
        OR PRESENT('PROCEDURE',TSTRING) THEN
          BEGIN
            WRITELN(FOUT);
            WRITELN(FOUT);
          END;
        WRITELN(FOUT,TSTRING) 
      END
  UNTIL EOF(FIN) OR DONE;

  IF SUMMARY = SHORT THEN
    BEGIN
      BALPAR(STR);	(* COPY THRU BALANCED PARENS, IF ANY *)
      READLN(FIN,TSTRING);
      PROGRESS;
    END;

(*IF KEYPRESSED THEN
    ABORT;              *)
  STR := TSTRING;  
  IF SUMMARY = LONG THEN
    WRITELN(FOUT);
  WRITELN(FOUT);
END;


PROCEDURE DOINDEX;
VAR
  I : INTEGER;
  STR : STRING;
BEGIN
  WHILE NOT EOF(FIN) DO
    BEGIN
      IF PRESENT('PROCEDURE',STR)
      OR PRESENT('FUNCTION',STR) THEN
        TRANSFER(STR);
      READLN(FIN,STR);
      PROGRESS;
      IF KEYPRESSED THEN
        ABORT;
    END;
END;

        
  

BEGIN
  STRP := @CMD;                 (* COPY COMMAND TAIL *)
  STR := STRP^;

  WRITELN('Pascal/MT+ Program Index Utility -- Release 5.2');
  WRITELN('Copyright (c) 1981 by MT MicroSYSTEMS');
  WRITELN;

  GETNAME(NAME);
  WRITELN('Reading text from:  ',NAME);
  IF DOFILE(FIN,RESETT,NAME) THEN
  BEGIN
    GETNAME(NAME);
    WRITELN('Output directed to: ',NAME);
    IF DOFILE(FOUT,REWRITTE,NAME) THEN
      BEGIN
      GETNAME(NAME);
      IF (LENGTH(NAME) > 0) AND (NAME[1] = 'L') THEN
        BEGIN
        SUMMARY := LONG;
	NAME := 'LONG'
        END
      ELSE
        BEGIN
        SUMMARY := SHORT;
	NAME := 'SHORT'
        END;
      WRITELN('Summary form:       ', NAME);
      READLN(FIN,STR);
      LINES := 1;
      DOINDEX;
      CLOSE(FOUT,I);
      WRITELN;
      WRITELN('Pascal/MT+ Program Index utility processing complete');
      END
    ELSE
      WRITELN('Cannot create ',NAME)
    END
  ELSE
    WRITELN('Cannot open ',NAME);
END.
