home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
walnut-creek-CDROM
/
CPM
/
TURBOPAS
/
TYPEX.PZS
/
TYPEX.PAS
Wrap
Pascal/Delphi Source File
|
2000-06-30
|
27KB
|
845 lines
{$U-}
{$C-}
{
TYPEX.PAS Jim Mischel, June 1, 1986
Program listing and variable cross-reference generator for
Turbo Pascal programs.
Usage is TYPEX <source> [<destination>] [;<options>]
Options are: I - INCLUDE files also
X - Create program Cross-reference
Defaults:
Output - LST:
Includes - NO
Xref - NO
If memory size is a consideration, INITIALIZE, PROCESS_FILE, and PRINT_XREF
can be made overlay procedures, with a savings of approximately 2.5K bytes.
This program evolved from LISTER.PAS that was included on the Turbo Pascal
distribution disk. Some of the original code still exists.
The procedure GETDATE may have to be changed for use with MS-DOS.
It will NOT work with CP/M 2.2 without modification. It will work
with MP/M, CP/M 3.x, and TurboDOS 1.3 or higher.
This program was written using Turbo Pascal version 3.0 for CP/M. I have
not tested it on any other operating system, though it should work except
as noted above.
MODIFICATIONS:
06/01/86 - jim - Initial coding.
10/21/86 - jim - Use a pointer-reversal in PRINT_REFS in place of the
recurrsive list traversal.
11/30/86 - jim - Make the tree a right in-threaded tree. This speeds
printing of the cross-reference.
Add the FSTPTR field to the node record. References are
now added in order of occurance. FSTPTR points to the
first reference record, and NXTPTR points to the last.
Also added NUMREFS to the record to prevent having
to scan the list twice. PRINT_REFS is now a simple linked
list traversal procedure.
}
program typex;
const
version_no = '2.5';
printwidth = 70; { print width for each line }
printlength = 55; { # of lines to print on each page }
pathlength = 14; { maximum length of file name }
default_output = 'LST:'; { default destination }
include_default = false; { default to no include files }
xref_default = false; { default to no cross-reference }
refs_per_line = 10; { max. number of references per line }
max_id_len = 15; { max. id length for references on same line }
optchr = ';'; { option seperator character }
type
filename = string[pathlength];
string8 = string[8];
string255 = string[255];
strptr = ^string255;
refptr = ^reference;
reference = record { item reference record }
line, { source line of reference }
incl : integer; { line in include file (if any) }
nxtptr : refptr; { pointer to next reference }
end;
itmptr = ^item;
item = record
idname : strptr; { pointer to id name }
left, { left node of binary tree }
right : itmptr; { right node of binary tree }
rthrd : boolean; { TRUE if right is thread pointer }
fstptr, { pointer to first reference }
nxtptr : refptr; { pointer to last reference }
numrefs : integer; { Reference counter. This is NOT a
count of references to this ID. It
is used by PRINT_REFS to figure out
how many lines it will take to print
all the references for this item. }
end;
var
page_no, { current page number }
currow : integer; { current row in output file }
outfile, { listing file }
mainfile : text; { source file }
mainfilename : filename; { input file name }
search : array[1..4] of string[4]; { search strings for includes }
date, { date returned from get_date }
time : string8; { time returned from get_date }
dots : string[70]; { line of dots for page header }
xref, { TRUE = generate cross-reference }
includes : boolean; { TRUE = process include files }
xref_head : itmptr; { root of cross-reference tree }
{ PAGE - move output to new page }
procedure page(var outfile : text);
const
ff = ^L;
begin
write(outfile,ff);
end;
{ HEADINGS - move to new page and print headings. }
procedure headings;
begin
page(outfile);
page_no := page_no + 1;
write(outfile,date:8);
write(outfile,mainfilename:39);
writeln(outfile,time:33);
writeln(outfile,dots,'Page ',page_no:5);
writeln(outfile);
currow := 0;
end; { headings }
{ OPEN - open file FP with name NAME. Return TRUE if operation successful. }
function open(var fp : text; name : filename) : boolean;
begin
assign(fp,name);
{$i- turn off I/O error checking}
reset(fp);
{$i+ error checking back on}
if ioresult <> 0 then
begin
open := false;
close(fp);
end
else
open := true;
end { open };
{ INITIALIZE - set parameters and open files }
procedure initialize;
{ GET_DATE - get date and time from system and convert to two strings.
Date is stored as MM/DD/YY. Time is stored as HH:MM:SS,
with seconds set to 00.
This routine will not work for dates prior to 01/01/78
}
procedure get_date(var date_ptr,time_ptr);
type
month_array = array[1..2,1..12] of integer;
string8 = string[8];
var
date : string8 absolute date_ptr;
time : string8 absolute time_ptr;
date_time : packed array [1..4] of char;
jdate : integer absolute date_time; { #days since 12/31/77 }
x,
month : byte;
year : integer;
const
day_table : month_array =
((31,59,90,120,151,181,212,243,273,304,334,365),
(31,60,91,121,152,182,213,244,274,305,335,366));
{ LEAP - return TRUE if YEAR is a leap year }
function leap(year : integer) : boolean;
begin
leap := (year mod 4 = 0) and (year <> 100);
end; {leap}
{ DAYS_IN - return number of days in YEAR }
function days_in(year : integer) : integer;
begin
if (leap(year)) then days_in := 366
else days_in := 365;
end; {days_in}
begin
bdos(105,addr(date_time)); { get system date/time }
time := '00:00:00'; { initialize time }
time[1] := chr((ord(date_time[3]) div 16) + 48); { hours first digit }
time[2] := chr((ord(date_time[3]) mod 16) + 48); { second digit }
time[4] := chr((ord(date_time[4]) div 16) + 48); { minutes first digit }
time[5] := chr((ord(date_time[4]) mod 16) + 48); { second digit }
year := 78;
while (jdate > days_in(year)) do
begin
jdate := jdate-days_in(year);
year := year + 1;
end;
if (leap(year)) then x := 2 { set proper date table }
else x := 1;
month := 1;
while (jdate > day_table[x,month]) do { move us to the proper month }
month := month + 1;
if (month > 1) then
jdate := jdate - day_table[x,month-1]; { and set the date }
date := '00/00/00';
date[1] := chr(month div 10 + 48); { month first digit }
date[2] := chr(month mod 10 + 48); { second digit }
date[4] := chr(jdate div 10 + 48); { day first digit }
date[5] := chr(jdate mod 10 + 48); { day second digit }
date[7] := chr(year div 10 + 48); { year first digit }
date[8] := chr(year mod 10 + 48); { second digit }
end; { get_date }
{ PRINTUSE - print usage information and exit }
procedure printuse;
begin
writeln;
writeln('Turbo Pascal program listing and variable Cross-reference generator');
writeln;
writeln('Usage is TYPEX <source> [<destination>] [',optchr:1,'<options>]');
writeln(' Options are: I - INCLUDE files also');
writeln(' X - Create program Cross-reference');
write (' DEFAULTS: Output - ');
writeln(default_output);
write (' Includes - ');
if include_default then
writeln('YES')
else
writeln('NO');
write (' Xref - ');
if xref_default then
writeln('YES')
else
writeln('NO');
halt;
end; { printuse }
{ OPENMAIN - Open main input and output files. Set XREF and INCLUDE options. }
procedure openmain;
var
tmpstr,
option_string : string[32];
param : byte;
outfilename : filename; { output file name }
function get_param(var param : byte) : string255;
var
x : byte;
begin
if (length(tmpstr) > 0) then
begin { there's an option string here }
get_param := tmpstr;
tmpstr := '';
end
else
if (param > paramcount) then
get_param := '' { no more parameters }
else
begin
tmpstr := paramstr(param); { get next parameter }
param := param+1; { bump parameter count }
x := pos(optchr,tmpstr);
if (x > 1) then { see if it's an option string }
begin
get_param := copy(tmpstr,1,x-1); { this is the returned parameter }
tmpstr := copy(tmpstr,x,length(tmpstr)-x+1); { save this for next time }
end
else
begin
get_param := tmpstr; { return this }
tmpstr := ''; { nothing saved }
end;
end;
end; { get_param }
begin { openmain }
if (paramcount = 0) then
printuse;
includes := include_default; { set default parameters }
xref := xref_default;
tmpstr := '';
option_string := '';
param := 1;
mainfilename := get_param(param); { get input file name }
if not (open(mainfile,mainfilename)) then
begin
writeln('ERROR - cannot open input file ',mainfilename);
halt;
end;
outfilename := get_param(param); { get output file name and options }
if (length(outfilename) > 0) then
if (outfilename[1] = optchr) then
begin
option_string := outfilename; { options }
outfilename := default_output; { but no defined file name }
end
else
option_string := get_param(param) { get options (if any) }
else
begin
option_string := ''; { no options }
outfilename := default_output; { no defined file name }
end;
assign(outfile,outfilename);
{$I-}
rewrite(outfile);
{$I+}
if (ioresult <> 0) then
begin
writeln('ERROR - cannot open output file ',outfilename);
halt;
end;
if (pos(optchr,option_string) = 1) then
begin { set options }
includes := (include_default xor (pos('I',option_string) > 0));
xref := (xref_default xor (pos('X',option_string) > 0));
end;
end {openmain};
begin {initialize}
openmain; { open files and get options }
get_date(date,time); { get date and time for headings }
fillchar(dots,sizeof(dots),'.');
dots[0] := chr(70); { set length of dot line }
search[1] := '{$'+'i';
search[2] := '{$'+'I';
search[3] := '(*$'+'i'; { setup search strings for includes }
search[4] := '(*$'+'I';
page_no := 0;
headings;
xref_head := nil;
end; {initialize}
{
PROCESS_FILE - print each line of the input file and INCLUDED files,
if requested. Create cross-reference records for each variable
if requested.
}
procedure process_file;
var
linebuffer : strptr;
line_no, { current line number in input file }
include_line : integer; { line number in include file }
including, { TRUE = processing include file }
quote : boolean; { quote flag }
comment_type : byte; { type of comment being processed:
0 = no comment
1 = '{'-type comment
2 = '(*'-type comment }
{ INCLUDEIN - return TRUE if there is an INCLUDE statement in the current line }
function includein(curstr : strptr) : boolean;
var
x,
column : byte;
begin
x := 0;
column := 0;
repeat
x := x+1;
column := pos(search[x],curstr^);
until (x = 4) or (column > 0);
if (column = 0) then
includein := false
else
includein := not (curstr^[column+length(search[x])] in ['-','+']);
end; {includein}
{ PROCESS_LINE - write PRINTSTR to the output file, updating work_line.
If cross-referencing, generate XREF records for each
item found in PRINTSTR }
procedure process_line(printstr : strptr; var work_line : integer);
var
x : byte;
{ XREF_LINE - create reference records for each item found in PRINTSTR }
procedure xref_line;
var
x : byte;
wkstr : string255;
ch : char;
{
ADD_TREE - add a reference to the tree. If WKSTR is not in the tree,
create a new node for it.
}
procedure add_tree(var tree : itmptr);
var
q,p : itmptr;
less,
found : boolean;
{ MAKETREE - create a new tree node. }
function maketree : itmptr;
var
p : itmptr;
begin {maketree}
new(p);
with p^ do
begin
getmem(idname,length(wkstr)+1); { allocate just enough for IDNAME }
idname^ := wkstr;
if (length(idname^) < max_id_len) then
numrefs := 0
else
numrefs := refs_per_line;
left := nil;
right := nil;
rthrd := false;
nxtptr := nil; { set reference pointer }
fstptr := nil;
end;
maketree := p;
end; {maketree}
procedure setleft(p : itmptr);
var
q : itmptr;
begin {setleft}
q := maketree;
p^.left := q;
q^.right := p; { inorder successor of q is p }
q^.rthrd := true;
end; {setleft}
procedure setright(p : itmptr);
var
q : itmptr;
begin {setright}
q := maketree;
q^.right := p^.right; { inorder successor of q is successor of p }
q^.rthrd := p^.rthrd; { may or may not be thread pointer }
p^.right := q;
p^.rthrd := false;
end; {setright}
procedure add_ref(p : itmptr; line_no,include_line : integer);
var
r : refptr;
begin {add_ref}
new(r); { create a new reference record }
with r^ do
begin
line := line_no;
incl := include_line;
nxtptr := nil;
end;
with p^ do
begin
if (fstptr = nil) then { if first reference for this record }
fstptr := r { setup list head pointer }
else
nxtptr^.nxtptr := r; { link previous last ref to new }
nxtptr := r; { point to last }
if (include_line > 0) then { update reference counter }
numrefs := numrefs+2 { INCLUDEs take 2 spaces }
else
numrefs := numrefs+1;
end;
end; {add_ref}
begin {add_tree}
if tree = nil then
begin { nothing in the tree }
tree := maketree; { so we'll make it }
p := tree;
end
else
begin
q := tree;
p := tree;
found := false;
while (q <> nil) and not found do { search the tree }
begin
p := q;
if (p^.idname^ = wkstr) then
found := true { found it }
else
begin
less := (wkstr < p^.idname^);
if (less) then
q := p^.left
else
if (p^.rthrd) then
q := nil
else
q := p^.right;
end;
end;
if (not found) then { not found, create a new node }
if (less) then
begin
setleft(p);
p := p^.left;
end
else
begin
setright(p);
p := p^.right;
end;
end;
add_ref(p,line_no,include_line); { create a new reference record }
end; {add_tree}
{ GETCHR - get the next character in the line. Return 0 at end of line }
procedure getchr;
begin
if (x = 0) or (x > length(printstr^)) then
x := 0 { end of line }
else
begin
ch := upcase(printstr^[x]); { convert to uppercase for xref }
x := x+1;
end;
end;
{ KEYWORD - return TRUE if WKSTR is in the key word table.
This is a simple binary search }
function keyword : boolean;
const
nkwords = 44; { number of key words in table }
type
key_word_table= array[1..nkwords] of string[9];
const
key_words : key_word_table =
('ABSOLUTE' ,'AND' ,'ARRAY' ,'BEGIN',
'CASE' ,'CONST' ,'DIV' ,'DO',
'DOWNTO' ,'ELSE' ,'END' ,'EXTERNAL',
'FILE' ,'FOR' ,'FORWARD' ,'FUNCTION',
'GOTO' ,'IF' ,'IN' ,'INLINE',
'LABEL' ,'MOD' ,'NIL' ,'NOT',
'OF' ,'OR' ,'OVERLAY' ,'PACKED',
'PROCEDURE','PROGRAM' ,'RECORD' ,'REPEAT',
'SET' ,'SHL' ,'SHR' ,'STRING',
'THEN' ,'TO' ,'TYPE' ,'UNTIL',
'VAR' ,'WHILE' ,'WITH' ,'XOR');
var
high,
low,
mid : byte;
begin
high := nkwords;
low := 1;
while (low <= high) do
begin
mid := (high+low) div 2;
if (key_words[mid] = wkstr) then
begin
keyword := true;
exit;
end
else
if (key_words[mid] > wkstr) then
high := mid-1
else
low := mid+1;
end;
keyword := false;
end;
begin {xref_line}
x := 1; { start at beginning }
wkstr := '';
getchr;
while (x > 0) do { while not end of line }
begin
if (ch = '''') and (comment_type = 0) then { set quote flag }
quote := not(quote)
else
if not quote then { if not in quote then go }
case comment_type of
0 : if ch = '{' then
comment_type := 1 { start a comment }
else
if ch = '(' then
begin
getchr;
if (x > 0) then
if (ch = '*') then
comment_type := 2 { start a comment }
else
x := x-1;
end
else
if ch in ['A'..'Z'] then { start a word }
begin
repeat
wkstr := wkstr+ch;
getchr;
until (not (ch in ['0'..'9','A'..'Z','_'])) or (x = 0);
if not keyword then { check for keyword }
add_tree(xref_head);{ not keyword, add to xref tree }
wkstr := '';
if x > 0 then { if not end of line }
x := x-1; { go back to previous character }
end;
1 : if ch = '}' then { end comment }
comment_type := 0;
2 : if ch = '*' then
begin
getchr;
if (x > 0) then
if (ch = ')') then
comment_type := 0 { end comment }
else
x := x-1;
end;
end; { case }
getchr;
end; { while }
end; {xref_line}
{ FINDSPACE - find end of last full word that will fit on the line }
function findspace(printstr : strptr; var x : byte) : byte;
var
y : byte;
begin
y := x;
x := x+printwidth;
if (x > length(printstr^)) then { the whole line will fit }
x := length(printstr^)+1
else
begin
while (printstr^[x] <> ' ') and (x > y) do { look back for first space }
x := x-1;
if (x > y) then { found it }
x := x+1
else
x := y+printwidth+1; { no space, break in middle of word }
end;
findspace := x-1;
end; {findspace}
{ DETAB - replace all tabs in the line with appropriate number of spaces }
procedure detab(var printstr : string255);
type
string8 = string[8];
const
tab = ^I;
tab_string : string8 = ' ';
var
x : byte;
begin
x := pos(tab,printstr);
while (x > 0) do
begin
delete(printstr,x,1); { remove the tab }
insert(copy(tab_string,1,8-((x-1) mod 8)),printstr,x); { insert spaces }
x := pos(tab,printstr);
end;
end; {detab}
begin {process_line}
detab(printstr^);
currow := currow + ((length(printstr^)-1) div printwidth) + 1;
if currow > printlength then
begin
headings;
currow := currow + ((length(printstr^)-1) div printwidth) + 1;
end;
work_line := work_line + 1;
if including then
write(outfile,'<',work_line:5,'> : ')
else
write(outfile,' ',work_line:5,' : ');
x := 1;
writeln(outfile,copy(printstr^,1,findspace(printstr,x)));
while x <= length(printstr^) do
writeln(outfile,' ':10,copy(printstr^,x,findspace(printstr,x)));
if xref then
xref_line;
end; {process_line}
procedure process_include_file(incstr : strptr);
var
namestart,
nameend : integer;
includefile : text;
includefilename : filename;
function parse(incstr : strptr) : filename;
begin
namestart := pos('$I',incstr^)+2;
if namestart = 2 then
namestart := pos('$i',incstr^)+2;
while (incstr^[namestart] = ' ') do
namestart := namestart + 1;
nameend := namestart;
while (not (incstr^[nameend] in [' ','}','*']))
and ((nameend - namestart) <= pathlength) do
nameend := nameend + 1;
nameend := nameend - 1;
parse := copy(incstr^,namestart,(nameend-namestart+1));
end; {parse}
begin {process_include_file}
includefilename := parse(incstr);
if (pos('.',includefilename) = 0) then
includefilename := includefilename + '.PAS';
including := true;
include_line := 0;
if not open(includefile,includefilename) then
begin
linebuffer^ := 'ERROR -- Include file not found: ' + includefilename;
process_line(linebuffer,include_line);
end
else
begin
while not eof(includefile) do
begin
readln(includefile,linebuffer^);
process_line(linebuffer,include_line);
end;
close(includefile);
end;
including := false;
include_line := 0;
end; {process_include_file}
begin {process_file}
new(linebuffer);
quote := false;
comment_type := 0;
line_no := 0;
include_line := 0;
including := false; { not including a file now }
while not eof(mainfile) do
begin
readln(mainfile,linebuffer^);
process_line(linebuffer,line_no);
if includes and includein(linebuffer) then
process_include_file(linebuffer);
end;
dispose(linebuffer);
end; {process_file}
{ PRINT_XREF - print the cross-reference listing }
procedure print_xref(xref_head : itmptr);
var
ref_count : integer;
p,q : itmptr;
{ LPWRITELN - write a newline on output file. Check for page break. }
procedure lpwriteln;
begin
if (currow > printlength) then
headings; { new page }
writeln(outfile);
currow := currow + 1;
end;
{ NEWLINE - need another line for references. Start at position (MAX_ID_LEN+1) }
procedure newline;
begin
lpwriteln;
write(outfile,' ':(max_id_len + 1));
ref_count := 1;
end;
{ PRINT_REFS - Print the list of references for the current node. }
procedure print_refs(node : itmptr);
var
list : refptr;
{ WRITE_REF - output one reference to the print file }
procedure write_ref(ref : refptr);
var
inclstr : string8;
inclen : byte absolute inclstr; {easier than length(inclstr)}
begin
with ref^ do
begin
if (ref_count > refs_per_line) then
newline;
write(outfile,line:1);
if (incl = 0) then
begin { no include in this reference }
str(line:1,inclstr);
if (inclen < 6) then
write(outfile,' ':(6-inclen));
ref_count := ref_count + 1;
end
else
begin { process INCLUDEd reference }
write(outfile,'<',incl:1,'>');
str(line:1,inclstr);
if (inclen < 6) then
write(outfile,' ':(6-inclen));
str(incl:1,inclstr);
if (inclen < 4) then
write(outfile,' ':(4-inclen));
ref_count := ref_count + 2;
end;
end; {with}
end; {write_ref}
begin {print_refs}
if ((node^.numrefs div refs_per_line) > (printlength - currow)) then
headings;
write(outfile,node^.idname^); { output idname }
if (length(node^.idname^) >= max_id_len) then
newline
else
write(outfile,' ':(max_id_len-length(node^.idname^)+1));
ref_count := 1;
list := node^.fstptr;
repeat
write_ref(list);
list := list^.nxtptr;
until (list = nil);
lpwriteln;
end; {print_refs}
{ in-order traversal of a right in-threaded binary tree. }
begin {print_xref}
headings;
p := xref_head;
repeat
q := nil;
while (p <> nil) do
begin { traverse left branch }
q := p;
p := p^.left;
end;
if (q <> nil) then
begin
print_refs(q);
p := q^.right;
while (q^.rthrd) do
begin { back up }
print_refs(p);
q := p;
p := p^.right;
end;
end;
until (q = nil);
end; {print_xref}
begin { typex }
writeln('[TYPEX Version ',version_no,']');
initialize;
process_file;
if xref then
print_xref(xref_head);
page(outfile);
close(mainfile);
close(outfile);
end. { typex }