xref: /plan9/sys/src/cmd/mv.c (revision ee17ab8015e4a168cf53c39988aec397b589220f)
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