123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- {
- 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 createAr(const Aarfn:string);override;
- destructor destroy;override;
- function createfile(const fn:string):boolean;override;
- procedure closefile;override;
- procedure writesym(const sym:string);override;
- procedure write(const b;len:longword);override;
- private
- arfn : string;
- arhdr : tarhdr;
- symreloc,
- symstr,
- lfnstr,
- ardata : TDynamicArray;
- objpos : longint;
- objfn : string;
- timestamp : string[12];
- procedure createarhdr(fn:string;asize:longint;const gid,uid,mode:string);
- procedure writear;
- end;
- tarobjectreader=class(tobjectreader)
- private
- ArSymbols : TFPHashObjectList;
- LFNStrs : PChar;
- LFNSize : longint;
- CurrMemberPos,
- CurrMemberSize : longint;
- CurrMemberName : string;
- isar: boolean;
- function DecodeMemberName(ahdr:TArHdr):string;
- function DecodeMemberSize(ahdr:TArHdr):longint;
- procedure ReadArchive;
- protected
- function getfilename:string;override;
- function GetPos: longint;override;
- function GetIsArchive: boolean; override;
- public
- constructor createAr(const Aarfn:string;allow_nonar:boolean=false);override;
- destructor destroy;override;
- function openfile(const fn:string):boolean;override;
- procedure closefile;override;
- procedure seek(len:longint);override;
- end;
- implementation
- uses
- SysUtils,
- cstreams,
- systems,
- globals,
- verbose;
- const
- symrelocbufsize = 4096;
- symstrbufsize = 8192;
- lfnstrbufsize = 4096;
- arbufsize = 65536;
- armagic:array[1..8] of char='!<arch>'#10;
- type
- TArSymbol = class(TFPHashObject)
- MemberPos : longint;
- end;
- {*****************************************************************************
- Helpers
- *****************************************************************************}
- const
- C1970=2440588;
- D0=1461;
- D1=146097;
- D2=1721119;
- Function Gregorian2Julian(DT:TSystemTime):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:TSystemTime):LongInt;
- Begin
- DT2Unix:=(Gregorian2Julian(DT)-C1970)*86400+(LongInt(DT.Hour)*3600)+(DT.Minute*60)+DT.Second;
- end;
- function lsb2msb(l:longint):longint;
- type
- bytearr=array[0..3] of byte;
- begin
- {$ifndef FPC_BIG_ENDIAN}
- bytearr(result)[0]:=bytearr(l)[3];
- bytearr(result)[1]:=bytearr(l)[2];
- bytearr(result)[2]:=bytearr(l)[1];
- bytearr(result)[3]:=bytearr(l)[0];
- {$else}
- result:=l;
- {$endif}
- end;
- {*****************************************************************************
- TArObjectWriter
- *****************************************************************************}
- constructor tarobjectwriter.createAr(const Aarfn:string);
- var
- time : TSystemTime;
- begin
- arfn:=Aarfn;
- ardata:=TDynamicArray.Create(arbufsize);
- symreloc:=TDynamicArray.Create(symrelocbufsize);
- symstr:=TDynamicArray.Create(symstrbufsize);
- lfnstr:=TDynamicArray.Create(lfnstrbufsize);
- { create timestamp }
- GetLocalTime(time);
- 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;asize:longint;const gid,uid,mode:string);
- var
- tmp : string[9];
- hfn : string;
- begin
- { create ar header }
- fillchar(arhdr,sizeof(tarhdr),' ');
- { 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:=ExtractFileName(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,length(timestamp));
- str(asize,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;
- fobjsize:=0;
- 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));
- fobjsize:=0;
- 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:longword);
- begin
- inc(fobjsize,len);
- inc(fsize,len);
- ardata.write(b,len);
- end;
- procedure tarobjectwriter.writear;
- var
- arf : TCCustomFileStream;
- fixup,l,
- relocs,i : longint;
- begin
- arf:=CFileStreamClass.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;
- {*****************************************************************************
- TArObjectReader
- *****************************************************************************}
- constructor tarobjectreader.createAr(const Aarfn:string;allow_nonar:boolean);
- var
- magic:array[0..sizeof(armagic)-1] of char;
- begin
- inherited Create;
- ArSymbols:=TFPHashObjectList.Create(true);
- CurrMemberPos:=0;
- CurrMemberSize:=0;
- CurrMemberName:='';
- if inherited openfile(Aarfn) then
- begin
- Read(magic,sizeof(armagic));
- isar:=(CompareByte(magic,armagic,sizeof(armagic))=0);
- if isar then
- ReadArchive
- else if (not allow_nonar) then
- Comment(V_Error,'Not a ar file, illegal magic: '+filename);
- Seek(0);
- end;
- end;
- destructor tarobjectreader.destroy;
- begin
- inherited closefile;
- ArSymbols.Free;
- if assigned(LFNStrs) then
- FreeMem(LFNStrs);
- inherited Destroy;
- end;
- function tarobjectreader.getfilename : string;
- begin
- result:=inherited getfilename;
- if CurrMemberName<>'' then
- result:=result+'('+CurrMemberName+')';
- end;
- function tarobjectreader.GetPos: longint;
- begin
- result:=inherited GetPos-CurrMemberPos;
- end;
- function tarobjectreader.GetIsArchive: boolean;
- begin
- Result:=isar;
- end;
- function tarobjectreader.DecodeMemberName(ahdr:TArHdr):string;
- var
- hs : string;
- code : integer;
- hsp,
- p : pchar;
- lfnidx : longint;
- begin
- result:='';
- p:[email protected][0];
- hsp:=@hs[1];
- while (p^<>' ') and (hsp-@hs[1]<16) do
- begin
- hsp^:=p^;
- inc(p);
- inc(hsp);
- end;
- hs[0]:=chr(hsp-@hs[1]);
- if (hs[1]='/') and (hs[2] in ['0'..'9']) then
- begin
- Delete(hs,1,1);
- val(hs,lfnidx,code);
- if (lfnidx<0) or (lfnidx>=LFNSize) then
- begin
- Comment(V_Error,'Invalid ar member lfn name index in '+filename);
- exit;
- end;
- p:=@LFNStrs[lfnidx];
- hsp:=@result[1];
- while p^<>#10 do
- begin
- hsp^:=p^;
- inc(p);
- inc(hsp);
- end;
- result[0]:=chr(hsp-@result[1]);
- end
- else
- result:=hs;
- { Strip ending / }
- if result[length(result)]='/' then
- dec(result[0]);
- end;
- function tarobjectreader.DecodeMemberSize(ahdr:TArHdr):longint;
- var
- hs : string;
- code : integer;
- hsp,
- p : pchar;
- begin
- p:[email protected][0];
- hsp:=@hs[1];
- while p^<>' ' do
- begin
- hsp^:=p^;
- inc(p);
- inc(hsp);
- end;
- hs[0]:=chr(hsp-@hs[1]);
- val(hs,result,code);
- if result<=0 then
- Comment(V_Error,'Invalid ar member size in '+filename);
- end;
- procedure tarobjectreader.ReadArchive;
- var
- currarhdr : tarhdr;
- nrelocs,
- relocidx,
- currfilesize,
- relocsize,
- symsize : longint;
- arsym : TArSymbol;
- s : string;
- syms,
- currp,
- endp,
- startp : pchar;
- relocs : plongint;
- begin
- Read(currarhdr,sizeof(currarhdr));
- { Read number of relocs }
- Read(nrelocs,sizeof(nrelocs));
- nrelocs:=lsb2msb(nrelocs);
- { Calculate sizes }
- currfilesize:=DecodeMemberSize(currarhdr);
- relocsize:=nrelocs*4;
- symsize:=currfilesize-relocsize-4;
- if symsize<0 then
- begin
- Comment(V_Error,'Illegal symtable in ar file '+filename);
- exit;
- end;
- { Read relocs }
- getmem(Relocs,relocsize);
- Read(relocs^,relocsize);
- { Read symbols, force terminating #0 to prevent overflow }
- getmem(syms,symsize+1);
- syms[symsize]:=#0;
- Read(syms^,symsize);
- { Parse symbols }
- relocidx:=0;
- currp:=syms;
- endp:=syms+symsize;
- for relocidx:=0 to nrelocs-1 do
- begin
- startp:=currp;
- while (currp^<>#0) do
- inc(currp);
- s[0]:=chr(currp-startp);
- move(startp^,s[1],byte(s[0]));
- arsym:=TArSymbol.create(ArSymbols,s);
- arsym.MemberPos:=lsb2msb(relocs[relocidx]);
- inc(currp);
- if currp>endp then
- begin
- Comment(V_Error,'Illegal symtable in ar file '+filename);
- break;
- end;
- end;
- freemem(relocs);
- freemem(syms);
- { LFN names }
- Read(currarhdr,sizeof(currarhdr));
- if DecodeMemberName(currarhdr)='/' then
- begin
- lfnsize:=DecodeMemberSize(currarhdr);
- getmem(lfnstrs,lfnsize);
- Read(lfnstrs^,lfnsize);
- end;
- end;
- function tarobjectreader.openfile(const fn:string):boolean;
- var
- arsym : TArSymbol;
- arhdr : TArHdr;
- begin
- result:=false;
- arsym:=TArSymbol(ArSymbols.Find(fn));
- if not assigned(arsym) then
- exit;
- inherited Seek(arsym.MemberPos);
- Read(arhdr,sizeof(arhdr));
- CurrMemberName:=DecodeMemberName(arhdr);
- CurrMemberSize:=DecodeMemberSize(arhdr);
- CurrMemberPos:=arsym.MemberPos+sizeof(arhdr);
- result:=true;
- end;
- procedure tarobjectreader.closefile;
- begin
- CurrMemberPos:=0;
- CurrMemberSize:=0;
- CurrMemberName:='';
- end;
- procedure tarobjectreader.seek(len:longint);
- begin
- inherited Seek(CurrMemberPos+len);
- end;
- end.
|