Sign in Join
Wiki copytemporary

paszlib: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: paszlib

OldNewLine
1+**paszlib** is a Pascal conversion of the standard zlib library: you don't need any external dependencies. It was implemented by Jacques Nomssi Nzali (his old homepage is dead, see a continuation of the project [here](http://sageshome.net/oss/paszlib-sg.php)). It is used in the FCL to implement the TCompressionStream class.
2+
3+This class lets you compress and decompress **.zip** files.
4+
5+The main unit of this package is **paszlib**. There are other, auxiliary units, but the only unit that needs to be included in a typical program is this one.
6+
7+# TZipper
8+
9+TZipper implements support for compressing and decompressing .zip files, but does not support all zip compression methods.
10+
11+## Documentation
12+
13+See [official FPC documentation for Zipper](http://www.freepascal.org/docs-html/fcl/zipper/index.html).
14+
15+## Examples
16+
17+### Zip files
18+
19+Create a zip file named as first parameter. Treats all other parameters as filenames to add, so you can specify e.g.
20+
21+```dos
22+zipper newzip.zip autoexec.bat config.sys
23+```
24+
25+``` pascal
26+uses
27+ Zipper;
28+var
29+ OurZipper: TZipper;
30+ I: Integer;
31+begin
32+ OurZipper := TZipper.Create;
33+ try
34+ // Define the file name of the zip file to be created
35+ OurZipper.FileName := ParamStr(1);
36+ for I := 2 to ParamCount do
37+ // Specify the names of the files to be included in the zip as first argument
38+ // The second argument is the name of the file as it appears in the zip and
39+ // later in the file system after unzipping
40+ OurZipper.Entries.AddFileEntry(ParamStr(I), ParamStr(I));
41+ // Execute the zipping operation and write the zip file.
42+ OurZipper.ZipAllFiles;
43+ finally
44+ OurZipper.Free;
45+ end;
46+end.
47+```
48+
49+Please note that the names of the files to be zipped are assumed to contain only characters of the old IBM PC character set (code page 437). For extended characters (UTF-8), see the section below.
50+
51+### Unzip files
52+
53+Unzip all files in archive c:\test.zip into directory c:\windows\temp\\
54+
55+``` pascal
56+uses
57+ Zipper;
58+var
59+ UnZipper: TUnZipper;
60+begin
61+ UnZipper := TUnZipper.Create;
62+ try
63+ UnZipper.FileName := 'c:\test.zip';
64+ UnZipper.OutputPath := 'c:\windows\temp';
65+ UnZipper.Examine;
66+ UnZipper.UnZipAllFiles;
67+ finally
68+ UnZipper.Free;
69+ end;
70+end.
71+```
72+
73+### Zip files with encoding filenames
74+
75+As already noted, the zip file format originally was written to support only code page 437 of the IBM PC. In modern operating systems, however, the encoding of file names is much more general.
76+
77+If your **FPC is version 3.2 or newer** you can take advantage of the boolean property `UseLanguageEncoding`. When set to `true`, file names are assumed to have the default encoding of FPC, in case of Lazarus programs this is UTF-8. Here any file name can be passed to the zipper (except for restrictions of the OS, of course).
78+
79+In the following practical routine for FPC 3.2, a zip file is created from the files in a given directory so that file names are considered correctly:
80+
81+``` pascal
82+uses
83+ FileUtil, zipper;
84+
85+function PackFiles(AZipFilename, ADirectory, AMask: String;
86+ IncludingSubDirs: Boolean): Boolean;
87+var
88+ OurZipper: TZipper;
89+ list: TStringList;
90+ i: Integer;
91+ diskFileName, archiveFileName: String;
92+begin
93+ Result := false;
94+ ADirectory := IncludeTrailingPathDelimiter(ADirectory);
95+ if DirectoryExists(ADirectory) then
96+ begin
97+ OurZipper := TZipper.Create;
98+ try
99+ // Set the name of the zip file to be created
100+ OurZipper.FileName := AZipFileName;
101+
102+ // Read names of the files contained in ADirectory to a stringlist
103+ list := TStringList.Create;
104+ try
105+ // FindAllFiles adds all file names matching the mask (e.g. '*.*')
106+ // found in the given directory to the provided list.
107+ // When IncludingSubDirs is true the search continues recursively in
108+ // the subdirectories.
109+ FindAllFiles(list, ADirectory, AMask, IncludingSubDirs);
110+ for i := 0 to list.Count - 1 do
111+ begin
112+ // diskfilename is the name of the file to be zipped on the disk
113+ diskFileName := list[i];
114+ // archivefilename is the name of the file to be zipped as it appears
115+ // in the zip. We remove the deirectory from the
116+ archiveFileName := StringReplace(diskFileName, ADirectory, '', []);
117+ // Store these filenames for the zipper
118+ OurZipper.Entries.AddFileEntry(diskFileName, archiveFileName);
119+ end;
120+ finally
121+ list.Free;
122+ end;
123+ // By default zipper writes file names in encoding of the IBM PC, CP437.
124+ // UTF8 encoding is written when UseLanguageEncoding is true.
125+ OurZipper.UseLanguageEncoding := true; // Requires FPC 3.2+
126+ // create and write the zip file
127+ OurZipper.ZipAllFiles;
128+ Result := true;
129+ finally
130+ OurZipper.Free;
131+ end;
132+ end else
133+ Result := false;
134+end;
135+```
136+
137+If your **FPC is older than v3.2** you must convert the ArchiveFileName argument to CP437 - of course, this is not possible for all characters, and thus you must be very careful with zipper in this case. Note also that the `DiskFileName` must have the encoding required by the operating system, otherwise the file will not be found for zipping; in case of FPC before 3.0 this may require another code page conversion.
138+
139+Here is the adaption of above example for an FPC version before 3.2:
140+
141+``` pascal
142+uses
143+ FileUtil, LConvEncoding, LazUTF8, Zipper;
144+
145+function PackFiles(AZipFilename, ADirectory, AMask: String;
146+ IncludingSubdirs: Boolean): Boolean;
147+var
148+ OurZipper: TZipper;
149+ list: TStringList;
150+ i: Integer;
151+ diskFileName, archiveFileName: String;
152+begin
153+ Result := false;
154+ if AMask = '' then AMask := '*.*';
155+ ADirectory := IncludeTrailingPathDelimiter(ADirectory);
156+ if DirectoryExists(ADirectory) then
157+ begin
158+ OurZipper := TZipper.Create;
159+ try
160+ OurZipper.FileName := AZipFileName;
161+ list := TStringList.Create;
162+ try
163+ // List all files in ADirectory and, if required, its sub-directories.
164+ FindAllFiles(list, ADirectory, AMask, IncludingSubDirs);
165+ for i := 0 to list.Count-1 do
166+ begin
167+ // Name of the file to be zipped on disk
168+ diskFileName := list[i];
169+ {$IF FPC_FullVersion < 30000}
170+ diskFileName := UTF8ToWinCP(diskFileName);
171+ {$IFEND}
172+ // Name of the file to be zipped in the archive: we remove the
173+ // common path and thus make the files relative to the directory
174+ // into which they will be unzipped later.
175+ archiveFileName := StringReplace(list[i], ADirectory, '', []);
176+ archiveFileName := UTF8ToCP437(archiveFileName);
177+ OurZipper.Entries.AddFileEntry(diskFileName, archiveFileName);
178+ end;
179+ finally
180+ list.Free;
181+ end;
182+ // Execute zipping action and create zip file
183+ OurZipper.ZipAllFiles;
184+ Result := true;
185+ finally
186+ OurZipper.Free;
187+ end;
188+ end;
189+end;
190+```
191+
192+Another restriction is the path delimiter in the archived file name. Specification of the zip format requires that forward slashes ('`/`') are used even in case of Windows. This is important for the one-parameter overload of `TZipper.Entries.AddFileEntry(DiskfileName)` which simply assume the archived file name to be equal to the `DiskFileName`, without any adaptions. Therefore, such a zip file will contain backslashes on Windows. It will unzip correctly on Windows, but not on Linux where the backslash is considered to be a valid file name character. It is highly recommended to use the two-parameter version of `TZipper.Entries.AFileEntry(DiskFileName, ArchiveFileName)` with `ArchivefileName = DiskFileName` because this procedure automatically replaces the path delimiters as required.
193+
194+### Unzip files with encoding filenames
195+
196+``` pascal
197+uses
198+ Zipper, LConvEncoding;
199+...
200+function EndPathCP866ToUTF8(AText:string):string;
201+var
202+ c,i:integer;
203+ s,s1,s2,chr:string;
204+begin
205+ s:='';
206+ c:=UTF8Length(AText);
207+ for i:=c downto 1 do
208+ begin
209+ chr:=UTF8Copy(AText,i,1);
210+ if ((not(chr='/')) and (not(chr='\')))or(i=c) then
211+ begin
212+ s:=UTF8Copy(AText,i,1)+s;
213+ end
214+ else begin
215+ s:=UTF8Copy(AText,i,1)+s;
216+ break;
217+ end;
218+ end;
219+ dec(i);
220+ s1:=UTF8Copy(AText,1,i);
221+ s2:=CP866ToUTF8(s);
222+ Result:=s1+s2;
223+end;
224+
225+function UnPackFiles(Filename, UnPackPath: String): Integer;
226+var
227+ UnZipper :TUnZipper; //PasZLib
228+ UnPackFileDir,
229+ ADiskFileName,
230+ ANewDiskFileName,
231+ AArchiveFileName :String;
232+ i :integer;
233+begin
234+ Result:=-1;
235+ if FileExists(Filename)and DirectoryExists(UnPackPath) then
236+ begin
237+ UnPackFileDir :=SysUtils.IncludeTrailingPathDelimiter(UnPackPath);
238+ UnZipper :=TUnZipper.Create;
239+ try
240+ UnZipper.FileName := Filename;
241+ UnZipper.OutputPath := UnPackPath;
242+ UnZipper.Examine;
243+ UnZipper.UnZipAllFiles;
244+
245+ for i:=UnZipper.Entries.Count-1 downto 0 do
246+ begin
247+ AArchiveFileName:=UnZipper.Entries.Entries[i].ArchiveFileName;
248+ AArchiveFileName:=EndPathCP866ToUTF8(AArchiveFileName);
249+ AArchiveFileName:=UTF8ToSys(AArchiveFileName);
250+ ANewDiskFileName:=UnPackFileDir+AArchiveFileName;
251+ ADiskFileName :=UnPackFileDir+UnZipper.Entries.Entries[i].DiskFileName;
252+
253+ if FileExists(ADiskFileName) then
254+ begin
255+ RenameFile(ADiskFileName, ANewDiskFileName);
256+ end
257+ else if DirectoryExists(ADiskFileName) then
258+ begin
259+ ADiskFileName :=SysUtils.IncludeTrailingPathDelimiter(ADiskFileName);
260+ ANewDiskFileName :=SysUtils.IncludeTrailingPathDelimiter(ANewDiskFileName);
261+ RenameFile(ADiskFileName, ANewDiskFileName);
262+ end;
263+ end;
264+
265+ Result:=1;
266+ finally
267+ UnZipper.Free;
268+ end;
269+ end;
270+end;
271+```
272+
273+More examples can be found in the FPC source directory:
274+
275+- examples: [1](http://svn.freepascal.org/svn/fpc/trunk/packages/paszlib/examples/)
276+- the test program: [2](http://svn.freepascal.org/svn/fpc/trunk/packages/paszlib/tests)
277+
278+### Unzip file to a stream
279+
280+In Lazarus, drop a TMemo, TButton, TEdit and TFileNameEdit on a form. Clicking the button will read the zip file in FileNameEdit, extract the file specified in the Edit box, and display the content in Memo.
281+
282+``` pascal
283+uses
284+ Zipper;
285+
286+...
287+
288+procedure TForm1.Button1Click(Sender: TObject);
289+begin
290+ ExtractFileFromZip(FileNameEdit1.FileName,Edit1.Text);
291+end;
292+
293+procedure TForm1.DoCreateOutZipStream(Sender: TObject; var AStream: TStream;
294+ AItem: TFullZipFileEntry);
295+begin
296+ AStream:=TMemorystream.Create;
297+end;
298+
299+procedure TForm1.DoDoneOutZipStream(Sender: TObject; var AStream: TStream;
300+ AItem: TFullZipFileEntry);
301+begin
302+ AStream.Position:=0;
303+ Memo1.lines.LoadFromStream(Astream);
304+ Astream.Free;
305+end;
306+
307+procedure TForm1.ExtractFileFromZip(ZipName, FileName: string);
308+var
309+ ZipFile: TUnZipper;
310+ sl:TStringList;
311+begin
312+ sl:=TStringList.Create;
313+ sl.Add(FileName);
314+ ZipFile := TUnZipper.Create;
315+ try
316+ ZipFile.FileName := ZipName;
317+ ZipFile.OnCreateStream := @DoCreateOutZipStream;
318+ ZipFile.OnDoneStream:=@DoDoneOutZipStream;
319+ ZipFile.UnZipFiles(sl);
320+ finally
321+ ZipFile.Free;
322+ sl.Free;
323+ end;
324+end;
325+```
326+
327+### Zipping a whole directory tree
328+
329+- This will recursively add the contents of 'C:\MyFolder' to the 'myzipfile.zip'
330+ - Note that the absolute path is stored in the zipfile
331+ - Note that this requires the Lazarus fileutil unit (which you can probably work around)
332+
333+``` pascal
334+Uses ...Zipper,FileUtil
335+var
336+ AZipper: TZipper;
337+ TheFileList:TStringList;
338+begin
339+ MyDirectory:='C:\MyFolder';
340+ AZipper := TZipper.Create;
341+ AZipper.Filename := 'myzipfile.zip';
342+ TheFileList:=TStringList.Create;
343+ try
344+ FindAllFiles(TheFileList, MyDirectory);
345+ AZipper.Entries.AddFileEntries(TheFileList);
346+ AZipper.ZipAllFiles;
347+ finally
348+ TheFileList.Free;
349+ AZipper.Free;
350+ end;
351+end;
352+```
353+
354+### Zipping a whole directory tree storing only a relative path
355+
356+This is more complicated, but it can be done. Note that this requires the Lazarus FileUtil unit (which you can probably work around).
357+
358+``` pascal
359+uses
360+ ..., Zipper, FileUtil, StrUtils;
361+var
362+ AZipper: TZipper;
363+ szPathEntry: String;
364+ i: Integer;
365+ ZEntries: TZipFileEntries;
366+ TheFileList: TStringList;
367+ RelativeDirectory: String;
368+begin
369+ AZipper := TZipper.Create;
370+ try
371+ try
372+ AZipper.Filename := 'myzipfile.zip';
373+ RelativeDirectory:='C:\MyFolder\MyFolder\';
374+ AZipper.Clear;
375+ ZEntries := TZipFileEntries.Create(TZipFileEntry);
376+ // Verify valid directory
377+ If DirPathExists(RelativeDirectory) then
378+ begin
379+ // Construct the path to the directory BELOW RelativeDirectory
380+ // If user specifies 'C:\MyFolder\Subfolder' it returns 'C:\MyFolder\'
381+ // If user specifies 'C:\MyFolder' it returns 'C:\'
382+ // If user specifies 'C:\' it returns 'C:\'
383+ i:=RPos(PathDelim,ChompPathDelim(RelativeDirectory));
384+ szPathEntry:=LeftStr(RelativeDirectory,i);
385+
386+ // Use the FileUtils.FindAllFiles function to get everything (files and folders) recursively
387+ TheFileList:=TstringList.Create;
388+ try
389+ FindAllFiles(TheFileList, RelativeDirectory);
390+ for i:=0 to TheFileList.Count -1 do
391+ begin
392+ // Make sure the RelativeDirectory files are not in the root of the ZipFile
393+ ZEntries.AddFileEntry(TheFileList[i],CreateRelativePath(TheFileList[i],szPathEntry));
394+ end;
395+ finally
396+ TheFileList.Free;
397+ end;
398+ end;
399+ if (ZEntries.Count > 0) then
400+ AZipper.ZipFiles(ZEntries);
401+ except
402+ On E: EZipError do
403+ E.CreateFmt('Zipfile could not be created%sReason: %s', [LineEnding, E.Message])
404+ end;
405+ result := True;
406+ finally
407+ FreeAndNil(ZEntries);
408+ AZipper.Free;
409+ end;
410+end;
411+```
412+
413+Note that this example uses an overloaded version of AddFileEntry() (compared to simple examples). This version allows you to specify the directory structure inside the Zip file, and, then, of course, the directory structure when its all unzipped. You can, for example, specify only the file name with no directory structure and have all files returned in one flat output directory. Even if they came for all over the place.
414+
415+``` pascal
416+ZEntries.AddFileEntry(FullDiskPathToFile, FileName);
417+```
418+
419+## See also
420+
421+- [official FPC documentation for Zipper](http://www.freepascal.org/docs-html/fcl/zipper/index.html)
422+- [Article](http://www.freepascal.org/~michael/articles/archives/archives.pdf) demonstrating handling tar, bzip2, gzip, zip files and Blowfish encryption in FreePascal/Lazarus. A good introduction even though it was written some time ago (a lot of functionality has been improved).
423+- [[unzip]]
424+- [[FreePascalArchivePackage]] Abbrevia archive/zip library
425+- [MIT licensed Delphi/Object Pascal library](http://code.google.com/p/kstools/) that includes zip file support.
426+
427+Go back to [[Package List|Packages List]]
428+
429+---
430+*From the Free Pascal wiki, page "paszlib", as it stood on 2023-08-06 after a change by Alextpp, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
431+Category: FPC
432+Category: Lazarus
433+Category: Packages
434+Category: Tutorials

Back to history