owar.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. {
  2. Copyright (c) 1998-2002 by Peter Vreman
  3. Contains the stuff for writing .a files directly
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. unit owar;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. globtype,
  22. cclasses,
  23. owbase;
  24. type
  25. tarhdr=packed record
  26. name : array[0..15] of char;
  27. date : array[0..11] of char;
  28. uid : array[0..5] of char;
  29. gid : array[0..5] of char;
  30. mode : array[0..7] of char;
  31. size : array[0..9] of char;
  32. fmag : array[0..1] of char;
  33. end;
  34. tarobjectwriter=class(tobjectwriter)
  35. constructor createAr(const Aarfn:string);override;
  36. destructor destroy;override;
  37. function createfile(const fn:string):boolean;override;
  38. procedure closefile;override;
  39. procedure writesym(const sym:string);override;
  40. procedure write(const b;len:longword);override;
  41. private
  42. arfn : string;
  43. arhdr : tarhdr;
  44. symreloc,
  45. symstr,
  46. lfnstr,
  47. ardata : TDynamicArray;
  48. objpos : longint;
  49. objfn : string;
  50. timestamp : string[12];
  51. procedure createarhdr(fn:string;asize:longint;const gid,uid,mode:string);
  52. procedure writear;
  53. end;
  54. tarobjectreader=class(tobjectreader)
  55. private
  56. ArSymbols : TFPHashObjectList;
  57. LFNStrs : TAnsiCharDynArray;
  58. LFNSize : longint;
  59. CurrMemberPos,
  60. CurrMemberSize : longint;
  61. CurrMemberName : string;
  62. isar: boolean;
  63. function DecodeMemberName(ahdr:TArHdr):string;
  64. function DecodeMemberSize(ahdr:TArHdr):longint;
  65. procedure ReadArchive;
  66. protected
  67. function getfilename:string;override;
  68. function GetSize: longint;override;
  69. function GetPos: longint;override;
  70. function GetIsArchive: boolean; override;
  71. public
  72. constructor createAr(const Aarfn:string;allow_nonar:boolean=false);override;
  73. destructor destroy;override;
  74. function openfile(const fn:string):boolean;override;
  75. procedure closefile;override;
  76. procedure seek(len:longint);override;
  77. end;
  78. implementation
  79. uses
  80. SysUtils,
  81. cstreams,
  82. systems,
  83. globals,
  84. verbose;
  85. const
  86. symrelocbufsize = 4096;
  87. symstrbufsize = 8192;
  88. lfnstrbufsize = 4096;
  89. arbufsize = 65536;
  90. armagic:array[1..8] of char='!<arch>'#10;
  91. type
  92. TArSymbol = class(TFPHashObject)
  93. MemberPos : longint;
  94. end;
  95. {*****************************************************************************
  96. Helpers
  97. *****************************************************************************}
  98. const
  99. C1970=2440588;
  100. D0=1461;
  101. D1=146097;
  102. D2=1721119;
  103. Function Gregorian2Julian(DT:TSystemTime):LongInt;
  104. Var
  105. Century,XYear,Month : LongInt;
  106. Begin
  107. Month:=DT.Month;
  108. If Month<=2 Then
  109. Begin
  110. Dec(DT.Year);
  111. Inc(Month,12);
  112. End;
  113. Dec(Month,3);
  114. Century:=(longint(DT.Year Div 100)*D1) shr 2;
  115. XYear:=(longint(DT.Year Mod 100)*D0) shr 2;
  116. Gregorian2Julian:=((((Month*153)+2) div 5)+DT.Day)+D2+XYear+Century;
  117. End;
  118. function DT2Unix(DT:TSystemTime):LongInt;
  119. Begin
  120. DT2Unix:=(Gregorian2Julian(DT)-C1970)*86400+(LongInt(DT.Hour)*3600)+(DT.Minute*60)+DT.Second;
  121. end;
  122. function lsb2msb(l:longint):longint;
  123. type
  124. bytearr=array[0..3] of byte;
  125. begin
  126. {$ifndef FPC_BIG_ENDIAN}
  127. bytearr(result)[0]:=bytearr(l)[3];
  128. bytearr(result)[1]:=bytearr(l)[2];
  129. bytearr(result)[2]:=bytearr(l)[1];
  130. bytearr(result)[3]:=bytearr(l)[0];
  131. {$else}
  132. result:=l;
  133. {$endif}
  134. end;
  135. {*****************************************************************************
  136. TArObjectWriter
  137. *****************************************************************************}
  138. constructor tarobjectwriter.createAr(const Aarfn:string);
  139. var
  140. time : TSystemTime;
  141. begin
  142. arfn:=Aarfn;
  143. ardata:=TDynamicArray.Create(arbufsize);
  144. symreloc:=TDynamicArray.Create(symrelocbufsize);
  145. symstr:=TDynamicArray.Create(symstrbufsize);
  146. lfnstr:=TDynamicArray.Create(lfnstrbufsize);
  147. { create timestamp }
  148. GetLocalTime(time);
  149. Str(DT2Unix(time),timestamp);
  150. end;
  151. destructor tarobjectwriter.destroy;
  152. begin
  153. if Errorcount=0 then
  154. writear;
  155. arData.Free;
  156. arData := nil;
  157. symreloc.Free;
  158. symreloc := nil;
  159. symstr.Free;
  160. symstr := nil;
  161. lfnstr.Free;
  162. lfnstr := nil;
  163. end;
  164. procedure tarobjectwriter.createarhdr(fn:string;asize:longint;const gid,uid,mode:string);
  165. var
  166. tmp : string[9];
  167. hfn : string;
  168. begin
  169. { create ar header }
  170. fillchar(arhdr,sizeof(tarhdr),' ');
  171. { win32 will change names starting with .\ to ./ when using lfn, corrupting
  172. the sort order required for the idata sections. To prevent this strip
  173. always the path from the filename. (PFV) }
  174. hfn:=ExtractFileName(fn);
  175. if hfn='' then
  176. hfn:=fn;
  177. fn:=hfn+'/';
  178. if length(fn)>16 then
  179. begin
  180. arhdr.name[0]:='/';
  181. str(lfnstr.size,tmp);
  182. move(tmp[1],arhdr.name[1],length(tmp));
  183. fn:=fn+#10;
  184. lfnstr.write(fn[1],length(fn));
  185. end
  186. else
  187. move(fn[1],arhdr.name,length(fn));
  188. { don't write a date if also no gid/uid/mode is specified }
  189. if gid<>'' then
  190. move(timestamp[1],arhdr.date,length(timestamp));
  191. str(asize,tmp);
  192. move(tmp[1],arhdr.size,length(tmp));
  193. move(gid[1],arhdr.gid,length(gid));
  194. move(uid[1],arhdr.uid,length(uid));
  195. move(mode[1],arhdr.mode,length(mode));
  196. arhdr.fmag:='`'#10;
  197. end;
  198. function tarobjectwriter.createfile(const fn:string):boolean;
  199. begin
  200. objfn:=fn;
  201. objpos:=ardata.size;
  202. ardata.seek(objpos + sizeof(tarhdr));
  203. createfile:=true;
  204. fobjsize:=0;
  205. end;
  206. procedure tarobjectwriter.closefile;
  207. const
  208. LF:char=#10;
  209. var
  210. filesize:longint;
  211. begin
  212. { preserve file size, before aligning on an even boundary }
  213. filesize:=ardata.size-objpos-sizeof(tarhdr);
  214. { align on an even boundary, by inserting an LF if necessary }
  215. if odd(ardata.size) then
  216. write(LF,1);
  217. { fix the size in the header }
  218. createarhdr(objfn,filesize,'42','42','644');
  219. { write the header }
  220. ardata.seek(objpos);
  221. ardata.write(arhdr,sizeof(tarhdr));
  222. fobjsize:=0;
  223. end;
  224. procedure tarobjectwriter.writesym(const sym:string);
  225. var
  226. c : char;
  227. begin
  228. c:=#0;
  229. symreloc.write(objpos,4);
  230. symstr.write(sym[1],length(sym));
  231. symstr.write(c,1);
  232. end;
  233. procedure tarobjectwriter.write(const b;len:longword);
  234. begin
  235. inc(fobjsize,len);
  236. inc(fsize,len);
  237. ardata.write(b,len);
  238. end;
  239. procedure tarobjectwriter.writear;
  240. var
  241. arf : TCCustomFileStream;
  242. fixup,l,
  243. relocs,i : longint;
  244. begin
  245. arf:=CFileStreamClass.Create(arfn,fmCreate);
  246. if CStreamError<>0 then
  247. begin
  248. Message1(exec_e_cant_create_archivefile,arfn);
  249. exit;
  250. end;
  251. arf.Write(armagic,sizeof(armagic));
  252. { align first, because we need the size for the fixups of the symbol reloc }
  253. if lfnstr.size>0 then
  254. lfnstr.align(2);
  255. if symreloc.size>0 then
  256. begin
  257. symstr.align(2);
  258. fixup:=12+sizeof(tarhdr)+symreloc.size+symstr.size;
  259. if lfnstr.size>0 then
  260. inc(fixup,lfnstr.size+sizeof(tarhdr));
  261. relocs:=symreloc.size div 4;
  262. { fixup relocs }
  263. for i:=0to relocs-1 do
  264. begin
  265. symreloc.seek(i*4);
  266. symreloc.read(l,4);
  267. symreloc.seek(i*4);
  268. l:=lsb2msb(l+fixup);
  269. symreloc.write(l,4);
  270. end;
  271. createarhdr('',4+symreloc.size+symstr.size,'0','0','0');
  272. arf.Write(arhdr,sizeof(tarhdr));
  273. relocs:=lsb2msb(relocs);
  274. arf.Write(relocs,4);
  275. symreloc.WriteStream(arf);
  276. symstr.WriteStream(arf);
  277. end;
  278. if lfnstr.size>0 then
  279. begin
  280. createarhdr('/',lfnstr.size,'','','');
  281. arf.Write(arhdr,sizeof(tarhdr));
  282. lfnstr.WriteStream(arf);
  283. end;
  284. ardata.WriteStream(arf);
  285. Arf.Free;
  286. Arf := nil;
  287. end;
  288. {*****************************************************************************
  289. TArObjectReader
  290. *****************************************************************************}
  291. constructor tarobjectreader.createAr(const Aarfn:string;allow_nonar:boolean);
  292. var
  293. magic:array[0..sizeof(armagic)-1] of char;
  294. begin
  295. inherited Create;
  296. ArSymbols:=TFPHashObjectList.Create(true);
  297. CurrMemberPos:=0;
  298. CurrMemberSize:=0;
  299. CurrMemberName:='';
  300. if inherited openfile(Aarfn) then
  301. begin
  302. Read(magic,sizeof(armagic));
  303. isar:=(CompareByte(magic,armagic,sizeof(armagic))=0);
  304. if isar then
  305. ReadArchive
  306. else if (not allow_nonar) then
  307. Comment(V_Error,'Not a ar file, illegal magic: '+filename);
  308. Seek(0);
  309. end;
  310. end;
  311. destructor tarobjectreader.destroy;
  312. begin
  313. inherited closefile;
  314. ArSymbols.Free;
  315. ArSymbols := nil;
  316. LFNStrs:=nil;
  317. inherited Destroy;
  318. end;
  319. function tarobjectreader.getfilename : string;
  320. begin
  321. result:=inherited getfilename;
  322. if CurrMemberName<>'' then
  323. result:=result+'('+CurrMemberName+')';
  324. end;
  325. function tarobjectreader.GetSize: longint;
  326. begin
  327. result:=CurrMemberSize;
  328. end;
  329. function tarobjectreader.GetPos: longint;
  330. begin
  331. result:=inherited GetPos-CurrMemberPos;
  332. end;
  333. function tarobjectreader.GetIsArchive: boolean;
  334. begin
  335. Result:=isar;
  336. end;
  337. function tarobjectreader.DecodeMemberName(ahdr:TArHdr):string;
  338. var
  339. hs : string;
  340. code : integer;
  341. hsp,
  342. p : pchar;
  343. lfnidx : longint;
  344. begin
  345. result:='';
  346. p:[email protected][0];
  347. hsp:=@hs[1];
  348. while (p^<>' ') and (hsp-@hs[1]<16) do
  349. begin
  350. hsp^:=p^;
  351. inc(p);
  352. inc(hsp);
  353. end;
  354. hs[0]:=chr(hsp-@hs[1]);
  355. if (hs[1]='/') and (hs[2] in ['0'..'9']) then
  356. begin
  357. Delete(hs,1,1);
  358. val(hs,lfnidx,code);
  359. if (lfnidx<0) or (lfnidx>=LFNSize) then
  360. begin
  361. Comment(V_Error,'Invalid ar member lfn name index in '+filename);
  362. exit;
  363. end;
  364. p:=@LFNStrs[lfnidx];
  365. hsp:=@result[1];
  366. while p^<>#10 do
  367. begin
  368. hsp^:=p^;
  369. inc(p);
  370. inc(hsp);
  371. end;
  372. result[0]:=chr(hsp-@result[1]);
  373. end
  374. else
  375. result:=hs;
  376. { Strip ending / }
  377. if result[length(result)]='/' then
  378. dec(result[0]);
  379. end;
  380. function tarobjectreader.DecodeMemberSize(ahdr:TArHdr):longint;
  381. var
  382. hs : string;
  383. code : integer;
  384. hsp,
  385. p : pchar;
  386. begin
  387. p:[email protected][0];
  388. hsp:=@hs[1];
  389. while p^<>' ' do
  390. begin
  391. hsp^:=p^;
  392. inc(p);
  393. inc(hsp);
  394. end;
  395. hs[0]:=chr(hsp-@hs[1]);
  396. val(hs,result,code);
  397. if result<=0 then
  398. Comment(V_Error,'Invalid ar member size in '+filename);
  399. end;
  400. procedure tarobjectreader.ReadArchive;
  401. var
  402. currarhdr : tarhdr;
  403. nrelocs,
  404. relocidx,
  405. currfilesize,
  406. relocsize,
  407. symsize : longint;
  408. arsym : TArSymbol;
  409. s : string;
  410. currp,
  411. endp,
  412. startp : integer;
  413. syms : TAnsiCharDynArray;
  414. relocs : Array of longint;
  415. begin
  416. Read(currarhdr,sizeof(currarhdr));
  417. { Read number of relocs }
  418. Read(nrelocs,sizeof(nrelocs));
  419. nrelocs:=lsb2msb(nrelocs);
  420. { Calculate sizes }
  421. currfilesize:=DecodeMemberSize(currarhdr);
  422. relocsize:=nrelocs*4;
  423. symsize:=currfilesize-relocsize-4;
  424. if symsize<0 then
  425. begin
  426. Comment(V_Error,'Illegal symtable in ar file '+filename);
  427. exit;
  428. end;
  429. { Read relocs }
  430. setlength(Relocs,relocsize);
  431. Read(relocs[0],relocsize);
  432. { Read symbols, force terminating #0 to prevent overflow }
  433. setlength(syms,symsize+1);
  434. syms[symsize]:=#0;
  435. Read(syms[0],symsize);
  436. { Parse symbols }
  437. relocidx:=0;
  438. currp:=0;
  439. endp:=symsize;
  440. for relocidx:=0 to nrelocs-1 do
  441. begin
  442. startp:=currp;
  443. while (syms[currp]<>#0) do
  444. inc(currp);
  445. SetLength(s,currp-startp);
  446. move(syms[startp],s[1],currp-startp);
  447. arsym:=TArSymbol.create(ArSymbols,s);
  448. arsym.MemberPos:=lsb2msb(relocs[relocidx]);
  449. inc(currp);
  450. if currp>endp then
  451. begin
  452. Comment(V_Error,'Illegal symtable in ar file '+filename);
  453. break;
  454. end;
  455. end;
  456. relocs:=nil;
  457. syms:=nil;
  458. { LFN names }
  459. Read(currarhdr,sizeof(currarhdr));
  460. if DecodeMemberName(currarhdr)='/' then
  461. begin
  462. lfnsize:=DecodeMemberSize(currarhdr);
  463. setLength(lfnstrs,lfnsize);
  464. Read(lfnstrs[0],lfnsize);
  465. end;
  466. end;
  467. function tarobjectreader.openfile(const fn:string):boolean;
  468. var
  469. arsym : TArSymbol;
  470. arhdr : TArHdr;
  471. begin
  472. result:=false;
  473. arsym:=TArSymbol(ArSymbols.Find(fn));
  474. if not assigned(arsym) then
  475. exit;
  476. inherited Seek(arsym.MemberPos);
  477. Read(arhdr,sizeof(arhdr));
  478. CurrMemberName:=DecodeMemberName(arhdr);
  479. CurrMemberSize:=DecodeMemberSize(arhdr);
  480. CurrMemberPos:=arsym.MemberPos+sizeof(arhdr);
  481. result:=true;
  482. end;
  483. procedure tarobjectreader.closefile;
  484. begin
  485. CurrMemberPos:=0;
  486. CurrMemberSize:=0;
  487. CurrMemberName:='';
  488. end;
  489. procedure tarobjectreader.seek(len:longint);
  490. begin
  491. inherited Seek(CurrMemberPos+len);
  492. end;
  493. end.