Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
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
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
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
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
}