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