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 39typedef 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 */ 53static 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 123static 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 156static 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 183static 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 217static void usage(int ret) NORETURN; 218static 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 230void 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} 324