owar.pas 15 KB

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