Initial import package mtools: Programs for accessing MS-DOS disks without mounting...
[profile/ivi/mtools.git] / mmove.c
1 /*  Copyright 1996-1998,2000-2002,2005,2007-2009 Alain Knaff.
2  *  This file is part of mtools.
3  *
4  *  Mtools is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  Mtools is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with Mtools.  If not, see <http://www.gnu.org/licenses/>.
16  *
17  * mmove.c
18  * Renames/moves an MSDOS file
19  *
20  */
21
22
23 #define LOWERCASE
24
25 #include "sysincludes.h"
26 #include "msdos.h"
27 #include "mtools.h"
28 #include "vfat.h"
29 #include "mainloop.h"
30 #include "plain_io.h"
31 #include "nameclash.h"
32 #include "file.h"
33 #include "fs.h"
34
35 /*
36  * Preserve the file modification times after the fclose()
37  */
38
39 typedef struct Arg_t {
40         const char *fromname;
41         int verbose;
42         MainParam_t mp;
43
44         direntry_t *entry;
45         ClashHandling_t ch;
46 } Arg_t;
47
48
49 /*
50  * Open the named file for read, create the cluster chain, return the
51  * directory structure or NULL on error.
52  */
53 static int renameit(dos_name_t *dosname,
54                     char *longname,
55                     void *arg0,
56                     direntry_t *targetEntry)
57 {
58         Arg_t *arg = (Arg_t *) arg0;
59         int fat;
60
61         targetEntry->dir = arg->entry->dir;
62         dosnameToDirentry(dosname, &targetEntry->dir);
63
64         if(IS_DIR(targetEntry)) {
65                 direntry_t *movedEntry;
66
67                 /* get old direntry. It is important that we do this
68                  * on the actual direntry which is stored in the file,
69                  * and not on a copy, because we will modify it, and the
70                  * modification should be visible at file 
71                  * de-allocation time */
72                 movedEntry = getDirentry(arg->mp.File);
73                 if(movedEntry->Dir != targetEntry->Dir) {
74                         /* we are indeed moving it to a new directory */
75                         direntry_t subEntry;
76                         Stream_t *oldDir;
77                         /* we have a directory here. Change its parent link */
78                         
79                         initializeDirentry(&subEntry, arg->mp.File);
80
81                         switch(vfat_lookup(&subEntry, "..", 2, ACCEPT_DIR,
82                                            NULL, NULL)) {
83                             case -1:
84                                 fprintf(stderr,
85                                         " Directory has no parent entry\n");
86                                 break;
87                             case -2:
88                                 return ERROR_ONE;
89                             case 0:
90                                 GET_DATA(targetEntry->Dir, 0, 0, 0, &fat);
91                                 if (fat == fat32RootCluster(targetEntry->Dir)) {
92                                     fat = 0;
93                                 }
94
95                                 subEntry.dir.start[1] = (fat >> 8) & 0xff;
96                                 subEntry.dir.start[0] = fat & 0xff;
97                                 dir_write(&subEntry);
98                                 if(arg->verbose){
99                                         fprintf(stderr,
100                                                 "Easy, isn't it? I wonder why DOS can't do this.\n");
101                                 }
102                                 break;
103                         }
104
105                         wipeEntry(movedEntry);
106                         
107                         /* free the old parent, allocate the new one. */
108                         oldDir = movedEntry->Dir;
109                         *movedEntry = *targetEntry;
110                         COPY(targetEntry->Dir);
111                         FREE(&oldDir);
112                         return 0;
113                 }
114         }
115
116         /* wipe out original entry */
117         wipeEntry(arg->mp.direntry);
118         return 0;
119 }
120
121
122
123 static int rename_file(direntry_t *entry, MainParam_t *mp)
124 /* rename a messy DOS file to another messy DOS file */
125 {
126         int result;
127         Stream_t *targetDir;
128         char *shortname;
129         const char *longname;
130
131         Arg_t * arg = (Arg_t *) (mp->arg);
132
133         arg->entry = entry;
134         targetDir = mp->targetDir;
135
136         if (targetDir == entry->Dir){
137                 arg->ch.ignore_entry = -1;
138                 arg->ch.source = entry->entry;
139                 arg->ch.source_entry = entry->entry;
140         } else {
141                 arg->ch.ignore_entry = -1;
142                 arg->ch.source = -2;
143         }
144
145         longname = mpPickTargetName(mp);
146         shortname = 0;
147         result = mwrite_one(targetDir, longname, shortname,
148                             renameit, (void *)arg, &arg->ch);
149         if(result == 1)
150                 return GOT_ONE;
151         else
152                 return ERROR_ONE;
153 }
154
155
156 static int rename_directory(direntry_t *entry, MainParam_t *mp)
157 {
158         int ret;
159
160         /* moves a DOS dir */
161         if(isSubdirOf(mp->targetDir, mp->File)) {
162                 fprintf(stderr, "Cannot move directory ");
163                 fprintPwd(stderr, entry,0);
164                 fprintf(stderr, " into one of its own subdirectories (");
165                 fprintPwd(stderr, getDirentry(mp->targetDir),0);
166                 fprintf(stderr, ")\n");
167                 return ERROR_ONE;
168         }
169
170         if(entry->entry == -3) {
171                 fprintf(stderr, "Cannot move a root directory: ");
172                 fprintPwd(stderr, entry,0);
173                 return ERROR_ONE;
174         }
175
176         ret = rename_file(entry, mp);
177         if(ret & ERROR_ONE)
178                 return ret;
179         
180         return ret;
181 }
182
183 static int rename_oldsyntax(direntry_t *entry, MainParam_t *mp)
184 {
185         int result;
186         Stream_t *targetDir;
187         const char *shortname, *longname;
188
189         Arg_t * arg = (Arg_t *) (mp->arg);
190         arg->entry = entry;
191         targetDir = entry->Dir;
192
193         arg->ch.ignore_entry = -1;
194         arg->ch.source = entry->entry;
195         arg->ch.source_entry = entry->entry;
196
197 #if 0
198         if(!strcasecmp(mp->shortname, arg->fromname)){
199                 longname = mp->longname;
200                 shortname = mp->targetName;
201         } else {
202 #endif
203                 longname = mp->targetName;
204                 shortname = 0;
205 #if 0
206         }
207 #endif
208         result = mwrite_one(targetDir, longname, shortname,
209                             renameit, (void *)arg, &arg->ch);
210         if(result == 1)
211                 return GOT_ONE;
212         else
213                 return ERROR_ONE;
214 }
215
216
217 static void usage(int ret) NORETURN;
218 static void usage(int ret)
219 {
220         fprintf(stderr,
221                 "Mtools version %s, dated %s\n", mversion, mdate);
222         fprintf(stderr,
223                 "Usage: %s [-vV] [-D clash_option] file targetfile\n", progname);
224         fprintf(stderr,
225                 "       %s [-vV] [-D clash_option] file [files...] target_directory\n", 
226                 progname);
227         exit(ret);
228 }
229
230 void mmove(int argc, char **argv, int oldsyntax)
231 {
232         Arg_t arg;
233         int c;
234         char shortname[13];
235         char longname[VBUFSIZE];
236         char def_drive;
237         int i;
238
239         /* get command line options */
240
241         init_clash_handling(& arg.ch);
242
243         /* get command line options */
244         arg.verbose = 0;
245         if(helpFlag(argc, argv))
246                 usage(0);
247         while ((c = getopt(argc, argv, "i:vD:oh")) != EOF) {
248                 switch (c) {
249                         case 'i':
250                                 set_cmd_line_image(optarg, 0);
251                                 break;
252                         case 'v':       /* dummy option for mcopy */
253                                 arg.verbose = 1;
254                                 break;
255                         case 'o':
256                                 handle_clash_options(&arg.ch, c);
257                                 break;
258                         case 'D':
259                                 if(handle_clash_options(&arg.ch, *optarg))
260                                         usage(1);
261                                 break;
262                         case 'h':
263                                 usage(0);
264                         case '?':
265                                 usage(1);
266                         default:
267                                 break;
268                 }
269         }
270
271         if (argc - optind < 2)
272                 usage(1);
273
274         init_mp(&arg.mp);               
275         arg.mp.arg = (void *) &arg;
276         arg.mp.openflags = O_RDWR;
277
278         /* look for a default drive */
279         def_drive = '\0';
280         for(i=optind; i<argc; i++)
281                 if(argv[i][0] && argv[i][1] == ':' ){
282                         if(!def_drive)
283                                 def_drive = toupper(argv[i][0]);
284                         else if(def_drive != toupper(argv[i][0])){
285                                 fprintf(stderr,
286                                         "Cannot move files across different drives\n");
287                                 exit(1);
288                         }
289                 }
290
291         if(def_drive)
292                 *(arg.mp.mcwd) = def_drive;
293
294         if (oldsyntax && (argc - optind != 2 || strpbrk(":/", argv[argc-1])))
295                 oldsyntax = 0;
296
297         arg.mp.lookupflags =
298           ACCEPT_PLAIN | ACCEPT_DIR | DO_OPEN_DIRS | NO_DOTS | NO_UNIX;
299
300         if (!oldsyntax){
301                 target_lookup(&arg.mp, argv[argc-1]);
302                 arg.mp.callback = rename_file;
303                 arg.mp.dirCallback = rename_directory;
304         } else {
305                 /* do not look up the target; it will be the same dir as the
306                  * source */
307                 arg.fromname = argv[optind];
308                 if(arg.fromname[0] && arg.fromname[1] == ':')
309                         arg.fromname += 2;
310                 arg.fromname = _basename(arg.fromname);
311                 arg.mp.targetName = strdup(argv[argc-1]);
312                 arg.mp.callback = rename_oldsyntax;
313         }
314
315
316         arg.mp.longname = longname;
317         longname[0]='\0';
318
319         arg.mp.shortname = shortname;
320         shortname[0]='\0';
321
322         exit(main_loop(&arg.mp, argv + optind, argc - optind - 1));
323 }