Copyright 2011 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

package zip

import (
	
	
	
	
	
	
	
	
)

var (
	errLongName  = errors.New("zip: FileHeader.Name too long")
	errLongExtra = errors.New("zip: FileHeader.Extra too long")
)
Writer implements a zip file writer.
testHookCloseSizeOffset if non-nil is called with the size of offset of the central directory at Close.
	testHookCloseSizeOffset func(size, offset uint64)
}

type header struct {
	*FileHeader
	offset uint64
}
NewWriter returns a new Writer writing a zip file to w.
func ( io.Writer) *Writer {
	return &Writer{cw: &countWriter{w: bufio.NewWriter()}}
}
SetOffset sets the offset of the beginning of the zip data within the underlying writer. It should be used when the zip data is appended to an existing file, such as a binary executable. It must be called before any data is written.
func ( *Writer) ( int64) {
	if .cw.count != 0 {
		panic("zip: SetOffset called after data was written")
	}
	.cw.count = 
}
Flush flushes any buffered data to the underlying writer. Calling Flush is not normally necessary; calling Close is sufficient.
func ( *Writer) () error {
	return .cw.w.(*bufio.Writer).Flush()
}
SetComment sets the end-of-central-directory comment field. It can only be called before Close.
func ( *Writer) ( string) error {
	if len() > uint16max {
		return errors.New("zip: Writer.Comment too long")
	}
	.comment = 
	return nil
}
Close finishes writing the zip file by writing the central directory. It does not close the underlying writer.
func ( *Writer) () error {
	if .last != nil && !.last.closed {
		if  := .last.close();  != nil {
			return 
		}
		.last = nil
	}
	if .closed {
		return errors.New("zip: writer closed twice")
	}
	.closed = true
write central directory
the file needs a zip64 header. store maxint in both 32 bit size fields (and offset later) to signal that the zip64 extra header should be used.
			.uint32(uint32max) // compressed size
			.uint32(uint32max) // uncompressed size
append a zip64 extra block to Extra
			var  [28]byte // 2x uint16 + 3x uint64
			 := writeBuf([:])
			.uint16(zip64ExtraID)
			.uint16(24) // size = 3x uint64
			.uint64(.UncompressedSize64)
			.uint64(.CompressedSize64)
			.uint64(.offset)
			.Extra = append(.Extra, [:]...)
		} else {
			.uint32(.CompressedSize)
			.uint32(.UncompressedSize)
		}

		.uint16(uint16(len(.Name)))
		.uint16(uint16(len(.Extra)))
		.uint16(uint16(len(.Comment)))
		 = [4:] // skip disk number start and internal file attr (2x uint16)
		.uint32(.ExternalAttrs)
		if .offset > uint32max {
			.uint32(uint32max)
		} else {
			.uint32(uint32(.offset))
		}
		if ,  := .cw.Write([:]);  != nil {
			return 
		}
		if ,  := io.WriteString(.cw, .Name);  != nil {
			return 
		}
		if ,  := .cw.Write(.Extra);  != nil {
			return 
		}
		if ,  := io.WriteString(.cw, .Comment);  != nil {
			return 
		}
	}
	 := .cw.count

	 := uint64(len(.dir))
	 := uint64( - )
	 := uint64()

	if  := .testHookCloseSizeOffset;  != nil {
		(, )
	}

	if  >= uint16max ||  >= uint32max ||  >= uint32max {
		var  [directory64EndLen + directory64LocLen]byte
		 := writeBuf([:])
zip64 end of central directory record
		.uint32(directory64EndSignature)
		.uint64(directory64EndLen - 12) // length minus signature (uint32) and length fields (uint64)
		.uint16(zipVersion45)           // version made by
		.uint16(zipVersion45)           // version needed to extract
		.uint32(0)                      // number of this disk
		.uint32(0)                      // number of the disk with the start of the central directory
		.uint64()                // total number of entries in the central directory on this disk
		.uint64()                // total number of entries in the central directory
		.uint64()                   // size of the central directory
		.uint64()                 // offset of start of central directory with respect to the starting disk number
zip64 end of central directory locator
		.uint32(directory64LocSignature)
		.uint32(0)           // number of the disk with the start of the zip64 end of central directory
		.uint64(uint64()) // relative offset of the zip64 end of central directory record
		.uint32(1)           // total number of disks

		if ,  := .cw.Write([:]);  != nil {
			return 
		}
store max values in the regular end record to signal that the zip64 values should be used instead
		 = uint16max
		 = uint32max
		 = uint32max
	}
write end record
	var  [directoryEndLen]byte
	 := writeBuf([:])
	.uint32(uint32(directoryEndSignature))
	 = [4:]                        // skip over disk number and first disk number (2x uint16)
	.uint16(uint16())        // number of entries this disk
	.uint16(uint16())        // number of entries total
	.uint32(uint32())           // size of directory
	.uint32(uint32())         // start of directory
	.uint16(uint16(len(.comment))) // byte size of EOCD comment
	if ,  := .cw.Write([:]);  != nil {
		return 
	}
	if ,  := io.WriteString(.cw, .comment);  != nil {
		return 
	}

	return .cw.w.(*bufio.Writer).Flush()
}
Create adds a file to the zip file using the provided name. It returns a Writer to which the file contents should be written. The file contents will be compressed using the Deflate method. The name must be a relative path: it must not start with a drive letter (e.g. C:) or leading slash, and only forward slashes are allowed. To create a directory instead of a file, add a trailing slash to the name. The file's contents must be written to the io.Writer before the next call to Create, CreateHeader, or Close.
func ( *Writer) ( string) (io.Writer, error) {
	 := &FileHeader{
		Name:   ,
		Method: Deflate,
	}
	return .CreateHeader()
}
detectUTF8 reports whether s is a valid UTF-8 string, and whether the string must be considered UTF-8 encoding (i.e., not compatible with CP-437, ASCII, or any other common encoding).
func ( string) (,  bool) {
	for  := 0;  < len(); {
		,  := utf8.DecodeRuneInString([:])
Officially, ZIP uses CP-437, but many readers use the system's local character encoding. Most encoding are compatible with a large subset of CP-437, which itself is ASCII-like. Forbid 0x7e and 0x5c since EUC-KR and Shift-JIS replace those characters with localized currency and overline characters.
		if  < 0x20 ||  > 0x7d ||  == 0x5c {
			if !utf8.ValidRune() || ( == utf8.RuneError &&  == 1) {
				return false, false
			}
			 = true
		}
	}
	return true, 
}
CreateHeader adds a file to the zip archive using the provided FileHeader for the file metadata. Writer takes ownership of fh and may mutate its fields. The caller must not modify fh after calling CreateHeader. This returns a Writer to which the file contents should be written. The file's contents must be written to the io.Writer before the next call to Create, CreateHeader, or Close.
func ( *Writer) ( *FileHeader) (io.Writer, error) {
	if .last != nil && !.last.closed {
		if  := .last.close();  != nil {
			return nil, 
		}
	}
See https://golang.org/issue/11144 confusion.
		return nil, errors.New("archive/zip: invalid duplicate FileHeader")
	}
The ZIP format has a sad state of affairs regarding character encoding. Officially, the name and comment fields are supposed to be encoded in CP-437 (which is mostly compatible with ASCII), unless the UTF-8 flag bit is set. However, there are several problems: * Many ZIP readers still do not support UTF-8. * If the UTF-8 flag is cleared, several readers simply interpret the name and comment fields as whatever the local system encoding is. In order to avoid breaking readers without UTF-8 support, we avoid setting the UTF-8 flag if the strings are CP-437 compatible. However, if the strings require multibyte UTF-8 encoding and is a valid UTF-8 string, then we set the UTF-8 bit. For the case, where the user explicitly wants to specify the encoding as UTF-8, they will need to set the flag bit themselves.
	,  := detectUTF8(.Name)
	,  := detectUTF8(.Comment)
	switch {
	case .NonUTF8:
		.Flags &^= 0x800
	case ( || ) && ( && ):
		.Flags |= 0x800
	}

	.CreatorVersion = .CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte
	.ReaderVersion = zipVersion20
If Modified is set, this takes precedence over MS-DOS timestamp fields.
Contrary to the FileHeader.SetModTime method, we intentionally do not convert to UTC, because we assume the user intends to encode the date using the specified timezone. A user may want this control because many legacy ZIP readers interpret the timestamp according to the local timezone. The timezone is only non-UTC if a user directly sets the Modified field directly themselves. All other approaches sets UTC.
Use "extended timestamp" format since this is what Info-ZIP uses. Nearly every major ZIP implementation uses a different format, but at least most seem to be able to understand the other formats. This format happens to be identical for both local and central header if modification time is the only timestamp being encoded.
		var  [9]byte // 2*SizeOf(uint16) + SizeOf(uint8) + SizeOf(uint32)
		 := uint32(.Modified.Unix())
		 := writeBuf([:])
		.uint16(extTimeExtraID)
		.uint16(5)  // Size: SizeOf(uint8) + SizeOf(uint32)
		.uint8(1)   // Flags: ModTime
		.uint32() // ModTime
		.Extra = append(.Extra, [:]...)
	}

	var (
		 io.Writer
		 *fileWriter
	)
	 := &header{
		FileHeader: ,
		offset:     uint64(.cw.count),
	}

Set the compression method to Store to ensure data length is truly zero, which the writeHeader method always encodes for the size fields. This is necessary as most compression formats have non-zero lengths even when compressing an empty string.
		.Method = Store
		.Flags &^= 0x8 // we will not write a data descriptor
Explicitly clear sizes as they have no meaning for directories.
		.CompressedSize = 0
		.CompressedSize64 = 0
		.UncompressedSize = 0
		.UncompressedSize64 = 0

		 = dirWriter{}
	} else {
		.Flags |= 0x8 // we will write a data descriptor

		 = &fileWriter{
			zipw:      .cw,
			compCount: &countWriter{w: .cw},
			crc32:     crc32.NewIEEE(),
		}
		 := .compressor(.Method)
		if  == nil {
			return nil, ErrAlgorithm
		}
		var  error
		.comp,  = (.compCount)
		if  != nil {
			return nil, 
		}
		.rawCount = &countWriter{w: .comp}
		.header = 
		 = 
	}
	.dir = append(.dir, )
	if  := writeHeader(.cw, );  != nil {
		return nil, 
If we're creating a directory, fw is nil.
	.last = 
	return , nil
}

func ( io.Writer,  *FileHeader) error {
	const  = 1<<16 - 1
	if len(.Name) >  {
		return errLongName
	}
	if len(.Extra) >  {
		return errLongExtra
	}

	var  [fileHeaderLen]byte
	 := writeBuf([:])
	.uint32(uint32(fileHeaderSignature))
	.uint16(.ReaderVersion)
	.uint16(.Flags)
	.uint16(.Method)
	.uint16(.ModifiedTime)
	.uint16(.ModifiedDate)
	.uint32(0) // since we are writing a data descriptor crc32,
	.uint32(0) // compressed size,
	.uint32(0) // and uncompressed size should be zero
	.uint16(uint16(len(.Name)))
	.uint16(uint16(len(.Extra)))
	if ,  := .Write([:]);  != nil {
		return 
	}
	if ,  := io.WriteString(, .Name);  != nil {
		return 
	}
	,  := .Write(.Extra)
	return 
}
RegisterCompressor registers or overrides a custom compressor for a specific method ID. If a compressor for a given method is not found, Writer will default to looking up the compressor at the package level.
func ( *Writer) ( uint16,  Compressor) {
	if .compressors == nil {
		.compressors = make(map[uint16]Compressor)
	}
	.compressors[] = 
}

func ( *Writer) ( uint16) Compressor {
	 := .compressors[]
	if  == nil {
		 = compressor()
	}
	return 
}

type dirWriter struct{}

func (dirWriter) ( []byte) (int, error) {
	if len() == 0 {
		return 0, nil
	}
	return 0, errors.New("zip: write to directory")
}

type fileWriter struct {
	*header
	zipw      io.Writer
	rawCount  *countWriter
	comp      io.WriteCloser
	compCount *countWriter
	crc32     hash.Hash32
	closed    bool
}

func ( *fileWriter) ( []byte) (int, error) {
	if .closed {
		return 0, errors.New("zip: write to closed file")
	}
	.crc32.Write()
	return .rawCount.Write()
}

func ( *fileWriter) () error {
	if .closed {
		return errors.New("zip: file closed twice")
	}
	.closed = true
	if  := .comp.Close();  != nil {
		return 
	}
update FileHeader
Write data descriptor. This is more complicated than one would think, see e.g. comments in zipfile.c:putextended() and http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7073588. The approach here is to write 8 byte sizes if needed without adding a zip64 extra in the local header (too late anyway).
	var  []byte
	if .isZip64() {
		 = make([]byte, dataDescriptor64Len)
	} else {
		 = make([]byte, dataDescriptorLen)
	}
	 := writeBuf()
	.uint32(dataDescriptorSignature) // de-facto standard, required by OS X
	.uint32(.CRC32)
	if .isZip64() {
		.uint64(.CompressedSize64)
		.uint64(.UncompressedSize64)
	} else {
		.uint32(.CompressedSize)
		.uint32(.UncompressedSize)
	}
	,  := .zipw.Write()
	return 
}

type countWriter struct {
	w     io.Writer
	count int64
}

func ( *countWriter) ( []byte) (int, error) {
	,  := .w.Write()
	.count += int64()
	return , 
}

type nopCloser struct {
	io.Writer
}

func ( nopCloser) () error {
	return nil
}

type writeBuf []byte

func ( *writeBuf) ( uint8) {
	(*)[0] = 
	* = (*)[1:]
}

func ( *writeBuf) ( uint16) {
	binary.LittleEndian.PutUint16(*, )
	* = (*)[2:]
}

func ( *writeBuf) ( uint32) {
	binary.LittleEndian.PutUint32(*, )
	* = (*)[4:]
}

func ( *writeBuf) ( uint64) {
	binary.LittleEndian.PutUint64(*, )
	* = (*)[8:]