123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734 |
- MODULE Tar; (** AUTHOR "ejz/FN"; PURPOSE "Aos tar program"; *)
- IMPORT
- Commands, Streams, Files, KernelLog, Strings, Archives, Locks;
- CONST
- RecordSize = 512;
- NamSiz = 100; TuNmLen = 32; TgNmLen = 32;
- EntryNameSize = 128;
- SegmentSize = 1024*8;
- StreamClosed* = 10; (** error *)
- TYPE
- Header = POINTER TO RECORD
- name: ARRAY NamSiz OF CHAR;
- mode: ARRAY 8 OF CHAR;
- uid: ARRAY 8 OF CHAR;
- gid: ARRAY 8 OF CHAR;
- size: ARRAY 12 OF CHAR;
- mtime: ARRAY 12 OF CHAR;
- chksum: ARRAY 8 OF CHAR;
- linkflag: ARRAY 1 OF CHAR;
- linkname: ARRAY NamSiz OF CHAR;
- magic: ARRAY 8 OF CHAR;
- uname: ARRAY TuNmLen OF CHAR;
- gname: ARRAY TgNmLen OF CHAR;
- devmajor: ARRAY 8 OF CHAR;
- devminor: ARRAY 8 OF CHAR;
- END;
- (** contains info about an archive entry *)
- EntryInfo*= OBJECT(Archives.EntryInfo)
- VAR
- name : ARRAY EntryNameSize OF CHAR;
- size : LONGINT;
- PROCEDURE & Init*(CONST name : ARRAY OF CHAR; size : LONGINT);
- BEGIN
- COPY(name, SELF.name); SELF.size := size
- END Init;
- PROCEDURE GetName*() : Strings.String;
- VAR n : Strings.String;
- BEGIN
- NEW(n, EntryNameSize); COPY(name, n^);
- RETURN n
- END GetName;
- PROCEDURE GetSize*() : LONGINT;
- BEGIN
- RETURN size
- END GetSize;
- PROCEDURE GetInfoString*() : Strings.String;
- VAR s : Strings.String;
- temp : ARRAY 10 OF CHAR;
- BEGIN
- NEW(s, 128);
- Strings.Append(s^, "Name : ");
- Strings.Append(s^, name);
- Strings.Append(s^, "; Size : ");
- Strings.IntToStr(size, temp);
- Strings.Append(s^, temp);
- Strings.Append(s^, ";");
- RETURN s
- END GetInfoString;
- END EntryInfo;
- (* for internal use only. represent an archive entry *)
- Entry = OBJECT
- VAR
- next : Entry;
- pos : LONGINT; (* pointer to beginning of entry header in tar file *)
- header : Header;
- PROCEDURE & Init*;
- BEGIN
- NEW(header)
- END Init;
- PROCEDURE SetName(CONST name : ARRAY OF CHAR);
- VAR i : LONGINT;
- BEGIN
- ASSERT(LEN(name) <= NamSiz);
- FOR i := 0 TO LEN(name)-1 DO header.name[i] := name[i] END
- END SetName;
- PROCEDURE SetSize(size : LONGINT);
- BEGIN
- IntToOctStr(size, SELF.header.size)
- END SetSize;
- PROCEDURE GetSize() : LONGINT;
- VAR i : LONGINT;
- BEGIN
- OctStrToInt(header.size, i); RETURN i
- END GetSize;
- PROCEDURE CalculateCheckSum;
- BEGIN
- CalcCheckSum(header)
- END CalculateCheckSum;
- END Entry;
- (* for internal use only. lets read a specified amount of data *)
- SizeReader = OBJECT
- VAR input : Streams.Reader;
- max : LONGINT;
- archive : Archive;
- PROCEDURE &Init*(input: Streams.Reader; size: LONGINT; archive : Archive);
- BEGIN
- SELF.input := input; SELF.max := size; SELF.archive := archive
- END Init;
- PROCEDURE Receive(VAR buf: ARRAY OF CHAR; ofs, size, min: LONGINT; VAR len: LONGINT; VAR res: WORD);
- BEGIN
- IF max = 0 THEN
- res := -1;
- RETURN
- END;
- IF size < min THEN size := min END;
- IF size > max THEN size := max END;
- input.Bytes(buf, ofs, size, len);
- DEC(max, len);
- res := input.res
- END Receive;
- END SizeReader;
- (* for internal use only. abstract buffer class *)
- Buffer = OBJECT
- PROCEDURE Send(CONST data : ARRAY OF CHAR; ofs, len : LONGINT; propagate : BOOLEAN; VAR res : WORD);
- BEGIN HALT(301)
- END Send;
- END Buffer;
- (* used by MemoryBuffer *)
- BufferSegment = OBJECT
- VAR buf : ARRAY SegmentSize OF CHAR;
- next : BufferSegment;
- END BufferSegment;
- (* infinite memory-data-buffer. Buffers any data sent to 'Send' until propagate is TRUE, then all data is written to 'archive' *)
- MemoryBuffer = OBJECT(Buffer)
- VAR
- first, current : BufferSegment;
- segmentCount, currentIndex : LONGINT;
- archive : Archive;
- name : ARRAY NamSiz OF CHAR;
- closed : BOOLEAN;
- (* parameters : a: Archive; name: archive entry that will be written to *)
- PROCEDURE & Init*(a : Archive; CONST name : ARRAY OF CHAR);
- BEGIN
- archive := a;
- CopyArchiveName(name, SELF.name);
- NEW(first);
- current := first;
- segmentCount := 1;
- currentIndex := 0;
- closed := FALSE
- END Init;
- (* buffer any data until propagate is TRUE *)
- PROCEDURE Send(CONST data : ARRAY OF CHAR; ofs, len : LONGINT; propagate : BOOLEAN; VAR res : WORD);
- VAR i : LONGINT;
- BEGIN
- IF closed THEN res := StreamClosed; RETURN END;
- res := Streams.Ok;
- FOR i := 0 TO len-1 DO
- IF currentIndex = SegmentSize THEN NewSegment() END;
- current.buf[currentIndex] := data[ofs+i];
- INC(currentIndex)
- END;
- IF propagate THEN WriteBuffer(); closed := TRUE END
- END Send;
- (* extend buffer *)
- PROCEDURE NewSegment;
- VAR b : BufferSegment;
- BEGIN
- NEW(b);
- current.next := b;
- current := b;
- INC(segmentCount);
- currentIndex := 0
- END NewSegment;
- (* lock archive for exclusive access and append header::buffer at the end *)
- PROCEDURE WriteBuffer;
- VAR w : Files.Writer;
- size : LONGINT;
- e : Entry;
- c : BufferSegment;
- BEGIN
- archive.Acquire;
- size := (segmentCount-1)*SegmentSize + currentIndex;
- archive.RemoveEntry(name);
- NEW(e);
- e.SetName(name);
- e.SetSize(size);
- e.pos := archive.file.Length();
- e.CalculateCheckSum();
- archive.AddEntryNode(e);
- Files.OpenWriter(w, archive.file, e.pos);
- (* write header *)
- WriteHeader(w, e.header);
- (* write data *)
- c := first;
- WHILE c # current DO
- w.Bytes(c.buf, 0, SegmentSize);
- c := c.next
- END;
- w.Bytes(c.buf, 0, currentIndex);
- (* padding *)
- size := (-size) MOD RecordSize;
- WHILE size > 0 DO w.Char(0X); DEC(size) END;
- w.Update;
- archive.Release
- END WriteBuffer;
- END MemoryBuffer;
- (** tar archive; store a number of files in one archive *)
- Archive* = OBJECT(Archives.Archive)
- VAR index : Entry;
- file : Files.File;
- lock : Locks.RecursiveLock;
- PROCEDURE & Init*(f : Files.File);
- BEGIN
- f.GetName(name);
- file := f;
- BuildIndex();
- NEW(lock)
- END Init;
- PROCEDURE Acquire*;
- BEGIN
- lock.Acquire
- END Acquire;
- PROCEDURE Release*;
- BEGIN
- lock.Release
- END Release;
- (** return list of archive entries *)
- PROCEDURE GetIndex*() : Archives.Index;
- VAR i : LONGINT;
- e : Entry;
- result : Archives.Index;
- ei : EntryInfo;
- BEGIN
- ASSERT(lock.HasLock());
- i := 0;
- e := index;
- WHILE e # NIL DO INC(i); e := e.next END;
- NEW(result, i);
- i := 0;
- e := index;
- WHILE e # NIL DO
- NEW(ei, e.header.name, e.GetSize());
- result[i] := ei;
- e := e.next;
- INC(i)
- END;
- RETURN result
- END GetIndex;
- (** get info for a specific entry. return NIL if no such entry exists *)
- PROCEDURE GetEntryInfo*(CONST name : ARRAY OF CHAR) : Archives.EntryInfo;
- VAR e : Entry;
- ei : EntryInfo;
- BEGIN
- e := FindEntry(name);
- IF e = NIL THEN RETURN NIL END;
- NEW(ei, e.header.name, e.GetSize());
- RETURN ei
- END GetEntryInfo;
- (** remove named entry *)
- PROCEDURE RemoveEntry*(CONST name : ARRAY OF CHAR);
- VAR newFile : Files.File;
- in : Files.Reader;
- out : Files.Writer;
- hdr : Header;
- pos, size: LONGINT;
- BEGIN
- ASSERT(lock.HasLock());
- newFile := Files.New(SELF.name);
- Files.Register(newFile);
- Files.OpenWriter(out, newFile, 0);
- NEW(hdr);
- pos := 0; Files.OpenReader(in, file, 0);
- WHILE (in.res = Streams.Ok) & ReadHeader(in, hdr) DO
- OctStrToInt(hdr.size, size);
- size := size + ((-size) MOD RecordSize); (* entry + padding *)
- IF hdr.name # name THEN
- WriteHeader(out, hdr);
- Files.OpenReader(in, file, pos + RecordSize);
- TransferBytes(in, out, size)
- END;
- pos := pos + RecordSize + size;
- Files.OpenReader(in, file, pos);
- NEW(hdr)
- END;
- out.Update;
- file := newFile;
- BuildIndex()
- END RemoveEntry;
- (** rename an archive entry. return new EntryInfo or NIL if failed. *)
- PROCEDURE RenameEntry*(CONST from, to : ARRAY OF CHAR) : Archives.EntryInfo;
- VAR e : Entry;
- w : Files.Writer;
- ei : EntryInfo;
- BEGIN
- ASSERT(lock.HasLock());
- e := FindEntry(from);
- IF e = NIL THEN RETURN NIL END;
- COPY(to, e.header.name);
- CalcCheckSum(e.header);
- Files.OpenWriter(w, file, e.pos);
- WriteHeader(w, e.header);
- w.Update();
- NEW(ei, to, e.GetSize());
- RETURN ei
- END RenameEntry;
- (** open a sender to write an entry with name to archive. the data will be written when Update is called *)
- PROCEDURE OpenSender*(CONST name : ARRAY OF CHAR) : Streams.Sender;
- VAR buffer : MemoryBuffer;
- BEGIN
- ASSERT(lock.HasLock());
- ASSERT(name # "");
- NEW(buffer, SELF, name);
- RETURN buffer.Send
- END OpenSender;
- (** read entry from archive *)
- PROCEDURE OpenReceiver*(CONST name : ARRAY OF CHAR) : Streams.Receiver;
- VAR r : Files.Reader;
- s : SizeReader;
- size : LONGINT;
- entry : Entry;
- BEGIN
- ASSERT(lock.HasLock());
- entry := FindEntry(name);
- IF entry = NIL THEN RETURN NIL END;
- Files.OpenReader(r, file, entry.pos+RecordSize);
- OctStrToInt(entry.header.size, size);
- NEW(s, r, size, SELF);
- RETURN s.Receive
- END OpenReceiver;
- (** save a clone of the archive under a different name *)
- PROCEDURE Copy*(CONST name : ARRAY OF CHAR) : Archives.Archive;
- VAR copy : Archive;
- new : Files.File;
- BEGIN
- ASSERT(lock.HasLock());
- new := Files.New(name);
- CopyFiles(file, new);
- Files.Register(new);
- NEW(copy, new);
- RETURN copy
- END Copy;
- (* ----- internal functions ------------------------------------------------*)
- (* build internal index structure *)
- PROCEDURE BuildIndex;
- VAR in : Files.Reader;
- hdr : Header;
- pos, size : LONGINT;
- e : Entry;
- BEGIN
- index := NIL;
- NEW(hdr);
- pos := 0; Files.OpenReader(in, file, 0);
- WHILE (in.res = Streams.Ok) & ReadHeader(in, hdr) DO
- NEW(e); e.header := hdr;
- AddEntryNode(e);
- OctStrToInt(hdr.size, size);
- e.pos := pos;
- pos := pos + RecordSize + size + ((-size) MOD RecordSize);
- IF in.CanSetPos() THEN
- in.SetPos(pos)
- ELSE
- Files.OpenReader(in, file, pos);
- END;
- NEW(hdr)
- END;
- IF (in.res = Streams.Ok) & (hdr.chksum # "") THEN
- KernelLog.String(hdr.name); KernelLog.String(" checksum error"); KernelLog.Ln
- END
- END BuildIndex;
- (* return Entry with name, return NIL if not found *)
- PROCEDURE FindEntry(CONST name : ARRAY OF CHAR) : Entry;
- VAR e : Entry;
- BEGIN
- e := index;
- WHILE e # NIL DO
- IF e.header.name = name THEN RETURN e END;
- e := e.next
- END;
- RETURN NIL
- END FindEntry;
- (* for internal use only. add an entry to the archive *)
- PROCEDURE AddEntryNode(e : Entry);
- BEGIN
- e.next := index; index := e
- END AddEntryNode;
- END Archive;
- (* ----- helpers ---------------------------------------------------------------------- *)
- PROCEDURE ReadHeaderBytes(R: Streams.Reader; VAR buf: ARRAY OF CHAR; len: LONGINT; VAR chksum: LONGINT);
- VAR i: LONGINT; ch: CHAR;
- BEGIN
- i := 0;
- WHILE i < len DO
- R.Char(ch); buf[i] := ch;
- INC(chksum, ORD(ch)); INC(i)
- END
- END ReadHeaderBytes;
- PROCEDURE ReadHeader(R: Streams.Reader; VAR hdr: Header): BOOLEAN;
- VAR chksum, chksum2, len: LONGINT;
- BEGIN
- ASSERT(hdr # NIL);
- chksum := 0;
- ReadHeaderBytes(R, hdr.name, NamSiz, chksum);
- ReadHeaderBytes(R, hdr.mode, 8, chksum);
- ReadHeaderBytes(R, hdr.uid, 8, chksum);
- ReadHeaderBytes(R, hdr.gid, 8, chksum);
- ReadHeaderBytes(R, hdr.size, 12, chksum);
- ReadHeaderBytes(R, hdr.mtime, 12, chksum);
- R.Bytes(hdr.chksum, 0, 8, len);
- ReadHeaderBytes(R, hdr.linkflag, 1, chksum);
- ReadHeaderBytes(R, hdr.linkname, NamSiz, chksum);
- ReadHeaderBytes(R, hdr.magic, 8, chksum);
- ReadHeaderBytes(R, hdr.uname, TuNmLen, chksum);
- ReadHeaderBytes(R, hdr.gname, TgNmLen, chksum);
- ReadHeaderBytes(R, hdr.devmajor, 8, chksum);
- ReadHeaderBytes(R, hdr.devminor, 8, chksum);
- INC(chksum, 8*32); OctStrToInt(hdr.chksum, chksum2);
- RETURN chksum = chksum2
- END ReadHeader;
- PROCEDURE Empty(VAR buf: ARRAY OF CHAR; len: LONGINT);
- VAR i: LONGINT;
- BEGIN
- i := 0; WHILE i < len DO buf[i] := 0X; INC(i) END
- END Empty;
- PROCEDURE EmptyHeader(VAR hdr: Header);
- BEGIN
- ASSERT(hdr # NIL);
- Empty(hdr.name, NamSiz);
- Empty(hdr.mode, 8);
- Empty(hdr.uid, 8);
- Empty(hdr.gid, 8);
- Empty(hdr.size, 12);
- Empty(hdr.mtime, 12);
- Empty(hdr.chksum, 8);
- Empty(hdr.linkflag, 1);
- Empty(hdr.linkname, NamSiz);
- Empty(hdr.magic, 8);
- Empty(hdr.uname, TuNmLen);
- Empty(hdr.gname, TgNmLen);
- Empty(hdr.devmajor, 8);
- Empty(hdr.devminor, 8)
- END EmptyHeader;
- PROCEDURE CheckHeaderBytes(CONST buf: ARRAY OF CHAR; len: LONGINT; VAR chksum: LONGINT);
- VAR i: LONGINT;
- BEGIN
- i := 0; WHILE i < len DO INC(chksum, ORD(buf[i])); INC(i) END
- END CheckHeaderBytes;
- PROCEDURE CalcCheckSum(VAR hdr: Header);
- VAR chksum: LONGINT;
- BEGIN
- ASSERT(hdr # NIL);
- CheckHeaderBytes(hdr.name, NamSiz, chksum);
- CheckHeaderBytes(hdr.mode, 8, chksum);
- CheckHeaderBytes(hdr.uid, 8, chksum);
- CheckHeaderBytes(hdr.gid, 8, chksum);
- CheckHeaderBytes(hdr.size, 12, chksum);
- CheckHeaderBytes(hdr.mtime, 12, chksum);
- CheckHeaderBytes(hdr.linkflag, 1, chksum);
- CheckHeaderBytes(hdr.linkname, NamSiz, chksum);
- CheckHeaderBytes(hdr.magic, 8, chksum);
- CheckHeaderBytes(hdr.uname, TuNmLen, chksum);
- CheckHeaderBytes(hdr.gname, TgNmLen, chksum);
- CheckHeaderBytes(hdr.devmajor, 8, chksum);
- CheckHeaderBytes(hdr.devminor, 8, chksum);
- INC(chksum, 8*32);
- IntToOctStr(chksum, hdr.chksum)
- END CalcCheckSum;
- PROCEDURE WriteHeader(W: Streams.Writer; VAR hdr: Header);
- VAR i: LONGINT;
- BEGIN
- ASSERT(hdr # NIL);
- W.Bytes(hdr.name, 0, NamSiz);
- W.Bytes(hdr.mode, 0, 8);
- W.Bytes(hdr.uid, 0, 8);
- W.Bytes(hdr.gid, 0, 8);
- W.Bytes(hdr.size, 0, 12);
- W.Bytes(hdr.mtime, 0, 12);
- W.Bytes(hdr.chksum, 0, 8);
- W.Bytes(hdr.linkflag, 0, 1);
- W.Bytes(hdr.linkname, 0, NamSiz);
- W.Bytes(hdr.magic, 0, 8);
- W.Bytes(hdr.uname, 0, TuNmLen);
- W.Bytes(hdr.gname, 0, TgNmLen);
- W.Bytes(hdr.devmajor, 0, 8);
- W.Bytes(hdr.devminor, 0, 8);
- i := 345;
- WHILE i < 512 DO
- W.Char(0X); INC(i)
- END
- END WriteHeader;
- PROCEDURE OctStrToInt(CONST str: ARRAY OF CHAR; VAR val: LONGINT);
- VAR i, d: LONGINT; ch: CHAR;
- BEGIN
- i := 0; ch := str[0]; val := 0;
- WHILE (ch = " ") DO
- INC(i); ch := str[i];
- END;
- WHILE (ch >= "0") & (ch <= "7") DO
- d := ORD(ch) - ORD("0");
- INC(i); ch := str[i];
- IF val <= ((MAX(LONGINT)-d) DIV 8) THEN
- val := 8*val+d
- ELSE
- HALT(99)
- END
- END
- END OctStrToInt;
- PROCEDURE IntToOctStr(val: LONGINT; VAR str: ARRAY OF CHAR);
- VAR i: LONGINT;
- BEGIN
- i := LEN(str)-1; str[i] := 0X;
- WHILE i > 0 DO
- DEC(i);
- str[i] := CHR((val MOD 8) + ORD("0"));
- val := val DIV 8
- END
- END IntToOctStr;
- PROCEDURE CopyArchiveName(CONST from : ARRAY OF CHAR; VAR to : ARRAY OF CHAR);
- VAR i : LONGINT;
- BEGIN
- IF LEN(from) < NamSiz THEN i := LEN(from)-1 ELSE i := NamSiz-1 END;
- WHILE i > -1 DO to[i] := from[i]; DEC(i) END
- END CopyArchiveName;
- PROCEDURE Backup(f: Files.File);
- VAR old, new: Files.FileName; res: WORD;
- BEGIN
- f.GetName(old); COPY(old, new);
- Strings.Append(new, ".Bak");
- KernelLog.String(" "); KernelLog.String(new); KernelLog.Ln();
- Files.Rename(old, new, res);
- ASSERT(res = 0)
- END Backup;
- PROCEDURE CopyFiles(VAR from, to : Files.File);
- VAR in : Files.Reader;
- out : Files.Writer;
- BEGIN
- Files.OpenReader(in, from, 0);
- Files.OpenWriter(out, to, 0);
- TransferBytes(in, out, from.Length());
- out.Update
- END CopyFiles;
- PROCEDURE TransferBytes(from : Streams.Reader; to : Streams.Writer; n : LONGINT);
- VAR buf : ARRAY 1024 OF CHAR;
- len : LONGINT;
- BEGIN
- WHILE n > 1024 DO
- from.Bytes(buf, 0, 1024, len);
- to.Bytes(buf, 0, 1024);
- DEC(n, 1024)
- END;
- from.Bytes(buf, 0, n, len);
- to.Bytes(buf, 0, n);
- to.Update()
- END TransferBytes;
- (* ----- api --------------------------------------------------------------------------- *)
- (** open an existing archive. applications should use the method Old in the superclass *)
- PROCEDURE Old*(name : Archives.StringObject) : Archives.Archive;
- VAR archive : Archive; file : Files.File;
- BEGIN
- file := Files.Old(name.value);
- IF file = NIL THEN
- RETURN NIL
- ELSE
- NEW(archive, file);
- RETURN archive
- END
- END Old;
- (** create a new archive, overwrite existing. applications should use the method New in the superclass *)
- PROCEDURE New*(name : Archives.StringObject) :Archives.Archive;
- VAR archive : Archive; file : Files.File;
- BEGIN
- file := Files.New(name.value);
- Files.Register(file);
- NEW(archive, file);
- RETURN archive
- END New;
- (* ----- command line tools --------------------------------------------------------------- *)
- PROCEDURE List*(context : Commands.Context);
- VAR
- fn: Files.FileName; F: Files.File; R: Files.Reader;
- hdr: Header; pos, size: LONGINT;
- BEGIN
- context.arg.SkipWhitespace; context.arg.String(fn);
- F := Files.Old(fn);
- IF F = NIL THEN context.out.String(fn); context.out.String(" : no such file found."); context.out.Ln; RETURN END;
- NEW(hdr);
- pos := 0; Files.OpenReader(R, F, 0);
- WHILE (R.res = Streams.Ok) & ReadHeader(R, hdr) DO
- context.out.String(hdr.name); context.out.String(" ");
- OctStrToInt(hdr.size, size);
- context.out.Int(size, 0); context.out.Ln;
- pos := pos + RecordSize + size + ((-size) MOD RecordSize);
- Files.OpenReader(R, F, pos)
- END;
- IF (R.res = Streams.Ok) & (hdr.chksum # "") THEN
- context.out.String(hdr.name); context.out.String(" checksum error"); context.out.Ln;
- END;
- END List;
- PROCEDURE Extract*(context : Commands.Context);
- VAR
- fn: Files.FileName; F, f: Files.File; R: Files.Reader; w: Files.Writer;
- hdr: Header; pos, size, i: LONGINT; ch: CHAR;
- BEGIN
- context.arg.SkipWhitespace; context.arg.String(fn);
- F := Files.Old(fn);
- IF F = NIL THEN context.out.String(fn); context.out.String(" : no such file found."); context.out.Ln; RETURN END;
- NEW(hdr);
- pos := 0; Files.OpenReader(R, F, 0);
- WHILE (R.res = Streams.Ok) & ReadHeader(R, hdr) DO
- context.out.String(hdr.name); context.out.String(" ");
- OctStrToInt(hdr.size, size);
- context.out.Int(size, 0); context.out.Ln;
- f := Files.Old(hdr.name);
- IF f # NIL THEN Backup(f) END;
- f := Files.New(hdr.name); Files.OpenWriter(w, f, 0);
- Files.OpenReader(R, F, pos + RecordSize);
- i := 0;
- WHILE i < size DO
- R.Char(ch); w.Char(ch); INC(i)
- END;
- w.Update(); Files.Register(f);
- pos := pos + RecordSize + size + ((-size) MOD RecordSize);
- Files.OpenReader(R, F, pos)
- END;
- IF (R.res = Streams.Ok) & (hdr.chksum # "") THEN
- context.out.String(hdr.name); context.out.String(" checksum error"); context.out.Ln()
- END;
- END Extract;
- PROCEDURE Create*(context : Commands.Context);
- VAR
- fn, archivename: Files.FileName; F, f: Files.File; W: Files.Writer; r: Files.Reader;
- hdr: Header; size, i: LONGINT; ch: CHAR;
- nofAdded, nofErrors : LONGINT;
- BEGIN
- context.arg.SkipWhitespace; context.arg.String(archivename);
- context.out.String("Creating "); context.out.String(archivename); context.out.Ln;
- F := Files.New(archivename); Files.OpenWriter(W, F, 0);
- nofAdded := 0; nofErrors := 0;
- WHILE context.arg.GetString(fn) DO
- f := Files.Old(fn);
- IF f # NIL THEN
- Files.OpenReader(r, f, 0); size := f.Length();
- NEW(hdr); COPY(fn, hdr.name);
- IntToOctStr(size, hdr.size);
- CalcCheckSum(hdr);
- WriteHeader(W, hdr);
- i := 0;
- WHILE i < size DO
- r.Char(ch); W.Char(ch); INC(i)
- END;
- size := (-size) MOD RecordSize;
- WHILE size > 0 DO
- W.Char(0X); DEC(size)
- END;
- INC(nofAdded);
- context.out.String(fn); context.out.String(" added"); context.out.Ln;
- ELSE
- INC(nofErrors);
- context.out.String(fn); context.out.String(" not found"); context.out.Ln;
- END;
- END;
- EmptyHeader(hdr); WriteHeader(W, hdr);
- W.Update(); Files.Register(F);
- context.out.String("Added "); context.out.Int(nofAdded, 0); context.out.String(" files to archive ");
- context.out.String(archivename);
- IF nofErrors > 0 THEN
- context.out.String(" ("); context.out.Int(nofErrors, 0); context.out.String(" errors)");
- END;
- context.out.Ln;
- END Create;
- END Tar.
- System.Free Tar ~
|