OSDN Git Service

libgo: Update to weekly.2012-03-13.
[pf3gnuchains/gcc-fork.git] / libgo / go / archive / zip / struct.go
index a32de5a..55f3dcf 100644 (file)
@@ -11,8 +11,11 @@ This package does not support ZIP64 or disk spanning.
 */
 package zip
 
-import "os"
-import "time"
+import (
+       "errors"
+       "os"
+       "time"
+)
 
 // Compression methods.
 const (
@@ -24,13 +27,18 @@ const (
        fileHeaderSignature      = 0x04034b50
        directoryHeaderSignature = 0x02014b50
        directoryEndSignature    = 0x06054b50
-       fileHeaderLen            = 30 // + filename + extra
-       directoryHeaderLen       = 46 // + filename + extra + comment
-       directoryEndLen          = 22 // + comment
-       dataDescriptorLen        = 12
+       dataDescriptorSignature  = 0x08074b50 // de-facto standard; required by OS X Finder
+       fileHeaderLen            = 30         // + filename + extra
+       directoryHeaderLen       = 46         // + filename + extra + comment
+       directoryEndLen          = 22         // + comment
+       dataDescriptorLen        = 16         // four uint32: descriptor signature, crc32, compressed size, size
 
        // Constants for the first byte in CreatorVersion
-       creatorUnix = 3
+       creatorFAT    = 0
+       creatorUnix   = 3
+       creatorNTFS   = 11
+       creatorVFAT   = 14
+       creatorMacOSX = 19
 )
 
 type FileHeader struct {
@@ -49,6 +57,39 @@ type FileHeader struct {
        Comment          string
 }
 
+// FileInfo returns an os.FileInfo for the FileHeader.
+func (h *FileHeader) FileInfo() os.FileInfo {
+       return headerFileInfo{h}
+}
+
+// headerFileInfo implements os.FileInfo.
+type headerFileInfo struct {
+       fh *FileHeader
+}
+
+func (fi headerFileInfo) Name() string       { return fi.fh.Name }
+func (fi headerFileInfo) Size() int64        { return int64(fi.fh.UncompressedSize) }
+func (fi headerFileInfo) IsDir() bool        { return fi.Mode().IsDir() }
+func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() }
+func (fi headerFileInfo) Mode() os.FileMode  { return fi.fh.Mode() }
+func (fi headerFileInfo) Sys() interface{}   { return fi.fh }
+
+// FileInfoHeader creates a partially-populated FileHeader from an
+// os.FileInfo.
+func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) {
+       size := fi.Size()
+       if size > (1<<32 - 1) {
+               return nil, errors.New("zip: file over 4GB")
+       }
+       fh := &FileHeader{
+               Name:             fi.Name(),
+               UncompressedSize: uint32(size),
+       }
+       fh.SetModTime(fi.ModTime())
+       fh.SetMode(fi.Mode())
+       return fh, nil
+}
+
 type directoryEnd struct {
        diskNbr            uint16 // unused
        dirDiskNbr         uint16 // unused
@@ -60,51 +101,165 @@ type directoryEnd struct {
        comment            string
 }
 
-func recoverError(err *os.Error) {
-       if e := recover(); e != nil {
-               if osErr, ok := e.(os.Error); ok {
-                       *err = osErr
-                       return
-               }
-               panic(e)
-       }
-}
-
 // msDosTimeToTime converts an MS-DOS date and time into a time.Time.
 // The resolution is 2s.
 // See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx
 func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
-       return time.Time{
+       return time.Date(
                // date bits 0-4: day of month; 5-8: month; 9-15: years since 1980
-               Year:  int64(dosDate>>9 + 1980),
-               Month: int(dosDate >> 5 & 0xf),
-               Day:   int(dosDate & 0x1f),
+               int(dosDate>>9+1980),
+               time.Month(dosDate>>5&0xf),
+               int(dosDate&0x1f),
 
                // time bits 0-4: second/2; 5-10: minute; 11-15: hour
-               Hour:   int(dosTime >> 11),
-               Minute: int(dosTime >> 5 & 0x3f),
-               Second: int(dosTime & 0x1f * 2),
-       }
+               int(dosTime>>11),
+               int(dosTime>>5&0x3f),
+               int(dosTime&0x1f*2),
+               0, // nanoseconds
+
+               time.UTC,
+       )
+}
+
+// timeToMsDosTime converts a time.Time to an MS-DOS date and time.
+// The resolution is 2s.
+// See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx
+func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
+       t = t.In(time.UTC)
+       fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
+       fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)
+       return
+}
+
+// ModTime returns the modification time.
+// The resolution is 2s.
+func (h *FileHeader) ModTime() time.Time {
+       return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
 }
 
-// Mtime_ns returns the modified time in ns since epoch.
+// SetModTime sets the ModifiedTime and ModifiedDate fields to the given time.
 // The resolution is 2s.
-func (h *FileHeader) Mtime_ns() int64 {
-       t := msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
-       return t.Seconds() * 1e9
+func (h *FileHeader) SetModTime(t time.Time) {
+       h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t)
 }
 
+const (
+       // Unix constants. The specification doesn't mention them,
+       // but these seem to be the values agreed on by tools.
+       s_IFMT   = 0xf000
+       s_IFSOCK = 0xc000
+       s_IFLNK  = 0xa000
+       s_IFREG  = 0x8000
+       s_IFBLK  = 0x6000
+       s_IFDIR  = 0x4000
+       s_IFCHR  = 0x2000
+       s_IFIFO  = 0x1000
+       s_ISUID  = 0x800
+       s_ISGID  = 0x400
+       s_ISVTX  = 0x200
+
+       msdosDir      = 0x10
+       msdosReadOnly = 0x01
+)
+
 // Mode returns the permission and mode bits for the FileHeader.
-// An error is returned in case the information is not available.
-func (h *FileHeader) Mode() (mode uint32, err os.Error) {
-       if h.CreatorVersion>>8 == creatorUnix {
-               return h.ExternalAttrs >> 16, nil
+func (h *FileHeader) Mode() (mode os.FileMode) {
+       switch h.CreatorVersion >> 8 {
+       case creatorUnix, creatorMacOSX:
+               mode = unixModeToFileMode(h.ExternalAttrs >> 16)
+       case creatorNTFS, creatorVFAT, creatorFAT:
+               mode = msdosModeToFileMode(h.ExternalAttrs)
+       }
+       if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' {
+               mode |= os.ModeDir
        }
-       return 0, os.NewError("file mode not available")
+       return mode
 }
 
 // SetMode changes the permission and mode bits for the FileHeader.
-func (h *FileHeader) SetMode(mode uint32) {
+func (h *FileHeader) SetMode(mode os.FileMode) {
        h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8
-       h.ExternalAttrs = mode << 16
+       h.ExternalAttrs = fileModeToUnixMode(mode) << 16
+
+       // set MSDOS attributes too, as the original zip does.
+       if mode&os.ModeDir != 0 {
+               h.ExternalAttrs |= msdosDir
+       }
+       if mode&0200 == 0 {
+               h.ExternalAttrs |= msdosReadOnly
+       }
+}
+
+func msdosModeToFileMode(m uint32) (mode os.FileMode) {
+       if m&msdosDir != 0 {
+               mode = os.ModeDir | 0777
+       } else {
+               mode = 0666
+       }
+       if m&msdosReadOnly != 0 {
+               mode &^= 0222
+       }
+       return mode
+}
+
+func fileModeToUnixMode(mode os.FileMode) uint32 {
+       var m uint32
+       switch mode & os.ModeType {
+       default:
+               m = s_IFREG
+       case os.ModeDir:
+               m = s_IFDIR
+       case os.ModeSymlink:
+               m = s_IFLNK
+       case os.ModeNamedPipe:
+               m = s_IFIFO
+       case os.ModeSocket:
+               m = s_IFSOCK
+       case os.ModeDevice:
+               if mode&os.ModeCharDevice != 0 {
+                       m = s_IFCHR
+               } else {
+                       m = s_IFBLK
+               }
+       }
+       if mode&os.ModeSetuid != 0 {
+               m |= s_ISUID
+       }
+       if mode&os.ModeSetgid != 0 {
+               m |= s_ISGID
+       }
+       if mode&os.ModeSticky != 0 {
+               m |= s_ISVTX
+       }
+       return m | uint32(mode&0777)
+}
+
+func unixModeToFileMode(m uint32) os.FileMode {
+       mode := os.FileMode(m & 0777)
+       switch m & s_IFMT {
+       case s_IFBLK:
+               mode |= os.ModeDevice
+       case s_IFCHR:
+               mode |= os.ModeDevice | os.ModeCharDevice
+       case s_IFDIR:
+               mode |= os.ModeDir
+       case s_IFIFO:
+               mode |= os.ModeNamedPipe
+       case s_IFLNK:
+               mode |= os.ModeSymlink
+       case s_IFREG:
+               // nothing to do
+       case s_IFSOCK:
+               mode |= os.ModeSocket
+       }
+       if m&s_ISGID != 0 {
+               mode |= os.ModeSetgid
+       }
+       if m&s_ISUID != 0 {
+               mode |= os.ModeSetuid
+       }
+       if m&s_ISVTX != 0 {
+               mode |= os.ModeSticky
+       }
+       return mode
 }