-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstargo.go
203 lines (184 loc) · 3.57 KB
/
stargo.go
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package main
import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"log"
"os"
"os/user"
"path/filepath"
"strconv"
)
type Action func(*tar.Reader) error
func main() {
var action byte
args := os.Args
zflag := false
if len(args) < 2 || len(args[1]) > 2 || len(args[1]) < 1 {
usage()
}
if args[1][0] == 'z' {
if len(args[1]) != 2 {
usage()
} else {
zflag = true
action = args[1][1]
}
} else {
action = args[1][0]
}
switch action {
case 'c':
c(args[2:], zflag)
case 'x':
targo(x, zflag)
case 't':
targo(t, zflag)
default:
usage()
}
}
func usage() {
log.Fatal("stargo [z][cxt] [files]\n")
}
func c(locs []string, zflag bool) {
var f io.Writer
if zflag {
gw := gzip.NewWriter(os.Stdout)
defer gw.Close()
f = gw
} else {
f = os.Stdout
}
tw := tar.NewWriter(f)
defer tw.Close()
c_file := func(loc string, fi os.FileInfo, _ error) error { // needs to be a closure to access the tar writer
hdr, err := tar.FileInfoHeader(fi, loc)
if err != nil {
return err
}
hdr.Name = loc // if we don't do this we only get the basename
tw.WriteHeader(hdr)
target, err := os.Open(loc)
if err != nil {
return err
}
n, err := io.CopyN(tw, target, hdr.Size)
if n != hdr.Size {
if err != nil || err != io.EOF {
return err
}
}
return nil
}
//error checking! also, what about symlinks? Lstat or Stat?
for _, loc := range locs {
if fi, err := os.Stat(loc); err == nil {
if fi.IsDir() {
filepath.Walk(loc, c_file)
} else {
c_file(loc, fi, nil)
}
} else if err == filepath.SkipDir {
log.Println(err)
} else {
panic(err)
}
}
}
func targo(action Action, zflag bool) {
var f io.Reader
if zflag {
gr, err := gzip.NewReader(os.Stdin)
defer gr.Close()
if err != nil {
log.Fatal(err)
}
f = gr
} else {
f = os.Stdin
}
tr := tar.NewReader(f)
if err := action(tr); err != nil {
log.Fatal(err)
}
}
func t(tr *tar.Reader) error {
for {
hdr, err := tr.Next()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
fmt.Printf("%s\n", hdr.Name)
}
return nil //shouldn't get here
}
func x(tr *tar.Reader) error {
u, err := user.Current() // as of know we overwrite the original user with the current.
if err != nil {
return err
}
for {
hdr, err := tr.Next()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
fi := hdr.FileInfo()
switch hdr.Typeflag {
case tar.TypeReg, tar.TypeRegA:
f, err := os.OpenFile(hdr.Name, os.O_CREATE|os.O_WRONLY, fi.Mode()) // don't clobber by default
if err != nil {
return err
}
if _, err := io.CopyN(f, tr, hdr.Size); err != nil {
return err
}
if err := f.Close(); err != nil {
return err
}
case tar.TypeLink:
//hard link
case tar.TypeSymlink:
if err := os.Symlink(hdr.Linkname, hdr.Name); err != nil {
return err
}
continue
case tar.TypeChar:
case tar.TypeBlock:
case tar.TypeDir:
if err := os.MkdirAll(hdr.Name, fi.Mode()); err != nil {
return err
}
case tar.TypeFifo:
case tar.TypeCont:
//reserved
case tar.TypeXHeader:
//extended header
case tar.TypeXGlobalHeader:
//extended global header
default:
log.Printf("Unknown type for %s\n", hdr.Name)
}
//TODO: error checking
uid, _ := strconv.Atoi(u.Uid)
gid, _ := strconv.Atoi(u.Gid)
if err := os.Chown(hdr.Name, uid, gid); err != nil {
return err
}
//TODO: fix ModTime
if err := os.Chmod(hdr.Name, fi.Mode()); err != nil {
return err
}
if err := os.Chtimes(hdr.Name, fi.ModTime(), hdr.ModTime); err != nil {
return err
}
}
return nil //shouldn't get here
}