1 /* Copyright 1995-1998,2000-2003,2005,2007-2009 Alain Knaff.
2 * This file is part of mtools.
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.
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.
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/>.
18 * Make new directory entries, and handles name clashes
23 * This file is used by those commands that need to create new directory entries
26 #include "sysincludes.h"
30 #include "nameclash.h"
34 #include "file_name.h"
37 * Converts input to shortname
38 * @param un unix name (in Unix charset)
40 * @return 1 if name had to be mangled
42 static __inline__ int convert_to_shortname(doscp_t *cp, ClashHandling_t *ch,
43 const char *un, dos_name_t *dn)
47 /* Then do conversion to dn */
48 ch->name_converter(cp, un, 0, &mangled, dn);
53 static __inline__ void chomp(char *line)
56 while(l > 0 && (line[l-1] == '\n' || line[l-1] == '\r')) {
62 * Asks for an alternative new name for a file, in case of a clash
64 static __inline__ int ask_rename(doscp_t *cp, ClashHandling_t *ch,
65 dos_name_t *shortname,
71 /* TODO: Would be nice to suggest "autorenamed" version of name, press
75 fprintf(stderr,"Entering ask_rename, isprimary=%d.\n", isprimary);
81 #define maxsize (isprimary ? MAX_VNAMELEN+1 : 11+1)
82 #define name (isprimary ? argname : shortname)
86 char tname[4*MAX_VNAMELEN+1];
87 fprintf(stderr, "New %s name for \"%s\": ",
88 isprimary ? "primary" : "secondary", longname);
90 if (! fgets(tname, 4*MAX_VNAMELEN+1, opentty(0)))
94 strcpy(longname, tname);
96 mangled = convert_to_shortname(cp,
97 ch, tname, shortname);
98 } while (mangled & 1);
105 * This function determines the action to be taken in case there is a problem
106 * with target name (clash, illegal characters, or reserved)
107 * The decision either comes from the default (ch), or the user will be
108 * prompted if there is no default
110 static __inline__ clash_action ask_namematch(doscp_t *cp,
118 /* User's answer letter (from keyboard). Only first letter is used,
119 * but we allocate space for 10 in order to account for extra garbage
120 * that user may enter
125 * Return value: action to be taken
130 * Should this decision be made permanent (do no longer ask same
136 * Buffer for shortname
138 char name_buffer[4*13];
149 static const char *reasons[]= {
152 "contains illegal character(s)"};
154 a = ch->action[isprimary];
156 if(a == NAMEMATCH_NONE && !opentty(1)) {
157 /* no default, and no tty either . Skip the troublesome file */
158 return NAMEMATCH_SKIP;
162 name = unix_normalize(cp, name_buffer, dosname);
167 while (a == NAMEMATCH_NONE) {
168 fprintf(stderr, "%s file name \"%s\" %s.\n",
169 isprimary ? "Long" : "Short", name, reasons[reason]);
171 "a)utorename A)utorename-all r)ename R)ename-all ");
173 fprintf(stderr,"o)verwrite O)verwrite-all");
175 "\ns)kip S)kip-all q)uit (aArR");
177 fprintf(stderr,"oO");
178 fprintf(stderr,"sSq): ");
181 if (mtools_raw_tty) {
183 rep = fgetc(opentty(1));
190 if(fgets(ans, 9, opentty(0)) == NULL)
193 perm = isupper((unsigned char)ans[0]);
194 switch(tolower((unsigned char)ans[0])) {
196 a = NAMEMATCH_AUTORENAME;
200 a = NAMEMATCH_PRENAME;
202 a = NAMEMATCH_RENAME;
207 a = NAMEMATCH_OVERWRITE;
221 /* Keep track of this action in case this file collides again */
222 ch->action[isprimary] = a;
224 ch->namematch_default[isprimary] = a;
226 /* if we were asked to overwrite be careful. We can't set the action
227 * to overwrite, else we get won't get a chance to specify another
228 * action, should overwrite fail. Indeed, we'll be caught in an
229 * infinite loop because overwrite will fail the same way for the
231 if(a == NAMEMATCH_OVERWRITE)
232 ch->action[isprimary] = NAMEMATCH_NONE;
237 * Processes a name match
238 * dosname short dosname (ignored if is_primary)
242 * 2 if file is to be overwritten
243 * 1 if file was renamed
244 * 0 if it was skipped
246 * If a short name is involved, handle conversion between the 11-character
247 * fixed-length record DOS name and a literal null-terminated name (e.g.
248 * "COMMAND COM" (no null) <-> "COMMAND.COM" (null terminated)).
250 * Also, immediately copy the original name so that messages can use it.
252 static __inline__ clash_action process_namematch(doscp_t *cp,
264 "process_namematch: name=%s, default_action=%d, ask=%d.\n",
265 name, default_action, ch->ask);
268 action = ask_namematch(cp, dosname, longname,
269 isprimary, ch, no_overwrite, reason);
274 return NAMEMATCH_SKIP;
276 return NAMEMATCH_SKIP;
277 case NAMEMATCH_RENAME:
278 case NAMEMATCH_PRENAME:
279 /* We need to rename the file now. This means we must pass
280 * back through the loop, a) ensuring there isn't a potential
281 * new name collision, and b) finding a big enough VSE.
282 * Change the name, so that it won't collide again.
284 ask_rename(cp, ch, dosname, longname, isprimary);
286 case NAMEMATCH_AUTORENAME:
287 /* Very similar to NAMEMATCH_RENAME, except that we need to
288 * first generate the name.
289 * TODO: Remember previous name so we don't
290 * keep trying the same one.
293 autorename_long(longname, 1);
294 return NAMEMATCH_PRENAME;
296 autorename_short(dosname, 1);
297 return NAMEMATCH_RENAME;
299 case NAMEMATCH_OVERWRITE:
301 return NAMEMATCH_SKIP;
303 return NAMEMATCH_OVERWRITE;
305 return NAMEMATCH_NONE;
309 static int contains_illegals(const char *string, const char *illegals,
312 for(; *string && len--; string++)
313 if((*string < ' ' && *string != '\005' && !(*string & 0x80)) ||
314 strchr(illegals, *string))
319 static int is_reserved(char *ans, int islong)
322 static const char *dev3[] = {"CON", "AUX", "PRN", "NUL", " "};
323 static const char *dev4[] = {"COM", "LPT" };
325 for (i = 0; i < sizeof(dev3)/sizeof(*dev3); i++)
326 if (!strncasecmp(ans, dev3[i], 3) &&
327 ((islong && !ans[3]) ||
328 (!islong && !strncmp(ans+3," ",5))))
331 for (i = 0; i < sizeof(dev4)/sizeof(*dev4); i++)
332 if (!strncasecmp(ans, dev4[i], 3) &&
333 (ans[3] >= '1' && ans[3] <= '4') &&
334 ((islong && !ans[4]) ||
335 (!islong && !strncmp(ans+4," ",4))))
341 static __inline__ clash_action get_slots(Stream_t *Dir,
344 struct scan_state *ssp,
354 int pessimisticShortRename;
355 doscp_t *cp = GET_DOSCONVERT(Dir);
357 pessimisticShortRename = (ch->action[0] == NAMEMATCH_AUTORENAME);
361 if((is_reserved(longname,1)) ||
362 longname[strspn(longname,". ")] == '\0'){
365 } else if(contains_illegals(longname,long_illegals,1024)) {
368 } else if(is_reserved(dosname->base,0)) {
370 ch->use_longname = 1;
372 } else if(contains_illegals(dosname->base,short_illegals,11)) {
374 ch->use_longname = 1;
378 switch (lookupForInsert(Dir,
380 dosname, longname, ssp,
383 pessimisticShortRename &&
387 return NAMEMATCH_ERROR;
390 return NAMEMATCH_SKIP;
391 /* Single-file error error or skip request */
394 return NAMEMATCH_GREW;
395 /* Grew directory, try again */
398 return NAMEMATCH_SUCCESS; /* Success */
401 if (ssp->longmatch > -1) {
402 /* Primary Long Name Match */
405 "Got longmatch=%d for name %s.\n",
406 longmatch, longname);
408 match_pos = ssp->longmatch;
410 } else if ((ch->use_longname & 1) && (ssp->shortmatch != -1)) {
411 /* Secondary Short Name Match */
414 "Got secondary short name match for name %s.\n",
418 match_pos = ssp->shortmatch;
420 } else if (ssp->shortmatch >= 0) {
421 /* Primary Short Name Match */
424 "Got primary short name match for name %s.\n",
427 match_pos = ssp->shortmatch;
430 return NAMEMATCH_RENAME;
433 entry.entry = match_pos;
434 dir_read(&entry, &error);
436 return NAMEMATCH_ERROR;
437 /* if we can't overwrite, don't propose it */
438 no_overwrite = (match_pos == ch->source || IS_DIR(&entry));
441 ret = process_namematch(cp, dosname, longname,
442 isprimary, ch, no_overwrite, reason);
444 if (ret == NAMEMATCH_OVERWRITE && match_pos > -1){
445 if((entry.dir.attr & 0x5) &&
446 (ask_confirmation("file is read only, overwrite anyway (y/n) ? ")))
447 return NAMEMATCH_RENAME;
448 /* Free up the file to be overwritten */
449 if(fatFreeWithDirentry(&entry))
450 return NAMEMATCH_ERROR;
454 match_pos - ssp->match_free + 1 >= ssp->size_needed){
455 /* reuse old entry and old short name for overwrite */
456 ssp->free_start = match_pos - ssp->size_needed + 1;
457 ssp->free_size = ssp->size_needed;
458 ssp->slot = match_pos;
460 strncpy(dosname, dir.name, 3);
461 strncpy(dosname + 8, dir.ext, 3);
467 return NAMEMATCH_RENAME;
475 static __inline__ int write_slots(Stream_t *Dir,
478 struct scan_state *ssp,
479 write_data_callback *cb,
490 entry.entry = ssp->slot;
491 native_to_wchar(longname, entry.name, MAX_VNAMELEN, 0, 0);
492 entry.name[MAX_VNAMELEN]='\0';
493 entry.dir.Case = Case & (EXTCASE | BASECASE);
494 if (cb(dosname, longname, arg, &entry) >= 0) {
495 if ((ssp->size_needed > 1) &&
496 (ssp->free_end - ssp->free_start >= ssp->size_needed)) {
497 ssp->slot = write_vfat(Dir, dosname, longname,
498 ssp->free_start, &entry);
500 ssp->size_needed = 1;
501 write_vfat(Dir, dosname, 0,
502 ssp->free_start, &entry);
504 /* clear_vses(Dir, ssp->free_start + ssp->size_needed,
509 return 1; /* Successfully wrote the file */
512 static void stripspaces(char *name)
525 static int _mwrite_one(Stream_t *Dir,
528 write_data_callback *cb,
532 char longname[VBUFSIZE];
536 struct scan_state scan;
538 doscp_t *cp = GET_DOSCONVERT(Dir);
542 if(isSpecial(argname)) {
543 fprintf(stderr, "Cannot create entry named . or ..\n");
547 if(ch->name_converter == dos_name) {
549 stripspaces(shortname);
551 stripspaces(argname);
555 convert_to_shortname(cp, ch, shortname, &dosname);
556 if(ch->use_longname & 1){
557 /* short name mangled, treat it as a long name */
563 if (argname[0] && (argname[1] == ':')) {
564 /* Skip drive letter */
565 dstname = argname + 2;
570 /* Copy original argument dstname to working value longname */
571 strncpy(longname, dstname, VBUFSIZE-1);
575 convert_to_shortname(cp, ch, shortname, &dosname);
576 if(strcmp(shortname, longname))
577 ch->use_longname |= 1;
580 convert_to_shortname(cp, ch, longname, &dosname);
583 ch->action[0] = ch->namematch_default[0];
584 ch->action[1] = ch->namematch_default[1];
587 switch((ret=get_slots(Dir, &dosname, longname, &scan, ch))){
588 case NAMEMATCH_ERROR:
589 return -1; /* Non-file-specific error,
593 return -1; /* Skip file (user request or
596 case NAMEMATCH_PRENAME:
598 convert_to_shortname(cp, ch,
602 case NAMEMATCH_RENAME:
603 continue; /* Renamed file, loop again */
606 /* No collision, and not enough slots.
607 * Try to grow the directory
609 if (expanded) { /* Already tried this
612 "%s: No directory slots\n",
618 if (dir_grow(Dir, scan.max_entry))
621 case NAMEMATCH_OVERWRITE:
622 case NAMEMATCH_SUCCESS:
623 return write_slots(Dir, &dosname, longname,
628 "Internal error: clash_action=%d\n",
636 int mwrite_one(Stream_t *Dir,
637 const char *_argname,
638 const char *_shortname,
639 write_data_callback *cb,
648 argname = strdup(_argname);
652 shortname = strdup(_shortname);
655 ret = _mwrite_one(Dir, argname, shortname, cb, arg, ch);
663 void init_clash_handling(ClashHandling_t *ch)
665 ch->ignore_entry = -1;
666 ch->source_entry = -2;
667 ch->nowarn = 0; /*Don't ask, just do default action if name collision */
668 ch->namematch_default[0] = NAMEMATCH_AUTORENAME;
669 ch->namematch_default[1] = NAMEMATCH_NONE;
670 ch->name_converter = dos_name; /* changed by mlabel */
674 int handle_clash_options(ClashHandling_t *ch, char c)
684 /* Overwrite if primary name matches */
685 ch->namematch_default[isprimary] = NAMEMATCH_OVERWRITE;
688 /* Rename primary name interactively */
689 ch->namematch_default[isprimary] = NAMEMATCH_RENAME;
692 /* Skip file if primary name collides */
693 ch->namematch_default[isprimary] = NAMEMATCH_SKIP;
696 ch->namematch_default[isprimary] = NAMEMATCH_NONE;
699 ch->namematch_default[isprimary] = NAMEMATCH_AUTORENAME;
706 void dosnameToDirentry(const struct dos_name_t *dn, struct directory *dir) {
707 strncpy(dir->name, dn->base, 8);
708 strncpy(dir->ext, dn->ext, 3);