{* Mangler, a program to mangle pascal source files.
Copyright (C) 1993 Berend de Boer
This program is free software for noncommercial users; you can
redistribute it and/or modify it under the terms of the license,
stated in de accompanying file LICENSE.TXT.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
license for more details.
See the accompanying READ.ME file for information on contacting the
author.
$Author: Berend_de_Boer $
$Date: 94/03/19 20:31:37 $
$Revision: 1.3 $
Last changes:
93-04-19 Fixed bug that occured parsing objects defined in implementation
section.
Improved white space removal.
Fixed bug in forward pointers referencing.
Fixed bug that caused the identifier after the inherited keyword
to be mangled, which should not of course.
Changed mangling of objects: object methods are not mangled anymore.
If all sources were read methods could be mangled well, but
currently mangler does not do this.
Encoding function improved so probability of collisions has greatly
been diminished
93-07-14 Error fixed. Section variable not reset after implementation was
read
93-07-19 Removed lexical analyzer to PASLEX.L
Not all variants of the with statement were recognized. Fixed.
93-11-19 Labels were not supported. Fixed
Now interface section remains intact. Only implementation section
is mangled.
93-11-25 Files which did not contain a unit or program keyword, broke mangler
*}
{* conditional defines *}
{$DEFINE Pass2} {* do crunch pass *}
{$DEFINE DelTmpFiles} {* delete temporary files *}
{{$DEFINE ShowProcs} {* show procedures *}
{{$DEFINE PMD} {* use post mortem debugger *}
{$X+}
program Mangler;
uses LexLib,
{$IFDEF PMD}
BBError, PMD, MemCheck,
{$ENDIF}
Objects, Dos;
const
Version = '1.32';
const
LineWidth:word = 120;
Prime = 67099547;
const
{* this list should be sorted! *}
_ABSOLUTE = 1;
_AND = 2;
_ARRAY = 3;
_ASM = 4;
_ASSEMBLER = 5;
_BEGIN = 6;
_CASE = 7;
_CONST = 8;
_CONSTRUCTOR = 9;
_DESTRUCTOR = 10;
_DIV = 11;
_DO = 12;
_DOWNTO = 13;
_ELSE = 14;
_END = 15;
_EXTERNAL = 16;
_FAR = 17;
_FILE = 18;
_FOR = 19;
_FORWARD = 20;
_FUNCTION = 21;
_GOTO = 22;
_IF = 23;
_IMPLEMENTATION = 24;
_IN = 25;
_INHERITED = 26;
_INLINE = 27;
_INTERFACE = 28;
_INTERRUPT = 29;
_LABEL = 30;
_MOD = 31;
_NEAR = 32;
_NIL = 33;
_NOT = 34;
_OBJECT = 35;
_OF = 36;
_OR = 37;
_PACKED = 38;
_PRIVATE = 39;
_PROCEDURE = 40;
_PROGRAM = 41;
_PUBLIC = 42;
_RECORD = 43;
_REPEAT = 44;
_SET = 45;
_SHL = 46;
_SHR = 47;
_STRING = 48;
_THEN = 49;
_TO = 50;
_TYPE = 51;
_UNIT = 52;
_UNTIL = 53;
_USES = 54;
_VAR = 55;
_VIRTUAL = 56;
_WHILE = 57;
_WITH = 58;
_XOR = 59;
SEMICOLON = 100;
CHARACTER_STRING = 101;
IDENTIFIER = 102;
DOT = 103;
DIRECTIVE = 104;
NUMBER = 105;
ASSIGNMENT = 106;
COLON = 107;
EQUAL = 108;
LPAREN = 109;
RPAREN = 110;
COMMA = 111;
OTHER = 112;
DOTDOT = 113;
GE = 114;
LE = 115;
NOTEQUAL = 116;
_CHAR = 117;
NEWLINE = 118;
KEYWORD = 119;
UPARROW = 120;
AMPERSAND = 121;
LBRAC = 122;
RBRAC = 123;
type
PScopeCol = ^TScopeCol;
PMangleItem = ^TMangleIteM;
TMangleItem = record
Name : PString;
HashedName : PString;
ScopeCol : PScopeCol;
end;
TScopeCol = object(TStringCollection)
procedure FreeItem(Item : pointer); virtual;
procedure Insert(Item : pointer); virtual;
function KeyOf(Item : pointer) : pointer; virtual;
function InsertIntrIdentifier(const Name : string; var Index : integer) : string;
function InsertIdentifier(const Name : string; var Index : integer) : string;
function AtHashedName(Index : integer) : string;
function AtScope(Index : integer) : PScopeCol;
function LastScope : PScopeCol;
end;
SectionTypes = (None, Decl, BetweenCaseAndOfDecl,
Func, FuncDecl, FuncOuter, FunctionType,
CompoundStatement, WithStatement, Inlin, LabelStatement);
PSectionItem = ^TSectionItem;
TSectionItem = record
Section : SectionTypes;
WithPushes,
OpeningLevel,
DeclType : integer;
end;
PSectionCol = ^TSectionCol;
TSectionCol = object(TStringCollection)
procedure FreeItem(Item : pointer); virtual;
end;
var
sourceDir : DirStr;
DirInfo : SearchRec;
ExitSave : pointer;
ImplementationLineNumber : word;
function HashIt(s : string) : string;
const
Base = 5;
chars:array [0..Base-1] of char = ('0', '1', 'I', 'O', 'l');
function StrBase(l : longint) : string;
var
s : string;
begin
s := '';
while l > Base-1 do begin
s := chars[l mod Base] + s;
l := l div Base;
end; { of while }
s := chars[l]+ s;
StrBase := s;
end;
var
l, d : longint;
i,j : word;
begin
l := 0;
Move(s[1], l, length(s) mod 4);
j := length(s) mod 4 + 1;
for i := 1 to length(s) div 4 do begin
Move(s[j], d, 4);
l := l xor d;
Inc(j, 4);
end; { of for }
HashIt := 'O' + StrBase(Abs(l) mod Prime);
end;
procedure TScopeCol.FreeItem(Item : pointer);
begin
with PMangleItem(Item)^ do begin
DisposeStr(Name);
DisposeStr(HashedName);
if ScopeCol <> nil then Dispose(ScopeCol, Done);
end;
Dispose(PMangleItem(Item));
end;
procedure TScopeCol.Insert(Item : pointer);
var
Index : integer;
begin
if Search(KeyOf(Item), Index)
then AtPut(Index, Item)
else AtInsert(Index, Item);
end;
function TScopeCol.KeyOf(Item : pointer) : pointer;
begin
KeyOf := PMangleItem(Item)^.Name;
end;
function TScopeCol.InsertIntrIdentifier(const Name : string; var Index : integer) : string;
var
p : PMangleItem;
begin
New(p);
p^.Name := NewStr(Name);
p^.HashedName := NewStr(Name);
p^.ScopeCol := nil;
Insert(p);
Index := IndexOf(p);
InsertIntrIdentifier := p^.Name^;
end;
function TScopeCol.InsertIdentifier(const Name : string; var Index : integer) : string;
var
p : PMangleItem;
begin
New(p);
p^.Name := NewStr(Name);
p^.HashedName := NewStr(HashIt(Name));
p^.ScopeCol := nil;
Insert(p);
Index := IndexOf(p);
InsertIdentifier := p^.HashedName^;
end;
function TScopeCol.AtHashedName(Index : integer) : string;
begin
AtHashedName := PMangleItem(At(Index))^.HashedName^;
end;
function TScopeCol.AtScope(Index : integer) : PScopeCol;
begin
if Index = -1
then AtScope := nil
else AtScope := PMangleItem(At(Index))^.ScopeCol;
end;
function TScopeCol.LastScope : PScopeCol;
begin
LastScope := PMangleItem(At(Count-1))^.ScopeCol;
end;
procedure TSectionCol.FreeItem(Item : pointer);
begin
Dispose(PSectionItem(Item));
end;
function UpStr(const s : string) : string; assembler;
asm
push ds
cld
lds si,s
les di,@Result
lodsb
stosb
xor ah,ah
xchg ax,cx
jcxz @3
@1:
lodsb
cmp al,'a'
jb @2
cmp al,'z'
ja @2
sub al,20H
@2:
stosb
loop @1
@3:
pop ds
end;
procedure WriteProgress;
{* writes current file with current linenumber *}
begin
write(#13, sourceDir+DirInfo.Name, ' (', yylineno-1, ')');
end;
procedure ExitHandler; far;
begin
ExitProc := ExitSave;
if TextRec(yyoutput).Mode <> fmClosed then begin
WriteProgress;
Close(yyoutput);
end;
end;
procedure Halt1;
{* stop program, delete temporary files*}
begin
{$I-}
Close(yyoutput);
{$IFDEF DelTmpFiles}
Erase(yyoutput);
{$I+}
{$ENDIF}
Halt(1);
end;
procedure commenteof;
begin
WriteProgress;
writeln(' unexpected EOF inside comment');
Halt1;
end;
function IsClosed(var t : text) : Boolean;
begin
IsClosed := TextRec(t).Mode = fmClosed;
end;
procedure PrintError(const s : string);
begin
WriteProgress;
writeln(' ', s);
end;
function is_keyword(const id : string; var token : integer) : Boolean;
const
id_len = 18;
type
Ident = string[id_len];
const
(* table of Pascal keywords: *)
no_of_keywords = 59;
keyword : array [1..no_of_keywords] of Ident = (
'ABSOLUTE', 'AND', 'ARRAY', 'ASM', 'ASSEMBLER', 'BEGIN', 'CASE', 'CONST',
'CONSTRUCTOR', 'DESTRUCTOR', 'DIV', 'DO',
'DOWNTO', 'ELSE', 'END', 'EXTERNAL', 'FAR', 'FILE', 'FOR', 'FORWARD',
'FUNCTION',
'GOTO', 'IF', 'IMPLEMENTATION', 'IN', 'INHERITED', 'INLINE', 'INTERFACE',
'INTERRUPT',
'LABEL', 'MOD', 'NEAR', 'NIL', 'NOT', 'OBJECT', 'OF', 'OR',
'PACKED', 'PRIVATE', 'PROCEDURE', 'PROGRAM', 'PUBLIC',
'RECORD', 'REPEAT', 'SET', 'SHL', 'SHR', 'STRING', 'THEN', 'TO', 'TYPE',
'UNIT', 'UNTIL', 'USES', 'VAR', 'VIRTUAL', 'WHILE', 'WITH', 'XOR');
var m, n, k : integer;
begin
m := 1; n := no_of_keywords;
while m<=n do begin
k := m+(n-m) div 2;
if id=keyword[k]
then begin
is_keyword := true;
token := k;
Exit;
end
else if id>keyword[k]
then m := k+1
else n := k-1
end; { of while }
is_keyword := false
end;
{$I PASLEX.PAS *}
function Scramble(FromFile, ToFile : FNameStr) : Boolean;
{* mangles a given file as much as possible *}
type
RecordTypes = (rtNone, rtRecord, rtVariantRecord);
PInteger = ^integer;
var
LastScopeIndex : integer;
Section : SectionTypes;
DeclType : word;
RecordType : RecordTypes;
SectionStack : PSectionCol;
ScopeStack : PCollection;
CurrentScope : PScopeCol;
WithPushes : integer;
OpeningLevel : integer;
ParenLevel : word;
AssemblerSection : Boolean;
ObjectImpl : Boolean;
ConstantArray : Boolean;
procedure PushScope(ps : PScopeCol);
begin
ScopeStack^.Insert(ps);
end;
function PopScope : PScopeCol;
begin
with ScopeStack^ do begin
PopScope := At(Count-1);
AtDelete(Count-1);
end;
end;
procedure PushSection(Section : SectionTypes);
var
p : PSectionItem;
begin
New(p);
p^.Section := Section;
p^.WithPushes := WithPushes;
p^.OpeningLevel := OpeningLevel;
p^.DeclType := DeclType;
with SectionStack^ do
AtInsert(Count, p);
end;
function PopSection : SectionTypes;
var
i : PInteger;
p : PSectionItem;
begin
with SectionStack^ do
p := At(Count-1);
DeclType := p^.DeclType;
OpeningLevel := p^.OpeningLevel;
WithPushes := p^.WithPushes;
PopSection := p^.Section;
with SectionStack^ do
AtFree(Count-1);
end;
function Encode(const s : string) : string;
begin
{* create new scope if necessary *}
if CurrentScope = nil then begin
CurrentScope := New(PScopeCol, Init(20,10));
with ScopeStack^ do
PMangleItem(PScopeCol(At(Count-1))^.At(LastScopeIndex))^.ScopeCol := CurrentScope;
end;
{* add identifier to current scope *}
Encode := CurrentScope^.InsertIdentifier(s, LastScopeIndex);
{* make the current identifier the new scope *}
PushScope(CurrentScope);
CurrentScope := CurrentScope^.AtScope(LastScopeIndex);
end;
function Encode2(const s : string) : string;
{* as Encode but without setting a new scope *}
var
Index : integer;
begin
{* create new scope if necessary *}
if CurrentScope = nil then begin
CurrentScope := New(PScopeCol, Init(20,10));
with ScopeStack^ do
PMangleItem(PScopeCol(At(Count-1))^.At(LastScopeIndex))^.ScopeCol := CurrentScope;
end;
{* add identifier to current scope *}
Encode2 := CurrentScope^.InsertIdentifier(s, Index);
end;
function EncodeNot2(const s : string) : string;
{* as Encode but without setting a new scope and without encoding *}
var
Index : integer;
begin
{* create new scope if necessary *}
if CurrentScope = nil then begin
CurrentScope := New(PScopeCol, Init(20,10));
with ScopeStack^ do
PMangleItem(PScopeCol(At(Count-1))^.At(LastScopeIndex))^.ScopeCol := CurrentScope;
end;
{* add identifier to current scope *}
EncodeNot2 := CurrentScope^.InsertIntrIdentifier(s, Index);
end;
function Encode3(const s : string) : string;
{* inserts identifier in last scope on stack, sets scope of inserted
identifier equal to current scope *}
var
Index : integer;
begin
with ScopeStack^, PScopeCol(At(Count-1))^ do begin
Encode3 := InsertIdentifier(s, Index);
PMangleItem(At(Index))^.ScopeCol := CurrentScope;
end;
end;
function EncodeNot3(const s : string) : string;
{* inserts identifier in last scope on stack, sets scope of inserted
identifier equal to current scope, but don't encode *}
var
Index : integer;
begin
with ScopeStack^, PScopeCol(At(Count-1))^ do begin
EncodeNOT3 := InsertIntrIdentifier(s, Index);
PMangleItem(At(Index))^.ScopeCol := CurrentScope;
end;
end;
function EncodeNot(const s : string) : string;
{* as Encode, but identifier is not mangled *}
begin
{* create new scope if necessary *}
if CurrentScope = nil then begin
CurrentScope := New(PScopeCol, Init(20,10));
with ScopeStack^ do
PMangleItem(PScopeCol(At(Count-1))^.At(LastScopeIndex))^.ScopeCol := CurrentScope;
end;
{* add identifier to current scope *}
CurrentScope^.InsertIntrIdentifier(s, LastScopeIndex);
EncodeNot := s;
{* make the current identifier the new scope *}
PushScope(CurrentScope);
CurrentScope := CurrentScope^.AtScope(LastScopeIndex);
end;
function GetScope(const s : string; var Index : integer) : PScopeCol;
{* returns scope in which s was defined if exists *}
function Containss(Item : PScopeCol) : Boolean; far;
begin
Containss := (Item <> nil) and (Item^.Search(@s, Index));
end;
begin
if (CurrentScope <> nil) and CurrentScope^.Search(@s, Index)
then GetScope := CurrentScope
else
{* search in scopes on ScopeStack *}
GetScope := ScopeStack^.LastThat(@Containss);
end;
function GiveEncodingFor(s : string) : string;
{* DO NOT MAKE s a const string!!! *}
{ PRE -
POST - contents of yytext is destroyed
}
var
p,d : PScopeCol;
e : string;
Index : integer;
begin
if yylex = DOT
then begin
{* a dot was used to select a different scope *}
p := GetScope(s, Index);
if p = nil
then begin {* an unknown scope was selected *}
e := s + '.';
while (yylex = IDENTIFIER) do begin
e := e + yytext;
if yylex = DOT
then e := e + '.'
else Break;
end;
yyless(0);
end
else begin
PushScope(CurrentScope);
CurrentScope := p^.AtScope(Index);
e := p^.AtHashedName(Index) + '.';
while (yylex = IDENTIFIER) do begin
if CurrentScope = nil
then begin
e := e + yytext;
(* why this source??? if nil you don't know anything it seems
d := GetScope(yytext, Index);
if d <> nil
then e := e + GiveEncodingFor(yytext)
else e := e + yytext;
*)
end
else begin
if CurrentScope^.Search(@yytext, Index)
then e := e + CurrentScope^.AtHashedName(Index)
else e := e + yytext;
end;
if yylex = DOT
then begin
if CurrentScope <> nil then
if CurrentScope^.Count = 0
then CurrentScope := nil
else CurrentScope := CurrentScope^.AtScope(Index);
e := e + '.';
end
else break;
end; { of while }
yyless(0);
CurrentScope := PopScope;
end;
GiveEncodingFor := e;
end
else begin
yyless(0);
p := GetScope(s, Index);
if p = nil
then GiveEncodingFor := s
else GiveEncodingFor := p^.AtHashedName(Index)
end;
end;
{$I ASMLEX.PAS}
var
Buffer : array[1..1024] of char;
GlobalSection : (Un, Intr, Impl);
RightHand : Boolean;
ObjectDecl : Boolean;
Index : integer; {* scratch variable *}
ObjectName : string;
i : integer; {* scratch varaible *}
Scope : PScopeCol; {* scratch variable *}
TypeDecl,
AbsoluteParsed : Boolean;
Paren : integer;
LastRetVal : integer; {* previous value of yyretval *}
procedure HandleSemiColon;
var
i : integer;
wp : integer;
begin
ConstantArray := FALSE;
writeln(yyoutput, yytext);
case GlobalSection of
Intr : case Section of
Decl : begin
if not ((RecordType = rtVariantRecord) and (LastRetVal = RPAREN)) then
RightHand := FALSE;
CurrentScope := PopScope;
end;
Func, FunctionType : begin
CurrentScope := PopScope;
RightHand := FALSE;
Section := Decl;
end;
FuncDecl : RightHand := FALSE;
end; { of case }
Impl : case Section of
Decl : begin
if not ((RecordType = rtVariantRecord) and (LastRetVal = RPAREN)) then
RightHand := FALSE;
CurrentScope := PopScope;
end;
Func : begin
if ObjectDecl then begin
CurrentScope := PopScope;
Section := Decl;
end;
end;
FunctionType : begin
CurrentScope := PopScope;
RightHand := FALSE;
Section := Decl;
end;
FuncDecl : RightHand := FALSE;
WithStatement : begin
wp := WithPushes;
for i := 0 to wp-1 do
CurrentScope := PopScope;
Section := PopSection;
end;
Inlin : begin
Section := PopSection;
CurrentScope := PopScope;
end;
LabelStatement : begin
RightHand := FALSE;
Section := Decl;
end;
end; { of case }
end; { of case }
end; { of proc HandleSemiClon *}
procedure ParseDeclaration;
var
i : integer;
saveyytext : string;
begin
if RightHand
then begin
if not AbsoluteParsed then begin
Scope := GetScope(yytext, Index);
if Scope <> nil then begin
{* variable of mangled types should get the *}
{* same scope as the mangled type *}
if CurrentScope = nil then begin
CurrentScope := New(PScopeCol, Init(20,10));
with ScopeStack^ do
PMangleItem(PScopeCol(At(Count-1))^.At(LastScopeIndex))^.ScopeCol := CurrentScope;
end;
Scope := Scope^.AtScope(Index);
if Scope <> nil then begin
for i := 0 to Scope^.Count-1 do begin
CurrentScope^.Insert(Scope^.At(i));
end;
end;
end;
end;
write(yyoutput, GiveEncodingFor(yytext));
end
else begin
ObjectName := yytext;
AbsoluteParsed := FALSE;
{* check if constant array variable is specified *}
if (DeclType = _CONST) and (not RightHand) then begin
saveyytext := yytext;
if (yylex = COLON) or (yyretval = EQUAL)
then begin
yyless(0);
yytext := saveyytext;
end
else begin
saveyytext := yytext;
write(yyoutput, GiveEncodingFor(saveyytext));
yytext := saveyytext;
yyless(0);
ConstantArray := TRUE;
Exit;
end;
end;
{* if we are in the interface section, don't encode *}
if GlobalSection = Intr
then write(yyoutput, EncodeNot(yytext))
else
{* encode the lefthand *}
write(yyoutput, Encode(yytext));
{* create new scope if COMMA detected, set ConstantRecord if COLON *}
if yylex = COMMA then begin
CurrentScope := New(PScopeCol, Init(20,10));
with ScopeStack^ do
PMangleItem(PScopeCol(At(Count-1))^.At(LastScopeIndex))^.ScopeCol := CurrentScope;
end;
yyless(0);
if yyretval = COMMA then begin
repeat
if yylex = COMMA
then begin
write(yyoutput, ',');
yylex;
if GlobalSection = Intr
then write(yyoutput, EncodeNot3(yytext))
else write(yyoutput, Encode3(yytext));
end
else break;
until false;
yyless(0);
end;
end;
end; { of ParseDeclaration }
procedure ParseFunctionDeclaration;
begin
if RightHand
then write(yyoutput, GiveEncodingFor(yytext))
else begin
if (CurrentScope <> nil) and CurrentScope^.Search(@yytext, Index)
then begin
repeat
write(yyoutput, GiveEncodingFor(yytext));
if yylex = COMMA
then begin
write(yyoutput, ',');
yylex;
end
else break;
until false;
yyless(0);
end
else begin
repeat
if GlobalSection = Intr
then write(yyoutput, EncodeNot2(yytext))
else write(yyoutput, Encode2(yytext));
if yylex = COMMA
then begin
writeln(yyoutput, ',');
yylex;
end
else break;
until false;
yyless(0);
end;
end;
end; { of ParseFunctionDeclaration }
label l1;
begin
Scramble := FALSE;
{* open inputfile *}
FileMode := 0; {* open inputfile in read-only mode *}
Assign(yyinput, FromFile);
Reset(yyinput);
SetTextBuf(yyinput, Buffer, 1024);
{* open output file *}
FileMode := 1; {* open outputfile in write-only mode *}
Assign(yyoutput, 'NUL'); {* depress output until implemenation section *}
Rewrite(yyoutput);
FileMode := 2; {* restore filemode *}
{* initialize variables *}
yylineno := 1;
GlobalSection := Un;
Section := None;
SectionStack := New(PSectionCol, Init(50, 10));
RecordType := rtNone;
WithPushes := 0;
OpeningLevel := -1;
ParenLevel := 0;
RightHand := FALSE;
ObjectDecl := FALSE;
ObjectImpl := FALSE;
CurrentScope := New(PScopeCol, Init(200, 100));
ScopeStack := New(PCollection, Init(100, 50));
LastScopeIndex := -1;
AssemblerSection := FALSE;
TypeDecl := FALSE;
AbsoluteParsed := FALSE;
ConstantArray := FALSE;
{* check if this is a unit *}
repeat
case yylex of
_UNIT : break;
_PROGRAM : begin
Close(yyinput);
Close(yyoutput);
writeln('This is a program. Mangler can only mangle units. File skipped.');
Exit;
end;
end; { of case }
until IsClosed(yyinput) or eof(yyinput);
if IsClosed(yyinput) or eof(yyinput) then begin
writeln('File is not a unit. File skipped.');
Exit;
end;
{* mangle *}
write(#13, FromFile, ' (', yylineno, ')');
while not eof(yyinput) do begin
LastRetVal := yyretval;
case yylex of
IDENTIFIER : begin
case Section of
Decl : ParseDeclaration;
FuncDecl : ParseFunctionDeclaration;
else writeln(yyoutput, GiveEncodingFor(yytext));