Tar.Mod 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. MODULE Tar; (** AUTHOR "ejz/FN"; PURPOSE "Aos tar program"; *)
  2. IMPORT
  3. Commands, Streams, Files, KernelLog, Strings, Archives, Locks;
  4. CONST
  5. RecordSize = 512;
  6. NamSiz = 100; TuNmLen = 32; TgNmLen = 32;
  7. EntryNameSize = 128;
  8. SegmentSize = 1024*8;
  9. StreamClosed* = 10; (** error *)
  10. TYPE
  11. Header = POINTER TO RECORD
  12. name: ARRAY NamSiz OF CHAR;
  13. mode: ARRAY 8 OF CHAR;
  14. uid: ARRAY 8 OF CHAR;
  15. gid: ARRAY 8 OF CHAR;
  16. size: ARRAY 12 OF CHAR;
  17. mtime: ARRAY 12 OF CHAR;
  18. chksum: ARRAY 8 OF CHAR;
  19. linkflag: ARRAY 1 OF CHAR;
  20. linkname: ARRAY NamSiz OF CHAR;
  21. magic: ARRAY 8 OF CHAR;
  22. uname: ARRAY TuNmLen OF CHAR;
  23. gname: ARRAY TgNmLen OF CHAR;
  24. devmajor: ARRAY 8 OF CHAR;
  25. devminor: ARRAY 8 OF CHAR;
  26. END;
  27. (** contains info about an archive entry *)
  28. EntryInfo*= OBJECT(Archives.EntryInfo)
  29. VAR
  30. name : ARRAY EntryNameSize OF CHAR;
  31. size : LONGINT;
  32. PROCEDURE & Init*(CONST name : ARRAY OF CHAR; size : LONGINT);
  33. BEGIN
  34. COPY(name, SELF.name); SELF.size := size
  35. END Init;
  36. PROCEDURE GetName*() : Strings.String;
  37. VAR n : Strings.String;
  38. BEGIN
  39. NEW(n, EntryNameSize); COPY(name, n^);
  40. RETURN n
  41. END GetName;
  42. PROCEDURE GetSize*() : LONGINT;
  43. BEGIN
  44. RETURN size
  45. END GetSize;
  46. PROCEDURE GetInfoString*() : Strings.String;
  47. VAR s : Strings.String;
  48. temp : ARRAY 10 OF CHAR;
  49. BEGIN
  50. NEW(s, 128);
  51. Strings.Append(s^, "Name : ");
  52. Strings.Append(s^, name);
  53. Strings.Append(s^, "; Size : ");
  54. Strings.IntToStr(size, temp);
  55. Strings.Append(s^, temp);
  56. Strings.Append(s^, ";");
  57. RETURN s
  58. END GetInfoString;
  59. END EntryInfo;
  60. (* for internal use only. represent an archive entry *)
  61. Entry = OBJECT
  62. VAR
  63. next : Entry;
  64. pos : LONGINT; (* pointer to beginning of entry header in tar file *)
  65. header : Header;
  66. PROCEDURE & Init*;
  67. BEGIN
  68. NEW(header)
  69. END Init;
  70. PROCEDURE SetName(CONST name : ARRAY OF CHAR);
  71. VAR i : LONGINT;
  72. BEGIN
  73. ASSERT(LEN(name) <= NamSiz);
  74. FOR i := 0 TO LEN(name)-1 DO header.name[i] := name[i] END
  75. END SetName;
  76. PROCEDURE SetSize(size : LONGINT);
  77. BEGIN
  78. IntToOctStr(size, SELF.header.size)
  79. END SetSize;
  80. PROCEDURE GetSize() : LONGINT;
  81. VAR i : LONGINT;
  82. BEGIN
  83. OctStrToInt(header.size, i); RETURN i
  84. END GetSize;
  85. PROCEDURE CalculateCheckSum;
  86. BEGIN
  87. CalcCheckSum(header)
  88. END CalculateCheckSum;
  89. END Entry;
  90. (* for internal use only. lets read a specified amount of data *)
  91. SizeReader = OBJECT
  92. VAR input : Streams.Reader;
  93. max : LONGINT;
  94. archive : Archive;
  95. PROCEDURE &Init*(input: Streams.Reader; size: LONGINT; archive : Archive);
  96. BEGIN
  97. SELF.input := input; SELF.max := size; SELF.archive := archive
  98. END Init;
  99. PROCEDURE Receive(VAR buf: ARRAY OF CHAR; ofs, size, min: LONGINT; VAR len: LONGINT; VAR res: WORD);
  100. BEGIN
  101. IF max = 0 THEN
  102. res := -1;
  103. RETURN
  104. END;
  105. IF size < min THEN size := min END;
  106. IF size > max THEN size := max END;
  107. input.Bytes(buf, ofs, size, len);
  108. DEC(max, len);
  109. res := input.res
  110. END Receive;
  111. END SizeReader;
  112. (* for internal use only. abstract buffer class *)
  113. Buffer = OBJECT
  114. PROCEDURE Send(CONST data : ARRAY OF CHAR; ofs, len : LONGINT; propagate : BOOLEAN; VAR res : WORD);
  115. BEGIN HALT(301)
  116. END Send;
  117. END Buffer;
  118. (* used by MemoryBuffer *)
  119. BufferSegment = OBJECT
  120. VAR buf : ARRAY SegmentSize OF CHAR;
  121. next : BufferSegment;
  122. END BufferSegment;
  123. (* infinite memory-data-buffer. Buffers any data sent to 'Send' until propagate is TRUE, then all data is written to 'archive' *)
  124. MemoryBuffer = OBJECT(Buffer)
  125. VAR
  126. first, current : BufferSegment;
  127. segmentCount, currentIndex : LONGINT;
  128. archive : Archive;
  129. name : ARRAY NamSiz OF CHAR;
  130. closed : BOOLEAN;
  131. (* parameters : a: Archive; name: archive entry that will be written to *)
  132. PROCEDURE & Init*(a : Archive; CONST name : ARRAY OF CHAR);
  133. BEGIN
  134. archive := a;
  135. CopyArchiveName(name, SELF.name);
  136. NEW(first);
  137. current := first;
  138. segmentCount := 1;
  139. currentIndex := 0;
  140. closed := FALSE
  141. END Init;
  142. (* buffer any data until propagate is TRUE *)
  143. PROCEDURE Send(CONST data : ARRAY OF CHAR; ofs, len : LONGINT; propagate : BOOLEAN; VAR res : WORD);
  144. VAR i : LONGINT;
  145. BEGIN
  146. IF closed THEN res := StreamClosed; RETURN END;
  147. res := Streams.Ok;
  148. FOR i := 0 TO len-1 DO
  149. IF currentIndex = SegmentSize THEN NewSegment() END;
  150. current.buf[currentIndex] := data[ofs+i];
  151. INC(currentIndex)
  152. END;
  153. IF propagate THEN WriteBuffer(); closed := TRUE END
  154. END Send;
  155. (* extend buffer *)
  156. PROCEDURE NewSegment;
  157. VAR b : BufferSegment;
  158. BEGIN
  159. NEW(b);
  160. current.next := b;
  161. current := b;
  162. INC(segmentCount);
  163. currentIndex := 0
  164. END NewSegment;
  165. (* lock archive for exclusive access and append header::buffer at the end *)
  166. PROCEDURE WriteBuffer;
  167. VAR w : Files.Writer;
  168. size : LONGINT;
  169. e : Entry;
  170. c : BufferSegment;
  171. BEGIN
  172. archive.Acquire;
  173. size := (segmentCount-1)*SegmentSize + currentIndex;
  174. archive.RemoveEntry(name);
  175. NEW(e);
  176. e.SetName(name);
  177. e.SetSize(size);
  178. e.pos := archive.file.Length();
  179. e.CalculateCheckSum();
  180. archive.AddEntryNode(e);
  181. Files.OpenWriter(w, archive.file, e.pos);
  182. (* write header *)
  183. WriteHeader(w, e.header);
  184. (* write data *)
  185. c := first;
  186. WHILE c # current DO
  187. w.Bytes(c.buf, 0, SegmentSize);
  188. c := c.next
  189. END;
  190. w.Bytes(c.buf, 0, currentIndex);
  191. (* padding *)
  192. size := (-size) MOD RecordSize;
  193. WHILE size > 0 DO w.Char(0X); DEC(size) END;
  194. w.Update;
  195. archive.Release
  196. END WriteBuffer;
  197. END MemoryBuffer;
  198. (** tar archive; store a number of files in one archive *)
  199. Archive* = OBJECT(Archives.Archive)
  200. VAR index : Entry;
  201. file : Files.File;
  202. lock : Locks.RecursiveLock;
  203. PROCEDURE & Init*(f : Files.File);
  204. BEGIN
  205. f.GetName(name);
  206. file := f;
  207. BuildIndex();
  208. NEW(lock)
  209. END Init;
  210. PROCEDURE Acquire*;
  211. BEGIN
  212. lock.Acquire
  213. END Acquire;
  214. PROCEDURE Release*;
  215. BEGIN
  216. lock.Release
  217. END Release;
  218. (** return list of archive entries *)
  219. PROCEDURE GetIndex*() : Archives.Index;
  220. VAR i : LONGINT;
  221. e : Entry;
  222. result : Archives.Index;
  223. ei : EntryInfo;
  224. BEGIN
  225. ASSERT(lock.HasLock());
  226. i := 0;
  227. e := index;
  228. WHILE e # NIL DO INC(i); e := e.next END;
  229. NEW(result, i);
  230. i := 0;
  231. e := index;
  232. WHILE e # NIL DO
  233. NEW(ei, e.header.name, e.GetSize());
  234. result[i] := ei;
  235. e := e.next;
  236. INC(i)
  237. END;
  238. RETURN result
  239. END GetIndex;
  240. (** get info for a specific entry. return NIL if no such entry exists *)
  241. PROCEDURE GetEntryInfo*(CONST name : ARRAY OF CHAR) : Archives.EntryInfo;
  242. VAR e : Entry;
  243. ei : EntryInfo;
  244. BEGIN
  245. e := FindEntry(name);
  246. IF e = NIL THEN RETURN NIL END;
  247. NEW(ei, e.header.name, e.GetSize());
  248. RETURN ei
  249. END GetEntryInfo;
  250. (** remove named entry *)
  251. PROCEDURE RemoveEntry*(CONST name : ARRAY OF CHAR);
  252. VAR newFile : Files.File;
  253. in : Files.Reader;
  254. out : Files.Writer;
  255. hdr : Header;
  256. pos, size: LONGINT;
  257. BEGIN
  258. ASSERT(lock.HasLock());
  259. newFile := Files.New(SELF.name);
  260. Files.Register(newFile);
  261. Files.OpenWriter(out, newFile, 0);
  262. NEW(hdr);
  263. pos := 0; Files.OpenReader(in, file, 0);
  264. WHILE (in.res = Streams.Ok) & ReadHeader(in, hdr) DO
  265. OctStrToInt(hdr.size, size);
  266. size := size + ((-size) MOD RecordSize); (* entry + padding *)
  267. IF hdr.name # name THEN
  268. WriteHeader(out, hdr);
  269. Files.OpenReader(in, file, pos + RecordSize);
  270. TransferBytes(in, out, size)
  271. END;
  272. pos := pos + RecordSize + size;
  273. Files.OpenReader(in, file, pos);
  274. NEW(hdr)
  275. END;
  276. out.Update;
  277. file := newFile;
  278. BuildIndex()
  279. END RemoveEntry;
  280. (** rename an archive entry. return new EntryInfo or NIL if failed. *)
  281. PROCEDURE RenameEntry*(CONST from, to : ARRAY OF CHAR) : Archives.EntryInfo;
  282. VAR e : Entry;
  283. w : Files.Writer;
  284. ei : EntryInfo;
  285. BEGIN
  286. ASSERT(lock.HasLock());
  287. e := FindEntry(from);
  288. IF e = NIL THEN RETURN NIL END;
  289. COPY(to, e.header.name);
  290. CalcCheckSum(e.header);
  291. Files.OpenWriter(w, file, e.pos);
  292. WriteHeader(w, e.header);
  293. w.Update();
  294. NEW(ei, to, e.GetSize());
  295. RETURN ei
  296. END RenameEntry;
  297. (** open a sender to write an entry with name to archive. the data will be written when Update is called *)
  298. PROCEDURE OpenSender*(CONST name : ARRAY OF CHAR) : Streams.Sender;
  299. VAR buffer : MemoryBuffer;
  300. BEGIN
  301. ASSERT(lock.HasLock());
  302. ASSERT(name # "");
  303. NEW(buffer, SELF, name);
  304. RETURN buffer.Send
  305. END OpenSender;
  306. (** read entry from archive *)
  307. PROCEDURE OpenReceiver*(CONST name : ARRAY OF CHAR) : Streams.Receiver;
  308. VAR r : Files.Reader;
  309. s : SizeReader;
  310. size : LONGINT;
  311. entry : Entry;
  312. BEGIN
  313. ASSERT(lock.HasLock());
  314. entry := FindEntry(name);
  315. IF entry = NIL THEN RETURN NIL END;
  316. Files.OpenReader(r, file, entry.pos+RecordSize);
  317. OctStrToInt(entry.header.size, size);
  318. NEW(s, r, size, SELF);
  319. RETURN s.Receive
  320. END OpenReceiver;
  321. (** save a clone of the archive under a different name *)
  322. PROCEDURE Copy*(CONST name : ARRAY OF CHAR) : Archives.Archive;
  323. VAR copy : Archive;
  324. new : Files.File;
  325. BEGIN
  326. ASSERT(lock.HasLock());
  327. new := Files.New(name);
  328. CopyFiles(file, new);
  329. Files.Register(new);
  330. NEW(copy, new);
  331. RETURN copy
  332. END Copy;
  333. (* ----- internal functions ------------------------------------------------*)
  334. (* build internal index structure *)
  335. PROCEDURE BuildIndex;
  336. VAR in : Files.Reader;
  337. hdr : Header;
  338. pos, size : LONGINT;
  339. e : Entry;
  340. BEGIN
  341. index := NIL;
  342. NEW(hdr);
  343. pos := 0; Files.OpenReader(in, file, 0);
  344. WHILE (in.res = Streams.Ok) & ReadHeader(in, hdr) DO
  345. NEW(e); e.header := hdr;
  346. AddEntryNode(e);
  347. OctStrToInt(hdr.size, size);
  348. e.pos := pos;
  349. pos := pos + RecordSize + size + ((-size) MOD RecordSize);
  350. IF in.CanSetPos() THEN
  351. in.SetPos(pos)
  352. ELSE
  353. Files.OpenReader(in, file, pos);
  354. END;
  355. NEW(hdr)
  356. END;
  357. IF (in.res = Streams.Ok) & (hdr.chksum # "") THEN
  358. KernelLog.String(hdr.name); KernelLog.String(" checksum error"); KernelLog.Ln
  359. END
  360. END BuildIndex;
  361. (* return Entry with name, return NIL if not found *)
  362. PROCEDURE FindEntry(CONST name : ARRAY OF CHAR) : Entry;
  363. VAR e : Entry;
  364. BEGIN
  365. e := index;
  366. WHILE e # NIL DO
  367. IF e.header.name = name THEN RETURN e END;
  368. e := e.next
  369. END;
  370. RETURN NIL
  371. END FindEntry;
  372. (* for internal use only. add an entry to the archive *)
  373. PROCEDURE AddEntryNode(e : Entry);
  374. BEGIN
  375. e.next := index; index := e
  376. END AddEntryNode;
  377. END Archive;
  378. (* ----- helpers ---------------------------------------------------------------------- *)
  379. PROCEDURE ReadHeaderBytes(R: Streams.Reader; VAR buf: ARRAY OF CHAR; len: LONGINT; VAR chksum: LONGINT);
  380. VAR i: LONGINT; ch: CHAR;
  381. BEGIN
  382. i := 0;
  383. WHILE i < len DO
  384. R.Char(ch); buf[i] := ch;
  385. INC(chksum, ORD(ch)); INC(i)
  386. END
  387. END ReadHeaderBytes;
  388. PROCEDURE ReadHeader(R: Streams.Reader; VAR hdr: Header): BOOLEAN;
  389. VAR chksum, chksum2, len: LONGINT;
  390. BEGIN
  391. ASSERT(hdr # NIL);
  392. chksum := 0;
  393. ReadHeaderBytes(R, hdr.name, NamSiz, chksum);
  394. ReadHeaderBytes(R, hdr.mode, 8, chksum);
  395. ReadHeaderBytes(R, hdr.uid, 8, chksum);
  396. ReadHeaderBytes(R, hdr.gid, 8, chksum);
  397. ReadHeaderBytes(R, hdr.size, 12, chksum);
  398. ReadHeaderBytes(R, hdr.mtime, 12, chksum);
  399. R.Bytes(hdr.chksum, 0, 8, len);
  400. ReadHeaderBytes(R, hdr.linkflag, 1, chksum);
  401. ReadHeaderBytes(R, hdr.linkname, NamSiz, chksum);
  402. ReadHeaderBytes(R, hdr.magic, 8, chksum);
  403. ReadHeaderBytes(R, hdr.uname, TuNmLen, chksum);
  404. ReadHeaderBytes(R, hdr.gname, TgNmLen, chksum);
  405. ReadHeaderBytes(R, hdr.devmajor, 8, chksum);
  406. ReadHeaderBytes(R, hdr.devminor, 8, chksum);
  407. INC(chksum, 8*32); OctStrToInt(hdr.chksum, chksum2);
  408. RETURN chksum = chksum2
  409. END ReadHeader;
  410. PROCEDURE Empty(VAR buf: ARRAY OF CHAR; len: LONGINT);
  411. VAR i: LONGINT;
  412. BEGIN
  413. i := 0; WHILE i < len DO buf[i] := 0X; INC(i) END
  414. END Empty;
  415. PROCEDURE EmptyHeader(VAR hdr: Header);
  416. BEGIN
  417. ASSERT(hdr # NIL);
  418. Empty(hdr.name, NamSiz);
  419. Empty(hdr.mode, 8);
  420. Empty(hdr.uid, 8);
  421. Empty(hdr.gid, 8);
  422. Empty(hdr.size, 12);
  423. Empty(hdr.mtime, 12);
  424. Empty(hdr.chksum, 8);
  425. Empty(hdr.linkflag, 1);
  426. Empty(hdr.linkname, NamSiz);
  427. Empty(hdr.magic, 8);
  428. Empty(hdr.uname, TuNmLen);
  429. Empty(hdr.gname, TgNmLen);
  430. Empty(hdr.devmajor, 8);
  431. Empty(hdr.devminor, 8)
  432. END EmptyHeader;
  433. PROCEDURE CheckHeaderBytes(CONST buf: ARRAY OF CHAR; len: LONGINT; VAR chksum: LONGINT);
  434. VAR i: LONGINT;
  435. BEGIN
  436. i := 0; WHILE i < len DO INC(chksum, ORD(buf[i])); INC(i) END
  437. END CheckHeaderBytes;
  438. PROCEDURE CalcCheckSum(VAR hdr: Header);
  439. VAR chksum: LONGINT;
  440. BEGIN
  441. ASSERT(hdr # NIL);
  442. CheckHeaderBytes(hdr.name, NamSiz, chksum);
  443. CheckHeaderBytes(hdr.mode, 8, chksum);
  444. CheckHeaderBytes(hdr.uid, 8, chksum);
  445. CheckHeaderBytes(hdr.gid, 8, chksum);
  446. CheckHeaderBytes(hdr.size, 12, chksum);
  447. CheckHeaderBytes(hdr.mtime, 12, chksum);
  448. CheckHeaderBytes(hdr.linkflag, 1, chksum);
  449. CheckHeaderBytes(hdr.linkname, NamSiz, chksum);
  450. CheckHeaderBytes(hdr.magic, 8, chksum);
  451. CheckHeaderBytes(hdr.uname, TuNmLen, chksum);
  452. CheckHeaderBytes(hdr.gname, TgNmLen, chksum);
  453. CheckHeaderBytes(hdr.devmajor, 8, chksum);
  454. CheckHeaderBytes(hdr.devminor, 8, chksum);
  455. INC(chksum, 8*32);
  456. IntToOctStr(chksum, hdr.chksum)
  457. END CalcCheckSum;
  458. PROCEDURE WriteHeader(W: Streams.Writer; VAR hdr: Header);
  459. VAR i: LONGINT;
  460. BEGIN
  461. ASSERT(hdr # NIL);
  462. W.Bytes(hdr.name, 0, NamSiz);
  463. W.Bytes(hdr.mode, 0, 8);
  464. W.Bytes(hdr.uid, 0, 8);
  465. W.Bytes(hdr.gid, 0, 8);
  466. W.Bytes(hdr.size, 0, 12);
  467. W.Bytes(hdr.mtime, 0, 12);
  468. W.Bytes(hdr.chksum, 0, 8);
  469. W.Bytes(hdr.linkflag, 0, 1);
  470. W.Bytes(hdr.linkname, 0, NamSiz);
  471. W.Bytes(hdr.magic, 0, 8);
  472. W.Bytes(hdr.uname, 0, TuNmLen);
  473. W.Bytes(hdr.gname, 0, TgNmLen);
  474. W.Bytes(hdr.devmajor, 0, 8);
  475. W.Bytes(hdr.devminor, 0, 8);
  476. i := 345;
  477. WHILE i < 512 DO
  478. W.Char(0X); INC(i)
  479. END
  480. END WriteHeader;
  481. PROCEDURE OctStrToInt(CONST str: ARRAY OF CHAR; VAR val: LONGINT);
  482. VAR i, d: LONGINT; ch: CHAR;
  483. BEGIN
  484. i := 0; ch := str[0]; val := 0;
  485. WHILE (ch = " ") DO
  486. INC(i); ch := str[i];
  487. END;
  488. WHILE (ch >= "0") & (ch <= "7") DO
  489. d := ORD(ch) - ORD("0");
  490. INC(i); ch := str[i];
  491. IF val <= ((MAX(LONGINT)-d) DIV 8) THEN
  492. val := 8*val+d
  493. ELSE
  494. HALT(99)
  495. END
  496. END
  497. END OctStrToInt;
  498. PROCEDURE IntToOctStr(val: LONGINT; VAR str: ARRAY OF CHAR);
  499. VAR i: LONGINT;
  500. BEGIN
  501. i := LEN(str)-1; str[i] := 0X;
  502. WHILE i > 0 DO
  503. DEC(i);
  504. str[i] := CHR((val MOD 8) + ORD("0"));
  505. val := val DIV 8
  506. END
  507. END IntToOctStr;
  508. PROCEDURE CopyArchiveName(CONST from : ARRAY OF CHAR; VAR to : ARRAY OF CHAR);
  509. VAR i : LONGINT;
  510. BEGIN
  511. IF LEN(from) < NamSiz THEN i := LEN(from)-1 ELSE i := NamSiz-1 END;
  512. WHILE i > -1 DO to[i] := from[i]; DEC(i) END
  513. END CopyArchiveName;
  514. PROCEDURE Backup(f: Files.File);
  515. VAR old, new: Files.FileName; res: WORD;
  516. BEGIN
  517. f.GetName(old); COPY(old, new);
  518. Strings.Append(new, ".Bak");
  519. KernelLog.String(" "); KernelLog.String(new); KernelLog.Ln();
  520. Files.Rename(old, new, res);
  521. ASSERT(res = 0)
  522. END Backup;
  523. PROCEDURE CopyFiles(VAR from, to : Files.File);
  524. VAR in : Files.Reader;
  525. out : Files.Writer;
  526. BEGIN
  527. Files.OpenReader(in, from, 0);
  528. Files.OpenWriter(out, to, 0);
  529. TransferBytes(in, out, from.Length());
  530. out.Update
  531. END CopyFiles;
  532. PROCEDURE TransferBytes(from : Streams.Reader; to : Streams.Writer; n : LONGINT);
  533. VAR buf : ARRAY 1024 OF CHAR;
  534. len : LONGINT;
  535. BEGIN
  536. WHILE n > 1024 DO
  537. from.Bytes(buf, 0, 1024, len);
  538. to.Bytes(buf, 0, 1024);
  539. DEC(n, 1024)
  540. END;
  541. from.Bytes(buf, 0, n, len);
  542. to.Bytes(buf, 0, n);
  543. to.Update()
  544. END TransferBytes;
  545. (* ----- api --------------------------------------------------------------------------- *)
  546. (** open an existing archive. applications should use the method Old in the superclass *)
  547. PROCEDURE Old*(name : Archives.StringObject) : Archives.Archive;
  548. VAR archive : Archive; file : Files.File;
  549. BEGIN
  550. file := Files.Old(name.value);
  551. IF file = NIL THEN
  552. RETURN NIL
  553. ELSE
  554. NEW(archive, file);
  555. RETURN archive
  556. END
  557. END Old;
  558. (** create a new archive, overwrite existing. applications should use the method New in the superclass *)
  559. PROCEDURE New*(name : Archives.StringObject) :Archives.Archive;
  560. VAR archive : Archive; file : Files.File;
  561. BEGIN
  562. file := Files.New(name.value);
  563. Files.Register(file);
  564. NEW(archive, file);
  565. RETURN archive
  566. END New;
  567. (* ----- command line tools --------------------------------------------------------------- *)
  568. PROCEDURE List*(context : Commands.Context);
  569. VAR
  570. fn: Files.FileName; F: Files.File; R: Files.Reader;
  571. hdr: Header; pos, size: LONGINT;
  572. BEGIN
  573. context.arg.SkipWhitespace; context.arg.String(fn);
  574. F := Files.Old(fn);
  575. IF F = NIL THEN context.out.String(fn); context.out.String(" : no such file found."); context.out.Ln; RETURN END;
  576. NEW(hdr);
  577. pos := 0; Files.OpenReader(R, F, 0);
  578. WHILE (R.res = Streams.Ok) & ReadHeader(R, hdr) DO
  579. context.out.String(hdr.name); context.out.String(" ");
  580. OctStrToInt(hdr.size, size);
  581. context.out.Int(size, 0); context.out.Ln;
  582. pos := pos + RecordSize + size + ((-size) MOD RecordSize);
  583. Files.OpenReader(R, F, pos)
  584. END;
  585. IF (R.res = Streams.Ok) & (hdr.chksum # "") THEN
  586. context.out.String(hdr.name); context.out.String(" checksum error"); context.out.Ln;
  587. END;
  588. END List;
  589. PROCEDURE Extract*(context : Commands.Context);
  590. VAR
  591. fn: Files.FileName; F, f: Files.File; R: Files.Reader; w: Files.Writer;
  592. hdr: Header; pos, size, i: LONGINT; ch: CHAR;
  593. BEGIN
  594. context.arg.SkipWhitespace; context.arg.String(fn);
  595. F := Files.Old(fn);
  596. IF F = NIL THEN context.out.String(fn); context.out.String(" : no such file found."); context.out.Ln; RETURN END;
  597. NEW(hdr);
  598. pos := 0; Files.OpenReader(R, F, 0);
  599. WHILE (R.res = Streams.Ok) & ReadHeader(R, hdr) DO
  600. context.out.String(hdr.name); context.out.String(" ");
  601. OctStrToInt(hdr.size, size);
  602. context.out.Int(size, 0); context.out.Ln;
  603. f := Files.Old(hdr.name);
  604. IF f # NIL THEN Backup(f) END;
  605. f := Files.New(hdr.name); Files.OpenWriter(w, f, 0);
  606. Files.OpenReader(R, F, pos + RecordSize);
  607. i := 0;
  608. WHILE i < size DO
  609. R.Char(ch); w.Char(ch); INC(i)
  610. END;
  611. w.Update(); Files.Register(f);
  612. pos := pos + RecordSize + size + ((-size) MOD RecordSize);
  613. Files.OpenReader(R, F, pos)
  614. END;
  615. IF (R.res = Streams.Ok) & (hdr.chksum # "") THEN
  616. context.out.String(hdr.name); context.out.String(" checksum error"); context.out.Ln()
  617. END;
  618. END Extract;
  619. PROCEDURE Create*(context : Commands.Context);
  620. VAR
  621. fn, archivename: Files.FileName; F, f: Files.File; W: Files.Writer; r: Files.Reader;
  622. hdr: Header; size, i: LONGINT; ch: CHAR;
  623. nofAdded, nofErrors : LONGINT;
  624. BEGIN
  625. context.arg.SkipWhitespace; context.arg.String(archivename);
  626. context.out.String("Creating "); context.out.String(archivename); context.out.Ln;
  627. F := Files.New(archivename); Files.OpenWriter(W, F, 0);
  628. nofAdded := 0; nofErrors := 0;
  629. WHILE context.arg.GetString(fn) DO
  630. f := Files.Old(fn);
  631. IF f # NIL THEN
  632. Files.OpenReader(r, f, 0); size := f.Length();
  633. NEW(hdr); COPY(fn, hdr.name);
  634. IntToOctStr(size, hdr.size);
  635. CalcCheckSum(hdr);
  636. WriteHeader(W, hdr);
  637. i := 0;
  638. WHILE i < size DO
  639. r.Char(ch); W.Char(ch); INC(i)
  640. END;
  641. size := (-size) MOD RecordSize;
  642. WHILE size > 0 DO
  643. W.Char(0X); DEC(size)
  644. END;
  645. INC(nofAdded);
  646. context.out.String(fn); context.out.String(" added"); context.out.Ln;
  647. ELSE
  648. INC(nofErrors);
  649. context.out.String(fn); context.out.String(" not found"); context.out.Ln;
  650. END;
  651. END;
  652. EmptyHeader(hdr); WriteHeader(W, hdr);
  653. W.Update(); Files.Register(F);
  654. context.out.String("Added "); context.out.Int(nofAdded, 0); context.out.String(" files to archive ");
  655. context.out.String(archivename);
  656. IF nofErrors > 0 THEN
  657. context.out.String(" ("); context.out.Int(nofErrors, 0); context.out.String(" errors)");
  658. END;
  659. context.out.Ln;
  660. END Create;
  661. END Tar.
  662. System.Free Tar ~