VBA: Jak na ZIPování?
Jan Matuška 1/2009
Tu je zase něco z mých zkušeností z posledních let. Někdo v konferenci access pandora cz řešil zipování, tak nabízím řešení.
Zipování souborů - dalo mi to kus hledání, laborování a
testování, ale nakonec vše funguje k naprosté spokojenosti
mé i mých klientů.
Použil jsem free knihovnu zip32.dll z projektu Info-Zip. K
ní jsem si pro VBA vyrobil třídu (modul třídy) a protože
některé funkce jinak nešly, ještě bylo nutné udělat
modul.Musejí se tedy používat oba moduly současně.
Bacha na věc: kvůli tomu, že to nešlo celé předělat na třídu a musel zůstat modul, tak se myslím nesmí pouštět zipování víckrát současně! Už je to delší dobu, co jsem to tvořil :-)
Pro info: třída jmaMeter je moje třída na "čadítko" neboli "ukazatel průběhu". Používám vlastní řešení místo systémového, protože u objektu potřebuji další funkce (povolení/zakázání stornování, nemodální form, barvičky, někdy dvě-tři čadítka, pole s výpisem logu apod.) Pokus si zkopírujete všecek kód, budete asi muset zapoznámkovat použití téhle třídy. Teď nemám kapacitu to nějak víc zkoumat. Kdyby to neznalo ještě něco nutného pro kompilaci, však se mi ozvete... :-)
Příklad použití:
Sub ziptest()
Dim z As jmaZipIZ
Set z = New jmaZipIZ
z.Level = 9
z.ZipFileName = "E:\XX\Pic.zip"
z.RootDir = "E:\XX\"
z.AddFileSpec "pic\*.*"
z.AddFileSpec "pic\thm\*.*"
z.Password = "abcd"
z.MakeArchive
Set z = Nothing
Set z = New jmaZipIZ
z.Level = 9
z.ZipFileName = "E:\XX\Data.zip"
z.RootDir = "E:\XX\"
z.AddFileSpec "KatPolozky.txt"
z.AddFileSpec "a\Kategorie.txt"
z.Password = "abcd"
z.MakeArchive
Set z = Nothing
End Sub
Třída (soubor jmaZipIZ.cls):
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "jmaZipIZ"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
'JMA třída jmaZipIZ pro práci se ZIPy s knihovnou zip32.dll (Info-ZIP)
'je nutný ještě modul jmaZipIZBaseFunc
'070128 .Message, aby se psalo do 1 řádku Metru
'061222 předěláno z příkladu (zip232dN.zip\vb\VBZipBas.bas) na třídu a modul
'Password ...pokud není prázdný, automaticky se použije
'AskForPassword ...zeptá se na heslo
'Meter ...objekt jmaMeter - pokud je nastaven, hlášky se vypíší tam.
'Mute ...zda mlčet nebo vypisovat info o průběhu
Option Compare Database
Option Explicit
Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long
Dim hLibrary As Long 'Stores the handle library
'Dim mError As FIMAGE_ERROR
Dim mErS As String 'hláška z objektu Err
'---------------------------------------------------------------
'-- Please Do Not Remove These Comments!!!
'---------------------------------------------------------------
'-- Sample VB 5 code to drive zip32.dll
'-- Contributed to the Info-ZIP project by Mike Le Voi
'--
'-- Contact me at: mlevoi@modemss.brisnet.org.au
'--
'-- Visit my home page at: http://modemss.brisnet.org.au/~mlevoi
'--
'-- Use this code at your own risk. Nothing implied or warranted
'-- to work on your machine :-)
'---------------------------------------------------------------
'--
'-- The Source Code Is Freely Available From Info-ZIP At:
'-- http://www.cdrom.com/pub/infozip/infozip.html
'--
'-- A Very Special Thanks To Mr. Mike Le Voi
'-- And Mr. Mike White Of The Info-ZIP
'-- For Letting Me Use And Modify His Orginal
'-- Visual Basic 5.0 Code! Thank You Mike Le Voi.
'---------------------------------------------------------------
'--
'-- Contributed To The Info-ZIP Project By Raymond L. King
'-- Modified June 21, 1998
'-- By Raymond L. King
'-- Custom Software Designers
'--
'-- Contact Me At: king@ntplx.net
'-- ICQ 434355
'-- Or Visit Our Home Page At: http://www.ntplx.net/~king
'--
'---------------------------------------------------------------
'
' This is the original example with some small changes. Only
' use with the original Zip32.dll (Zip 2.3). Do not use this VB
' example with Zip32z64.dll (Zip 3.0).
'
' 4/29/2004 Ed Gordon
'---------------------------------------------------------------
' Usage notes:
'
' This code uses Zip32.dll. You DO NOT need to register the
' DLL to use it. You also DO NOT need to reference it in your
' VB project. You DO have to copy the DLL to your SYSTEM
' directory, your VB project directory, or place it in a directory
' on your command PATH.
'
' A bug has been found in the Zip32.dll when called from VB. If
' you try to pass any values other than NULL in the ZPOPT strings
' Date, szRootDir, or szTempDir they get converted from the
' VB internal wide character format to temporary byte strings by
' the calling interface as they are supposed to. However when
' ZpSetOptions returns the passed strings are deallocated unless the
' VB debugger prevents it by a break between ZpSetOptions and
' ZpArchive. When Zip32.dll uses these pointers later it
' can result in unpredictable behavior. A kluge is available
' for Zip32.dll, just replacing api.c in Zip 2.3, but better to just
' use the new Zip32z64.dll where these bugs are fixed. However,
' the kluge has been added to Zip 2.31. To determine the version
' of the dll you have right click on it, select the Version tab,
' and verify the Product Version is at least 2.31.
'
' Another bug is where -R is used with some other options and can
' crash the dll. This is a bug in how zip processes the command
' line and should be mostly fixed in Zip 2.31. If you run into
' problems try using -r instead for recursion. The bug is fixed
' in Zip 3.0 but note that Zip 3.0 creates dll zip32z64.dll and
' it is not compatible with older VB including this example. See
' the new VB example code included with Zip 3.0 for calling
' interface changes.
'
' Note that Zip32 is probably not thread safe. It may be made
' thread safe in a later version, but for now only one thread in
' one program should use the DLL at a time. Unlike Zip, UnZip is
' probably thread safe, but an exception to this has been
' found. See the UnZip documentation for the latest on this.
'
' All code in this VB project is provided under the Info-Zip license.
'
' If you have any questions please contact Info-Zip at
' http://www.info-zip.org.
'
' 4/29/2004 EG (Updated 3/1/2005 EG)
'
'---------------------------------------------------------------
'-- C Style argv
'-- Holds The Zip Archive Filenames
' Max for this just over 8000 as each pointer takes up 4 bytes and
' VB only allows 32 kB of local variables and that includes function
' parameters. - 3/19/2004 EG
'
Private Type ZIPnames
Files(0 To 99) As String
End Type
'-- Call Back "String"
Private Type ZipCBChar
ch(4096) As Byte
End Type
'-- ZPOPT Is Used To Set The Options In The ZIP32.DLL
Private Type ZPOPT
Date As String ' US Date (8 Bytes Long) "12/31/98"?
szRootDir As String ' Root Directory Pathname (Up To 256 Bytes Long)
szTempDir As String ' Temp Directory Pathname (Up To 256 Bytes Long)
fTemp As Long ' 1 If Temp dir Wanted, Else 0
fSuffix As Long ' Include Suffixes (Not Yet Implemented!)
fEncrypt As Long ' 1 If Encryption Wanted, Else 0
fSystem As Long ' 1 To Include System/Hidden Files, Else 0
fVolume As Long ' 1 If Storing Volume Label, Else 0
fExtra As Long ' 1 If Excluding Extra Attributes, Else 0
fNoDirEntries As Long ' 1 If Ignoring Directory Entries, Else 0
fExcludeDate As Long ' 1 If Excluding Files Earlier Than Specified Date, Else 0
fIncludeDate As Long ' 1 If Including Files Earlier Than Specified Date, Else 0
fVerbose As Long ' 1 If Full Messages Wanted, Else 0
fQuiet As Long ' 1 If Minimum Messages Wanted, Else 0
fCRLF_LF As Long ' 1 If Translate CR/LF To LF, Else 0
fLF_CRLF As Long ' 1 If Translate LF To CR/LF, Else 0
fJunkDir As Long ' 1 If Junking Directory Names, Else 0
fGrow As Long ' 1 If Allow Appending To Zip File, Else 0
fForce As Long ' 1 If Making Entries Using DOS File Names, Else 0
fMove As Long ' 1 If Deleting Files Added Or Updated, Else 0
fDeleteEntries As Long ' 1 If Files Passed Have To Be Deleted, Else 0
fUpdate As Long ' 1 If Updating Zip File-Overwrite Only If Newer, Else 0
fFreshen As Long ' 1 If Freshing Zip File-Overwrite Only, Else 0
fJunkSFX As Long ' 1 If Junking SFX Prefix, Else 0
fLatestTime As Long ' 1 If Setting Zip File Time To Time Of Latest File In Archive, Else 0
fComment As Long ' 1 If Putting Comment In Zip File, Else 0
fOffsets As Long ' 1 If Updating Archive Offsets For SFX Files, Else 0
fPrivilege As Long ' 1 If Not Saving Privileges, Else 0
fEncryption As Long ' Read Only Property!!!
fRecurse As Long ' 1 (-r), 2 (-R) If Recursing Into Sub-Directories, Else 0
fRepair As Long ' 1 = Fix Archive, 2 = Try Harder To Fix, Else 0
flevel As Byte ' Compression Level - 0 = Stored 6 = Default 9 = Max
End Type
'-- This Structure Is Used For The ZIP32.DLL Function Callbacks
Private Type ZIPUSERFUNCTIONS
ZDLLPrnt As Long ' Callback ZIP32.DLL Print Function
ZDLLCOMMENT As Long ' Callback ZIP32.DLL Comment Function
ZDLLPASSWORD As Long ' Callback ZIP32.DLL Password Function
ZDLLSERVICE As Long ' Callback ZIP32.DLL Service Function
End Type
'-- Local Declarations
Private ZOPT As ZPOPT
Private ZUSER As ZIPUSERFUNCTIONS
'-- This Assumes ZIP32.DLL Is In Your \Windows\System Directory!
'-- (alternatively, a copy of ZIP32.DLL needs to be located in the program
'-- directory or in some other directory listed in PATH.)
Private Declare Function ZpInit Lib "zip32.dll" _
(ByRef Zipfun As ZIPUSERFUNCTIONS) As Long '-- Set Zip Callbacks
Private Declare Function ZpSetOptions Lib "zip32.dll" _
(ByRef Opts As ZPOPT) As Long '-- Set Zip Options
Private Declare Function ZpGetOptions Lib "zip32.dll" _
() As ZPOPT '-- Used To Check Encryption Flag Only
Private Declare Function ZpArchive Lib "zip32.dll" _
(ByVal Argc As Long, ByVal funame As String, _
ByRef argv As ZIPnames) As Long '-- Real Zipping Action
'-------------------------------------------------------
'-- Public Variables For Setting The ZPOPT Structure...
'-- (WARNING!!!) You Must Set The Options That You
'-- Want The ZIP32.DLL To Do!
'-- Before Calling VBZip32!
'--
'-- NOTE: See The Above ZPOPT Structure Or The VBZip32
'-- Function, For The Meaning Of These Variables
'-- And How To Use And Set Them!!!
'-- These Parameters Must Be Set Before The Actual Call
'-- To The VBZip32 Function!
'-------------------------------------------------------
Public ZipDate As String
Public RootDir As String
Public TempDir As String
Public Suffix As Integer
Private Encrypt As Integer
Public System As Integer
Public Volume As Integer
Public Extra As Integer
Public NoDirEntries As Integer
Public ExcludeDate As Integer
Public IncludeDate As Integer
Public Verbose As Integer
Public Quiet As Integer
Public CRLF_LF As Integer
Public LF_CRLF As Integer
Public JunkDir As Integer
Public Recurse As Integer
Public Grow As Integer
Public Force As Integer
Public Move As Integer
Public DelEntries As Integer
Public Update As Integer
Public Freshen As Integer
Public JunkSFX As Integer
Public LatestTime As Integer
Public Comment As Integer
Public Offsets As Integer
Public Privilege As Integer
Public Encryption As Integer
Public Repair As Integer
Public Level As Integer
Private mPassword As String 'JMA 061222
Public AskForPassword As Boolean 'JMA 061222
Public Meter As jmaMeter
Public Mute As Boolean 'zda mlčet nebo vypisovat info o průběhu
Private sMsg As String
'-- Public Program Variables
Private Argc As Integer ' Number Of Files To Zip Up
Public ZipFileName As String ' The Zip File Name ie: Myzip.zip
Private ZipFileNames As ZIPnames ' File Names To Zip Up
Public ZipInfo As String ' Holds The Zip File Information
'-- Public Constants
'-- For Zip & UnZip Error Codes!
Public Enum ZipErrorEnum
ZE_OK = 0 ' Success (No Error)
ZE_EOF = 2 ' Unexpected End Of Zip File Error
ZE_FORM = 3 ' Zip File Structure Error
ZE_MEM = 4 ' Out Of Memory Error
ZE_LOGIC = 5 ' Internal Logic Error
ZE_BIG = 6 ' Entry Too Large To Split Error
ZE_NOTE = 7 ' Invalid Comment Format Error
ZE_TEST = 8 ' Zip Test (-T) Failed Or Out Of Memory Error
ZE_ABORT = 9 ' User Interrupted Or Termination Error
ZE_TEMP = 10 ' Error Using A Temp File
ZE_READ = 11 ' Read Or Seek Error
ZE_NONE = 12 ' Nothing To Do Error
ZE_NAME = 13 ' Missing Or Empty Zip File Error
ZE_WRITE = 14 ' Error Writing To A File
ZE_CREAT = 15 ' Could't Open To Write Error
ZE_PARMS = 16 ' Bad Command Line Argument Error
ZE_OPEN = 18 ' Could Not Open A Specified File To Read Error
End Enum
'-- These Functions Are For The ZIP32.DLL
'--
'-- Puts A Function Pointer In A Structure
'-- For Use With Callbacks...
Public Function FnPtr(ByVal lp As Long) As Long
FnPtr = lp
End Function
'-- Main ZIP32.DLL Subroutine.
'-- This Is Where It All Happens!!!
'--
'-- (WARNING!) Do Not Change This Function!!!
'--
Public Function MakeArchive() As Long 'upr. JMA 061222
Dim retcode As Long
Set jmaZipIZBaseFunc.oZipIZ = Me ' JMA 061222
On Error Resume Next '-- Nothing Will Go Wrong :-)
retcode = 0
'-- Set Address Of ZIP32.DLL Callback Functions
'-- (WARNING!) Do Not Change!!!
ZUSER.ZDLLPrnt = FnPtr(AddressOf ZDLLPrnt)
ZUSER.ZDLLPASSWORD = FnPtr(AddressOf ZDLLPass)
ZUSER.ZDLLCOMMENT = FnPtr(AddressOf ZDLLComm)
ZUSER.ZDLLSERVICE = FnPtr(AddressOf ZDLLServ)
'-- Set ZIP32.DLL Callbacks
retcode = ZpInit(ZUSER)
If retcode = 0 Then
MsgBox "Zip32.dll did not initialize. Is it in the current directory " & _
"or on the command path?", vbOKOnly, "VB Zip"
Exit Function
End If
'-- Setup ZIP32 Options
'-- (WARNING!) Do Not Change!
ZOPT.Date = ZipDate ' "12/31/79"? US Date?
ZOPT.szRootDir = RootDir ' Root Directory Pathname
ZOPT.szTempDir = TempDir ' Temp Directory Pathname
ZOPT.fSuffix = Suffix ' Include Suffixes (Not Yet Implemented)
ZOPT.fEncrypt = Encrypt ' 1 If Encryption Wanted
ZOPT.fSystem = System ' 1 To Include System/Hidden Files
ZOPT.fVolume = Volume ' 1 If Storing Volume Label
ZOPT.fExtra = Extra ' 1 If Including Extra Attributes
ZOPT.fNoDirEntries = NoDirEntries ' 1 If Ignoring Directory Entries
ZOPT.fExcludeDate = ExcludeDate ' 1 If Excluding Files Earlier Than A Specified Date
ZOPT.fIncludeDate = IncludeDate ' 1 If Including Files Earlier Than A Specified Date
ZOPT.fVerbose = Verbose ' 1 If Full Messages Wanted
ZOPT.fQuiet = Quiet ' 1 If Minimum Messages Wanted
ZOPT.fCRLF_LF = CRLF_LF ' 1 If Translate CR/LF To LF
ZOPT.fLF_CRLF = LF_CRLF ' 1 If Translate LF To CR/LF
ZOPT.fJunkDir = JunkDir ' 1 If Junking Directory Names
ZOPT.fGrow = Grow ' 1 If Allow Appending To Zip File
ZOPT.fForce = Force ' 1 If Making Entries Using DOS Names
ZOPT.fMove = Move ' 1 If Deleting Files Added Or Updated
ZOPT.fDeleteEntries = DelEntries ' 1 If Files Passed Have To Be Deleted
ZOPT.fUpdate = Update ' 1 If Updating Zip File-Overwrite Only If Newer
ZOPT.fFreshen = Freshen ' 1 If Freshening Zip File-Overwrite Only
ZOPT.fJunkSFX = JunkSFX ' 1 If Junking SFX Prefix
ZOPT.fLatestTime = LatestTime ' 1 If Setting Zip File Time To Time Of Latest File In Archive
ZOPT.fComment = Comment ' 1 If Putting Comment In Zip File
ZOPT.fOffsets = Offsets ' 1 If Updating Archive Offsets For SFX Files
ZOPT.fPrivilege = Privilege ' 1 If Not Saving Privelages
ZOPT.fEncryption = Encryption ' Read Only Property!
ZOPT.fRecurse = Recurse ' 1 or 2 If Recursing Into Subdirectories
ZOPT.fRepair = Repair ' 1 = Fix Archive, 2 = Try Harder To Fix
ZOPT.flevel = Asc(Level) ' Compression Level - (0 To 9) Should Be 0!!!
'-- Set ZIP32.DLL Options
retcode = ZpSetOptions(ZOPT)
'-- Go Zip It Them Up!
retcode = ZpArchive(Argc, ZipFileName, ZipFileNames)
'-- Return The Function Code
MakeArchive = retcode
Set jmaZipIZBaseFunc.oZipIZ = Nothing ' JMA 061222
End Function
Private Sub Class_Initialize()
'JMA 061222
On Error GoTo EH
hLibrary = LoadLibrary(Bs(CurrentProject.Path) & "zip32.dll")
If hLibrary = 0 Then
MsgBox "Failed to load zip32.dll", vbCritical, "DLL Load Error"
Exit Sub
End If
EX:
Exit Sub
EH:
' mError = FIMAGE_ERROR_RUNTIME
mErS = Err.Number & ": " & Err.Description
Resume EX
End Sub
Private Sub Class_Terminate()
'JMA 061222
On Error GoTo EH
FreeLibrary hLibrary
EX:
Exit Sub
EH:
MsgBox Err.Description
Resume EX
End Sub
Public Sub AddFileSpec(ByVal FileSpec As String)
'JMA 061222
Dim i As Byte
For i = LBound(ZipFileNames.Files) To UBound(ZipFileNames.Files)
If Len(ZipFileNames.Files(i)) = 0 Then
ZipFileNames.Files(i) = FileSpec
Exit For
End If
Next
Argc = Argc + 1
End Sub
Friend Property Let Password(ByVal NewValue As String)
Encrypt = Abs(Len(NewValue) > 0)
mPassword = NewValue
End Property
Friend Property Get Password() As String
Password = mPassword
End Property
Public Function Message(ByVal s As String)
On Error Resume Next
If Meter Is Nothing Then
Debug.Print s;
Else
sMsg = sMsg & s
Select Case Asc(Right(sMsg, 1))
Case 10, 13, 116
Meter.Message Left(sMsg, Len(sMsg) - 1)
sMsg = vbNullString
End Select
End If
End Function
Modul s globálními definicemi (soubor ):
Attribute VB_Name = "jmaZipIZBaseFunc"
'JMA třída jmaZipIZ pro práci se ZIPy s knihovnou zip32.dll (Info-ZIP)
'toto je pouze pomocný modul jmaZipIZBaseFunc pro správnou funkci třídy jmaZipIZ
'070128 .Message
'061222 předěláno z příkladu (zip232dN.zip\vb\VBZipBas.bas) na třídu a modul
Option Explicit
Public oZipIZ As jmaZipIZ 'pouze pro použití z jmaZipIZ
'-- Call Back "String"
Public Type ZipCBChar
ch(4096) As Byte
End Type
'-- Callback For ZIP32.DLL - DLL Print Function
Public Function ZDLLPrnt(ByRef fname As ZipCBChar, ByVal X As Long) As Long
Dim s0 As String
Dim xx As Long
'-- Always Put This In Callback Routines!
On Error Resume Next
s0 = ""
'-- Get Zip32.DLL Message For processing
For xx = 0 To X
If fname.ch(xx) = 0 Then
Exit For
Else
s0 = s0 + Chr(fname.ch(xx))
End If
Next
'----------------------------------------------
'-- This Is Where The DLL Passes Back Messages
'-- To You! You Can Change The Message Printing
'-- Below Here!
'----------------------------------------------
'-- Display Zip File Information
'-- zZipInfo = zZipInfo & s0
If oZipIZ.Mute Then
Else
oZipIZ.Message s0
End If
DoEvents
ZDLLPrnt = 0
End Function
'-- Callback For ZIP32.DLL - DLL Service Function
Public Function ZDLLServ(ByRef mname As ZipCBChar, ByVal X As Long) As Long
' x is the size of the file
Dim s0 As String
Dim xx As Long
'-- Always Put This In Callback Routines!
On Error Resume Next
s0 = ""
'-- Get Zip32.DLL Message For processing
For xx = 0 To 4096
If mname.ch(xx) = 0 Then
Exit For
Else
s0 = s0 + Chr(mname.ch(xx))
End If
Next
' Form1.Print "-- " & s0 & " - " & x & " bytes"
' This is called for each zip entry.
' mname is usually the null terminated file name and x the file size.
' s0 has trimmed file name as VB string.
' At this point, s0 contains the message passed from the DLL
' It is up to the developer to code something useful here :)
ZDLLServ = 0 ' Setting this to 1 will abort the zip!
End Function
'-- Callback For ZIP32.DLL - DLL Password Function
Public Function ZDLLPass(ByRef p As ZipCBChar, _
ByVal n As Long, ByRef m As ZipCBChar, _
ByRef Name As ZipCBChar) As Integer
Dim prompt As String
Dim xx As Integer
Dim szpassword As String
'-- Always Put This In Callback Routines!
On Error Resume Next
ZDLLPass = 1
'-- If There Is A Password Have The User Enter It!
'-- This Can Be Changed
If oZipIZ.Mute Then
ElseIf oZipIZ.AskForPassword Then 'JMA 061222
szpassword = InputBox("Please Enter The Password!")
Else
szpassword = oZipIZ.Password 'JMA 061222
End If
'-- The User Did Not Enter A Password So Exit The Function
If szpassword = "" Then Exit Function
'-- User Entered A Password So Proccess It
For xx = 0 To 255
If m.ch(xx) = 0 Then
Exit For
Else
prompt = prompt & Chr(m.ch(xx))
End If
Next
For xx = 0 To n - 1
p.ch(xx) = 0
Next
For xx = 0 To Len(szpassword) - 1
p.ch(xx) = Asc(Mid(szpassword, xx + 1, 1))
Next
p.ch(xx) = Chr(0) ' Put Null Terminator For C
ZDLLPass = 0
End Function
'-- Callback For ZIP32.DLL - DLL Comment Function
Public Function ZDLLComm(ByRef s1 As ZipCBChar) As Integer
Dim xx%, szcomment$
'-- Always Put This In Callback Routines!
On Error Resume Next
ZDLLComm = 1
szcomment = InputBox("Enter the comment")
If szcomment = "" Then Exit Function
For xx = 0 To Len(szcomment) - 1
s1.ch(xx) = Asc(Mid$(szcomment, xx + 1, 1))
Next xx
s1.ch(xx) = Chr(0) ' Put null terminator for C
End Function
Abych poznal, že jsou to moje knihovny a konstanty, a ne
mikrosoftí, a aby se mi dobře hledaly, píšu si důsledně
před názvy své "jma*", tak se nedivte.
Použijete-li kus mého kódu, je slušnost mezi programátory uvést v kódu zdroj: www.tuzka.cz/jma
|