Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 1 | /* Copyright 1995-1998,2000-2003,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 | * mk_direntry.c |
| 18 | * Make new directory entries, and handles name clashes |
| 19 | * |
| 20 | */ |
| 21 | |
| 22 | /* |
| 23 | * This file is used by those commands that need to create new directory entries |
| 24 | */ |
| 25 | |
| 26 | #include "sysincludes.h" |
| 27 | #include "msdos.h" |
| 28 | #include "mtools.h" |
| 29 | #include "vfat.h" |
| 30 | #include "nameclash.h" |
| 31 | #include "fs.h" |
| 32 | #include "stream.h" |
| 33 | #include "mainloop.h" |
| 34 | #include "file_name.h" |
| 35 | |
| 36 | /** |
| 37 | * Converts input to shortname |
| 38 | * @param un unix name (in Unix charset) |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 39 | * |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 40 | * @return 1 if name had to be mangled |
| 41 | */ |
| 42 | static __inline__ int convert_to_shortname(doscp_t *cp, ClashHandling_t *ch, |
| 43 | const char *un, dos_name_t *dn) |
| 44 | { |
| 45 | int mangled; |
| 46 | |
| 47 | /* Then do conversion to dn */ |
| 48 | ch->name_converter(cp, un, 0, &mangled, dn); |
| 49 | dn->sentinel = '\0'; |
| 50 | if (dn->base[0] == '\xE5') |
| 51 | dn->base[0] = '\x05'; |
| 52 | return mangled; |
| 53 | } |
| 54 | |
| 55 | static __inline__ void chomp(char *line) |
| 56 | { |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 57 | size_t l = strlen(line); |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 58 | while(l > 0 && (line[l-1] == '\n' || line[l-1] == '\r')) { |
| 59 | line[--l] = '\0'; |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | /** |
| 64 | * Asks for an alternative new name for a file, in case of a clash |
| 65 | */ |
| 66 | static __inline__ int ask_rename(doscp_t *cp, ClashHandling_t *ch, |
| 67 | dos_name_t *shortname, |
| 68 | char *longname, |
| 69 | int isprimary) |
| 70 | { |
| 71 | int mangled; |
| 72 | |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 73 | /* TODO: Would be nice to suggest "autorenamed" version of name, press |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 74 | * <Return> to get it. |
| 75 | */ |
| 76 | #if 0 |
| 77 | fprintf(stderr,"Entering ask_rename, isprimary=%d.\n", isprimary); |
| 78 | #endif |
| 79 | |
| 80 | if(!opentty(0)) |
| 81 | return 0; |
| 82 | |
| 83 | mangled = 0; |
| 84 | do { |
| 85 | char tname[4*MAX_VNAMELEN+1]; |
| 86 | fprintf(stderr, "New %s name for \"%s\": ", |
| 87 | isprimary ? "primary" : "secondary", longname); |
| 88 | fflush(stderr); |
| 89 | if (! fgets(tname, 4*MAX_VNAMELEN+1, opentty(0))) |
| 90 | return 0; |
| 91 | chomp(tname); |
| 92 | if (isprimary) |
| 93 | strcpy(longname, tname); |
| 94 | else |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 95 | mangled = convert_to_shortname(cp, |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 96 | ch, tname, shortname); |
| 97 | } while (mangled & 1); |
| 98 | return 1; |
| 99 | } |
| 100 | |
| 101 | /** |
| 102 | * This function determines the action to be taken in case there is a problem |
| 103 | * with target name (clash, illegal characters, or reserved) |
| 104 | * The decision either comes from the default (ch), or the user will be |
| 105 | * prompted if there is no default |
| 106 | */ |
| 107 | static __inline__ clash_action ask_namematch(doscp_t *cp, |
| 108 | dos_name_t *dosname, |
| 109 | char *longname, |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 110 | int isprimary, |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 111 | ClashHandling_t *ch, |
| 112 | int no_overwrite, |
| 113 | int reason) |
| 114 | { |
| 115 | /* User's answer letter (from keyboard). Only first letter is used, |
| 116 | * but we allocate space for 10 in order to account for extra garbage |
| 117 | * that user may enter |
| 118 | */ |
| 119 | char ans[10]; |
| 120 | |
| 121 | /** |
| 122 | * Return value: action to be taken |
| 123 | */ |
| 124 | clash_action a; |
| 125 | |
| 126 | /** |
| 127 | * Should this decision be made permanent (do no longer ask same |
| 128 | * question) |
| 129 | */ |
| 130 | int perm; |
| 131 | |
| 132 | /** |
| 133 | * Buffer for shortname |
| 134 | */ |
| 135 | char name_buffer[4*13]; |
| 136 | |
| 137 | /** |
| 138 | * Name to be printed |
| 139 | */ |
| 140 | char *name; |
| 141 | |
| 142 | #define EXISTS 0 |
| 143 | #define RESERVED 1 |
| 144 | #define ILLEGALS 2 |
| 145 | |
| 146 | static const char *reasons[]= { |
| 147 | "already exists", |
| 148 | "is reserved", |
| 149 | "contains illegal character(s)"}; |
| 150 | |
| 151 | a = ch->action[isprimary]; |
| 152 | |
| 153 | if(a == NAMEMATCH_NONE && !opentty(1)) { |
| 154 | /* no default, and no tty either . Skip the troublesome file */ |
| 155 | return NAMEMATCH_SKIP; |
| 156 | } |
| 157 | |
| 158 | if (!isprimary) |
| 159 | name = unix_normalize(cp, name_buffer, |
| 160 | dosname, sizeof(*dosname)); |
| 161 | else |
| 162 | name = longname; |
| 163 | |
| 164 | perm = 0; |
| 165 | while (a == NAMEMATCH_NONE) { |
| 166 | fprintf(stderr, "%s file name \"%s\" %s.\n", |
| 167 | isprimary ? "Long" : "Short", name, reasons[reason]); |
| 168 | fprintf(stderr, |
| 169 | "a)utorename A)utorename-all r)ename R)ename-all "); |
| 170 | if(!no_overwrite) |
| 171 | fprintf(stderr,"o)verwrite O)verwrite-all"); |
| 172 | fprintf(stderr, |
| 173 | "\ns)kip S)kip-all q)uit (aArR"); |
| 174 | if(!no_overwrite) |
| 175 | fprintf(stderr,"oO"); |
| 176 | fprintf(stderr,"sSq): "); |
| 177 | fflush(stderr); |
| 178 | fflush(opentty(1)); |
| 179 | if (mtools_raw_tty) { |
| 180 | int rep; |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 181 | rep = fgetc(opentty(1)); |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 182 | fputs("\n", stderr); |
| 183 | if(rep == EOF) |
| 184 | ans[0] = 'q'; |
| 185 | else |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 186 | ans[0] = (char) rep; |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 187 | } else { |
| 188 | if(fgets(ans, 9, opentty(0)) == NULL) |
| 189 | ans[0] = 'q'; |
| 190 | } |
| 191 | perm = isupper((unsigned char)ans[0]); |
| 192 | switch(tolower((unsigned char)ans[0])) { |
| 193 | case 'a': |
| 194 | a = NAMEMATCH_AUTORENAME; |
| 195 | break; |
| 196 | case 'r': |
| 197 | if(isprimary) |
| 198 | a = NAMEMATCH_PRENAME; |
| 199 | else |
| 200 | a = NAMEMATCH_RENAME; |
| 201 | break; |
| 202 | case 'o': |
| 203 | if(no_overwrite) |
| 204 | continue; |
| 205 | a = NAMEMATCH_OVERWRITE; |
| 206 | break; |
| 207 | case 's': |
| 208 | a = NAMEMATCH_SKIP; |
| 209 | break; |
| 210 | case 'q': |
| 211 | perm = 0; |
| 212 | a = NAMEMATCH_QUIT; |
| 213 | break; |
| 214 | default: |
| 215 | perm = 0; |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | /* Keep track of this action in case this file collides again */ |
| 220 | ch->action[isprimary] = a; |
| 221 | if (perm) |
| 222 | ch->namematch_default[isprimary] = a; |
| 223 | |
| 224 | /* if we were asked to overwrite be careful. We can't set the action |
| 225 | * to overwrite, else we get won't get a chance to specify another |
| 226 | * action, should overwrite fail. Indeed, we'll be caught in an |
| 227 | * infinite loop because overwrite will fail the same way for the |
| 228 | * second time */ |
| 229 | if(a == NAMEMATCH_OVERWRITE) |
| 230 | ch->action[isprimary] = NAMEMATCH_NONE; |
| 231 | return a; |
| 232 | } |
| 233 | |
| 234 | /* |
| 235 | * Processes a name match |
| 236 | * dosname short dosname (ignored if is_primary) |
| 237 | * |
| 238 | * |
| 239 | * Returns: |
| 240 | * 2 if file is to be overwritten |
| 241 | * 1 if file was renamed |
| 242 | * 0 if it was skipped |
| 243 | * |
| 244 | * If a short name is involved, handle conversion between the 11-character |
| 245 | * fixed-length record DOS name and a literal null-terminated name (e.g. |
| 246 | * "COMMAND COM" (no null) <-> "COMMAND.COM" (null terminated)). |
| 247 | * |
| 248 | * Also, immediately copy the original name so that messages can use it. |
| 249 | */ |
| 250 | static __inline__ clash_action process_namematch(doscp_t *cp, |
| 251 | dos_name_t *dosname, |
| 252 | char *longname, |
| 253 | int isprimary, |
| 254 | ClashHandling_t *ch, |
| 255 | int no_overwrite, |
| 256 | int reason) |
| 257 | { |
| 258 | clash_action action; |
| 259 | |
| 260 | #if 0 |
| 261 | fprintf(stderr, |
| 262 | "process_namematch: name=%s, default_action=%d, ask=%d.\n", |
| 263 | name, default_action, ch->ask); |
| 264 | #endif |
| 265 | |
| 266 | action = ask_namematch(cp, dosname, longname, |
| 267 | isprimary, ch, no_overwrite, reason); |
| 268 | |
| 269 | switch(action){ |
| 270 | case NAMEMATCH_QUIT: |
| 271 | got_signal = 1; |
| 272 | return NAMEMATCH_SKIP; |
| 273 | case NAMEMATCH_SKIP: |
| 274 | return NAMEMATCH_SKIP; |
| 275 | case NAMEMATCH_RENAME: |
| 276 | case NAMEMATCH_PRENAME: |
| 277 | /* We need to rename the file now. This means we must pass |
| 278 | * back through the loop, a) ensuring there isn't a potential |
| 279 | * new name collision, and b) finding a big enough VSE. |
| 280 | * Change the name, so that it won't collide again. |
| 281 | */ |
| 282 | ask_rename(cp, ch, dosname, longname, isprimary); |
| 283 | return action; |
| 284 | case NAMEMATCH_AUTORENAME: |
| 285 | /* Very similar to NAMEMATCH_RENAME, except that we need to |
| 286 | * first generate the name. |
| 287 | * TODO: Remember previous name so we don't |
| 288 | * keep trying the same one. |
| 289 | */ |
| 290 | if (isprimary) { |
| 291 | autorename_long(longname, 1); |
| 292 | return NAMEMATCH_PRENAME; |
| 293 | } else { |
| 294 | autorename_short(dosname, 1); |
| 295 | return NAMEMATCH_RENAME; |
| 296 | } |
| 297 | case NAMEMATCH_OVERWRITE: |
| 298 | if(no_overwrite) |
| 299 | return NAMEMATCH_SKIP; |
| 300 | else |
| 301 | return NAMEMATCH_OVERWRITE; |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 302 | case NAMEMATCH_NONE: |
| 303 | case NAMEMATCH_ERROR: |
| 304 | case NAMEMATCH_SUCCESS: |
| 305 | case NAMEMATCH_GREW: |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 306 | return NAMEMATCH_NONE; |
| 307 | } |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 308 | return action; |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 309 | } |
| 310 | |
| 311 | static int contains_illegals(const char *string, const char *illegals, |
| 312 | int len) |
| 313 | { |
| 314 | for(; *string && len--; string++) |
| 315 | if((*string < ' ' && *string != '\005' && !(*string & 0x80)) || |
| 316 | strchr(illegals, *string)) |
| 317 | return 1; |
| 318 | return 0; |
| 319 | } |
| 320 | |
| 321 | static int is_reserved(char *ans, int islong) |
| 322 | { |
| 323 | unsigned int i; |
| 324 | static const char *dev3[] = {"CON", "AUX", "PRN", "NUL", " "}; |
| 325 | static const char *dev4[] = {"COM", "LPT" }; |
| 326 | |
| 327 | for (i = 0; i < sizeof(dev3)/sizeof(*dev3); i++) |
| 328 | if (!strncasecmp(ans, dev3[i], 3) && |
| 329 | ((islong && !ans[3]) || |
| 330 | (!islong && !strncmp(ans+3," ",5)))) |
| 331 | return 1; |
| 332 | |
| 333 | for (i = 0; i < sizeof(dev4)/sizeof(*dev4); i++) |
| 334 | if (!strncasecmp(ans, dev4[i], 3) && |
| 335 | (ans[3] >= '1' && ans[3] <= '4') && |
| 336 | ((islong && !ans[4]) || |
| 337 | (!islong && !strncmp(ans+4," ",4)))) |
| 338 | return 1; |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 339 | |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 340 | return 0; |
| 341 | } |
| 342 | |
| 343 | static __inline__ clash_action get_slots(Stream_t *Dir, |
| 344 | dos_name_t *dosname, |
| 345 | char *longname, |
| 346 | struct scan_state *ssp, |
| 347 | ClashHandling_t *ch) |
| 348 | { |
| 349 | int error; |
| 350 | clash_action ret; |
| 351 | int match_pos=0; |
| 352 | direntry_t entry; |
| 353 | int isprimary; |
| 354 | int no_overwrite; |
| 355 | int reason; |
| 356 | int pessimisticShortRename; |
| 357 | doscp_t *cp = GET_DOSCONVERT(Dir); |
| 358 | |
| 359 | pessimisticShortRename = (ch->action[0] == NAMEMATCH_AUTORENAME); |
| 360 | |
| 361 | entry.Dir = Dir; |
| 362 | no_overwrite = 1; |
| 363 | if((is_reserved(longname,1)) || |
| 364 | longname[strspn(longname,". ")] == '\0'){ |
| 365 | reason = RESERVED; |
| 366 | isprimary = 1; |
| 367 | } else if(contains_illegals(longname,long_illegals,1024)) { |
| 368 | reason = ILLEGALS; |
| 369 | isprimary = 1; |
| 370 | } else if(is_reserved(dosname->base,0)) { |
| 371 | reason = RESERVED; |
| 372 | ch->use_longname = 1; |
| 373 | isprimary = 0; |
| 374 | } else if(!ch->is_label && |
| 375 | contains_illegals(dosname->base,short_illegals,11)) { |
| 376 | reason = ILLEGALS; |
| 377 | ch->use_longname = 1; |
| 378 | isprimary = 0; |
| 379 | } else { |
| 380 | reason = EXISTS; |
| 381 | switch (lookupForInsert(Dir, |
| 382 | &entry, |
| 383 | dosname, longname, ssp, |
| 384 | ch->ignore_entry, |
| 385 | ch->source_entry, |
| 386 | pessimisticShortRename && |
| 387 | ch->use_longname, |
| 388 | ch->use_longname)) { |
| 389 | case -1: |
| 390 | return NAMEMATCH_ERROR; |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 391 | |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 392 | case 0: |
| 393 | return NAMEMATCH_SKIP; |
| 394 | /* Single-file error error or skip request */ |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 395 | |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 396 | case 5: |
| 397 | return NAMEMATCH_GREW; |
| 398 | /* Grew directory, try again */ |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 399 | |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 400 | case 6: |
| 401 | return NAMEMATCH_SUCCESS; /* Success */ |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 402 | } |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 403 | match_pos = -2; |
| 404 | if (ssp->longmatch > -1) { |
| 405 | /* Primary Long Name Match */ |
| 406 | #ifdef debug |
| 407 | fprintf(stderr, |
| 408 | "Got longmatch=%d for name %s.\n", |
| 409 | longmatch, longname); |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 410 | #endif |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 411 | match_pos = ssp->longmatch; |
| 412 | isprimary = 1; |
| 413 | } else if ((ch->use_longname & 1) && (ssp->shortmatch != -1)) { |
| 414 | /* Secondary Short Name Match */ |
| 415 | #ifdef debug |
| 416 | fprintf(stderr, |
| 417 | "Got secondary short name match for name %s.\n", |
| 418 | longname); |
| 419 | #endif |
| 420 | |
| 421 | match_pos = ssp->shortmatch; |
| 422 | isprimary = 0; |
| 423 | } else if (ssp->shortmatch >= 0) { |
| 424 | /* Primary Short Name Match */ |
| 425 | #ifdef debug |
| 426 | fprintf(stderr, |
| 427 | "Got primary short name match for name %s.\n", |
| 428 | longname); |
| 429 | #endif |
| 430 | match_pos = ssp->shortmatch; |
| 431 | isprimary = 1; |
| 432 | } else |
| 433 | return NAMEMATCH_RENAME; |
| 434 | |
| 435 | if(match_pos > -1) { |
| 436 | entry.entry = match_pos; |
| 437 | dir_read(&entry, &error); |
| 438 | if (error) |
| 439 | return NAMEMATCH_ERROR; |
| 440 | /* if we can't overwrite, don't propose it */ |
| 441 | no_overwrite = (match_pos == ch->source || IS_DIR(&entry)); |
| 442 | } |
| 443 | } |
| 444 | ret = process_namematch(cp, dosname, longname, |
| 445 | isprimary, ch, no_overwrite, reason); |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 446 | |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 447 | if (ret == NAMEMATCH_OVERWRITE && match_pos > -1){ |
| 448 | if((entry.dir.attr & 0x5) && |
| 449 | (ask_confirmation("file is read only, overwrite anyway (y/n) ? "))) |
| 450 | return NAMEMATCH_RENAME; |
| 451 | /* Free up the file to be overwritten */ |
| 452 | if(fatFreeWithDirentry(&entry)) |
| 453 | return NAMEMATCH_ERROR; |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 454 | |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 455 | #if 0 |
| 456 | if(isprimary && |
| 457 | match_pos - ssp->match_free + 1 >= ssp->size_needed){ |
| 458 | /* reuse old entry and old short name for overwrite */ |
| 459 | ssp->free_start = match_pos - ssp->size_needed + 1; |
| 460 | ssp->free_size = ssp->size_needed; |
| 461 | ssp->slot = match_pos; |
| 462 | ssp->got_slots = 1; |
| 463 | strncpy(dosname, dir.name, 3); |
| 464 | strncpy(dosname + 8, dir.ext, 3); |
| 465 | return ret; |
| 466 | } else |
| 467 | #endif |
| 468 | { |
| 469 | wipeEntry(&entry); |
| 470 | return NAMEMATCH_RENAME; |
| 471 | } |
| 472 | } |
| 473 | |
| 474 | return ret; |
| 475 | } |
| 476 | |
| 477 | |
| 478 | static __inline__ int write_slots(Stream_t *Dir, |
| 479 | dos_name_t *dosname, |
| 480 | char *longname, |
| 481 | struct scan_state *ssp, |
| 482 | write_data_callback *cb, |
| 483 | void *arg, |
| 484 | int Case) |
| 485 | { |
| 486 | direntry_t entry; |
| 487 | |
| 488 | /* write the file */ |
| 489 | if (fat_error(Dir)) |
| 490 | return 0; |
| 491 | |
| 492 | entry.Dir = Dir; |
| 493 | entry.entry = ssp->slot; |
| 494 | native_to_wchar(longname, entry.name, MAX_VNAMELEN, 0, 0); |
| 495 | entry.name[MAX_VNAMELEN]='\0'; |
| 496 | entry.dir.Case = Case & (EXTCASE | BASECASE); |
| 497 | if (cb(dosname, longname, arg, &entry) >= 0) { |
| 498 | if ((ssp->size_needed > 1) && |
| 499 | (ssp->free_end - ssp->free_start >= ssp->size_needed)) { |
| 500 | ssp->slot = write_vfat(Dir, dosname, longname, |
| 501 | ssp->free_start, &entry); |
| 502 | } else { |
| 503 | ssp->size_needed = 1; |
| 504 | write_vfat(Dir, dosname, 0, |
| 505 | ssp->free_start, &entry); |
| 506 | } |
| 507 | /* clear_vses(Dir, ssp->free_start + ssp->size_needed, |
| 508 | ssp->free_end); */ |
| 509 | } else |
| 510 | return 0; |
| 511 | |
| 512 | return 1; /* Successfully wrote the file */ |
| 513 | } |
| 514 | |
| 515 | static void stripspaces(char *name) |
| 516 | { |
| 517 | char *p,*non_space; |
| 518 | |
| 519 | non_space = name; |
| 520 | for(p=name; *p; p++) |
| 521 | if (*p != ' ') |
| 522 | non_space = p; |
| 523 | if(name[0]) |
| 524 | non_space[1] = '\0'; |
| 525 | } |
| 526 | |
| 527 | |
| 528 | static int _mwrite_one(Stream_t *Dir, |
| 529 | char *argname, |
| 530 | char *shortname, |
| 531 | write_data_callback *cb, |
| 532 | void *arg, |
| 533 | ClashHandling_t *ch) |
| 534 | { |
| 535 | char longname[VBUFSIZE]; |
| 536 | const char *dstname; |
| 537 | dos_name_t dosname; |
| 538 | int expanded; |
| 539 | struct scan_state scan; |
| 540 | clash_action ret; |
| 541 | doscp_t *cp = GET_DOSCONVERT(Dir); |
| 542 | |
| 543 | expanded = 0; |
| 544 | |
| 545 | if(isSpecial(argname)) { |
| 546 | fprintf(stderr, "Cannot create entry named . or ..\n"); |
| 547 | return -1; |
| 548 | } |
| 549 | |
| 550 | if(ch->name_converter == dos_name) { |
| 551 | if(shortname) |
| 552 | stripspaces(shortname); |
| 553 | if(argname) |
| 554 | stripspaces(argname); |
| 555 | } |
| 556 | |
| 557 | if(shortname){ |
| 558 | convert_to_shortname(cp, ch, shortname, &dosname); |
| 559 | if(ch->use_longname & 1){ |
| 560 | /* short name mangled, treat it as a long name */ |
| 561 | argname = shortname; |
| 562 | shortname = 0; |
| 563 | } |
| 564 | } |
| 565 | |
| 566 | if (argname[0] && (argname[1] == ':')) { |
| 567 | /* Skip drive letter */ |
| 568 | dstname = argname + 2; |
| 569 | } else { |
| 570 | dstname = argname; |
| 571 | } |
| 572 | |
| 573 | /* Copy original argument dstname to working value longname */ |
| 574 | strncpy(longname, dstname, VBUFSIZE-1); |
| 575 | |
| 576 | if(shortname) { |
| 577 | ch->use_longname = |
| 578 | convert_to_shortname(cp, ch, shortname, &dosname); |
| 579 | if(strcmp(shortname, longname)) |
| 580 | ch->use_longname |= 1; |
| 581 | } else { |
| 582 | ch->use_longname = |
| 583 | convert_to_shortname(cp, ch, longname, &dosname); |
| 584 | } |
| 585 | |
| 586 | ch->action[0] = ch->namematch_default[0]; |
| 587 | ch->action[1] = ch->namematch_default[1]; |
| 588 | |
| 589 | while (1) { |
| 590 | switch((ret=get_slots(Dir, &dosname, longname, &scan, ch))){ |
| 591 | case NAMEMATCH_ERROR: |
| 592 | return -1; /* Non-file-specific error, |
| 593 | * quit */ |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 594 | |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 595 | case NAMEMATCH_SKIP: |
| 596 | return -1; /* Skip file (user request or |
| 597 | * error) */ |
| 598 | |
| 599 | case NAMEMATCH_PRENAME: |
| 600 | ch->use_longname = |
| 601 | convert_to_shortname(cp, ch, |
| 602 | longname, |
| 603 | &dosname); |
| 604 | continue; |
| 605 | case NAMEMATCH_RENAME: |
| 606 | continue; /* Renamed file, loop again */ |
| 607 | |
| 608 | case NAMEMATCH_GREW: |
| 609 | /* No collision, and not enough slots. |
| 610 | * Try to grow the directory |
| 611 | */ |
| 612 | if (expanded) { /* Already tried this |
| 613 | * once, no good */ |
| 614 | fprintf(stderr, |
| 615 | "%s: No directory slots\n", |
| 616 | progname); |
| 617 | return -1; |
| 618 | } |
| 619 | expanded = 1; |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 620 | |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 621 | if (dir_grow(Dir, scan.max_entry)) |
| 622 | return -1; |
| 623 | continue; |
| 624 | case NAMEMATCH_OVERWRITE: |
| 625 | case NAMEMATCH_SUCCESS: |
| 626 | return write_slots(Dir, &dosname, longname, |
| 627 | &scan, cb, arg, |
| 628 | ch->use_longname); |
Yi Kong | 39bbd96 | 2022-01-09 19:41:38 +0800 | [diff] [blame] | 629 | case NAMEMATCH_NONE: |
| 630 | case NAMEMATCH_AUTORENAME: |
| 631 | case NAMEMATCH_QUIT: |
Alistair Delva | beaee83 | 2021-02-24 11:27:23 -0800 | [diff] [blame] | 632 | fprintf(stderr, |
| 633 | "Internal error: clash_action=%d\n", |
| 634 | ret); |
| 635 | return -1; |
| 636 | } |
| 637 | |
| 638 | } |
| 639 | } |
| 640 | |
| 641 | int mwrite_one(Stream_t *Dir, |
| 642 | const char *_argname, |
| 643 | const char *_shortname, |
| 644 | write_data_callback *cb, |
| 645 | void *arg, |
| 646 | ClashHandling_t *ch) |
| 647 | { |
| 648 | char *argname; |
| 649 | char *shortname; |
| 650 | int ret; |
| 651 | |
| 652 | if(_argname) |
| 653 | argname = strdup(_argname); |
| 654 | else |
| 655 | argname = 0; |
| 656 | if(_shortname) |
| 657 | shortname = strdup(_shortname); |
| 658 | else |
| 659 | shortname = 0; |
| 660 | ret = _mwrite_one(Dir, argname, shortname, cb, arg, ch); |
| 661 | if(argname) |
| 662 | free(argname); |
| 663 | if(shortname) |
| 664 | free(shortname); |
| 665 | return ret; |
| 666 | } |
| 667 | |
| 668 | void init_clash_handling(ClashHandling_t *ch) |
| 669 | { |
| 670 | ch->ignore_entry = -1; |
| 671 | ch->source_entry = -2; |
| 672 | ch->nowarn = 0; /*Don't ask, just do default action if name collision */ |
| 673 | ch->namematch_default[0] = NAMEMATCH_AUTORENAME; |
| 674 | ch->namematch_default[1] = NAMEMATCH_NONE; |
| 675 | ch->name_converter = dos_name; /* changed by mlabel */ |
| 676 | ch->source = -2; |
| 677 | ch->is_label = 0; |
| 678 | } |
| 679 | |
| 680 | int handle_clash_options(ClashHandling_t *ch, char c) |
| 681 | { |
| 682 | int isprimary; |
| 683 | if(isupper(c)) |
| 684 | isprimary = 0; |
| 685 | else |
| 686 | isprimary = 1; |
| 687 | c = ch_tolower(c); |
| 688 | switch(c) { |
| 689 | case 'o': |
| 690 | /* Overwrite if primary name matches */ |
| 691 | ch->namematch_default[isprimary] = NAMEMATCH_OVERWRITE; |
| 692 | return 0; |
| 693 | case 'r': |
| 694 | /* Rename primary name interactively */ |
| 695 | ch->namematch_default[isprimary] = NAMEMATCH_RENAME; |
| 696 | return 0; |
| 697 | case 's': |
| 698 | /* Skip file if primary name collides */ |
| 699 | ch->namematch_default[isprimary] = NAMEMATCH_SKIP; |
| 700 | return 0; |
| 701 | case 'm': |
| 702 | ch->namematch_default[isprimary] = NAMEMATCH_NONE; |
| 703 | return 0; |
| 704 | case 'a': |
| 705 | ch->namematch_default[isprimary] = NAMEMATCH_AUTORENAME; |
| 706 | return 0; |
| 707 | default: |
| 708 | return -1; |
| 709 | } |
| 710 | } |
| 711 | |
| 712 | void dosnameToDirentry(const struct dos_name_t *dn, struct directory *dir) { |
| 713 | strncpy(dir->name, dn->base, 8); |
| 714 | strncpy(dir->ext, dn->ext, 3); |
| 715 | } |