-- CREATE ZIP -- -- ---------------------------------------------------------------------- -- © June 2001, James Newton -- -- REQUIRES: -- ZipXtra -- FileXtra4 ---------------------------------------------------------------------- on createZip(zipFileName, itemToZip, itemIsFolder, allFiles) --------- -- PARAMETERS: -- All parameters may be VOID. If this is the case, the user will -- be asked to identify a single file to zip, and the file path to -- the zip file which is to be created. To zip the entire contents -- of a folder, either: -- + itemToZip should be the absolute path to a folder, or -- + itemIsFolder should be TRUE -- -- Absolute path where the zip file should be created -- Absolute path to a file or folder whose contents -- are to be zipped -- TRUE if itemToZip is VOID or EMPTY and the user is -- to be offered a choice of folders to zip -- If a folder is selected for zipping and -- is TRUE, then all sub-folders will be included in -- the zip. -- -- Alternative syntax: -- may also be a list of relative paths to files -- within the folder identified by itemToZip (which -- must therefore be a valid folder reference). If -- this is the case, only the files defined by -- itemIsFolder will be included. Relative paths to -- files in sub-folders are supported. -- (itemIsFolder => fileTree) -- -- ACTION: -- Executes the command... -- -- zipInstance.jarFiles(zipFolder, temp, fileTree) -- -- ... where is a list of relative paths to files to zip, -- starting in , and is the name of the zip file -- that is to be created. -- -- ZipXtra creates the file inside . However, -- , the full path to the requested emplacement of the -- zip file, may be different. If this is the case, the newly -- created temporary file will be copied to its final emplacement, -- and the original temporary copy will be deleted. -- -- NOTE: Existing versions of the ZipXtra will create a ZIP file -- using the command zipFiles(). However, no compression is -- applied so the resulting file is about 150 bytes LARGER than the -- original. -- -- LIMITATIONS: On Macintosh, items in subfolders will be saved as -- "subfolder/item". If the file is unzipped on a Macintosh, the -- subfolder will not be recreated, and the file will retain the -- slash-delimited name. On Mac OS X, longer names will be -- truncated. The use of CreateZip() is thus not recommended for -- folder hierarchies on Macintosh. -- -- REQUIRES: -- ZipXtra, FileXtra4 and the MUI dialog xtra. -------------------------------------------------------------------- -- Check that all necessary xtras are available if anyXtrasAreMissing() then exit end if -- Create an instance of FileXtra4 to check that ZipXtra won't crash -- Director, and to check that the operation has succeeded. fx4 = xtra("FileXtra4").new() if not stringP(itemToZip) then -- Ask user for the file or folder to zip chooseFolder = ((itemIsFolder = TRUE ) or listP(itemIsFolder)) itemToZip = GetItemToZip(chooseFolder, fx4) if itemToZip = "" then -- User canceled dialog exit end if end if -- Check that itemToZip is valid if fx4.fx_FolderExists(itemToZip) then zipFolder = itemToZip -- Ensure that the folder name ends correctly pathDelimiter = the last char of the applicationPath if the last char of zipFolder <> pathDelimiter then put pathDelimiter after zipFolder end if if listP(itemIsFolder) then fileTree = itemIsFolder else fileTree = GetFileTree(zipFolder, (allFiles = TRUE), fx4) end if itemIsFolder = 1 else if fx4.fx_FileExists(itemToZip) then if listP(itemIsFolder) then -- itemToZip must be a valid folder if itemIsFolder is a list exit else zipFolder = GetParentFolder(itemToZip) fileTree = [GetShortName(itemToZip, TRUE)] -- name of file itemIsFolder = 0 end if else -- itemToZip is neither a file nor a folder exit end if -- Get the name of the zip file which will be created if not stringP(zipFileName) then if itemIsFolder then zipFileName = zipFolder else zipFileName = fileTree[1] end if zipFileName = GetFileName(zipFolder, zipFileName, fx4) if zipFileName = "" then -- User canceled the dialog exit end if end if -- Check if the file is to be saved elsewhere than in zipFolder saveElsewhere = GetParentFolder(zipFileName) <> zipFolder if saveElsewhere then -- Create a temporary file which will be moved to chosen location repeat while TRUE temp = "temp"&random(9999)&".zip" if not fx4.fx_FileExists(zipFolder&temp) then exit repeat end if end repeat else -- Use final file name immediately temp = GetShortName(zipFileName, TRUE) -- short file name end if -- Ensure that there are no read-only files, since this can cause -- a crash. readOnlyFiles = GetReadOnlyFiles(fileTree, zipFolder, fx4) -- => R/W fullTemp = zipFolder&temp if fx4.fx_FileExists(fullTemp) then -- An existing file with the same name must not be read-only readOnly = not fx4.fx_FileGetWriteState(fullTemp) if readOnly then fx4.fx_FileSetWriteState(fullTemp, 1) end if else readOnly = FALSE end if -- Create the zip file zipInstance = xtra("ZipXtra").new() --zipInstance.zipFiles(zipFolder, temp, fileTree) zipInstance.jarFiles(zipFolder, temp, fileTree) zipInstance = VOID if fx4.fx_FileExists(fullTemp) then if saveElsewhere then fx4.fx_FileCopy(fullTemp, zipFileName) errorCode = fx4.fx_ErrorNumber() repeat while errorCode message = GetLocalizedString(#tempFile) errorString = fx4.fx_ErrorString() childStrings = ["^0": zipFileName, "^1": fullTemp] message = SubstituteStrings(message, childStrings) alert message -- zipFileName was invalid: ask for a better one zipFileName = GetFileName(zipFolder, temp, fx4) if zipFileName = "" then -- Return path to temporary zip file return temp else -- Try again with the new file name fx4.fx_FileCopy(fullTemp, zipFileName) errorCode = fx4.fx_ErrorNumber() if errorCode = -7 then -- File not found: a new file has been created errorCode = 0 end if end if end repeat fx4.fx_FileDelete(fullTemp) end if else -- fullTemp was an invalid filename debug() end if setToReadOnly(readOnlyFiles, fx4) return zipFileName end createZip on anyXtrasAreMissing() ---------------------------------------------- -- Called by createZip() -- -- RETURNS: TRUE if ZipXtra, Mui Xtra or FileXtra4 are missing. -- FALSE if all the above xtras are present -- -- ACTION: Shows an alert if any of the required xtras are missing. -------------------------------------------------------------------- requiredXtras = [#zipXtra, #mui, #FileXtra4] xtrasAreMissing = XtrasMissing(requiredXtras) if xtrasAreMissing then message = GetLocalizedString(#xtrasMissing) put RETURN&RETURN&requiredXtras after message alert message end if return xtrasAreMissing end anyXtrasAreMissing on GetItemToZip(itemIsFolder, fx4) ------------------------------------ -- Called by createZip() -- -- PARAMETERS: -- is TRUE if a folder is to be zipped -- is an instance of FileXtra4 -- -- RETURNS: the absolute path to a file or folder chosen by the user -------------------------------------------------------------------- if itemIsFolder then -- Ask the user which folder to zip prompt = "Create zip file of the contents of which folder?" return fx4.fx_FolderSelectDialog(prompt) else -- Ask which file to zip header = "Choose file" prompt = "Zip which file?" if the platform starts "Win" then return fx4.fx_FileOpenDialog("C:\", "", header, prompt, 1) else return fx4.fx_FileOpenDialog("ABC", "") end if end if end getRoot on GetFileName(folderToZip, temp, fx4) -------------------------------- -- Called by createZip() -- -- PARAMETERS: -- is a path to a folder -- is a suggested path to a folder or a file -- is an instance of FileXtra4 -- -- RETURNS: an absolute path to a file whose name ends with ".zip" -------------------------------------------------------------------- temp = GetShortName(temp) suffix = GetExtension(temp) case suffix of "zip": -- continue "": put ".zip" after temp otherwise charCount = temp.char.count delete temp.char[charCount - suffix.char.count..charCount] put ".zip" after temp end case prompt = "Save zip file as..." repeat while TRUE if the platform starts "Win" then temp = fx4.fx_FileSaveAsDialog(folderToZip, temp, prompt, 0) else temp = fx4.fx_FileSaveAsDialog(folderToZip, temp, prompt) end if if temp = "" then -- Dialog was canceled return temp else -- Ensure that file name ends with the "*.zip" extension suffix = GetExtension(temp) case suffix of "zip": -- continue "": put ".zip" after temp otherwise charCount = temp.char.count delete temp.char[charCount - suffix.char.count..charCount] put ".zip" after temp end case end if if fx4.fx_FileExists(temp) then -- Warn that a file with the same name already exists if MuiReplaceFile(temp) then -- The user chose to overwrite an existing file return temp else temp = GetShortName(temp, TRUE) -- short name of a file end if else -- zipFileName is unique return temp end if end repeat end GetFileName on GetParentFolder(element) ------------------------------------------ -- Called by createZip() -- -- PARAMETERS: -- is an absolute path to a file or folder -- -- RETURNS: an absolute path to the parent folder (the current file -- or folder name is stripped) -------------------------------------------------------------------- saveDelimiter = the itemDelimiter pathDelimiter = the last char of the applicationPath the itemDelimiter = pathDelimiter delete the last item of element the itemDelimiter = saveDelimiter return element&pathDelimiter end GetParentFolder on GetShortName(element, elementIsFile) ------------------------------ -- Called by createZip(), GetFileName() -- -- PARAMETERS: -- is an absolute path to a file or folder -- is TRUE if is a file -- -- RETURNS: the name of the element, stripped of the rest of its -- filePath -------------------------------------------------------------------- saveDelimiter = the itemDelimiter the itemDelimiter = the last char of the applicationPath if elementIsFile then element = element.item[element.item.count] else element = element.item[element.item.count - 1] end if the itemDelimiter = saveDelimiter return element end GetShortName on GetExtension(nameOfFile) ------------------------------------------ -- Called by GetFileName() -- -- PARAMETERS: -- is the short name of a file -- -- RETURNS: the file extension (characters after any "." character) -------------------------------------------------------------------- saveDelimiter = the itemDelimiter the itemDelimiter = "." itemCount = nameOfFile.item.count if itemCount > 1 then extension = nameOfFile.item[itemCount] else extension = "" end if the itemDelimiter = saveDelimiter return extension end GetExtension on getReadOnlyFiles(fileList, folderPath, fx4) ----------------------- -- Called by createZip() -- -- PARAMETERS: -- is a list of files to include in the zip file -- is the absolute path to the folder which contains -- the files -- is an instance of FileXtra4 -- -- RETURNS: a list of the files defined by and -- which are read-only. Such files must be converted -- to read/write before being zipped by zipXtra, otherwise -- Director will crash. -------------------------------------------------------------------- readOnlyFileList = [] i = fileList.count repeat while i fullName = folderPath&fileList[i] if not fx4.fx_FileGetWriteState(fullName) then fx4.fx_FileSetWriteState(fullName, TRUE) readOnlyFileList.append(fullName) end if i = i - 1 end repeat return readOnlyFileList end getReadOnlyFiles on setToReadOnly(fileList, fx4)--------------------------------------- -- Called by createZip() -- -- PARAMETERS: -- is a list of files to set to read-only -- is an instance of FileXtra4 -- -- ACTION: sets the Write state of the listed files to FALSE -------------------------------------------------------------------- i = fileList.count repeat while i fx4.fx_FileSetWriteState(fileList[i], FALSE) i = i - 1 end repeat end setToReadOnly on GetFileTree(zipFolder, allFiles, fx4, relativeFolder, fullList) --- -- Called by createZip() -- -- PARAMETERS: -- is the absolute path to the folder which contains -- thefiles to be zipped -- is TRUE if all subfolders must also be zipped -- is an instance of FileXtra4 -- -- The following parameter are only used internally, when this -- handler is called recursively to create a list of all files in -- the current folder and its subfolders. -- is the relative path from the source folder -- is a list of relative paths -- -- RETURNS: a list of all the folders in , or an empty -- list to indicate that all folders and subfolders are to be zipped -------------------------------------------------------------------- if allFiles then if ilk(fullList) <> #list then fullList = [] relativeFolder = "" end if pathDelimiter = the last char of the applicationPath folderList = fx4.fx_folderToList(zipFolder) i = folderList.count repeat while i fileOrFolder = folderList[i] if the last char of fileOrFolder = pathDelimiter then -- This is a subFolder: add its contents to fullList subFolder = zipFolder&fileOrFolder relative = relativeFolder&fileOrFolder GetFileTree(subFolder, allFiles, fx4, relative, fullList) else fullList.append(relativeFolder&fileOrFolder) end if i = i - 1 end repeat return fullList else return fx4.fx_folderToList(zipFolder) end if end GetFileTree on MuiReplaceFile(theFile) ------------------------------------------ -- Asks the user if s/he wants to replace an existing file with a -- given name. -------------------------------------------------------------------- muiInstance = xtra("Mui").new() if ilk(muiInstance) = #instance then -- Customize the message message = GetLocalizedString(#replaceFile) childStringList = ["^0": QUOTE&theFile"E] message = SubstituteStrings(message, childStringList) alertList = [\ #title: GetLocalizedString(#replace_title), \ #icon: #caution, \ #message: message, \ #buttons: #OKCancel, \ #default: 2, \ #movable: FALSE \ ] return muiInstance.alert(alertList) = 1 end if -- If we get here, the Mui Xtra could not be instanciated: don't let -- the user overwrite an existing file unwittingly. beep return FALSE end MuiReplaceFile -- LOCALIZATION -- -- In order to provide localized versions of each behavior, set the -- global gUserLanguage to one of the values used in the handler -- below. Default strings will be returned in English if -- * gUserLanguage is -- which it will be be default -- * gUserLanguage is not a string -- * gUserLanguage is different from the strings used in the handler -- below. If you wish to add a new language, simply copy and paste -- one of the case sections and provide your own localized strings. on GetLocalizedString(stringSymbol) userLanguage = (the globals)[#gUserLanguage] if voidP(userLanguage) then -- Try to use the system language (D8+) ... environmentData = the environment userLanguage = environmentData[#osLanguage] if voidP(userLanguage) then -- ... or failing that, the language the movie was developed in userLanguage = environmentData[#uiLanguage] end if end if case userLanguage of "French": case stringSymbol of #xtrasMissing: return \ "Impossible de crŽer un fichier Zip puisque les xtras suivants ne sont pas disponibles :" #replaceFile: return \ "Un fichier nommŽ ^0 existe dŽjˆ."&RETURN&"Voulez-vous le remplacer ?" #replace_title: return "Remplacer Fichier ?" #tempFile: return \ "Le chemin d'acces "&RETURN&RETURN"E&"^0""E&RETURN&RETURN&\ "est invalide. Un fichier temporaire ˆ ŽtŽ crŽŽ ˆ :"&RETURN&RETURN&\ QUOTE&"^1""E&RETURN&RETURN&\ "Veuillez choisir une nouvelle destination pour votre fichier Zip." otherwise -- stringSymbol unknown return string(stringSymbol) end case otherwise case stringSymbol of #xtrasMissing: return \ "Creation of a Zip file failed because the following xtras are missing:" #replaceFile: return \ "A file named ^0 already exists."&RETURN&\ "Do you wish to replace this existing file?" #replace_title: return "Replace existing file?" #tempFile: return \ "The file path "&RETURN&RETURN"E&"^0""E&RETURN&RETURN&\ "is not invalid. A temporary file has been created at:"&\ RETURN&RETURN"E&"^1""E&RETURN&RETURN&\ "Please choose a new final destination for your Zip file." otherwise -- stringSymbol unknown return string(stringSymbol) end case end case end GetLocalizedString on SubstituteStrings(parentString, childStringList) -------------- -- * Modifies parentString so that the strings which appear as -- properties in childStringList are replaced by the values -- associated with those properties. -- -- has the format ["^0": "replacement string"] -------------------------------------------------------------------- i = childStringList.count() repeat while i tempString = "" dummyString = childStringList.getPropAt(i) replacement = childStringList[i] lengthAdjust = dummyString.char.count - 1 repeat while TRUE position = offset(dummyString, parentString) if not position then parentString = tempString&parentString exit repeat else if position <> 1 then tempString = tempString&parentString.char[1..position - 1] end if tempString = tempString&replacement delete parentString.char[1..position + lengthAdjust] end if end repeat i = i - 1 end repeat return parentString end SubstituteStrings -- UTILITIES -- on XtrasMissing(xtrasList) ------------------------------------------- -- * Returns TRUE if any of the xtras in xtraList are missing. Once -- the handler has finished executing, xtrasList contains only the -- names of the missing xtras. -------------------------------------------------------------------- i = the number of xtras repeat while i xtrasList.deleteOne(xtra(i).name) i = i - 1 end repeat return xtrasList.count() end XtrasMissing