ZipTool.Mod 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  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 ZipTool; (** AUTHOR "Stefan Walthert"; PURPOSE "Command line front-end for Zip **)
  4. IMPORT
  5. Streams, Commands, Options, Files, Strings, Zip;
  6. CONST
  7. EXTRACT = 1;
  8. OPEN = 2;
  9. Tab = 9X;
  10. (* Get the suffix of str. The suffix is started by the last sepchar in str. If sepchar does not occur in str, str is returned *)
  11. PROCEDURE GetSuffix(CONST str : ARRAY OF CHAR; VAR suf : ARRAY OF CHAR; sepchar: CHAR);
  12. VAR i, j, len, sep: LONGINT;
  13. BEGIN
  14. i := 0; sep := -1;
  15. WHILE str[i] # 0X DO
  16. IF str[i] = sepchar THEN
  17. sep := i
  18. END;
  19. INC(i)
  20. END;
  21. j := 0;
  22. len := LEN(suf) - 1; i := sep + 1;
  23. WHILE (j < len) & (str[i] # 0X) DO
  24. suf[j] := str[i]; INC(j); INC(i)
  25. END;
  26. suf[j] := 0X
  27. END GetSuffix;
  28. (* Append this to to *)
  29. PROCEDURE Append(VAR to: ARRAY OF CHAR; CONST this: ARRAY OF CHAR);
  30. VAR i, j, l: LONGINT;
  31. BEGIN
  32. i := 0;
  33. WHILE to[i] # 0X DO
  34. INC(i)
  35. END;
  36. l := LEN(to)-1; j := 0;
  37. WHILE (i < l) & (this[j] # 0X) DO
  38. to[i] := this[j]; INC(i); INC(j)
  39. END;
  40. to[i] := 0X
  41. END Append;
  42. PROCEDURE OpenArchive(CONST archiveName : ARRAY OF CHAR; errorLog : Streams.Writer) : Zip.Archive;
  43. VAR archive : Zip.Archive; res : WORD;
  44. BEGIN
  45. archive := Zip.OpenArchive(archiveName, res);
  46. IF (res # Zip.Ok) THEN
  47. archive := NIL;
  48. errorLog.String("Could not open archive '"); errorLog.String(archiveName); errorLog.String("': ");
  49. Zip.ShowError(res, errorLog); errorLog.Ln; errorLog.Update;
  50. END;
  51. RETURN archive;
  52. END OpenArchive;
  53. (** Writes the directory of an archive. **)
  54. PROCEDURE WriteDirectory*(out, error : Streams.Writer; CONST archiveName: ARRAY OF CHAR; details: BOOLEAN; VAR res: WORD);
  55. VAR
  56. archive: Zip.Archive;
  57. entry: Zip.Entry;
  58. ratio : LONGINT;
  59. BEGIN
  60. ASSERT(out # NIL);
  61. archive := OpenArchive(archiveName, error);
  62. IF (archive # NIL) THEN
  63. IF details THEN
  64. out.String("Name"); out.Char(Tab); out.Char(Tab); out.String("Date"); out.Char(Tab); out.Char(Tab);
  65. out.String("Size"); out.Char(Tab); out.String("Ratio"); out.Char(Tab);
  66. out.String("Compressed"); out.Ln; out.Ln;
  67. END;
  68. entry := Zip.FirstEntry(archive);
  69. WHILE (entry # NIL) DO
  70. out.String(entry.name);
  71. IF details THEN
  72. out.Char(Tab); out.Char(Tab); out.Date(entry.time, entry.date);
  73. out.Char(Tab); out.Char(Tab); out.Int(entry.uncompSize, 0);
  74. ratio := ENTIER(((1 - entry.compSize / entry.uncompSize) * 100) + 0.5);
  75. IF ratio < 0 THEN ratio := 0 END; (* ratio can not be less than zero *)
  76. out.Char(Tab); out.Int(ratio, 0); out.String("%");
  77. out.Char(Tab); out.Int(entry.compSize, 0);
  78. END;
  79. out.Ln;
  80. entry := Zip.NextEntry(entry)
  81. END;
  82. out.Ln;
  83. out.Int(archive.nofEntries, 0);
  84. IF (archive.nofEntries = 1) THEN out.String(" entry");
  85. ELSE out.String(" entries");
  86. END;
  87. out.Ln;
  88. END
  89. END WriteDirectory;
  90. (** Shows the content of the selected zip-archive in a new viewer.
  91. ZipTool.Directory [-d ] ZipFile ~
  92. Options:
  93. --details: If set, details of entries of selected zip-archive are shown *)
  94. PROCEDURE Directory*(context : Commands.Context);
  95. VAR
  96. archiveName : Files.FileName;
  97. options : Options.Options;
  98. res: WORD;
  99. BEGIN
  100. NEW(options);
  101. options.Add("d", "details", Options.Flag);
  102. IF options.Parse(context.arg, context.error) THEN
  103. context.arg.SkipWhitespace; context.arg.String(archiveName);
  104. WriteDirectory(context.out, context.error, archiveName, options.GetFlag("details"), res);
  105. ELSE
  106. context.result := Commands.CommandParseError;
  107. END;
  108. END Directory;
  109. PROCEDURE DoExtract(
  110. action: LONGINT;
  111. archive: Zip.Archive; entry: Zip.Entry; name: ARRAY OF CHAR; VAR tempfile: Files.File;
  112. path, overwrite, show: BOOLEAN; out, error : Streams.Writer; VAR res: WORD);
  113. VAR
  114. f, of: Files.File; r: Files.Rider;
  115. bakname, temp: ARRAY 256 OF CHAR; res2: LONGINT;
  116. suf: ARRAY 32 OF CHAR;
  117. BEGIN
  118. IF action = EXTRACT THEN
  119. IF ~path THEN
  120. (* GetSuffix(name, name, ':'); *)
  121. GetSuffix(name, name, '/')
  122. END;
  123. f := Files.New(name);
  124. IF (f # NIL) THEN
  125. IF (out # NIL) THEN
  126. out.String("Extracting "); out.String(entry.name);
  127. IF (entry.name # name) THEN
  128. out.String(" -> "); out.String(name);
  129. END;
  130. out.String(" ... ");
  131. END;
  132. ELSE
  133. IF (error # NIL) THEN error.String("Could not create file "); error.String(name); END;
  134. res := -1;
  135. RETURN;
  136. END;
  137. ELSE
  138. temp := "Temp.Zip.";
  139. GetSuffix(name,suf,'.');
  140. Append(temp,suf);
  141. f := Files.New(temp);
  142. IF (f = NIL) THEN
  143. IF (error # NIL) THEN error.String("Could not create temporary file Temp.Zip"); END;
  144. res := -1;
  145. RETURN;
  146. END;
  147. END;
  148. tempfile := f;
  149. f.Set(r, 0);
  150. Zip.ExtractEntry(archive, entry, r, res);
  151. IF (res = Zip.Ok) THEN
  152. IF action = EXTRACT THEN
  153. of := Files.Old(name);
  154. IF (of # NIL) THEN (* file exists on this volume or another volume in search path *)
  155. IF ~overwrite THEN
  156. COPY(name, bakname); Append(bakname, ".Bak"); (* assume enough space for .Bak *)
  157. Files.Rename(name, bakname, res2);
  158. IF (res2 = Files.Ok) THEN
  159. IF (out # NIL) THEN out.String(" done (backup in "); out.String(bakname); out.String(")."); END;
  160. ELSE (* assume old file was in another place in the search path *)
  161. of.GetName(bakname);
  162. IF (out # NIL) THEN out.String(" done (masks "); out.String(bakname); out.String(")."); END;
  163. END
  164. ELSE
  165. IF (out # NIL) THEN out.String("done (overwritten)."); END;
  166. END;
  167. ELSE
  168. IF (out # NIL) THEN out.String("done."); END;
  169. END;
  170. f.SetDate(entry.time, entry.date);
  171. END;
  172. Files.Register(f);
  173. tempfile := f;
  174. ELSE
  175. IF (out # NIL) THEN Zip.ShowError(res, out); END;
  176. END;
  177. IF (out # NIL) THEN out.Ln; out.Update; END;
  178. END DoExtract;
  179. (** Extracts the entry ent from the zip-archive ent and stores as under the filename name. Some log-output is generated.
  180. If path is set, the file is stored in the directory according to the relative path in name.
  181. If overwrite is set, files with the same name are overwritten, otherwise they are renamed to name.Bak.
  182. Possible results: cf. Zip.ExtractEntry **)
  183. PROCEDURE ExtractFile*(arc: Zip.Archive; ent: Zip.Entry; CONST name: ARRAY OF CHAR; path, overwrite: BOOLEAN; log, error : Streams.Writer; VAR res: WORD);
  184. VAR temp: Files.File;
  185. BEGIN
  186. DoExtract(EXTRACT, arc, ent, name, temp, path, overwrite, FALSE, log, error, res);
  187. END ExtractFile;
  188. (** Extracts the entry ent from the zip-archive ent and stores as under the filename name. Some log-output is generated.
  189. If path is set, the file is stored in the directory according to the relative path in name.
  190. If overwrite is set, files with the same name are overwritten, otherwise they are renamed to name.Bak.
  191. Possible results: cf. Zip.ExtractEntry **)
  192. PROCEDURE OpenFile*(
  193. arc: Zip.Archive; ent: Zip.Entry; CONST name: ARRAY OF CHAR; VAR tempfile: Files.File;
  194. path, overwrite, show: BOOLEAN; log, error : Streams.Writer; VAR res: WORD);
  195. BEGIN
  196. DoExtract(OPEN, arc, ent, name, tempfile, path, overwrite, show, log, error, res);
  197. END OpenFile;
  198. (** Extracts the selected entries of the selected zip-Archive. The relative path in the file name of the entry
  199. is ignored (c.f. option \d).
  200. ZipTool.Extract [-d] [-o] ZipFile {Entry [=> NewName]}
  201. Options:
  202. --directory: If set, the file is stored in the directory according to the relative path in the file name of the entry
  203. --overwrite: If set, files with the same name are overwritten, otherwise they are renamed to filename.Bak
  204. --ignore: Continue in case of errors
  205. --prefix: Add prefix to extracted files **)
  206. PROCEDURE Extract*(context : Commands.Context);
  207. VAR
  208. archivename, entryname, filename, prefix: Files.FileName;
  209. options : Options.Options;
  210. archive: Zip.Archive; entry: Zip.Entry;
  211. path, overwrite, stopOnError: BOOLEAN;
  212. nofExtracted, nofErrors: LONGINT; res: WORD;
  213. BEGIN
  214. NEW(options);
  215. options.Add("d", "directory", Options.Flag);
  216. options.Add("o", "overwrite", Options.Flag);
  217. options.Add("i", "ignore", Options.Flag);
  218. options.Add("p", "prefix", Options.String);
  219. IF options.Parse(context.arg, context.error) THEN
  220. path := options.GetFlag("directory");
  221. overwrite := options.GetFlag("overwrite");
  222. stopOnError := ~options.GetFlag("ignore");
  223. IF ~options.GetString("prefix", prefix) THEN prefix := ""; END;
  224. context.arg.SkipWhitespace; context.arg.String(archivename);
  225. archive := OpenArchive(archivename, context.error);
  226. IF (archive # NIL) THEN
  227. nofExtracted := 0; nofErrors := 0;
  228. WHILE context.arg.GetString(entryname) & ((nofErrors = 0) OR ~stopOnError) DO
  229. entry := Zip.GetEntry(archive, entryname, res);
  230. IF (res = Zip.Ok) THEN
  231. IF (prefix # "") THEN
  232. COPY(prefix, filename); Append(filename, entry.name);
  233. ELSE
  234. COPY(entry.name, filename);
  235. END;
  236. ExtractFile(archive, entry, filename, path, overwrite, context.out, context.error, res);
  237. IF (res = Zip.Ok) THEN
  238. INC(nofExtracted);
  239. ELSE
  240. INC(nofErrors);
  241. END;
  242. ELSE
  243. INC(nofErrors);
  244. context.out.String("Extracting "); context.out.String(entryname);
  245. context.out.String(" ... "); Zip.ShowError(res, context.out); context.out.Ln;
  246. END;
  247. END;
  248. context.out.Int(nofExtracted, 0);
  249. IF (nofExtracted = 1) THEN context.out.String(" entry extracted"); ELSE context.out.String(" entries extracted"); END;
  250. IF (nofErrors > 0) THEN
  251. context.out.String(" ("); context.out.Int(nofErrors, 0);
  252. IF (nofErrors = 1) THEN context.out.String(" error)");
  253. ELSE context.out.String(" errors)");
  254. END;
  255. context.result := Commands.CommandError;
  256. END;
  257. context.out.Ln;
  258. END;
  259. ELSE
  260. context.result := Commands.CommandParseError;
  261. END;
  262. END Extract;
  263. (** Extracts all entries of the selected zip-archives. The relative path in the file name of the entry
  264. is ignored (c.f. option \d).
  265. ZipTool.ExtractAll [-d] [-o] [-p=DstPrefix] [-sourcePath=SrcPrefix] [-s] {ZipFile} ~
  266. Options:
  267. --directory: If set, the file is stored in the directory according to the relative path in the file name of the entry
  268. --overwrite: If set, files with the same name are overwritten, otherwise they are renamed to filename.Bak
  269. --ignore: If set, continue extraction in case of an error, otherwise abort
  270. --prefix: If set, DstPrefix is prefixed to all file names of the entries in the zip-archives
  271. --sourcePath: If set, SrcPrefix is prefixed to all archive names *)
  272. PROCEDURE ExtractAll*(context : Commands.Context);
  273. VAR
  274. fullArchiveName, archiveName, filename, prefix : Files.FileName;
  275. options : Options.Options;
  276. path, overwrite, stopOnError, silent: BOOLEAN;
  277. archive: Zip.Archive;
  278. entry: Zip.Entry;
  279. nofExtracted, nofErrors: LONGINT; res: WORD;
  280. BEGIN
  281. NEW(options);
  282. options.Add("d", "directory", Options.Flag);
  283. options.Add("o", "overwrite", Options.Flag);
  284. options.Add("i", "ignore", Options.Flag);
  285. options.Add("p", "prefix", Options.String);
  286. options.Add(0X, "sourcePath", Options.String);
  287. options.Add("s", "silent", Options.Flag);
  288. IF options.Parse(context.arg, context.error) THEN
  289. path := options.GetFlag("directory");
  290. overwrite := options.GetFlag("overwrite");
  291. stopOnError := options.GetFlag("ignore");
  292. silent := options.GetFlag("silent");
  293. IF ~options.GetString("prefix", prefix) THEN prefix := ""; END;
  294. WHILE context.arg.GetString(archiveName) DO
  295. IF ~options.GetString("sourcePath", fullArchiveName) THEN fullArchiveName := ""; END;
  296. Strings.Append(fullArchiveName, archiveName);
  297. archive := OpenArchive(fullArchiveName, context.error);
  298. IF (archive # NIL) THEN
  299. context.out.String("Extracting "); context.out.String(fullArchiveName); context.out.String(" ... ");
  300. IF ~silent THEN context.out.Ln; END;
  301. context.out.Update;
  302. nofExtracted := 0; nofErrors := 0;
  303. entry := Zip.FirstEntry(archive);
  304. WHILE (entry # NIL) & ((nofErrors = 0) OR ~stopOnError) DO
  305. IF (prefix # "") THEN
  306. COPY(prefix, filename); Append(filename, entry.name);
  307. ELSE
  308. COPY(entry.name, filename);
  309. END;
  310. IF silent THEN
  311. ExtractFile(archive, entry, filename, path, overwrite, NIL, NIL, res);
  312. ELSE
  313. ExtractFile(archive, entry, filename, path, overwrite, context.out, context.error, res);
  314. END;
  315. IF (res = Zip.Ok) THEN
  316. INC(nofExtracted);
  317. ELSE
  318. INC(nofErrors);
  319. END;
  320. entry := Zip.NextEntry(entry);
  321. END;
  322. IF (nofExtracted > 1) THEN
  323. context.out.Int(nofExtracted, 0); context.out.String(" entries extracted");
  324. END;
  325. IF (nofErrors > 0) THEN
  326. context.out.String(" (");
  327. IF (nofErrors = 1) THEN context.out.String("1 error)");
  328. ELSE context.out.Int(nofErrors, 0); context.out.String(" errors)");
  329. END;
  330. context.result := Commands.CommandError;
  331. END;
  332. IF (nofExtracted > 1) OR (nofErrors > 0) THEN context.out.Ln; END;
  333. END;
  334. END;
  335. ELSE
  336. context.result := Commands.CommandParseError;
  337. END;
  338. END ExtractAll;
  339. (** Adds a file to the selected zip-archive.
  340. level: specifies the compression level (0: no compression, 9: best compression)
  341. strategy: specifies the compression strategy (from 0 - 2)
  342. res = Zip.Ok, Zip.BadName, Zip.EntryAlreadyExists, Zip.DataError **)
  343. PROCEDURE AddFile*(arc: Zip.Archive; CONST srcname : ARRAY OF CHAR; CONST dstname: ARRAY OF CHAR; level, strategy: LONGINT; VAR res: WORD);
  344. VAR f: Files.File; r: Files.Rider;
  345. BEGIN
  346. f := Files.Old(srcname);
  347. IF f = NIL THEN
  348. res := Zip.BadName
  349. ELSE
  350. f.Set(r, 0);
  351. Zip.AddEntry(arc, dstname, r, f.Length(), SHORT(SHORT(level)), SHORT(SHORT(strategy)), res);
  352. END;
  353. END AddFile;
  354. PROCEDURE GetFileName(CONST fullname : ARRAY OF CHAR; VAR filename : ARRAY OF CHAR);
  355. VAR prefix : Files.Prefix; pathname, path : Files.FileName;
  356. BEGIN
  357. Files.SplitName(fullname, prefix, pathname);
  358. Files.SplitPath(pathname, path, filename);
  359. END GetFileName;
  360. PROCEDURE GetName(CONST fullname : ARRAY OF CHAR; VAR name : ARRAY OF CHAR);
  361. VAR prefix : Files.Prefix;
  362. BEGIN
  363. Files.SplitName(fullname, prefix, name);
  364. IF (name[0] = Files.PathDelimiter) THEN Strings.Delete(name, 0, 1); END;
  365. END GetName;
  366. (** Adds the selected files to the selected zip-archive.
  367. ZipTool.Add [--level=<int> [--strategy=<int>]] [--nopath] ZipFile {Entry} ~
  368. Options:
  369. --level=<integer>: specifies the compression level (0: no compression, 9: best compression)
  370. if not set, default level (-1) is used
  371. --strategy=<integer>: specifies the compression strategy (from 0 - 2)
  372. --nopath: remove prefix & path from filename
  373. --removePrefix: remove prefix from filename (but keep path)
  374. --ignore: continue in case of errors
  375. --silent: Only error output *)
  376. PROCEDURE Add*(context : Commands.Context);
  377. VAR
  378. archiveName, entryName : Files.FileName;
  379. options : Options.Options;
  380. archive: Zip.Archive;
  381. strategy, level: LONGINT; stopOnError : BOOLEAN;
  382. oldname, newname: ARRAY 256 OF CHAR;
  383. nofAdded, nofErrors: LONGINT; res: WORD;
  384. PROCEDURE ShowFile(CONST oldname, newname : ARRAY OF CHAR; out : Streams.Writer);
  385. BEGIN
  386. context.out.String("Adding "); context.out.String(oldname);
  387. IF (oldname # newname) THEN context.out.String(" -> "); context.out.String(newname); END;
  388. context.out.String(" ... ");
  389. END ShowFile;
  390. BEGIN
  391. NEW(options);
  392. options.Add("l", "level", Options.Integer);
  393. options.Add("s", "strategy", Options.Integer);
  394. options.Add("n", "nopath", Options.Flag);
  395. options.Add("i", "ignore", Options.Flag);
  396. options.Add("r", "removePrefix", Options.Flag);
  397. options.Add(0X, "silent", Options.Flag);
  398. IF options.Parse(context.arg, context.error) THEN
  399. IF ~options.GetInteger("level", level) THEN level := Zip.DefaultCompression; END;
  400. IF ~options.GetInteger("strategy", strategy) THEN strategy := Zip.DefaultStrategy; END;
  401. stopOnError := ~options.GetFlag("ignore");
  402. context.arg.SkipWhitespace; context.arg.String(archiveName);
  403. archive := Zip.CreateArchive(archiveName, res);
  404. IF (res = Zip.Ok) THEN
  405. nofAdded := 0; nofErrors := 0;
  406. WHILE context.arg.GetString(entryName) & ((nofErrors = 0) OR ~stopOnError) DO
  407. COPY(entryName, oldname);
  408. IF options.GetFlag("nopath") THEN
  409. GetFileName(entryName, newname);
  410. ELSE
  411. IF options.GetFlag("removePrefix") THEN
  412. GetName(entryName, newname);
  413. ELSE
  414. COPY(entryName, newname);
  415. END;
  416. END;
  417. IF ~options.GetFlag("silent") THEN
  418. ShowFile(oldname, newname, context.out);
  419. END;
  420. AddFile(archive, oldname, newname, level, strategy, res);
  421. IF (res = Zip.Ok) THEN
  422. INC(nofAdded);
  423. IF ~options.GetFlag("silent") THEN
  424. context.out.String("done."); context.out.Ln;
  425. END;
  426. ELSE
  427. INC(nofErrors);
  428. IF options.GetFlag("silent") THEN
  429. ShowFile(oldname, newname, context.out);
  430. END;
  431. Zip.ShowError(res, context.out); context.out.Ln;
  432. END;
  433. END;
  434. IF (nofAdded > 1) THEN
  435. context.out.Int(nofAdded, 0); context.out.String(" entries added to archive "); context.out.String(archiveName);
  436. END;
  437. IF (nofErrors > 0) THEN
  438. context.out.String(" ("); context.out.Int(nofErrors, 0);
  439. IF (nofErrors = 1) THEN context.out.String(" error)"); ELSE context.out.String(" errors)"); END;
  440. context.result := Commands.CommandError;
  441. END;
  442. IF (nofAdded > 1) OR (nofErrors > 0) THEN context.out.Ln; END;
  443. ELSE
  444. context.error.String("Could not create archive '"); context.error.String(archiveName); context.error.String("': ");
  445. Zip.ShowError(res, context.error); context.error.Ln;
  446. context.result := Commands.CommandError;
  447. END;
  448. ELSE
  449. context.result := Commands.CommandParseError;
  450. END;
  451. END Add;
  452. (** Deletes the selected entries from the selected zip-archive.
  453. ZipTool.Delete [--ignore] ZipFile {Entry} ~ **)
  454. PROCEDURE Delete*(context : Commands.Context);
  455. VAR
  456. archiveName, entryName : Files.FileName;
  457. options : Options.Options;
  458. archive: Zip.Archive;
  459. entry: Zip.Entry;
  460. stopOnError : BOOLEAN;
  461. nofDeleted, nofErrors: LONGINT; res: WORD;
  462. BEGIN
  463. NEW(options);
  464. options.Add("i", "ignore", Options.Flag);
  465. IF options.Parse(context.arg, context.error) THEN
  466. stopOnError := ~options.GetFlag("ignore");
  467. context.arg.SkipWhitespace; context.arg.String(archiveName);
  468. archive := OpenArchive(archiveName, context.error);
  469. IF (archive # NIL) THEN
  470. nofDeleted := 0; nofErrors := 0;
  471. WHILE context.arg.GetString(entryName) & ((nofErrors = 0) OR ~stopOnError) DO
  472. entry := Zip.GetEntry(archive, entryName, res);
  473. context.out.String("Deleting entry "); context.out.String(entryName); context.out.String(" ... ");
  474. IF (res = Zip.Ok) THEN
  475. Zip.DeleteEntry(archive, entry, res);
  476. IF (res = Zip.Ok) THEN
  477. INC(nofDeleted);
  478. context.out.String("done.");
  479. END;
  480. END;
  481. IF (res # Zip.Ok) THEN
  482. INC(nofErrors);
  483. Zip.ShowError(res, context.out);
  484. END;
  485. context.out.Ln;
  486. END;
  487. IF (nofDeleted > 1) THEN
  488. context.out.Int(nofDeleted, 0);
  489. IF (nofDeleted = 1) THEN context.out.String(" entry deleted");
  490. ELSE context.out.String(" entries deleted");
  491. END;
  492. END;
  493. IF (nofErrors > 0) THEN
  494. context.out.String(" (");
  495. context.out.Int(nofErrors, 0);
  496. IF (nofErrors = 1) THEN context.out.String("error)");
  497. ELSE context.out.String(" errors)");
  498. END;
  499. context.result := Commands.CommandError;
  500. END;
  501. IF (nofDeleted > 1) OR (nofErrors > 0) THEN context.out.Ln; END;
  502. END;
  503. ELSE
  504. context.result := Commands.CommandParseError;
  505. END;
  506. END Delete;
  507. END ZipTool.
  508. System.Free ZipTool ~
  509. ZipTool.Directory ZeroSkin.zip ~
  510. ZipTool.Directory --details ZeroSkin.zip ~
  511. ZipTool.Extract ZeroSkin.zip arrow.png ~
  512. ZipTool.ExtractAll ZeroSkin.zip ~