| | |
| | |
| | |
| |
|
| | package main |
| |
|
| | import ( |
| | "cmd/internal/archive" |
| | "cmd/internal/telemetry/counter" |
| | "fmt" |
| | "io" |
| | "io/fs" |
| | "log" |
| | "os" |
| | "path/filepath" |
| | ) |
| |
|
| | const usageMessage = `Usage: pack op file.a [name....] |
| | Where op is one of cprtx optionally followed by v for verbose output. |
| | For compatibility with old Go build environments the op string grc is |
| | accepted as a synonym for c. |
| | |
| | For more information, run |
| | go doc cmd/pack` |
| |
|
| | func usage() { |
| | fmt.Fprintln(os.Stderr, usageMessage) |
| | os.Exit(2) |
| | } |
| |
|
| | func main() { |
| | log.SetFlags(0) |
| | log.SetPrefix("pack: ") |
| | counter.Open() |
| | |
| | if len(os.Args) < 3 { |
| | log.Print("not enough arguments") |
| | fmt.Fprintln(os.Stderr) |
| | usage() |
| | } |
| | setOp(os.Args[1]) |
| | counter.Inc("pack/invocations") |
| | counter.Inc("pack/op:" + string(op)) |
| | var ar *Archive |
| | switch op { |
| | case 'p': |
| | ar = openArchive(os.Args[2], os.O_RDONLY, os.Args[3:]) |
| | ar.scan(ar.printContents) |
| | case 'r': |
| | ar = openArchive(os.Args[2], os.O_RDWR|os.O_CREATE, os.Args[3:]) |
| | ar.addFiles() |
| | case 'c': |
| | ar = openArchive(os.Args[2], os.O_RDWR|os.O_TRUNC|os.O_CREATE, os.Args[3:]) |
| | ar.addPkgdef() |
| | ar.addFiles() |
| | case 't': |
| | ar = openArchive(os.Args[2], os.O_RDONLY, os.Args[3:]) |
| | ar.scan(ar.tableOfContents) |
| | case 'x': |
| | ar = openArchive(os.Args[2], os.O_RDONLY, os.Args[3:]) |
| | ar.scan(ar.extractContents) |
| | default: |
| | log.Printf("invalid operation %q", os.Args[1]) |
| | fmt.Fprintln(os.Stderr) |
| | usage() |
| | } |
| | if len(ar.files) > 0 { |
| | log.Fatalf("file %q not in archive", ar.files[0]) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | var ( |
| | op rune |
| | verbose bool |
| | ) |
| |
|
| | |
| | func setOp(arg string) { |
| | |
| | |
| | |
| | |
| | if arg == "grc" { |
| | arg = "c" |
| | } |
| |
|
| | for _, r := range arg { |
| | switch r { |
| | case 'c', 'p', 'r', 't', 'x': |
| | if op != 0 { |
| | |
| | usage() |
| | } |
| | op = r |
| | case 'v': |
| | if verbose { |
| | |
| | usage() |
| | } |
| | verbose = true |
| | default: |
| | usage() |
| | } |
| | } |
| | } |
| |
|
| | const ( |
| | arHeader = "!<arch>\n" |
| | ) |
| |
|
| | |
| | |
| | type Archive struct { |
| | a *archive.Archive |
| | files []string |
| | pad int |
| | matchAll bool |
| | } |
| |
|
| | |
| | func openArchive(name string, mode int, files []string) *Archive { |
| | f, err := os.OpenFile(name, mode, 0666) |
| | if err != nil { |
| | log.Fatal(err) |
| | } |
| | var a *archive.Archive |
| | if mode&os.O_TRUNC != 0 { |
| | a, err = archive.New(f) |
| | } else { |
| | a, err = archive.Parse(f, verbose) |
| | if err != nil && mode&os.O_CREATE != 0 { |
| | a, err = archive.New(f) |
| | } |
| | } |
| | if err != nil { |
| | log.Fatal(err) |
| | } |
| | return &Archive{ |
| | a: a, |
| | files: files, |
| | matchAll: len(files) == 0, |
| | } |
| | } |
| |
|
| | |
| | func (ar *Archive) scan(action func(*archive.Entry)) { |
| | for i := range ar.a.Entries { |
| | e := &ar.a.Entries[i] |
| | action(e) |
| | } |
| | } |
| |
|
| | |
| | func listEntry(e *archive.Entry, verbose bool) { |
| | if verbose { |
| | fmt.Fprintf(stdout, "%s\n", e.String()) |
| | } else { |
| | fmt.Fprintf(stdout, "%s\n", e.Name) |
| | } |
| | } |
| |
|
| | |
| | func (ar *Archive) output(e *archive.Entry, w io.Writer) { |
| | r := io.NewSectionReader(ar.a.File(), e.Offset, e.Size) |
| | n, err := io.Copy(w, r) |
| | if err != nil { |
| | log.Fatal(err) |
| | } |
| | if n != e.Size { |
| | log.Fatal("short file") |
| | } |
| | } |
| |
|
| | |
| | |
| | func (ar *Archive) match(e *archive.Entry) bool { |
| | if ar.matchAll { |
| | return true |
| | } |
| | for i, name := range ar.files { |
| | if e.Name == name { |
| | copy(ar.files[i:], ar.files[i+1:]) |
| | ar.files = ar.files[:len(ar.files)-1] |
| | return true |
| | } |
| | } |
| | return false |
| | } |
| |
|
| | |
| | |
| | |
| | func (ar *Archive) addFiles() { |
| | if len(ar.files) == 0 { |
| | usage() |
| | } |
| | for _, file := range ar.files { |
| | if verbose { |
| | fmt.Printf("%s\n", file) |
| | } |
| |
|
| | f, err := os.Open(file) |
| | if err != nil { |
| | log.Fatal(err) |
| | } |
| | aro, err := archive.Parse(f, false) |
| | if err != nil || !isGoCompilerObjFile(aro) { |
| | f.Seek(0, io.SeekStart) |
| | ar.addFile(f) |
| | goto close |
| | } |
| |
|
| | for _, e := range aro.Entries { |
| | if e.Type != archive.EntryGoObj || e.Name != "_go_.o" { |
| | continue |
| | } |
| | ar.a.AddEntry(archive.EntryGoObj, filepath.Base(file), 0, 0, 0, 0644, e.Size, io.NewSectionReader(f, e.Offset, e.Size)) |
| | } |
| | close: |
| | f.Close() |
| | } |
| | ar.files = nil |
| | } |
| |
|
| | |
| | type FileLike interface { |
| | Name() string |
| | Stat() (fs.FileInfo, error) |
| | Read([]byte) (int, error) |
| | Close() error |
| | } |
| |
|
| | |
| | func (ar *Archive) addFile(fd FileLike) { |
| | |
| | |
| | info, err := fd.Stat() |
| | if err != nil { |
| | log.Fatal(err) |
| | } |
| | |
| | mtime := int64(0) |
| | uid := 0 |
| | gid := 0 |
| | ar.a.AddEntry(archive.EntryNativeObj, info.Name(), mtime, uid, gid, info.Mode(), info.Size(), fd) |
| | } |
| |
|
| | |
| | |
| | |
| | func (ar *Archive) addPkgdef() { |
| | done := false |
| | for _, file := range ar.files { |
| | f, err := os.Open(file) |
| | if err != nil { |
| | log.Fatal(err) |
| | } |
| | aro, err := archive.Parse(f, false) |
| | if err != nil || !isGoCompilerObjFile(aro) { |
| | goto close |
| | } |
| |
|
| | for _, e := range aro.Entries { |
| | if e.Type != archive.EntryPkgDef { |
| | continue |
| | } |
| | if verbose { |
| | fmt.Printf("__.PKGDEF # %s\n", file) |
| | } |
| | ar.a.AddEntry(archive.EntryPkgDef, "__.PKGDEF", 0, 0, 0, 0644, e.Size, io.NewSectionReader(f, e.Offset, e.Size)) |
| | done = true |
| | } |
| | close: |
| | f.Close() |
| | if done { |
| | break |
| | } |
| | } |
| | } |
| |
|
| | |
| |
|
| | |
| | var stdout io.Writer = os.Stdout |
| |
|
| | |
| | func (ar *Archive) printContents(e *archive.Entry) { |
| | ar.extractContents1(e, stdout) |
| | } |
| |
|
| | |
| | func (ar *Archive) tableOfContents(e *archive.Entry) { |
| | if ar.match(e) { |
| | listEntry(e, verbose) |
| | } |
| | } |
| |
|
| | |
| | func (ar *Archive) extractContents(e *archive.Entry) { |
| | ar.extractContents1(e, nil) |
| | } |
| |
|
| | func (ar *Archive) extractContents1(e *archive.Entry, out io.Writer) { |
| | if ar.match(e) { |
| | if verbose { |
| | listEntry(e, false) |
| | } |
| | if out == nil { |
| | f, err := os.OpenFile(e.Name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0444 ) |
| | if err != nil { |
| | log.Fatal(err) |
| | } |
| | defer f.Close() |
| | out = f |
| | } |
| | ar.output(e, out) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | func isGoCompilerObjFile(a *archive.Archive) bool { |
| | switch len(a.Entries) { |
| | case 1: |
| | return (a.Entries[0].Type == archive.EntryGoObj && a.Entries[0].Name == "_go_.o") || |
| | (a.Entries[0].Type == archive.EntryPkgDef && a.Entries[0].Name == "__.PKGDEF") |
| | case 2: |
| | var foundPkgDef, foundGo bool |
| | for _, e := range a.Entries { |
| | if e.Type == archive.EntryPkgDef && e.Name == "__.PKGDEF" { |
| | foundPkgDef = true |
| | } |
| | if e.Type == archive.EntryGoObj && e.Name == "_go_.o" { |
| | foundGo = true |
| | } |
| | } |
| | return foundPkgDef && foundGo |
| | default: |
| | return false |
| | } |
| | } |
| |
|