Tutorials & ManualsHow-tosLessons MiscellaneousZipping/Unzipping a Folder Using RevZip

Zipping/Unzipping a Folder Using RevZip

The revZIP external is great if you need a cross-platform means of creating ZIP archives. Unfortunately there is no built in handler that will ZIP an entire folder for you. This lesson contains handlers that will ZIP up a folder and decompress a ZIP archive to a folder. All of the supporting handlers are included as well.

You are responsible for calling revZipOpenArchive() before and revZipCloseArchive() after calling any of the handlers in this atricle.

Code For Zipping/Unzipping Folders

-- Leave pFolderPath empty. It is used recursively
command zipAddFolderToArchive pZipArchivePath, pRootFolderPath, pIncludeRootFolderInArchiveItemNames, pFilesToExclude, pExtensionsThatArentCompressed, pFolderPath
   local theArchiveItemName,theCharNo,theError,theFile
   local theFiles,theFolder,theFolders

   put pIncludeRootFolderInArchiveItemNames is true into pIncludeRootFolderInArchiveItemNames
   set the wholematches to true

   replace comma with cr in pExtensionsThatArentCompressed

   if pFolderPath is empty then
      put pRootFolderPath into pFolderPath	
      if pIncludeRootFolderInArchiveItemNames then
         # we want folder to be reflected in archive item name
         set the itemdel to slash
         delete item -1 of pRootFolderPath
      end if
   end if

   put fileFilesInFolder(pFolderPath, true, true) into theFiles
   filter theFiles without "*/.DS_Store"
   set the itemdel to "."

   repeat for each line theFile in theFiles
      if theFile is among the lines of pFilesToExclude then next repeat

      put theFile into theArchiveItemName
      put offset(pRootFolderPath, theArchiveItemName) into theCharNo
      if theCharNo is 0 then return "file is not in expected folder"

      # strip root folder up to slash (zip item names shouldn't start with a slash)
      delete char 1 to (the number of chars of pRootFolderPath + 1) of theArchiveItemName

      if item -1 of theArchiveItemName is among the lines of pExtensionsThatArentCompressed then
         revZipAddUncompressedItemWithFile pZipArchivePath, theArchiveItemName, theFile
      else
         revZipAddItemWithFile pZipArchivePath, theArchiveItemName, theFile
      end if
      if the result is not empty then
         put the result into theError
         exit REPEAT
      end if
   end REPEAT

   if theError is empty then
      put fileFoldersInFolder(pFolderPath, true, true) into theFolders
      repeat for each line theFolder in theFolders
         zipAddFolderToArchive pZipArchivePath, pRootFolderPath, pIncludeRootFolderInArchiveItemNames, pFilesToExclude, pExtensionsThatArentCompressed, theFolder
         put the result into theError
         if theError is not empty then
            exit REPEAT
         end if
      end REPEAT
   end if

   return theError
end zipAddFolderToArchive

command zipDecompressArchiveToFolder pZipArchivePath, pOutputFolder, pItemsToExclude, pCallbackHandler
   local theError

   if the last char of pOutputFolder is slash then delete the last char of pOutputFolder

   set the wholematches to true
   put the filetype into theFileType
   set the filetype to empty

   put revZipEnumerateItems(pZipArchivePath) into theArchiveItems
   put the number of lines of theArchiveItems into theItemCount

   repeat for each line theArchiveItem in theArchiveItems
      add 1 to i

      if theArchiveItem is among the lines of pItemsToExclude then next repeat

      put pOutputFolder & slash & theArchiveItem into theFilePath
      put fileExtractDirectory(theFilePath) into theDirectoryPath

      if theDirectoryPath is not pOutputFolder then
         fileCreateAllFoldersInPath theDirectoryPath, pOutputFolder
         put the result into theError
      end if

      if theError is empty then
         if pCallbackHandler is not empty then
            put _CardOf(the long id of the target) into theCard
            put "send" && quote & pCallbackHandler && "i, theItemCount" & quote && "to theCard" into theDo
            do theDo
         end if

         revZipExtractItemToFile pZipArchivePath, theArchiveItem, theFilePath
         put the result into theError
      end if

      if theError is not empty then exit REPEAT
   end REPEAT

   set the filetype to theFileType

   return theError
end zipDecompressArchiveToFolder

function fileFilesInFolder pFolder, pFullPath, pIncludeHidden
   local theDefault,theFile,theFiles,theFullFiles

   if there is not a folder pFolder then return empty
   if last char of pFolder is not slash then put slash after pFolder

   put the defaultfolder into theDefault
   set the defaultfolder to pFolder
   put the files into theFiles
   set the defaultfolder to theDefault
   if pIncludeHidden is not true then
      filter theFiles without ".*"
   else
      filter theFiles without "..*"
   end if

   if pFullPath then
      repeat for each line theFile in theFiles
         put pFolder & theFile & cr after theFullFiles
      end REPEAT
      delete last char of theFullFiles
      return theFullFiles
   else
      return theFiles
   end if
end fileFilesInFolder

function fileFoldersInFolder pFolder, pFullPath, pIncludeHidden
   local theDefault,theFolders,theFullFolders,theFolder

   if there is not a folder pFolder then return empty
   if last char of pFolder is not slash then put slash after pFolder

   put the defaultfolder into theDefault
   set the defaultfolder to pFolder
   put the folders into theFolders
   set the defaultfolder to theDefault
   if pIncludeHidden is not true then
      filter theFolders without ".*"
   else
      filter theFolders without "..*"
   end if

   if pFullPath then
      repeat for each line theFolder in theFolders
         put pFolder & theFolder &cr after theFullFolders
      end REPEAT
      delete last char of theFullFolders
      return theFullFolders
   else
      return theFolders
   end if
end fileFoldersInFolder

function fileExtractDirectory pFilePath
   set the itemdelimiter to slash
   return item 1 to -2 of pFilePath
end fileExtractDirectory

## Used for removing multiple slashes from folder paths
private command _stripDoubleSlash @pVariable
   local theCharNo,thePrefix

   ## Don't wipe out UNC prefixes
   if char 1 to 2 of pVariable is "//" then
      put "//" into thePrefix
      delete char 1 to 2 of pVariable
   end if

   repeat forever
      put offset("//", pVariable) into theCharNo
      if theCharNo > 0 then
         replace "//" with slash in pVariable
      else
         exit repeat
      end if
   end repeat

   if thePrefix is not empty then
      put thePrefix before pVariable
   end if

   return empty
end _stripDoubleSlash

private function _escapeForShell pStr
   local theChar,theSpecialChars

   if the platform contains "MacOS" then
      put "\" & space & quote & "'`<>!;()[]?#$^&*=|" into theSpecialChars
      repeat for each char theChar in theSpecialChars
         replace theChar with ("\" & theChar) in pStr
      end REPEAT
   else
      replace "/" with "\" in pStr
      put quote & pStr & quote into pStr
   end if

   return pStr
end _escapeForShell

private function _executableBitFromType pType
   switch pType
      case "execute"
         put 7 into pType
         break
      case "write"
         put 6 into pType
         break
      case "read"
         put 5 into pType
         break
      default
         put 0 into pType
   end switch
   return pType
end _executableBitFromType

private function _CardOf pControl
   local theCharNo

   put the long id of pControl into pControl ## force the long id

   if word 1 of pControl is "stack" then
      put empty into pControl -- has no card
   else
      if word 1 of pControl is not "card" then
         put offset(" of card ", pControl) into theCharNo
         if theCharNo > 0 then
            delete char 1 to (theCharNo + 3) of pControl
         else
            put empty into pControl
         end if
      end if
   end if
   return pControl
end _CardOf

command fileCreateAllFoldersInPath pPath, pRootPath, pPerms
   local theCheck,theError,thePathSegment

   ## Watch for double slashes /Folder/To//Something//
   ## You will not enter a never ending loop if you aren't careful.
   _stripDoubleSlash pPath
   _stripDoubleSlash pRootPath

   ## Get rid of trailing slashes
   ## We can safely ignore UNC paths starting with "//"
   ## Neither pPath or pRootPath with values of just "//" would be valid
   repeat until the last char of pPath is not slash
      delete the last char of pPath        
   end repeat
   if the number of chars of pRootPath > 1 and the last char of pRootPath is slash then
      repeat forever
         delete the last char of pRootPath
         if the last char of pRootPath is not slash or the number of chars of pRootPath is 1 then
            exit repeat
         end if
      end repeat
   end if

   ## Permissions
   if pPerms is not "shared" then put "user" into pPerms
   set the itemdelimiter to slash

   if pPath is empty or the number of items of pPath is 1 then
      put "cannot create folder (invalid path)" into theError
   end if

   ## VALIDATE pRootPath
   if theError is empty then
      if pRootPath is empty then
         put item 1 to 2 of pPath into pRootPath ## "/NODE"
      end if

      if theError is empty then
         if last char of pRootPath is not slash then put slash after pRootPath ## Makes it easier to deal with "/" path

         if there is not a folder pRootPath then
            put "root path does not exist" into theError
         end if
      end if
   end if

   ## VALIDATE ANCESTORY OF PATH
   if theError is empty then
      put char 1 to -2 of pRootPath into theCheck ## -2 gets rid of trailing slash
      if char 1 to (number of chars of theCheck) of pPath is not theCheck then 
         put "path is not a child of root path" into theError
      end if
   end if

   ## CREATE FOLDERS
   if theError is empty then
      if number of items of pPath > number of items of pRootPath then
         put pRootPath & item (number of items of pRootPath + 1) of pPath into thePathSegment

         if there is not a folder thePathSegment then
            create folder thePathSegment
            if the result is not empty then
               put "error creating folder (" & the result & ")" into theError
            else
               if pPerms is "shared" then
                  fileSetPermissions thePathSegment, "execute", "execute", "execute"
                  if the result is not empty then
                     put format("error setting permissions for folder \"%s\" (%s)", thePathSegment, line 1 of the result) into theError 
                  end if
               end if
            end if
         end if

         if theError is empty then
            fileCreateAllFoldersInPath pPath, thePathSegment, pPerms
            put the result into theError
         end if
      end if
   end if

   return theError
end fileCreateAllFoldersInPath

on fileSetPermissions pFile, pOwner, pGroup, pAll
   local theError,theResult

   put _escapeForShell(pFile) into pFile

   switch the platform
      case "Win32"
         /*
         Displays or changes file attributes.
         ATTRIB [+R | -R] [+A | -A] [+S | -S] [+H | -H] [[drive:][path]filename]  [/S] [/D]

         + Sets an attribute.
         - Clears an attribute.
         R Read-only file attribute.
         A Archive file attribute.
         S system file attribute.
         H Hidden file attribute.
         /S Processes files in all directories in the specified path.
         /D Processes folders as well
         */

         switch pOwner
            case "read"
               put "+r" into pOwner
               break
            case "write"
               put "-r" into pOwner
               break
         end switch
         put shell (format ("attrib %s %s", pOwner, pFile) ) into theResult
         break

      case "MacOS"
         put _executableBitFromType(pOwner) into pOwner
         put _executableBitFromType(pGroup) into pGroup
         put _executableBitFromType(pAll) into pAll             
         put shell(format("chmod %u%u%u %s ", pOwner, pGroup, pAll, pFile)) into theResult
         break
   end SWITCH

   if the result is not empty then
      put line 1 of theResult into theError
   end if

   return theError
end fileSetPermissions

2 Comments

Trevix

Did you forget a "revZipOpenArchive"?
Also: the line:
put format("error setting permissions for folder \"%s\" (%s)", thePathSegment, line 1 of the result) into theError formats with funny colours in the script editor.

Trevor DeVore

@Trevix - Yes I did. I've added a note about calling that before using the handles in this article. As for the script editor coloring, you would need to report that to LiveCode. The syntax is correct.

Add your comment

E-Mail me when someone replies to this comment