Zip.Mod 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  1. (* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
  2. Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
  3. MODULE Zip; (** AUTHOR "Stefan Walthert"; PURPOSE "ZIP codec" **)
  4. IMPORT
  5. Streams, Files, Zlib, ZlibReaders, ZlibWriters;
  6. CONST
  7. (** result codes **)
  8. Ok* = 0; (** operation on zip-file was successful **)
  9. FileError* = -1; (** file not found **)
  10. NotZipArchiveError* = -2; (** file is not in zip format **)
  11. EntryNotFound* = -3; (** specified file was not found in zip-file **)
  12. EntryAlreadyExists* = -4; (** file is already stored in zip-file -> can not add specified file to zip-file **)
  13. NotSupportedError* = -5; (** can not extract specified file (compression method not supported/file is encrypted) **)
  14. DataError* = -6; (** file is corrupted **)
  15. BadName* = -7; (** bad file name *)
  16. ReaderError* = -8; (** e.g. Reader not opened before Read **)
  17. (** compression levels **)
  18. DefaultCompression* = ZlibWriters.DefaultCompression;
  19. NoCompression* = ZlibWriters.NoCompression;
  20. BestSpeed* = ZlibWriters.BestSpeed;
  21. BestCompression* = ZlibWriters.BestCompression;
  22. (** compression strategies **)
  23. DefaultStrategy* = ZlibWriters.DefaultStrategy;
  24. Filtered* = ZlibWriters.Filtered;
  25. HuffmanOnly* = ZlibWriters.HuffmanOnly;
  26. (* support *)
  27. Supported = 0; (* can extract file *)
  28. IncompatibleVersion = 1; (* version needed to extract < PKZIP 1.00 *)
  29. Encrypted = 2; (* file is encrypted *)
  30. UnsupCompMethod = 3; (* file not stored or deflated *)
  31. Stored = 0; (* file is stored (no compression) *)
  32. Deflated = 8; (* file is deflated *)
  33. SupportedCompMethods = {Stored, Deflated};
  34. CompatibleVersions = 1; (* versions >= CompatibleVersions are supported *)
  35. (* headers *)
  36. LocalFileHeaderSignature = 04034B50H;
  37. CentralFileHeaderSignature = 02014B50H;
  38. EndOfCentralDirSignature = 06054B50H;
  39. TYPE
  40. Entry* = POINTER TO EntryDesc; (** description of a file stored in the zip-archive **)
  41. EntryDesc* = RECORD
  42. name-: ARRAY 256 OF CHAR; (** name of file stored in the zip-archive **)
  43. method: INTEGER; (* compression method *)
  44. time-, date-: LONGINT; (** (Oberon) time and date when file was last modified **)
  45. crc32: LONGINT; (* checksum of uncompressed file data *)
  46. compSize-, uncompSize-: LONGINT; (** size of compressed / uncompressed file **)
  47. intFileAttr: INTEGER; (* internal file attributes, not used in this implementation *)
  48. extFileAttr: LONGINT; (* external file attributes, not used in this implementation *)
  49. extraField (* for future expansions *), comment-: POINTER TO ARRAY OF CHAR; (** comment for this file **)
  50. genPurpBitFlag: INTEGER;
  51. support: SHORTINT;
  52. dataDescriptor: BOOLEAN; (* if set, data descriptor after (compressed) file data *)
  53. offsetLocal: LONGINT; (* offset of file header in central directory *)
  54. offsetFileData: LONGINT; (* offset of (compressed) file data *)
  55. offsetCentralDir: LONGINT; (* offset of local file header *)
  56. next: Entry
  57. END;
  58. Archive* = POINTER TO ArchiveDesc; (** description of a zipfile **)
  59. ArchiveDesc* = RECORD
  60. nofEntries-: INTEGER; (** total number of files stored in the zipfile **)
  61. comment-: POINTER TO ARRAY OF CHAR; (** comment for zipfile **)
  62. file-: Files.File; (* pointer to the according zip-file *)
  63. offset: LONGINT; (* offset of end of central dir record *)
  64. firstEntry, lastEntry: Entry (* first and last Entry of Archive *)
  65. END;
  66. Reader* = POINTER TO ReaderDesc;
  67. ReaderDesc* = RECORD (** structure for reading from a zip-file into a buffer **)
  68. res-: LONGINT; (** result of last operation **)
  69. open: BOOLEAN;
  70. ent: Entry
  71. END;
  72. UncompReader = POINTER TO UncompReaderDesc;
  73. UncompReaderDesc = RECORD (ReaderDesc) (* structur for reading from a uncompressed entry *)
  74. fr: Files.Rider;
  75. crc32: LONGINT; (* crc32 of uncomressed data *)
  76. END;
  77. DefReader = POINTER TO DefReaderDesc;
  78. DefReaderDesc = RECORD (ReaderDesc) (* structure for reading from a deflated entry *)
  79. zr: ZlibReaders.Reader
  80. END;
  81. PROCEDURE ShowError*(errorCode : WORD; out : Streams.Writer);
  82. BEGIN
  83. ASSERT(out # NIL);
  84. CASE errorCode OF
  85. |Ok: out.String("No Error");
  86. |FileError: out.String("File not found");
  87. |NotZipArchiveError: out.String("File is not in zip format");
  88. |EntryNotFound: out.String("File not found in zip archive");
  89. |EntryAlreadyExists: out.String("File already present in zip archive");
  90. |NotSupportedError: out.String("Compression method not supported or file encrypted");
  91. |DataError: out.String("File is corrupted");
  92. |BadName: out.String("Bad file name");
  93. |ReaderError: out.String("Reader Error");
  94. ELSE
  95. out.String("Unkown error, res: "); out.Int(errorCode, 0);
  96. END;
  97. END ShowError;
  98. (* length of str *)
  99. PROCEDURE StringLength(CONST str: ARRAY OF CHAR): LONGINT;
  100. VAR i, l: LONGINT;
  101. BEGIN
  102. l := LEN(str); i := 0;
  103. WHILE (i < l) & (str[i] # 0X) DO
  104. INC(i)
  105. END;
  106. RETURN i
  107. END StringLength;
  108. (* Converts Oberon time into MS-DOS time *)
  109. PROCEDURE OberonToDosTime(t: LONGINT): INTEGER;
  110. BEGIN
  111. RETURN SHORT(t DIV 1000H MOD 20H * 800H + t DIV 40H MOD 40H * 20H + t MOD 40H DIV 2)
  112. END OberonToDosTime;
  113. (* Converts Oberon date into MS-DOS time *)
  114. PROCEDURE OberonToDosDate(d: LONGINT): INTEGER;
  115. BEGIN
  116. RETURN SHORT((d DIV 200H + 1900 - 1980) * 200H + d MOD 200H)
  117. END OberonToDosDate;
  118. (* Converts MS-DOS time into Oberon time *)
  119. PROCEDURE DosToOberonTime(t: INTEGER): LONGINT;
  120. BEGIN
  121. RETURN LONG(t) DIV 800H MOD 20H * 1000H + t DIV 20H MOD 40H * 40H + t MOD 20H * 2
  122. END DosToOberonTime;
  123. (* Converts MS-DOS date into Oberon date *)
  124. PROCEDURE DosToOberonDate(d: INTEGER): LONGINT;
  125. BEGIN
  126. RETURN (LONG(d) DIV 200H MOD 80H + 1980 - 1900) * 200H + d MOD 200H
  127. END DosToOberonDate;
  128. (* Copy len bytes from src to dst; if compCRC32 is set, then the crc 32-checksum is computed *)
  129. PROCEDURE Copy(VAR src, dst: Files.Rider; len: LONGINT; compCRC32: BOOLEAN; VAR crc32: LONGINT);
  130. CONST
  131. BufSize = 4000H;
  132. VAR
  133. n: LONGINT;
  134. buf: ARRAY BufSize OF CHAR;
  135. BEGIN
  136. IF compCRC32 THEN crc32 := Zlib.CRC32(0, buf, -1, -1) END;
  137. REPEAT
  138. IF len < BufSize THEN n := len
  139. ELSE n := BufSize
  140. END;
  141. src.file.ReadBytes(src, buf, 0, n);
  142. IF compCRC32 THEN crc32 := Zlib.CRC32(crc32, buf, 0, n - src.res) END;
  143. dst.file.WriteBytes(dst, buf, 0, n - src.res);
  144. DEC(len, n)
  145. UNTIL len = 0
  146. END Copy;
  147. (* Reads an Entry, r must be at the start of a file header; returns NIL if read was not successful *)
  148. PROCEDURE ReadEntry(VAR r: Files.Rider): Entry;
  149. VAR
  150. ent: Entry;
  151. intDummy, nameLen, extraLen, commentLen: INTEGER;
  152. longDummy: LONGINT;
  153. BEGIN
  154. Files.ReadLInt(r, longDummy);
  155. IF longDummy = CentralFileHeaderSignature THEN
  156. NEW(ent);
  157. ent.offsetCentralDir := r.file.Pos(r) - 4;
  158. ent.support := 0;
  159. Files.ReadInt(r, intDummy); (* version made by *)
  160. Files.ReadInt(r, intDummy); (* version needed to extract *)
  161. IF (intDummy MOD 100H) / 10 < CompatibleVersions THEN
  162. ent.support := IncompatibleVersion
  163. END;
  164. Files.ReadInt(r, ent.genPurpBitFlag); (* general purpose bit flag *)
  165. IF ODD(ent.genPurpBitFlag) THEN
  166. ent.support := Encrypted (* bit 0: if set, file encrypted *)
  167. END;
  168. ent.dataDescriptor := ODD(intDummy DIV 8); (* bit 3: data descriptor after (compressed) file data *)
  169. Files.ReadInt(r, ent.method); (* compression method *)
  170. IF (ent.support = Supported) & ~(ent.method IN SupportedCompMethods) THEN
  171. ent.support := UnsupCompMethod
  172. END;
  173. Files.ReadInt(r, intDummy); ent.time := DosToOberonTime(intDummy); (* last mod file time *)
  174. Files.ReadInt(r, intDummy); ent.date := DosToOberonDate(intDummy); (* last mod file date *)
  175. Files.ReadLInt(r, ent.crc32); (* crc-32 *)
  176. Files.ReadLInt(r, ent.compSize); (* compressed size *)
  177. Files.ReadLInt(r, ent.uncompSize); (* uncompressed size *)
  178. Files.ReadInt(r, nameLen); (* filename length *)
  179. Files.ReadInt(r, extraLen); (* extra field length *)
  180. Files.ReadInt(r, commentLen); (* file comment length *)
  181. Files.ReadInt(r, intDummy); (* disk number start *)
  182. Files.ReadInt(r, ent.intFileAttr); (* internal file attributes *)
  183. Files.ReadLInt(r, ent.extFileAttr); (* external file attributes *)
  184. Files.ReadLInt(r, ent.offsetLocal); (* relative offset of local header *)
  185. r.file.ReadBytes(r, ent.name, 0, nameLen); (* filename *)
  186. IF extraLen # 0 THEN
  187. NEW(ent.extraField, extraLen);
  188. r.file.ReadBytes(r, ent.extraField^, 0, extraLen) (* extra field *)
  189. END;
  190. IF commentLen > 0 THEN
  191. NEW(ent.comment, commentLen);
  192. r.file.ReadBytes(r, ent.comment^, 0, commentLen) (* file comment *)
  193. END;
  194. (* read extra field length in the local file header (can be different from extra field length stored in the file header...) *)
  195. longDummy := r.file.Pos(r); (* store actual position of file reader *)
  196. r.file.Set(r, ent.offsetLocal + 28); (* set r to position of extra field length in local file header *)
  197. Files.ReadInt(r, extraLen); (* extra field length *)
  198. ent.offsetFileData := ent.offsetLocal + 30 + nameLen + extraLen; (* compute offset of file data *)
  199. r.file.Set(r, longDummy); (* set position of file reader to previous position *)
  200. IF r.eof THEN (* if file is a zip-archive, r is not at end of file *)
  201. ent := NIL
  202. END
  203. END;
  204. RETURN ent;
  205. END ReadEntry;
  206. (* Writes a local file header *)
  207. PROCEDURE WriteLocalFileHeader(ent: Entry; VAR r: Files.Rider);
  208. BEGIN
  209. Files.WriteLInt(r, LocalFileHeaderSignature); (* local file header signature *)
  210. Files.WriteInt(r, CompatibleVersions * 10); (* version needed to extract *)
  211. Files.WriteInt(r, ent.genPurpBitFlag); (* general purpose bit flag *)
  212. Files.WriteInt(r, ent.method); (* compression method *)
  213. Files.WriteInt(r, OberonToDosTime(ent.time)); (* last mod file time *)
  214. Files.WriteInt(r, OberonToDosDate(ent.date)); (* last mod file date *)
  215. Files.WriteLInt(r, ent.crc32); (* crc-32 *)
  216. Files.WriteLInt(r, ent.compSize); (* compressed size *)
  217. Files.WriteLInt(r, ent.uncompSize); (* uncompressed size *)
  218. Files.WriteInt(r, SHORT(StringLength(ent.name))); (* filename length *)
  219. IF ent.extraField # NIL THEN
  220. Files.WriteInt(r, SHORT(LEN(ent.extraField^))) (* extra field length *)
  221. ELSE
  222. Files.WriteInt(r, 0)
  223. END;
  224. r.file.WriteBytes(r, ent.name, 0, StringLength(ent.name)); (* filename *)
  225. IF ent.extraField # NIL THEN
  226. r.file.WriteBytes(r, ent.extraField^, 0, LEN(ent.extraField^)) (* extra field *)
  227. END
  228. END WriteLocalFileHeader;
  229. (* Writes file header in central directory, updates ent.offsetCentralDir *)
  230. PROCEDURE WriteFileHeader(ent: Entry; VAR r: Files.Rider);
  231. BEGIN
  232. ent.offsetCentralDir := r.file.Pos(r);
  233. Files.WriteLInt(r, CentralFileHeaderSignature); (* central file header signature *)
  234. Files.WriteInt(r, CompatibleVersions * 10); (* version made by *)
  235. Files.WriteInt(r, CompatibleVersions * 10); (* version needed to extract *)
  236. Files.WriteInt(r, ent.genPurpBitFlag); (* general purpose bit flag *)
  237. Files.WriteInt(r, ent.method); (* compression method *)
  238. Files.WriteInt(r, OberonToDosTime(ent.time)); (* last mod file time *)
  239. Files.WriteInt(r, OberonToDosDate(ent.date)); (* last mod file date *)
  240. Files.WriteLInt(r, ent.crc32); (* crc-32 *)
  241. Files.WriteLInt(r, ent.compSize); (* compressed size *)
  242. Files.WriteLInt(r, ent.uncompSize); (* uncompressed size *)
  243. Files.WriteInt(r, SHORT(StringLength(ent.name))); (* filename length *)
  244. IF ent.extraField = NIL THEN
  245. Files.WriteInt(r, 0)
  246. ELSE
  247. Files.WriteInt(r, SHORT(LEN(ent.extraField^))); (* extra field length *)
  248. END;
  249. IF ent.comment = NIL THEN
  250. Files.WriteInt(r, 0)
  251. ELSE
  252. Files.WriteInt(r, SHORT(LEN(ent.comment^))); (* file comment length *)
  253. END;
  254. Files.WriteInt(r, 0); (* disk number start *)
  255. Files.WriteInt(r, ent.intFileAttr); (* internal file attributes *)
  256. Files.WriteLInt(r, ent.extFileAttr); (* external file attributes *)
  257. Files.WriteLInt(r, ent.offsetLocal); (* relative offset of local header *)
  258. r.file.WriteBytes(r, ent.name, 0, StringLength(ent.name)); (* filename *)
  259. IF ent.extraField # NIL THEN
  260. r.file.WriteBytes(r, ent.extraField^, 0, LEN(ent.extraField^)) (* extra field *)
  261. END;
  262. IF ent.comment # NIL THEN
  263. r.file.WriteBytes(r, ent.comment^, 0, LEN(ent.comment^)) (* file comment *)
  264. END
  265. END WriteFileHeader;
  266. (* Writes end of central directory record *)
  267. PROCEDURE WriteEndOfCentDir(arc: Archive; VAR r: Files.Rider);
  268. BEGIN
  269. Files.WriteLInt(r, EndOfCentralDirSignature); (* end of central dir signature *)
  270. Files.WriteInt(r, 0); (* number of this disk *)
  271. Files.WriteInt(r, 0); (* number of the disk with the start of the central directory *)
  272. Files.WriteInt(r, arc.nofEntries); (* total number of entries in the central dir on this disk *)
  273. Files.WriteInt(r, arc.nofEntries); (* total number of entries in the central dir *)
  274. IF arc.firstEntry # NIL THEN
  275. Files.WriteLInt(r, arc.offset - arc.firstEntry.offsetCentralDir) (* size of the central directory (without end of central dir record) *)
  276. ELSE
  277. Files.WriteLInt(r, 0)
  278. END;
  279. IF arc.firstEntry = NIL THEN
  280. Files.WriteLInt(r, arc.offset) (* offset of start of central directory with respect to the starting disk number *)
  281. ELSE
  282. Files.WriteLInt(r, arc.firstEntry.offsetCentralDir) (* offset of start of central directory with respect to the starting disk number *)
  283. END;
  284. IF arc.comment = NIL THEN
  285. Files.WriteInt(r, 0) (* zipfile comment length *)
  286. ELSE
  287. Files.WriteInt(r, SHORT(LEN(arc.comment^))); (* zipfile comment length *)
  288. r.file.WriteBytes(r, arc.comment^, 0, LEN(arc.comment^)) (* zipfile comment *)
  289. END
  290. END WriteEndOfCentDir;
  291. (* Writes central directory + end of central directory record, updates arc.offset and offsetCentralDir of entries *)
  292. PROCEDURE WriteCentralDirectory(arc: Archive; VAR r: Files.Rider);
  293. VAR
  294. ent: Entry;
  295. BEGIN
  296. ent := arc.firstEntry;
  297. WHILE ent # NIL DO
  298. WriteFileHeader(ent, r);
  299. ent := ent.next
  300. END;
  301. arc.offset := r.file.Pos(r);
  302. WriteEndOfCentDir(arc, r)
  303. END WriteCentralDirectory;
  304. (** Returns an Archive data structure corresponding to the specified zipfile;
  305. possible results:
  306. - Ok: operation was successful
  307. - FileError: file with specified name does not exist
  308. - NotZipArchiveError: file is not a correct zipfile **)
  309. PROCEDURE OpenArchive*(CONST name: ARRAY OF CHAR; VAR res: WORD): Archive;
  310. VAR
  311. arc: Archive;
  312. ent: Entry;
  313. f: Files.File;
  314. r: Files.Rider;
  315. longDummy: LONGINT;
  316. intDummy: INTEGER;
  317. BEGIN
  318. res := Ok;
  319. f := Files.Old(name);
  320. IF f = NIL THEN
  321. res := FileError
  322. ELSIF f.Length() < 22 THEN
  323. res := NotZipArchiveError
  324. ELSE
  325. longDummy := 0;
  326. f.Set(r, f.Length() - 17);
  327. WHILE (longDummy # EndOfCentralDirSignature) & (r.file.Pos(r) > 4) DO
  328. f.Set(r, f.Pos(r) - 5);
  329. Files.ReadLInt(r, longDummy)
  330. END;
  331. IF longDummy # EndOfCentralDirSignature THEN
  332. res := NotZipArchiveError
  333. ELSE
  334. NEW(arc);
  335. arc.file := f;
  336. arc.offset := f.Pos(r) - 4;
  337. Files.ReadInt(r, intDummy); (* number of this disk *)
  338. Files.ReadInt(r, intDummy); (* number of the disk with the start of the central directory *)
  339. Files.ReadInt(r, intDummy); (* total number of entries in the central dir on this disk *)
  340. Files.ReadInt(r, arc.nofEntries); (* total number of entries in the central dir *)
  341. Files.ReadLInt(r, longDummy); (* size of the central directory *)
  342. Files.ReadLInt(r, longDummy); (* offset of start of central directory with respect to the starting disk number *)
  343. Files.ReadInt(r, intDummy); (* zipfile comment length *)
  344. IF intDummy # 0 THEN
  345. NEW(arc.comment, intDummy);
  346. r.file.ReadBytes(r, arc.comment^, 0, intDummy) (* zipfile comment *)
  347. END;
  348. IF r.file.Pos(r) # r.file.Length() THEN
  349. res := NotZipArchiveError;
  350. arc := NIL
  351. ELSE
  352. r.file.Set(r, longDummy); (* set r on position of first file header in central dir *)
  353. arc.firstEntry := ReadEntry(r); arc.lastEntry := arc.firstEntry;
  354. ent := arc.firstEntry; intDummy := 0;
  355. WHILE ent # NIL DO
  356. arc.lastEntry := ent; INC(intDummy); (* count number of entries *)
  357. ent.next := ReadEntry(r);
  358. ent := ent.next
  359. END;
  360. IF intDummy # arc.nofEntries THEN
  361. res := NotZipArchiveError;
  362. arc := NIL
  363. END
  364. END;
  365. f.Update();
  366. END
  367. END;
  368. RETURN arc
  369. END OpenArchive;
  370. (** Returns an Archive that corresponds to a file with specified name;
  371. if there is already a zip-file with the same name, this already existing archive is returned;
  372. possible results: cf. OpenArchive **)
  373. PROCEDURE CreateArchive*(CONST name: ARRAY OF CHAR; VAR res: WORD): Archive;
  374. VAR
  375. f: Files.File;
  376. r: Files.Rider;
  377. arc: Archive;
  378. BEGIN
  379. IF name#"" THEN f := Files.Old(name); END;
  380. IF f # NIL THEN
  381. RETURN OpenArchive(name, res)
  382. ELSE
  383. f := Files.New(name);
  384. NEW(arc);
  385. arc.file := f;
  386. arc.nofEntries := 0;
  387. arc.offset := 0;
  388. f.Set(r, 0);
  389. WriteEndOfCentDir(arc, r);
  390. IF name#"" THEN Files.Register(f) END;
  391. res := Ok;
  392. RETURN arc
  393. END
  394. END CreateArchive;
  395. (** Returns the first entry of the Archive arc (NIL if there is no Entry) **)
  396. PROCEDURE FirstEntry*(arc: Archive): Entry;
  397. BEGIN
  398. IF arc = NIL THEN
  399. RETURN NIL
  400. ELSE
  401. RETURN arc.firstEntry
  402. END
  403. END FirstEntry;
  404. (** Returns the next Entry after ent **)
  405. PROCEDURE NextEntry*(ent: Entry): Entry;
  406. BEGIN
  407. RETURN ent.next
  408. END NextEntry;
  409. (** Returns the Entry that corresponds to the file with the specified name and that is stored in the Archive arc;
  410. possible results:
  411. - Ok: Operation was successful
  412. - NotZipArchiveError: arc is not a valid Archive
  413. - EntryNotFound: no Entry corresponding to name was found **)
  414. PROCEDURE GetEntry*(arc: Archive; CONST name: ARRAY OF CHAR; VAR res: WORD): Entry;
  415. VAR
  416. ent: Entry;
  417. BEGIN
  418. IF arc = NIL THEN
  419. res := NotZipArchiveError
  420. ELSE
  421. ent := arc.firstEntry;
  422. WHILE (ent # NIL) & (ent.name # name) DO
  423. ent := ent.next
  424. END;
  425. IF ent = NIL THEN
  426. res := EntryNotFound
  427. ELSE
  428. res := Ok
  429. END
  430. END;
  431. RETURN ent
  432. END GetEntry;
  433. (** Uncompresses and writes the data of Entry ent to Files.Rider dst;
  434. possible results:
  435. - Ok: Data extracted
  436. - NotZipArchiveError: arc is not a valid zip-archive
  437. - EntryNotFound: ent is not an Entry of arc
  438. - NotSupportedError: data of ent are encrypted or compression method is not supported
  439. - DataError: zipfile is corrupted
  440. - BadName: entry has a bad file name **)
  441. PROCEDURE ExtractEntry*(arc: Archive; ent: Entry; VAR dst: Files.Rider; VAR res: WORD);
  442. VAR
  443. src: Files.Rider; crc32: LONGINT;
  444. BEGIN
  445. IF arc = NIL THEN
  446. res := NotZipArchiveError
  447. ELSIF (dst.file = NIL) THEN
  448. res := BadName
  449. ELSIF (ent = NIL) OR (ent # GetEntry(arc, ent.name, res)) THEN
  450. res := EntryNotFound
  451. ELSIF ~(ent.method IN SupportedCompMethods) OR (ent.support > Supported) THEN
  452. res := NotSupportedError
  453. ELSE
  454. CASE ent.method OF
  455. | Stored:
  456. arc.file.Set(src, ent.offsetFileData);
  457. Copy(src, dst, ent.uncompSize, TRUE, crc32);
  458. IF crc32 = ent.crc32 THEN
  459. res := Ok
  460. ELSE
  461. res := DataError
  462. END
  463. | Deflated:
  464. arc.file.Set(src, ent.offsetFileData);
  465. ZlibReaders.Uncompress(src, dst, crc32, res);
  466. IF (res = ZlibReaders.Ok) & (crc32 = ent.crc32) THEN
  467. res := Ok
  468. ELSE
  469. res := DataError
  470. END
  471. END;
  472. IF res = Ok THEN
  473. dst.file.Update();
  474. END
  475. END
  476. END ExtractEntry;
  477. (** Reads and compresses len bytes from Files.Rider src with specified level and strategy
  478. and writes them to a new Entry in the Archive arc;
  479. possible results:
  480. - Ok: file was added to arc
  481. - NotZipArchiveError: arc is not a valid zip-archive
  482. - EntryAlreadyExists: there is already an Entry in arc with the same name
  483. - DataError: error during compression
  484. - BadName: src is not based on a valid file **)
  485. PROCEDURE AddEntry*(arc: Archive; CONST name: ARRAY OF CHAR; VAR src: Files.Rider; len: LONGINT; level, strategy: SHORTINT; VAR res: WORD);
  486. VAR
  487. dst: Files.Rider; ent: Entry; start: LONGINT;
  488. BEGIN
  489. IF arc = NIL THEN
  490. res := NotZipArchiveError
  491. ELSIF (src.file = NIL) THEN
  492. res := BadName
  493. ELSIF (GetEntry(arc, name, res) # NIL) & (res = Ok) THEN
  494. res := EntryAlreadyExists
  495. ELSE
  496. NEW(ent);
  497. COPY(name, ent.name);
  498. ent.genPurpBitFlag := 0;
  499. IF level = NoCompression THEN
  500. ent.method := Stored
  501. ELSE
  502. ent.method := Deflated
  503. END;
  504. src.file.GetDate(ent.time, ent.date);
  505. ent.uncompSize := len;
  506. ent.intFileAttr := 0;
  507. ent.extFileAttr := 0;
  508. ent.comment := NIL;
  509. ent.support := Supported;
  510. ent.dataDescriptor := FALSE;
  511. IF arc.firstEntry # NIL THEN
  512. ent.offsetLocal := arc.firstEntry.offsetCentralDir
  513. ELSE
  514. ent.offsetLocal := 0
  515. END;
  516. arc.file.Set(dst, ent.offsetLocal);
  517. WriteLocalFileHeader(ent, dst);
  518. ent.offsetFileData := dst.file.Pos(dst);
  519. arc.file.Update();
  520. start := src.file.Pos(src);
  521. IF level = 0 THEN
  522. Copy(src, dst, len, TRUE, ent.crc32);
  523. ent.compSize := len;
  524. res := Ok
  525. ELSE
  526. ZlibWriters.Compress(src, dst, len, ent.compSize, level, strategy, ent.crc32, res);
  527. IF res # ZlibWriters.Ok THEN
  528. res := DataError
  529. ELSE
  530. res := Ok
  531. END
  532. END;
  533. IF res = Ok THEN
  534. ent.uncompSize := src.file.Pos(src) - start;
  535. arc.file.Update();
  536. arc.file.Set(dst, ent.offsetLocal + 14);
  537. Files.WriteLInt(dst, ent.crc32);
  538. Files.WriteLInt(dst, ent.compSize);
  539. arc.file.Update;
  540. IF arc.lastEntry # NIL THEN
  541. arc.lastEntry.next := ent
  542. ELSE (* archive has no entries *)
  543. arc.firstEntry := ent
  544. END;
  545. arc.lastEntry := ent;
  546. INC(arc.nofEntries);
  547. arc.file.Set(dst, ent.offsetFileData + ent.compSize);
  548. WriteCentralDirectory(arc, dst);
  549. arc.file.Update();
  550. res := Ok
  551. END;
  552. END
  553. END AddEntry;
  554. (** Deletes Entry ent from Archive arc;
  555. Possible results:
  556. - Ok: ent was deleted, ent is set to NIL
  557. - NotZipArchiveError: arc is not a valid zip-archive
  558. - EntryNotFound: ent is not an Entry of Archive arc **)
  559. PROCEDURE DeleteEntry*(arc: Archive; VAR ent: Entry; VAR res: WORD);
  560. VAR
  561. f: Files.File; r1, r2: Files.Rider;
  562. ent2: Entry;
  563. arcname: ARRAY 256 OF CHAR;
  564. offset, diff: LONGINT;
  565. BEGIN
  566. IF arc = NIL THEN
  567. res := NotZipArchiveError
  568. ELSIF arc.firstEntry = NIL THEN
  569. res := EntryNotFound
  570. ELSIF arc.firstEntry = ent THEN
  571. offset := arc.firstEntry.offsetLocal; (* arc.firstEntry.offsetLocal = 0 *)
  572. IF arc.lastEntry = arc.firstEntry THEN
  573. arc.lastEntry := arc.firstEntry.next (* = NIL *)
  574. END;
  575. arc.firstEntry := arc.firstEntry.next;
  576. ent2 := arc.firstEntry;
  577. res := Ok
  578. ELSE
  579. ent2 := arc.firstEntry;
  580. WHILE (ent2.next # NIL) & (ent2.next # ent) DO
  581. ent2 := ent2.next
  582. END;
  583. IF ent2.next = NIL THEN
  584. res := EntryNotFound
  585. ELSE
  586. IF arc.lastEntry = ent2.next THEN
  587. arc.lastEntry := ent2
  588. END;
  589. offset := ent2.next.offsetLocal;
  590. ent2.next := ent2.next.next;
  591. ent2 := ent2.next;
  592. res := Ok
  593. END
  594. END;
  595. IF res = Ok THEN
  596. arc.file.GetName(arcname);
  597. f := Files.New(arcname);
  598. f.Set(r2, 0);
  599. arc.file.Set(r1, 0);
  600. Copy(r1, r2, offset, FALSE, diff); (* no crc 32-checksum is computed -> diff used as dummy *)
  601. f.Update;
  602. ASSERT(ent2 = ent.next);
  603. IF ent2 # NIL THEN
  604. arc.file.Set(r1, ent2.offsetLocal);
  605. Copy(r1, r2, arc.firstEntry.offsetCentralDir - ent2.offsetLocal, FALSE, diff); (* arc.firstEntry can not be NIL because ent # NIL *)
  606. f.Update;
  607. diff := ent2.offsetLocal - offset
  608. ELSE
  609. diff := arc.offset - offset
  610. END;
  611. WHILE (ent2 # NIL) DO (* update offsets of entries *)
  612. DEC(ent2.offsetLocal, diff); DEC(ent2.offsetFileData, diff); DEC(ent2.offsetCentralDir, diff);
  613. ent2 := ent2.next
  614. END;
  615. DEC(arc.offset, diff);
  616. DEC(arc.nofEntries);
  617. WriteCentralDirectory(arc, r2);
  618. Files.Register(f); arc.file := f; ent := NIL
  619. END
  620. END DeleteEntry;
  621. (** open a Reader to read uncompressed data from a zip entry directly to memory **)
  622. PROCEDURE OpenReader*(arc: Archive; ent: Entry): Reader;
  623. VAR
  624. dummyBuf: ARRAY 1 OF CHAR;
  625. fr: Files.Rider;
  626. r: Reader;
  627. ur: UncompReader;
  628. dr: DefReader;
  629. BEGIN
  630. IF ent.support = Supported THEN
  631. IF ent.method = Stored THEN
  632. NEW(ur);
  633. ur.crc32 := Zlib.CRC32(0, dummyBuf, -1, -1);
  634. arc.file.Set(ur.fr, ent.offsetFileData);
  635. r := ur;
  636. r.open := TRUE;
  637. r.res := Ok
  638. ELSIF ent.method = Deflated THEN
  639. arc.file.Set(fr, ent.offsetFileData);
  640. NEW(dr);
  641. ZlibReaders.Open(dr.zr, FALSE, fr);
  642. dr.res := dr.zr.res;
  643. r := dr;
  644. r.open := TRUE
  645. ELSE
  646. NEW(r);
  647. r.open := FALSE;
  648. r.res := NotSupportedError
  649. END;
  650. ELSE
  651. NEW(r);
  652. r.open := FALSE;
  653. r.res := NotSupportedError
  654. END;
  655. r.ent := ent;
  656. RETURN r;
  657. END OpenReader;
  658. (** read len bytes of uncompressed data into buf[offset] and return number of bytes actually read; Reader must be opened **)
  659. PROCEDURE ReadBytes*(r: Reader; VAR buf: ARRAY OF CHAR; offset, len: LONGINT; VAR read: LONGINT);
  660. VAR
  661. bufp: POINTER TO ARRAY OF CHAR; i: LONGINT;
  662. BEGIN
  663. IF r.open THEN
  664. IF r IS UncompReader THEN
  665. IF offset = 0 THEN
  666. r(UncompReader).fr.file.ReadBytes(r(UncompReader).fr, buf, 0, len);
  667. ELSE
  668. NEW(bufp, len);
  669. r(UncompReader).fr.file.ReadBytes(r(UncompReader).fr, bufp^, 0, len);
  670. FOR i := 0 TO len - 1 DO
  671. buf[offset + i] := bufp[i]
  672. END
  673. END;
  674. read := len - r(UncompReader).fr.res;
  675. r(UncompReader).crc32 := Zlib.CRC32(r(UncompReader).crc32, buf, offset, read)
  676. ELSIF r IS DefReader THEN
  677. ZlibReaders.ReadBytes(r(DefReader).zr, buf, offset, len, read);
  678. r.res := r(DefReader).zr.res
  679. END
  680. ELSE
  681. r.res := ReaderError
  682. END
  683. END ReadBytes;
  684. (** read decompressed byte **)
  685. PROCEDURE Read*(r: Reader; VAR ch: CHAR);
  686. VAR
  687. buf: ARRAY 1 OF CHAR; read: LONGINT;
  688. BEGIN
  689. ReadBytes(r, buf, 0, 1, read);
  690. ch := buf[0];
  691. END Read;
  692. (** close Reader **)
  693. PROCEDURE Close*(r: Reader);
  694. BEGIN
  695. IF r.open THEN
  696. IF r IS UncompReader THEN
  697. IF r(UncompReader).crc32 # r.ent.crc32 THEN
  698. r.res := DataError
  699. ELSE
  700. r.res := Ok
  701. END
  702. ELSIF r IS DefReader THEN
  703. ZlibReaders.Close(r(DefReader).zr);
  704. IF r(DefReader).zr.crc32 # r.ent.crc32 THEN
  705. r.res := DataError
  706. ELSE
  707. r.res := r(DefReader).zr.res
  708. END
  709. ELSE
  710. r.res := ReaderError
  711. END;
  712. r.open := FALSE
  713. ELSE
  714. r.res := ReaderError
  715. END
  716. END Close;
  717. END Zip.