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