owar.pas 14 KB

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