github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/Unknwon/cae/tz/tz.go (about) 1 // Copyright 2014 Unknown 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 // not use this file except in compliance with the License. You may obtain 5 // a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations 13 // under the License. 14 15 // Package tz enables you to transparently read or write TAR.GZ compressed archives and the files inside them. 16 package tz 17 18 import ( 19 "archive/tar" 20 "errors" 21 "io" 22 "os" 23 "path" 24 "path/filepath" 25 "strings" 26 27 "github.com/insionng/yougam/libraries/Unknwon/cae" 28 ) 29 30 // A File represents a file or directory entry in archive. 31 type File struct { 32 *tar.Header 33 absPath string 34 } 35 36 // A TzArchive represents a file archive, compressed with Tar and Gzip. 37 type TzArchive struct { 38 *ReadCloser 39 FileName string 40 NumFiles int 41 Flag int 42 Permission os.FileMode 43 44 files []*File 45 isHasChanged bool 46 47 // For supporting flushing to io.Writer. 48 writer io.Writer 49 isHasWriter bool 50 } 51 52 // OpenFile is the generalized open call; most users will use Open 53 // instead. It opens the named tar.gz file with specified flag 54 // (O_RDONLY etc.) if applicable. If successful, 55 // methods on the returned TzArchive can be used for I/O. 56 // If there is an error, it will be of type *PathError. 57 func OpenFile(fileName string, flag int, perm os.FileMode) (*TzArchive, error) { 58 tz := new(TzArchive) 59 err := tz.Open(fileName, flag, perm) 60 return tz, err 61 } 62 63 // Create creates the named tar.gz file, truncating 64 // it if it already exists. If successful, methods on the returned 65 // TzArchive can be used for I/O; the associated file descriptor has mode 66 // O_RDWR. 67 // If there is an error, it will be of type *PathError. 68 func Create(fileName string) (*TzArchive, error) { 69 os.MkdirAll(path.Dir(fileName), os.ModePerm) 70 return OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 71 } 72 73 // Open opens the named tar.gz file for reading. If successful, methods on 74 // the returned TzArchive can be used for reading; the associated file 75 // descriptor has mode O_RDONLY. 76 // If there is an error, it will be of type *PathError. 77 func Open(fileName string) (*TzArchive, error) { 78 return OpenFile(fileName, os.O_RDONLY, 0) 79 } 80 81 // New accepts a variable that implemented interface io.Writer 82 // for write-only purpose operations. 83 func New(w io.Writer) *TzArchive { 84 return &TzArchive{ 85 writer: w, 86 isHasWriter: true, 87 } 88 } 89 90 // List returns a string slice of files' name in TzArchive. 91 // Specify prefixes will be used as filters. 92 func (tz *TzArchive) List(prefixes ...string) []string { 93 isHasPrefix := len(prefixes) > 0 94 names := make([]string, 0, tz.NumFiles) 95 for _, f := range tz.files { 96 if isHasPrefix && !cae.HasPrefix(f.Name, prefixes) { 97 continue 98 } 99 names = append(names, f.Name) 100 } 101 return names 102 } 103 104 // AddEmptyDir adds a raw directory entry to TzArchive, 105 // it returns false if same directory enry already existed. 106 func (tz *TzArchive) AddEmptyDir(dirPath string) bool { 107 if !strings.HasSuffix(dirPath, "/") { 108 dirPath += "/" 109 } 110 111 for _, f := range tz.files { 112 if dirPath == f.Name { 113 return false 114 } 115 } 116 117 dirPath = strings.TrimSuffix(dirPath, "/") 118 if strings.Contains(dirPath, "/") { 119 // Auto add all upper level directories. 120 tz.AddEmptyDir(path.Dir(dirPath)) 121 } 122 tz.files = append(tz.files, &File{ 123 Header: &tar.Header{ 124 Name: dirPath + "/", 125 }, 126 }) 127 tz.updateStat() 128 return true 129 } 130 131 // AddDir adds a directory and subdirectories entries to TzArchive. 132 func (tz *TzArchive) AddDir(dirPath, absPath string) error { 133 dir, err := os.Open(absPath) 134 if err != nil { 135 return err 136 } 137 defer dir.Close() 138 139 tz.AddEmptyDir(dirPath) 140 141 fis, err := dir.Readdir(0) 142 if err != nil { 143 return err 144 } 145 for _, fi := range fis { 146 curPath := strings.Replace(absPath+"/"+fi.Name(), "\\", "/", -1) 147 tmpRecPath := strings.Replace(filepath.Join(dirPath, fi.Name()), "\\", "/", -1) 148 if fi.IsDir() { 149 if err = tz.AddDir(tmpRecPath, curPath); err != nil { 150 return err 151 } 152 } else { 153 if err = tz.AddFile(tmpRecPath, curPath); err != nil { 154 return err 155 } 156 } 157 } 158 return nil 159 } 160 161 // updateStat should be called after every change for rebuilding statistic. 162 func (tz *TzArchive) updateStat() { 163 tz.NumFiles = len(tz.files) 164 tz.isHasChanged = true 165 } 166 167 // AddFile adds a file entry to TzArchive. 168 func (tz *TzArchive) AddFile(fileName, absPath string) error { 169 if cae.IsFilter(absPath) { 170 return nil 171 } 172 173 si, err := os.Lstat(absPath) 174 if err != nil { 175 return err 176 } 177 178 target := "" 179 if si.Mode()&os.ModeSymlink != 0 { 180 target, err = os.Readlink(absPath) 181 if err != nil { 182 return err 183 } 184 } 185 186 file := new(File) 187 file.Header, err = tar.FileInfoHeader(si, target) 188 if err != nil { 189 return err 190 } 191 file.Name = fileName 192 file.absPath = absPath 193 194 tz.AddEmptyDir(path.Dir(fileName)) 195 196 isExist := false 197 for _, f := range tz.files { 198 if fileName == f.Name { 199 f = file 200 isExist = true 201 break 202 } 203 } 204 if !isExist { 205 tz.files = append(tz.files, file) 206 } 207 208 tz.updateStat() 209 return nil 210 } 211 212 // DeleteIndex deletes an entry in the archive by its index. 213 func (tz *TzArchive) DeleteIndex(idx int) error { 214 if idx >= tz.NumFiles { 215 return errors.New("index out of range of number of files") 216 } 217 218 tz.files = append(tz.files[:idx], tz.files[idx+1:]...) 219 return nil 220 } 221 222 // DeleteName deletes an entry in the archive by its name. 223 func (tz *TzArchive) DeleteName(name string) error { 224 for i, f := range tz.files { 225 if f.Name == name { 226 return tz.DeleteIndex(i) 227 } 228 } 229 return errors.New("entry with given name not found") 230 }