1 #include <u.h>
2 #include <libc.h>
3
4 int copy1(int fdf, int fdt, char *from, char *to);
5 void hardremove(char *);
6 int mv(char *from, char *todir, char *toelem);
7 int mv1(char *from, Dir *dirb, char *todir, char *toelem);
8 int samefile(char *, char *);
9 void split(char *, char **, char **);
10
11 void
main(int argc,char * argv[])12 main(int argc, char *argv[])
13 {
14 int i, failed;
15 Dir *dirto, *dirfrom;
16 char *todir, *toelem;
17
18 if(argc<3){
19 fprint(2, "usage: mv fromfile tofile\n");
20 fprint(2, " mv fromfile ... todir\n");
21 exits("bad usage");
22 }
23
24 /* prepass to canonicalise names before splitting, etc. */
25 for(i=1; i < argc; i++)
26 cleanname(argv[i]);
27
28 if((dirto = dirstat(argv[argc-1])) != nil && (dirto->mode&DMDIR)){
29 dirfrom = nil;
30 if(argc == 3
31 && (dirfrom = dirstat(argv[1])) != nil
32 && (dirfrom->mode & DMDIR))
33 split(argv[argc-1], &todir, &toelem); /* mv dir1 dir2 */
34 else{ /* mv file... dir */
35 todir = argv[argc-1];
36 toelem = nil; /* toelem will be fromelem */
37 }
38 free(dirfrom);
39 }else
40 split(argv[argc-1], &todir, &toelem); /* mv file1 file2 */
41 free(dirto);
42 if(argc>3 && toelem != nil){
43 fprint(2, "mv: %s not a directory\n", argv[argc-1]);
44 exits("bad usage");
45 }
46
47 failed = 0;
48 for(i=1; i < argc-1; i++)
49 if(mv(argv[i], todir, toelem) < 0)
50 failed++;
51 if(failed)
52 exits("failure");
53 exits(0);
54 }
55
56 int
mv(char * from,char * todir,char * toelem)57 mv(char *from, char *todir, char *toelem)
58 {
59 int stat;
60 Dir *dirb;
61
62 dirb = dirstat(from);
63 if(dirb == nil){
64 fprint(2, "mv: can't stat %s: %r\n", from);
65 return -1;
66 }
67 stat = mv1(from, dirb, todir, toelem);
68 free(dirb);
69 return stat;
70 }
71
72 int
mv1(char * from,Dir * dirb,char * todir,char * toelem)73 mv1(char *from, Dir *dirb, char *todir, char *toelem)
74 {
75 int fdf, fdt, i, j, stat;
76 char toname[4096], fromname[4096];
77 char *fromdir, *fromelem;
78 Dir *dirt, null;
79
80 strncpy(fromname, from, sizeof fromname);
81 split(from, &fromdir, &fromelem);
82 if(toelem == 0)
83 toelem = fromelem;
84 i = strlen(toelem);
85 if(i==0){
86 fprint(2, "mv: null last name element moving %s\n", fromname);
87 return -1;
88 }
89 j = strlen(todir);
90 if(i + j + 2 > sizeof toname){
91 fprint(2, "mv: path too big (max %d): %s/%s\n",
92 sizeof toname, todir, toelem);
93 return -1;
94 }
95 memmove(toname, todir, j);
96 toname[j] = '/';
97 memmove(toname+j+1, toelem, i);
98 toname[i+j+1] = 0;
99
100 if(samefile(fromdir, todir)){
101 if(samefile(fromname, toname)){
102 fprint(2, "mv: %s and %s are the same\n",
103 fromname, toname);
104 return -1;
105 }
106
107 /* remove target if present */
108 dirt = dirstat(toname);
109 if(dirt != nil) {
110 hardremove(toname);
111 free(dirt);
112 }
113
114 /* try wstat */
115 nulldir(&null);
116 null.name = toelem;
117 if(dirwstat(fromname, &null) >= 0)
118 return 0;
119 if(dirb->mode & DMDIR){
120 fprint(2, "mv: can't rename directory %s: %r\n",
121 fromname);
122 return -1;
123 }
124 }
125 /*
126 * Renaming won't work --- must copy
127 */
128 if(dirb->mode & DMDIR){
129 fprint(2, "mv: %s is a directory, not copied to %s\n",
130 fromname, toname);
131 return -1;
132 }
133 fdf = open(fromname, OREAD);
134 if(fdf < 0){
135 fprint(2, "mv: can't open %s: %r\n", fromname);
136 return -1;
137 }
138
139 dirt = dirstat(toname);
140 if(dirt != nil && (dirt->mode & DMAPPEND))
141 hardremove(toname); /* because create() won't truncate file */
142 free(dirt);
143
144 fdt = create(toname, OWRITE, dirb->mode);
145 if(fdt < 0){
146 fprint(2, "mv: can't create %s: %r\n", toname);
147 close(fdf);
148 return -1;
149 }
150 stat = copy1(fdf, fdt, fromname, toname);
151 close(fdf);
152
153 if(stat >= 0){
154 nulldir(&null);
155 null.mtime = dirb->mtime;
156 null.mode = dirb->mode;
157 dirfwstat(fdt, &null); /* ignore errors; e.g. user none always fails */
158 if(remove(fromname) < 0){
159 fprint(2, "mv: can't remove %s: %r\n", fromname);
160 stat = -1;
161 }
162 }
163 close(fdt);
164 return stat;
165 }
166
167 int
copy1(int fdf,int fdt,char * from,char * to)168 copy1(int fdf, int fdt, char *from, char *to)
169 {
170 char buf[8192];
171 long n, n1;
172
173 while ((n = read(fdf, buf, sizeof buf)) > 0) {
174 n1 = write(fdt, buf, n);
175 if(n1 != n){
176 fprint(2, "mv: error writing %s: %r\n", to);
177 return -1;
178 }
179 }
180 if(n < 0){
181 fprint(2, "mv: error reading %s: %r\n", from);
182 return -1;
183 }
184 return 0;
185 }
186
187 void
split(char * name,char ** pdir,char ** pelem)188 split(char *name, char **pdir, char **pelem)
189 {
190 char *s;
191
192 s = utfrrune(name, '/');
193 if(s){
194 *s = 0;
195 *pelem = s+1;
196 *pdir = name;
197 }else if(strcmp(name, "..") == 0){
198 *pdir = "..";
199 *pelem = ".";
200 }else{
201 *pdir = ".";
202 *pelem = name;
203 }
204 }
205
206 int
samefile(char * a,char * b)207 samefile(char *a, char *b)
208 {
209 Dir *da, *db;
210 int ret;
211
212 if(strcmp(a, b) == 0)
213 return 1;
214 da = dirstat(a);
215 db = dirstat(b);
216 ret = (da != nil && db != nil &&
217 da->qid.type==db->qid.type &&
218 da->qid.path==db->qid.path &&
219 da->qid.vers==db->qid.vers &&
220 da->dev==db->dev &&
221 da->type==db->type);
222 free(da);
223 free(db);
224 return ret;
225 }
226
227 void
hardremove(char * a)228 hardremove(char *a)
229 {
230 if(remove(a) == -1){
231 fprint(2, "mv: can't remove %s: %r\n", a);
232 exits("mv");
233 }
234 while(remove(a) != -1)
235 ;
236 }
237