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