diff options
Diffstat (limited to 'compiler/owar.pas')
-rw-r--r-- | compiler/owar.pas | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/compiler/owar.pas b/compiler/owar.pas new file mode 100644 index 0000000000..4980c0a793 --- /dev/null +++ b/compiler/owar.pas @@ -0,0 +1,282 @@ +{ + Copyright (c) 1998-2002 by Peter Vreman + + Contains the stuff for writing .a files directly + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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 + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + **************************************************************************** +} +unit owar; + +{$i fpcdefs.inc} + +interface + +uses + cclasses, + owbase; + +type + tarhdr=packed record + name : array[0..15] of char; + date : array[0..11] of char; + uid : array[0..5] of char; + gid : array[0..5] of char; + mode : array[0..7] of char; + size : array[0..9] of char; + fmag : array[0..1] of char; + end; + + tarobjectwriter=class(tobjectwriter) + constructor create(const Aarfn:string); + destructor destroy;override; + function createfile(const fn:string):boolean;override; + procedure closefile;override; + procedure writesym(const sym:string);override; + procedure write(const b;len:longint);override; + private + arfn : string; + arhdr : tarhdr; + symreloc, + symstr, + lfnstr, + ardata : TDynamicArray; + objpos : longint; + objfn : string; + timestamp : string[12]; + procedure createarhdr(fn:string;size:longint;const gid,uid,mode:string); + procedure writear; + end; + + +implementation + +uses + cstreams, + systems, + globals, + verbose, + dos; + +const + symrelocbufsize = 4096; + symstrbufsize = 8192; + lfnstrbufsize = 4096; + arbufsize = 65536; + +{***************************************************************************** + Helpers +*****************************************************************************} + +const + C1970=2440588; + D0=1461; + D1=146097; + D2=1721119; +Function Gregorian2Julian(DT:DateTime):LongInt; +Var + Century,XYear,Month : LongInt; +Begin + Month:=DT.Month; + If Month<=2 Then + Begin + Dec(DT.Year); + Inc(Month,12); + End; + Dec(Month,3); + Century:=(longint(DT.Year Div 100)*D1) shr 2; + XYear:=(longint(DT.Year Mod 100)*D0) shr 2; + Gregorian2Julian:=((((Month*153)+2) div 5)+DT.Day)+D2+XYear+Century; +End; + +function DT2Unix(DT:DateTime):LongInt; +Begin + DT2Unix:=(Gregorian2Julian(DT)-C1970)*86400+(LongInt(DT.Hour)*3600)+(DT.Min*60)+DT.Sec; +end; + + +{***************************************************************************** + TArObjectWriter +*****************************************************************************} + +constructor tarobjectwriter.create(const Aarfn:string); +var + time : datetime; + dummy : word; +begin + arfn:=Aarfn; + ardata:=TDynamicArray.Create(arbufsize); + symreloc:=TDynamicArray.Create(symrelocbufsize); + symstr:=TDynamicArray.Create(symstrbufsize); + lfnstr:=TDynamicArray.Create(lfnstrbufsize); +{ create timestamp } + getdate(time.year,time.month,time.day,dummy); + gettime(time.hour,time.min,time.sec,dummy); + Str(DT2Unix(time),timestamp); +end; + + +destructor tarobjectwriter.destroy; +begin + if Errorcount=0 then + writear; + arData.Free; + symreloc.Free; + symstr.Free; + lfnstr.Free; +end; + + +procedure tarobjectwriter.createarhdr(fn:string;size:longint;const gid,uid,mode:string); +var + tmp : string[9]; + hfn : string; +begin + fillchar(arhdr,sizeof(tarhdr),' '); +{ create ar header } + { win32 will change names starting with .\ to ./ when using lfn, corrupting + the sort order required for the idata sections. To prevent this strip + always the path from the filename. (PFV) } + hfn:=SplitFileName(fn); + if hfn='' then + hfn:=fn; + fn:=hfn+'/'; + if length(fn)>16 then + begin + arhdr.name[0]:='/'; + str(lfnstr.size,tmp); + move(tmp[1],arhdr.name[1],length(tmp)); + fn:=fn+#10; + lfnstr.write(fn[1],length(fn)); + end + else + move(fn[1],arhdr.name,length(fn)); + { don't write a date if also no gid/uid/mode is specified } + if gid<>'' then + move(timestamp[1],arhdr.date,sizeof(timestamp)); + str(size,tmp); + move(tmp[1],arhdr.size,length(tmp)); + move(gid[1],arhdr.gid,length(gid)); + move(uid[1],arhdr.uid,length(uid)); + move(mode[1],arhdr.mode,length(mode)); + arhdr.fmag:='`'#10; +end; + + +function tarobjectwriter.createfile(const fn:string):boolean; +begin + objfn:=fn; + objpos:=ardata.size; + ardata.seek(objpos + sizeof(tarhdr)); + createfile:=true; +end; + + +procedure tarobjectwriter.closefile; +begin + ardata.align(2); +{ fix the size in the header } + createarhdr(objfn,ardata.size-objpos-sizeof(tarhdr),'42','42','644'); +{ write the header } + ardata.seek(objpos); + ardata.write(arhdr,sizeof(tarhdr)); +end; + + +procedure tarobjectwriter.writesym(const sym:string); +var + c : char; +begin + c:=#0; + symreloc.write(objpos,4); + symstr.write(sym[1],length(sym)); + symstr.write(c,1); +end; + + +procedure tarobjectwriter.write(const b;len:longint); +begin + ardata.write(b,len); +end; + + +procedure tarobjectwriter.writear; + + function lsb2msb(l:longint):longint; + type + bytearr=array[0..3] of byte; + var + l1 : longint; + begin + bytearr(l1)[0]:=bytearr(l)[3]; + bytearr(l1)[1]:=bytearr(l)[2]; + bytearr(l1)[2]:=bytearr(l)[1]; + bytearr(l1)[3]:=bytearr(l)[0]; + lsb2msb:=l1; + end; + +const + armagic:array[1..8] of char='!<arch>'#10; +var + arf : TCFileStream; + fixup,l, + relocs,i : longint; +begin + arf:=TCFileStream.Create(arfn,fmCreate); + if CStreamError<>0 then + begin + Message1(exec_e_cant_create_archivefile,arfn); + exit; + end; + arf.Write(armagic,sizeof(armagic)); + { align first, because we need the size for the fixups of the symbol reloc } + if lfnstr.size>0 then + lfnstr.align(2); + if symreloc.size>0 then + begin + symstr.align(2); + fixup:=12+sizeof(tarhdr)+symreloc.size+symstr.size; + if lfnstr.size>0 then + inc(fixup,lfnstr.size+sizeof(tarhdr)); + relocs:=symreloc.size div 4; + { fixup relocs } + for i:=0to relocs-1 do + begin + symreloc.seek(i*4); + symreloc.read(l,4); + symreloc.seek(i*4); + l:=lsb2msb(l+fixup); + symreloc.write(l,4); + end; + createarhdr('',4+symreloc.size+symstr.size,'0','0','0'); + arf.Write(arhdr,sizeof(tarhdr)); + relocs:=lsb2msb(relocs); + arf.Write(relocs,4); + symreloc.WriteStream(arf); + symstr.WriteStream(arf); + end; + if lfnstr.size>0 then + begin + createarhdr('/',lfnstr.size,'','',''); + arf.Write(arhdr,sizeof(tarhdr)); + lfnstr.WriteStream(arf); + end; + ardata.WriteStream(arf); + Arf.Free; +end; + + +end. |