File: writer.go

package info (click to toggle)
golang-github-mkrautz-goar 0.0~git20150919.282caa8-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, buster, sid
  • size: 112 kB
  • sloc: makefile: 2
file content (180 lines) | stat: -rw-r--r-- 4,290 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// Copyright (c) 2011 Mikkel Krautz
// The use of this source code is goverened by a BSD-style
// license that can be found in the LICENSE-file.

package ar

import (
	"io"
	"strconv"
)

// A Writer provides sequential writing of an ar archive in BSD format. It does not support
// writing in the GNU format, since the GNU-style extended filenames cannot be written sequentially.
// The BSD ar format is widely compatible with most modern ar readers out there.
//
// An ar archive consists of a sequence of files. Call WriteHeader to begin a new file,
// and then call Write to supply that file's data, writing at most hdr.Size bytes in total.
//
// Example:
//	aw := ar.NewWriter(w)
//	hdr := new(ar.Header)
//	hdr.Size = length of data in bytes
//	// populate other hdr fields as desired
//	if err := aw.WriteHeader(hdr); err != nil {
//		// handle error
//	}
//	io.Copy(tw, data)
//	tw.Close()
type Writer struct {
	w          io.Writer
	offset     int64
	dataRemain int64
	closed     bool
}

// NewWriter creates a new Writer writing to w.
func NewWriter(w io.Writer) *Writer {
	return &Writer{w, 0, 0, false}
}

// Closes the ar achive, flushing any unwritten data to the underlying writer.
func (aw *Writer) Close() error {
	err := aw.Flush()
	if err != nil {
		return err
	}
	aw.closed = true
	return nil
}

// Flush finishes writing the current file (optional).
func (aw *Writer) Flush() error {
	if aw.closed {
		return ErrWriteAfterClose
	}
	if aw.offset%2 != 0 {
		_, err := io.WriteString(aw.w, "\n")
		aw.offset += int64(1)
		if err != nil {
			return err
		}
	}
	return nil
}

// Write writes the current entry in the ar archive. Write returns the error ErrWriteTooLong
// if more than hdr.Size bytes are written following a call to WriteHeader.
func (aw *Writer) Write(b []byte) (n int, err error) {
	if aw.closed {
		return 0, ErrWriteAfterClose
	}
	// Overflow check
	tooLong := false
	if int64(len(b)) > aw.dataRemain {
		b = b[0:aw.dataRemain]
		tooLong = true
	}
	n, err = aw.w.Write(b)
	aw.dataRemain -= int64(n)
	// Warn if the write would have overflowed the
	// space set aside for the provided data.
	if err == nil && tooLong {
		err = ErrWriteTooLong
		return
	}
	return
}

// WriteHeader writes hdr and prepares to accept the file's content.  WriteHeader calls Flush to
// correctly pad the last written file. Calling after WriteHeader a Close will return ErrWriteAfterClose.
func (aw *Writer) WriteHeader(hdr *Header) (err error) {
	if aw.closed {
		return ErrWriteAfterClose
	}

	// Flush previous data write
	err = aw.Flush()
	if err != nil {
		return err
	}

	// If we're at the beginning of the writer, write
	// the global header.
	if aw.offset == 0 {
		nwritten, err := io.WriteString(aw.w, globalHeader)
		aw.offset += int64(nwritten)
		if err != nil {
			return err
		}
	}

	var (
		newName string
		newSize int64
	)

	longFn := len(hdr.Name) > 15
	if longFn {
		newName = bsdLongFileNamePrefix + strconv.Itoa(len(hdr.Name))
		newSize = hdr.Size + int64(len(hdr.Name))
	} else {
		newName = hdr.Name
		newSize = hdr.Size
	}

	nwritten, err := io.WriteString(aw.w, encodeArString(newName, 16))
	aw.offset += int64(nwritten)
	if err != nil {
		return err
	}

	nwritten, err = io.WriteString(aw.w, encodeArString(strconv.FormatInt(hdr.Mtime, 10), 12))
	aw.offset += int64(nwritten)
	if err != nil {
		return err
	}

	nwritten, err = io.WriteString(aw.w, encodeArString(strconv.Itoa(hdr.Uid), 6))
	aw.offset += int64(nwritten)
	if err != nil {
		return err
	}

	nwritten, err = io.WriteString(aw.w, encodeArString(strconv.Itoa(hdr.Gid), 6))
	aw.offset += int64(nwritten)
	if err != nil {
		return err
	}

	nwritten, err = io.WriteString(aw.w, encodeArString(strconv.FormatInt(hdr.Mode, 8), 8))
	aw.offset += int64(nwritten)
	if err != nil {
		return err
	}

	nwritten, err = io.WriteString(aw.w, encodeArString(strconv.FormatInt(newSize, 10), 10))
	aw.offset += int64(nwritten)
	if err != nil {
		return err
	}

	nwritten, err = io.WriteString(aw.w, fileHeaderMagic)
	aw.offset += int64(nwritten)
	if err != nil {
		return err
	}

	if longFn {
		nwritten, err = io.WriteString(aw.w, hdr.Name)
		aw.offset += int64(nwritten)
		aw.dataRemain = newSize - int64(nwritten)
		if err != nil {
			return err
		}
	} else {
		aw.dataRemain = newSize
	}

	return nil
}