home *** CD-ROM | disk | FTP | other *** search
- {$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 }