Source file
src/cmd/pack/pack.go
1
2
3
4
5 package main
6
7 import (
8 "cmd/internal/archive"
9 "cmd/internal/telemetry/counter"
10 "fmt"
11 "io"
12 "io/fs"
13 "log"
14 "os"
15 "path/filepath"
16 )
17
18 const usageMessage = `Usage: pack op file.a [name....]
19 Where op is one of cprtx optionally followed by v for verbose output.
20 For compatibility with old Go build environments the op string grc is
21 accepted as a synonym for c.
22
23 For more information, run
24 go doc cmd/pack`
25
26 func usage() {
27 fmt.Fprintln(os.Stderr, usageMessage)
28 os.Exit(2)
29 }
30
31 func main() {
32 log.SetFlags(0)
33 log.SetPrefix("pack: ")
34 counter.Open()
35
36 if len(os.Args) < 3 {
37 log.Print("not enough arguments")
38 fmt.Fprintln(os.Stderr)
39 usage()
40 }
41 setOp(os.Args[1])
42 counter.Inc("pack/invocations")
43 counter.Inc("pack/op:" + string(op))
44 var ar *Archive
45 switch op {
46 case 'p':
47 ar = openArchive(os.Args[2], os.O_RDONLY, os.Args[3:])
48 ar.scan(ar.printContents)
49 case 'r':
50 ar = openArchive(os.Args[2], os.O_RDWR|os.O_CREATE, os.Args[3:])
51 ar.addFiles()
52 case 'c':
53 ar = openArchive(os.Args[2], os.O_RDWR|os.O_TRUNC|os.O_CREATE, os.Args[3:])
54 ar.addPkgdef()
55 ar.addFiles()
56 case 't':
57 ar = openArchive(os.Args[2], os.O_RDONLY, os.Args[3:])
58 ar.scan(ar.tableOfContents)
59 case 'x':
60 ar = openArchive(os.Args[2], os.O_RDONLY, os.Args[3:])
61 ar.scan(ar.extractContents)
62 default:
63 log.Printf("invalid operation %q", os.Args[1])
64 fmt.Fprintln(os.Stderr)
65 usage()
66 }
67 if len(ar.files) > 0 {
68 log.Fatalf("file %q not in archive", ar.files[0])
69 }
70 }
71
72
73
74
75
76 var (
77 op rune
78 verbose bool
79 )
80
81
82 func setOp(arg string) {
83
84
85
86
87 if arg == "grc" {
88 arg = "c"
89 }
90
91 for _, r := range arg {
92 switch r {
93 case 'c', 'p', 'r', 't', 'x':
94 if op != 0 {
95
96 usage()
97 }
98 op = r
99 case 'v':
100 if verbose {
101
102 usage()
103 }
104 verbose = true
105 default:
106 usage()
107 }
108 }
109 }
110
111 const (
112 arHeader = "!<arch>\n"
113 )
114
115
116
117 type Archive struct {
118 a *archive.Archive
119 files []string
120 pad int
121 matchAll bool
122 }
123
124
125 func openArchive(name string, mode int, files []string) *Archive {
126 f, err := os.OpenFile(name, mode, 0666)
127 if err != nil {
128 log.Fatal(err)
129 }
130 var a *archive.Archive
131 if mode&os.O_TRUNC != 0 {
132 a, err = archive.New(f)
133 } else {
134 a, err = archive.Parse(f, verbose)
135 if err != nil && mode&os.O_CREATE != 0 {
136 a, err = archive.New(f)
137 }
138 }
139 if err != nil {
140 log.Fatal(err)
141 }
142 return &Archive{
143 a: a,
144 files: files,
145 matchAll: len(files) == 0,
146 }
147 }
148
149
150 func (ar *Archive) scan(action func(*archive.Entry)) {
151 for i := range ar.a.Entries {
152 e := &ar.a.Entries[i]
153 action(e)
154 }
155 }
156
157
158 func listEntry(e *archive.Entry, verbose bool) {
159 if verbose {
160 fmt.Fprintf(stdout, "%s\n", e.String())
161 } else {
162 fmt.Fprintf(stdout, "%s\n", e.Name)
163 }
164 }
165
166
167 func (ar *Archive) output(e *archive.Entry, w io.Writer) {
168 r := io.NewSectionReader(ar.a.File(), e.Offset, e.Size)
169 n, err := io.Copy(w, r)
170 if err != nil {
171 log.Fatal(err)
172 }
173 if n != e.Size {
174 log.Fatal("short file")
175 }
176 }
177
178
179
180 func (ar *Archive) match(e *archive.Entry) bool {
181 if ar.matchAll {
182 return true
183 }
184 for i, name := range ar.files {
185 if e.Name == name {
186 copy(ar.files[i:], ar.files[i+1:])
187 ar.files = ar.files[:len(ar.files)-1]
188 return true
189 }
190 }
191 return false
192 }
193
194
195
196
197 func (ar *Archive) addFiles() {
198 if len(ar.files) == 0 {
199 usage()
200 }
201 for _, file := range ar.files {
202 if verbose {
203 fmt.Printf("%s\n", file)
204 }
205
206 f, err := os.Open(file)
207 if err != nil {
208 log.Fatal(err)
209 }
210 aro, err := archive.Parse(f, false)
211 if err != nil || !isGoCompilerObjFile(aro) {
212 f.Seek(0, io.SeekStart)
213 ar.addFile(f)
214 goto close
215 }
216
217 for _, e := range aro.Entries {
218 if e.Type != archive.EntryGoObj || e.Name != "_go_.o" {
219 continue
220 }
221 ar.a.AddEntry(archive.EntryGoObj, filepath.Base(file), 0, 0, 0, 0644, e.Size, io.NewSectionReader(f, e.Offset, e.Size))
222 }
223 close:
224 f.Close()
225 }
226 ar.files = nil
227 }
228
229
230 type FileLike interface {
231 Name() string
232 Stat() (fs.FileInfo, error)
233 Read([]byte) (int, error)
234 Close() error
235 }
236
237
238 func (ar *Archive) addFile(fd FileLike) {
239
240
241 info, err := fd.Stat()
242 if err != nil {
243 log.Fatal(err)
244 }
245
246 mtime := int64(0)
247 uid := 0
248 gid := 0
249 ar.a.AddEntry(archive.EntryNativeObj, info.Name(), mtime, uid, gid, info.Mode(), info.Size(), fd)
250 }
251
252
253
254
255 func (ar *Archive) addPkgdef() {
256 done := false
257 for _, file := range ar.files {
258 f, err := os.Open(file)
259 if err != nil {
260 log.Fatal(err)
261 }
262 aro, err := archive.Parse(f, false)
263 if err != nil || !isGoCompilerObjFile(aro) {
264 goto close
265 }
266
267 for _, e := range aro.Entries {
268 if e.Type != archive.EntryPkgDef {
269 continue
270 }
271 if verbose {
272 fmt.Printf("__.PKGDEF # %s\n", file)
273 }
274 ar.a.AddEntry(archive.EntryPkgDef, "__.PKGDEF", 0, 0, 0, 0644, e.Size, io.NewSectionReader(f, e.Offset, e.Size))
275 done = true
276 }
277 close:
278 f.Close()
279 if done {
280 break
281 }
282 }
283 }
284
285
286
287
288 var stdout io.Writer = os.Stdout
289
290
291 func (ar *Archive) printContents(e *archive.Entry) {
292 ar.extractContents1(e, stdout)
293 }
294
295
296 func (ar *Archive) tableOfContents(e *archive.Entry) {
297 if ar.match(e) {
298 listEntry(e, verbose)
299 }
300 }
301
302
303 func (ar *Archive) extractContents(e *archive.Entry) {
304 ar.extractContents1(e, nil)
305 }
306
307 func (ar *Archive) extractContents1(e *archive.Entry, out io.Writer) {
308 if ar.match(e) {
309 if verbose {
310 listEntry(e, false)
311 }
312 if out == nil {
313 f, err := os.OpenFile(e.Name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0444 )
314 if err != nil {
315 log.Fatal(err)
316 }
317 defer f.Close()
318 out = f
319 }
320 ar.output(e, out)
321 }
322 }
323
324
325
326
327 func isGoCompilerObjFile(a *archive.Archive) bool {
328 switch len(a.Entries) {
329 case 1:
330 return (a.Entries[0].Type == archive.EntryGoObj && a.Entries[0].Name == "_go_.o") ||
331 (a.Entries[0].Type == archive.EntryPkgDef && a.Entries[0].Name == "__.PKGDEF")
332 case 2:
333 var foundPkgDef, foundGo bool
334 for _, e := range a.Entries {
335 if e.Type == archive.EntryPkgDef && e.Name == "__.PKGDEF" {
336 foundPkgDef = true
337 }
338 if e.Type == archive.EntryGoObj && e.Name == "_go_.o" {
339 foundGo = true
340 }
341 }
342 return foundPkgDef && foundGo
343 default:
344 return false
345 }
346 }
347
View as plain text